swift-im-2.0+dev6/0000755000175000017500000000000012227051774013663 5ustar kismithkismithswift-im-2.0+dev6/scons0000755000175000017500000000006312227051774014735 0ustar kismithkismith#!/bin/sh `dirname $0`/3rdParty/SCons/scons.py $@ swift-im-2.0+dev6/.project0000644000175000017500000000745312227051773015342 0ustar kismithkismith Swift org.eclipse.cdt.managedbuilder.core.genmakebuilder ?name? org.eclipse.cdt.make.core.append_environment true org.eclipse.cdt.make.core.autoBuildTarget org.eclipse.cdt.make.core.buildArguments ${ProjDirPath}/3rdParty/SCons/scons.py org.eclipse.cdt.make.core.buildCommand python org.eclipse.cdt.make.core.cleanBuildTarget -c org.eclipse.cdt.make.core.contents org.eclipse.cdt.make.core.activeConfigSettings org.eclipse.cdt.make.core.enableAutoBuild true org.eclipse.cdt.make.core.enableCleanBuild true org.eclipse.cdt.make.core.enableFullBuild true org.eclipse.cdt.make.core.fullBuildTarget org.eclipse.cdt.make.core.stopOnError true org.eclipse.cdt.make.core.useDefaultBuildCmd false org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder full,incremental, org.eclipse.cdt.core.cnature org.eclipse.cdt.core.ccnature org.eclipse.cdt.managedbuilder.core.managedBuildNature org.eclipse.cdt.managedbuilder.core.ScannerConfigNature 1282241138279 22 org.eclipse.ui.ide.multiFilter 1.0-name-matches-false-false-*.obj 1282241138280 22 org.eclipse.ui.ide.multiFilter 1.0-name-matches-false-false-*.o 1282241138282 22 org.eclipse.ui.ide.multiFilter 1.0-name-matches-false-false-*.a 1282241138283 22 org.eclipse.ui.ide.multiFilter 1.0-name-matches-false-false-*.lib 1282241138306 22 org.eclipse.ui.ide.multiFilter 1.0-name-matches-false-false-*~ 1282241138307 22 org.eclipse.ui.ide.multiFilter 1.0-name-matches-false-false-moc_* 1282241138308 10 org.eclipse.ui.ide.multiFilter 1.0-name-matches-false-false-.sconf_temp swift-im-2.0+dev6/Limber/0000755000175000017500000000000012227051774015075 5ustar kismithkismithswift-im-2.0+dev6/Limber/SConscript0000644000175000017500000000145212227051773017110 0ustar kismithkismithImport("env") if env["SCONS_STAGE"] == "flags" : env["LIMBER_FLAGS"] = { "LIBPATH": [Dir(".")], "LIBS": ["Limber"], } elif env["SCONS_STAGE"] == "build" : libenv = env.Clone() libenv.UseFlags(env["SWIFTEN_FLAGS"]) libenv.UseFlags(env["SWIFTEN_DEP_FLAGS"]) libenv.StaticLibrary("Limber", [ "Server/ServerFromClientSession.cpp", "Server/ServerSession.cpp", "Server/ServerStanzaRouter.cpp", "Server/SimpleUserRegistry.cpp", "Server/UserRegistry.cpp", ]) myenv = env.Clone() myenv.BuildVersion("BuildVersion.h", project = "limber") myenv.UseFlags(env["LIMBER_FLAGS"]) myenv.UseFlags(env["SWIFTEN_FLAGS"]) myenv.UseFlags(env["SWIFTEN_DEP_FLAGS"]) myenv.Program("limber", ["main.cpp"]) env.Append(UNITTEST_SOURCES = [ File("Server/UnitTest/ServerStanzaRouterTest.cpp"), ]) swift-im-2.0+dev6/Limber/Server/0000755000175000017500000000000012227051773016342 5ustar kismithkismithswift-im-2.0+dev6/Limber/Server/ServerSession.cpp0000644000175000017500000000040412227051773021656 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Limber/Server/ServerSession.h" namespace Swift { ServerSession::~ServerSession() { } } swift-im-2.0+dev6/Limber/Server/UserRegistry.h0000644000175000017500000000065212227051773021165 0ustar kismithkismith/* * 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 #include namespace Swift { class JID; class UserRegistry { public: virtual ~UserRegistry(); virtual bool isValidUserPassword(const JID& user, const SafeByteArray& password) const = 0; }; } swift-im-2.0+dev6/Limber/Server/SimpleUserRegistry.h0000644000175000017500000000112412227051773022332 0ustar kismithkismith/* * 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 #include #include #include "Limber/Server/UserRegistry.h" namespace Swift { class SimpleUserRegistry : public UserRegistry { public: SimpleUserRegistry(); virtual bool isValidUserPassword(const JID& user, const SafeByteArray& password) const; void addUser(const JID& user, const std::string& password); private: std::map users; }; } swift-im-2.0+dev6/Limber/Server/ServerSession.h0000644000175000017500000000073212227051773021327 0ustar kismithkismith/* * 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 #include namespace Swift { class ServerSession { public: virtual ~ServerSession(); virtual const JID& getJID() const = 0; virtual int getPriority() const = 0; virtual void sendStanza(boost::shared_ptr) = 0; }; } swift-im-2.0+dev6/Limber/Server/ServerFromClientSession.h0000644000175000017500000000277712227051773023325 0ustar kismithkismith/* * 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 #include #include #include #include #include #include #include namespace Swift { class ProtocolHeader; class Element; class Stanza; class PayloadParserFactoryCollection; class PayloadSerializerCollection; class StreamStack; class UserRegistry; class XMPPLayer; class ConnectionLayer; class Connection; class XMLParserFactory; class ServerFromClientSession : public Session { public: ServerFromClientSession( const std::string& id, boost::shared_ptr connection, PayloadParserFactoryCollection* payloadParserFactories, PayloadSerializerCollection* payloadSerializers, XMLParserFactory* xmlParserFactory, UserRegistry* userRegistry); boost::signal onSessionStarted; void setAllowSASLEXTERNAL(); private: void handleElement(boost::shared_ptr); void handleStreamStart(const ProtocolHeader& header); void setInitialized(); bool isInitialized() const { return initialized; } private: std::string id_; UserRegistry* userRegistry_; bool authenticated_; bool initialized; bool allowSASLEXTERNAL; std::string user_; }; } swift-im-2.0+dev6/Limber/Server/ServerStanzaRouter.cpp0000644000175000017500000000400012227051773022670 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Limber/Server/ServerStanzaRouter.h" #include "Limber/Server/ServerSession.h" #include #include #include namespace Swift { namespace { struct PriorityLessThan { bool operator()(const ServerSession* s1, const ServerSession* s2) const { return s1->getPriority() < s2->getPriority(); } }; struct HasJID { HasJID(const JID& jid) : jid(jid) {} bool operator()(const ServerSession* session) const { return session->getJID().equals(jid, JID::WithResource); } JID jid; }; } ServerStanzaRouter::ServerStanzaRouter() { } bool ServerStanzaRouter::routeStanza(boost::shared_ptr stanza) { JID to = stanza->getTo(); assert(to.isValid()); // For a full JID, first try to route to a session with the full JID if (!to.isBare()) { std::vector::const_iterator i = std::find_if(clientSessions_.begin(), clientSessions_.end(), HasJID(to)); if (i != clientSessions_.end()) { (*i)->sendStanza(stanza); return true; } } // Look for candidate sessions to = to.toBare(); std::vector candidateSessions; for (std::vector::const_iterator i = clientSessions_.begin(); i != clientSessions_.end(); ++i) { if ((*i)->getJID().equals(to, JID::WithoutResource) && (*i)->getPriority() >= 0) { candidateSessions.push_back(*i); } } if (candidateSessions.empty()) { return false; } // Find the session with the highest priority std::vector::const_iterator i = std::max_element(clientSessions_.begin(), clientSessions_.end(), PriorityLessThan()); (*i)->sendStanza(stanza); return true; } void ServerStanzaRouter::addClientSession(ServerSession* clientSession) { clientSessions_.push_back(clientSession); } void ServerStanzaRouter::removeClientSession(ServerSession* clientSession) { erase(clientSessions_, clientSession); } } swift-im-2.0+dev6/Limber/Server/ServerFromClientSession.cpp0000644000175000017500000000767512227051773023662 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Limber/Server/ServerFromClientSession.h" #include #include #include "Swiften/Elements/ProtocolHeader.h" #include "Limber/Server/UserRegistry.h" #include "Swiften/Network/Connection.h" #include "Swiften/StreamStack/XMPPLayer.h" #include "Swiften/Elements/StreamFeatures.h" #include "Swiften/Elements/ResourceBind.h" #include "Swiften/Elements/StartSession.h" #include "Swiften/Elements/IQ.h" #include "Swiften/Elements/AuthSuccess.h" #include "Swiften/Elements/AuthFailure.h" #include "Swiften/Elements/AuthRequest.h" #include "Swiften/SASL/PLAINMessage.h" namespace Swift { ServerFromClientSession::ServerFromClientSession( const std::string& id, boost::shared_ptr connection, PayloadParserFactoryCollection* payloadParserFactories, PayloadSerializerCollection* payloadSerializers, XMLParserFactory* xmlParserFactory, UserRegistry* userRegistry) : Session(connection, payloadParserFactories, payloadSerializers, xmlParserFactory), id_(id), userRegistry_(userRegistry), authenticated_(false), initialized(false), allowSASLEXTERNAL(false) { } void ServerFromClientSession::handleElement(boost::shared_ptr element) { if (isInitialized()) { onElementReceived(element); } else { if (AuthRequest* authRequest = dynamic_cast(element.get())) { if (authRequest->getMechanism() == "PLAIN" || (allowSASLEXTERNAL && authRequest->getMechanism() == "EXTERNAL")) { if (authRequest->getMechanism() == "EXTERNAL") { getXMPPLayer()->writeElement(boost::make_shared()); authenticated_ = true; getXMPPLayer()->resetParser(); } else { PLAINMessage plainMessage(authRequest->getMessage() ? *authRequest->getMessage() : createSafeByteArray("")); if (userRegistry_->isValidUserPassword(JID(plainMessage.getAuthenticationID(), getLocalJID().getDomain()), plainMessage.getPassword())) { getXMPPLayer()->writeElement(boost::make_shared()); user_ = plainMessage.getAuthenticationID(); authenticated_ = true; getXMPPLayer()->resetParser(); } else { getXMPPLayer()->writeElement(boost::shared_ptr(new AuthFailure)); finishSession(AuthenticationFailedError); } } } else { getXMPPLayer()->writeElement(boost::shared_ptr(new AuthFailure)); finishSession(NoSupportedAuthMechanismsError); } } else if (IQ* iq = dynamic_cast(element.get())) { if (boost::shared_ptr resourceBind = iq->getPayload()) { setRemoteJID(JID(user_, getLocalJID().getDomain(), resourceBind->getResource())); boost::shared_ptr resultResourceBind(new ResourceBind()); resultResourceBind->setJID(getRemoteJID()); getXMPPLayer()->writeElement(IQ::createResult(JID(), iq->getID(), resultResourceBind)); } else if (iq->getPayload()) { getXMPPLayer()->writeElement(IQ::createResult(getRemoteJID(), iq->getID())); setInitialized(); } } } } void ServerFromClientSession::handleStreamStart(const ProtocolHeader& incomingHeader) { setLocalJID(JID("", incomingHeader.getTo())); ProtocolHeader header; header.setFrom(incomingHeader.getTo()); header.setID(id_); getXMPPLayer()->writeHeader(header); boost::shared_ptr features(new StreamFeatures()); if (!authenticated_) { features->addAuthenticationMechanism("PLAIN"); if (allowSASLEXTERNAL) { features->addAuthenticationMechanism("EXTERNAL"); } } else { features->setHasResourceBind(); features->setHasSession(); } getXMPPLayer()->writeElement(features); } void ServerFromClientSession::setInitialized() { initialized = true; onSessionStarted(); } void ServerFromClientSession::setAllowSASLEXTERNAL() { allowSASLEXTERNAL = true; } } swift-im-2.0+dev6/Limber/Server/UserRegistry.cpp0000644000175000017500000000040112227051773021510 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Limber/Server/UserRegistry.h" namespace Swift { UserRegistry::~UserRegistry() { } } swift-im-2.0+dev6/Limber/Server/SimpleUserRegistry.cpp0000644000175000017500000000121712227051773022670 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Limber/Server/SimpleUserRegistry.h" namespace Swift { SimpleUserRegistry::SimpleUserRegistry() { } bool SimpleUserRegistry::isValidUserPassword(const JID& user, const SafeByteArray& password) const { std::map::const_iterator i = users.find(user); return i != users.end() ? i->second == password : false; } void SimpleUserRegistry::addUser(const JID& user, const std::string& password) { users.insert(std::make_pair(user, createSafeByteArray(password))); } } swift-im-2.0+dev6/Limber/Server/UnitTest/0000755000175000017500000000000012227051773020121 5ustar kismithkismithswift-im-2.0+dev6/Limber/Server/UnitTest/ServerStanzaRouterTest.cpp0000644000175000017500000001163712227051773025325 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include "Swiften/Elements/Message.h" #include "Limber/Server/ServerStanzaRouter.h" #include "Limber/Server/ServerSession.h" using namespace Swift; class ServerStanzaRouterTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(ServerStanzaRouterTest); CPPUNIT_TEST(testRouteStanza_FullJID); CPPUNIT_TEST(testRouteStanza_FullJIDWithNegativePriority); CPPUNIT_TEST(testRouteStanza_FullJIDWithOnlyBareJIDMatchingSession); CPPUNIT_TEST(testRouteStanza_BareJIDWithoutMatchingSession); CPPUNIT_TEST(testRouteStanza_BareJIDWithMultipleSessions); CPPUNIT_TEST(testRouteStanza_BareJIDWithOnlyNegativePriorities); CPPUNIT_TEST(testRouteStanza_BareJIDWithChangingPresence); CPPUNIT_TEST_SUITE_END(); public: ServerStanzaRouterTest() {} void setUp() { } void tearDown() { } void testRouteStanza_FullJID() { ServerStanzaRouter testling; MockServerSession session1(JID("foo@bar.com/Bla"), 0); testling.addClientSession(&session1); MockServerSession session2(JID("foo@bar.com/Baz"), 0); testling.addClientSession(&session2); bool result = testling.routeStanza(createMessageTo("foo@bar.com/Baz")); CPPUNIT_ASSERT(result); CPPUNIT_ASSERT_EQUAL(0, static_cast(session1.sentStanzas.size())); CPPUNIT_ASSERT_EQUAL(1, static_cast(session2.sentStanzas.size())); } void testRouteStanza_FullJIDWithNegativePriority() { ServerStanzaRouter testling; MockServerSession session1(JID("foo@bar.com/Bla"), -1); testling.addClientSession(&session1); MockServerSession session2(JID("foo@bar.com/Baz"), 0); testling.addClientSession(&session2); bool result = testling.routeStanza(createMessageTo("foo@bar.com/Bla")); CPPUNIT_ASSERT(result); CPPUNIT_ASSERT_EQUAL(1, static_cast(session1.sentStanzas.size())); CPPUNIT_ASSERT_EQUAL(0, static_cast(session2.sentStanzas.size())); } void testRouteStanza_FullJIDWithOnlyBareJIDMatchingSession() { ServerStanzaRouter testling; MockServerSession session(JID("foo@bar.com/Bla"), 0); testling.addClientSession(&session); bool result = testling.routeStanza(createMessageTo("foo@bar.com/Baz")); CPPUNIT_ASSERT(result); CPPUNIT_ASSERT_EQUAL(1, static_cast(session.sentStanzas.size())); } void testRouteStanza_BareJIDWithoutMatchingSession() { ServerStanzaRouter testling; bool result = testling.routeStanza(createMessageTo("foo@bar.com")); CPPUNIT_ASSERT(!result); } void testRouteStanza_BareJIDWithMultipleSessions() { ServerStanzaRouter testling; MockServerSession session1(JID("foo@bar.com/Bla"), 1); testling.addClientSession(&session1); MockServerSession session2(JID("foo@bar.com/Baz"), 8); testling.addClientSession(&session2); MockServerSession session3(JID("foo@bar.com/Bar"), 5); testling.addClientSession(&session3); bool result = testling.routeStanza(createMessageTo("foo@bar.com")); CPPUNIT_ASSERT(result); CPPUNIT_ASSERT_EQUAL(0, static_cast(session1.sentStanzas.size())); CPPUNIT_ASSERT_EQUAL(1, static_cast(session2.sentStanzas.size())); CPPUNIT_ASSERT_EQUAL(0, static_cast(session3.sentStanzas.size())); } void testRouteStanza_BareJIDWithOnlyNegativePriorities() { ServerStanzaRouter testling; MockServerSession session(JID("foo@bar.com/Bla"), -1); testling.addClientSession(&session); bool result = testling.routeStanza(createMessageTo("foo@bar.com")); CPPUNIT_ASSERT(!result); } void testRouteStanza_BareJIDWithChangingPresence() { ServerStanzaRouter testling; MockServerSession session1(JID("foo@bar.com/Baz"), 8); testling.addClientSession(&session1); MockServerSession session2(JID("foo@bar.com/Bar"), 5); testling.addClientSession(&session2); session1.priority = 3; session2.priority = 4; bool result = testling.routeStanza(createMessageTo("foo@bar.com")); CPPUNIT_ASSERT(result); CPPUNIT_ASSERT_EQUAL(0, static_cast(session1.sentStanzas.size())); CPPUNIT_ASSERT_EQUAL(1, static_cast(session2.sentStanzas.size())); } private: boost::shared_ptr createMessageTo(const std::string& recipient) { boost::shared_ptr message(new Message()); message->setTo(JID(recipient)); return message; } class MockServerSession : public ServerSession { public: MockServerSession(const JID& jid, int priority) : jid(jid), priority(priority) {} virtual const JID& getJID() const { return jid; } virtual int getPriority() const { return priority; } virtual void sendStanza(boost::shared_ptr stanza) { sentStanzas.push_back(stanza); } JID jid; int priority; std::vector< boost::shared_ptr > sentStanzas; }; }; CPPUNIT_TEST_SUITE_REGISTRATION(ServerStanzaRouterTest); swift-im-2.0+dev6/Limber/Server/ServerStanzaRouter.h0000644000175000017500000000112412227051773022341 0ustar kismithkismith/* * 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 #include #include #include namespace Swift { class ServerSession; class ServerStanzaRouter { public: ServerStanzaRouter(); bool routeStanza(boost::shared_ptr); void addClientSession(ServerSession*); void removeClientSession(ServerSession*); private: std::vector clientSessions_; }; } swift-im-2.0+dev6/Limber/main.cpp0000644000175000017500000001054612227051773016532 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include "Swiften/Elements/IQ.h" #include "Swiften/Elements/RosterPayload.h" #include "Swiften/Elements/VCard.h" #include "Swiften/Base/IDGenerator.h" #include "Swiften/EventLoop/EventLoop.h" #include "Swiften/EventLoop/SimpleEventLoop.h" #include "Swiften/EventLoop/EventOwner.h" #include "Swiften/Elements/Stanza.h" #include "Swiften/Network/ConnectionServer.h" #include "Swiften/Network/BoostConnection.h" #include "Swiften/Network/BoostIOServiceThread.h" #include "Swiften/Network/BoostConnectionServer.h" #include "Limber/Server/SimpleUserRegistry.h" #include "Limber/Server/ServerFromClientSession.h" #include "Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.h" #include "Swiften/Parser/PlatformXMLParserFactory.h" #include "Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.h" using namespace Swift; class Server { public: Server(UserRegistry* userRegistry, EventLoop* eventLoop) : userRegistry_(userRegistry) { serverFromClientConnectionServer_ = BoostConnectionServer::create(5222, boostIOServiceThread_.getIOService(), eventLoop); serverFromClientConnectionServer_->onNewConnection.connect(boost::bind(&Server::handleNewConnection, this, _1)); serverFromClientConnectionServer_->start(); } private: void handleNewConnection(boost::shared_ptr c) { boost::shared_ptr session(new ServerFromClientSession(idGenerator_.generateID(), c, &payloadParserFactories_, &payloadSerializers_, &xmlParserFactory, userRegistry_)); serverFromClientSessions_.push_back(session); session->onElementReceived.connect(boost::bind(&Server::handleElementReceived, this, _1, session)); session->onSessionFinished.connect(boost::bind(&Server::handleSessionFinished, this, session)); session->startSession(); } void handleSessionFinished(boost::shared_ptr session) { serverFromClientSessions_.erase(std::remove(serverFromClientSessions_.begin(), serverFromClientSessions_.end(), session), serverFromClientSessions_.end()); } void handleElementReceived(boost::shared_ptr element, boost::shared_ptr session) { boost::shared_ptr stanza(boost::dynamic_pointer_cast(element)); if (!stanza) { return; } stanza->setFrom(session->getRemoteJID()); if (!stanza->getTo().isValid()) { stanza->setTo(JID(session->getLocalJID())); } if (!stanza->getTo().isValid() || stanza->getTo() == session->getLocalJID() || stanza->getTo() == session->getRemoteJID().toBare()) { if (boost::shared_ptr iq = boost::dynamic_pointer_cast(stanza)) { if (iq->getPayload()) { session->sendElement(IQ::createResult(iq->getFrom(), iq->getID(), boost::make_shared())); } if (iq->getPayload()) { if (iq->getType() == IQ::Get) { boost::shared_ptr vcard(new VCard()); vcard->setNickname(iq->getFrom().getNode()); session->sendElement(IQ::createResult(iq->getFrom(), iq->getID(), vcard)); } else { session->sendElement(IQ::createError(iq->getFrom(), iq->getID(), ErrorPayload::Forbidden, ErrorPayload::Cancel)); } } else { session->sendElement(IQ::createError(iq->getFrom(), iq->getID(), ErrorPayload::FeatureNotImplemented, ErrorPayload::Cancel)); } } } } private: IDGenerator idGenerator_; PlatformXMLParserFactory xmlParserFactory; UserRegistry* userRegistry_; BoostIOServiceThread boostIOServiceThread_; boost::shared_ptr serverFromClientConnectionServer_; std::vector< boost::shared_ptr > serverFromClientSessions_; FullPayloadParserFactoryCollection payloadParserFactories_; FullPayloadSerializerCollection payloadSerializers_; }; int main() { SimpleEventLoop eventLoop; SimpleUserRegistry userRegistry; userRegistry.addUser(JID("remko@localhost"), "remko"); userRegistry.addUser(JID("kevin@localhost"), "kevin"); userRegistry.addUser(JID("remko@limber.swift.im"), "remko"); userRegistry.addUser(JID("kevin@limber.swift.im"), "kevin"); Server server(&userRegistry, &eventLoop); eventLoop.run(); return 0; } swift-im-2.0+dev6/Documentation/0000755000175000017500000000000012227051773016473 5ustar kismithkismithswift-im-2.0+dev6/Documentation/SConscript0000644000175000017500000000017212227051773020505 0ustar kismithkismithImport("env") if env["SCONS_STAGE"] == "build" : SConscript(dirs = ["SwiftenDevelopersGuide", "SwiftUserGuide", "API"]) swift-im-2.0+dev6/Documentation/Licenses/0000755000175000017500000000000012227051773020240 5ustar kismithkismithswift-im-2.0+dev6/Documentation/Licenses/BSD-simplified.txt0000644000175000017500000000240112227051773023531 0ustar kismithkismithCopyright (c) , All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. swift-im-2.0+dev6/Documentation/Licenses/GPLv3.txt0000644000175000017500000010451312227051773021700 0ustar kismithkismith GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . swift-im-2.0+dev6/Documentation/BuildingOnUnix.txt0000644000175000017500000000144312227051773022134 0ustar kismithkismithPrerequisites ------------ - GCC - Python - OpenSSL - Qt Open Source Edition (optional; not needed for Swiften) Building -------- - Create a file 'config.py' with the following contents, reflecting your local setup: qt = "path/to/qt" (e.g. qt = "/usr/share/qt4") - Run ./scons - To build only a subdir, add the path as a target to scons. E.g., for Swift: ./scons Swift Running tests ------------- - Run ./scons test=unit for running the unit tests. Installing ---------- - To install swift in /usr/local, run ./scons SWIFT_INSTALLDIR=/usr/local /usr/local Swiften-only ------------ - To compile just Swiften, add Swiften to the end of the scons command ./scons Swiften - To compile and install only Swiften, use SWIFTEN_INSTALLDIR ./scons SWIFTEN_INSTALLDIR=/usr/local /usr/local swift-im-2.0+dev6/Documentation/BuildingOnWindows.txt0000644000175000017500000000300312227051773022635 0ustar kismithkismithPrerequisites ------------ - Microsoft Visual C++ Express Edition - Windows SDK - Python (2.5 <= version < 3) - OpenSSL * OpenSSL is optional - without it the Windows platform crypto will be used * Download and extract the Windows binary version of OpenSSL from http://www.slproweb.com/products/Win32OpenSSL.html - Qt Open Source Edition (optional; not needed for Swiften) Building Qt for Microsoft Visual C++ ------------------------------------ - These steps are optional - the pre-packaged Qt is fine - From the 'Visual C++' 'Programs' group, launch the Visual C++ command prompt - Go to the dir where you installed Qt - Configure Qt: configure - Build Qt: nmake Building Swift -------------- - From the 'Visual C++' 'Programs' group, launch the Visual C++ command prompt - Go to the Swift source dir - Create a file 'config.py' with the following contents, reflecting your local setup: openssl = "path\to\openssl" #optional qt = "path\to\qt" - Run 'scons' - To build only a subdir, add the path as a target to scons. E.g., for Swift: scons Swift Running tests ------------- - Run scons test=unit for running the unit tests, or scons test=all for running all tests. Packaging Swift --------------- For packaging use: - Microsoft Visual C++ Express 2008 - No OpenSSL - WiX - config.py should contain: qt = "c:\\qt\\4.7.4" vcredist = "c:\\Program Files\\Common Files\\Merge Modules" debug = 1 optimize = 1 wix_bindir = "c:\\program files\\Windows Installer XML v3.5\\bin" - run scons dist=1 swift-im-2.0+dev6/Documentation/SwiftUserGuide/0000755000175000017500000000000012227051774021405 5ustar kismithkismithswift-im-2.0+dev6/Documentation/SwiftUserGuide/SConscript0000644000175000017500000000066512227051773023425 0ustar kismithkismithImport("env") env.Tool("DocBook", toolpath = ["#/BuildTools/DocBook/SCons"]) ################################################################################ # Code generation helper ################################################################################ import sys, re, os.path ################################################################################ if "doc" in ARGUMENTS : env.DocBook("Swift Users Guide.xml") swift-im-2.0+dev6/Documentation/SwiftUserGuide/Swift Users Guide.xml0000644000175000017500000000436512227051773025332 0ustar kismithkismith Swift User Guide Introduction
Swift Swift is a chat client using the XMPP protocol. This document describes use of the client for end users; for a further description of the protocol read XMPP: The Definitive Guide or for development using XMPP see the Swiften Developer's Guide
Getting Started Managing Contacts Chatting to Contacts Chatting in Rooms (MUCs) Room (MUC) Administration Server Configuration Eagle Mode Bibliography XMPP-TDG <ulink url="http://oreilly.com/catalog/9780596157197/">XMPP: The Definitive Guide</ulink> Peter Saint-Andre Kevin Smith Remko Tronçon Swiften-dev <ulink url='http://swift.im/swiften/guide/'>Swiften Developer's Guide</ulink> Remko Tronçon
swift-im-2.0+dev6/Documentation/API/0000755000175000017500000000000012227051774017105 5ustar kismithkismithswift-im-2.0+dev6/Documentation/API/SConscript0000644000175000017500000000022712227051773021117 0ustar kismithkismithImport("env") if "doc" in ARGUMENTS : myenv = env.Clone() myenv.Tool("DoxyGen", toolpath = ["#/BuildTools/SCons/Tools"]) myenv.DoxyGen("Doxyfile") swift-im-2.0+dev6/Documentation/API/Doxyfile0000644000175000017500000002327012227051773020616 0ustar kismithkismith#--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- DOXYFILE_ENCODING = UTF-8 PROJECT_NAME = Swiften PROJECT_NUMBER = OUTPUT_DIRECTORY = Documentation/API CREATE_SUBDIRS = NO OUTPUT_LANGUAGE = English BRIEF_MEMBER_DESC = YES REPEAT_BRIEF = YES ABBREVIATE_BRIEF = ALWAYS_DETAILED_SEC = NO INLINE_INHERITED_MEMB = YES FULL_PATH_NAMES = YES STRIP_FROM_PATH = STRIP_FROM_INC_PATH = SHORT_NAMES = NO JAVADOC_AUTOBRIEF = YES QT_AUTOBRIEF = NO MULTILINE_CPP_IS_BRIEF = NO INHERIT_DOCS = YES SEPARATE_MEMBER_PAGES = NO TAB_SIZE = 2 ALIASES = OPTIMIZE_OUTPUT_FOR_C = NO OPTIMIZE_OUTPUT_JAVA = NO OPTIMIZE_FOR_FORTRAN = NO OPTIMIZE_OUTPUT_VHDL = NO BUILTIN_STL_SUPPORT = YES CPP_CLI_SUPPORT = NO SIP_SUPPORT = NO IDL_PROPERTY_SUPPORT = YES DISTRIBUTE_GROUP_DOC = NO SUBGROUPING = YES TYPEDEF_HIDES_STRUCT = NO #SYMBOL_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- EXTRACT_ALL = YES EXTRACT_PRIVATE = NO EXTRACT_STATIC = YES EXTRACT_LOCAL_CLASSES = YES EXTRACT_LOCAL_METHODS = NO EXTRACT_ANON_NSPACES = NO HIDE_UNDOC_MEMBERS = NO HIDE_UNDOC_CLASSES = NO HIDE_FRIEND_COMPOUNDS = NO HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = NO CASE_SENSE_NAMES = NO HIDE_SCOPE_NAMES = NO SHOW_INCLUDE_FILES = NO INLINE_INFO = YES SORT_MEMBER_DOCS = YES SORT_BRIEF_DOCS = NO SORT_GROUP_NAMES = NO SORT_BY_SCOPE_NAME = NO GENERATE_TODOLIST = YES GENERATE_TESTLIST = YES GENERATE_BUGLIST = YES GENERATE_DEPRECATEDLIST= YES ENABLED_SECTIONS = MAX_INITIALIZER_LINES = 30 SHOW_USED_FILES = NO SHOW_DIRECTORIES = NO SHOW_FILES = NO SHOW_NAMESPACES = NO FILE_VERSION_FILTER = #LAYOUT_FILE = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- QUIET = YES WARNINGS = YES WARN_IF_UNDOCUMENTED = YES WARN_IF_DOC_ERROR = YES WARN_NO_PARAMDOC = NO WARN_FORMAT = "$file:$line: $text " WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- INPUT = \ Swiften/Avatars \ Swiften/Base \ Swiften/Chat \ Swiften/Client/Client.h \ Swiften/Client/CoreClient.h \ Swiften/Client/ClientError.h \ Swiften/Client/StanzaChannel.h \ Swiften/Client/NickResolver.h \ Swiften/Client/Storages.h \ Swiften/Client/MemoryStorages.h \ Swiften/Client/FileStorages.h \ Swiften/Component/Component.h \ Swiften/Component/CoreComponent.h \ Swiften/Disco \ Swiften/Elements \ Swiften/EventLoop \ Swiften/JID \ Swiften/MUC \ Swiften/Presence \ Swiften/Queries \ Swiften/Roster/XMPPRoster.h \ Swiften/StringCodecs \ Swiften/TLS/BlindCertificateTrustChecker.h \ Swiften/TLS/Certificate.h \ Swiften/TLS/CertificateTrustChecker.h \ Swiften/VCards INPUT_ENCODING = UTF-8 FILE_PATTERNS = *.h RECURSIVE = YES EXCLUDE = EXCLUDE_SYMLINKS = NO EXCLUDE_PATTERNS = */3rdParty/* */UnitTest/* */QA/* EXCLUDE_SYMBOLS = EXAMPLE_PATH = EXAMPLE_PATTERNS = EXAMPLE_RECURSIVE = NO IMAGE_PATH = INPUT_FILTER = FILTER_PATTERNS = FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- SOURCE_BROWSER = YES INLINE_SOURCES = NO STRIP_CODE_COMMENTS = YES REFERENCED_BY_RELATION = YES REFERENCES_RELATION = YES REFERENCES_LINK_SOURCE = YES USE_HTAGS = NO VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- COLS_IN_ALPHA_INDEX = 5 IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- GENERATE_HTML = YES HTML_OUTPUT = HTML_FILE_EXTENSION = .html HTML_HEADER = HTML_FOOTER = HTML_STYLESHEET = HTML_ALIGN_MEMBERS = YES HTML_DYNAMIC_SECTIONS = YES GENERATE_DOCSET = YES DOCSET_FEEDNAME = "Swiften API Documentation" DOCSET_BUNDLE_ID = im.swift.Swiften GENERATE_HTMLHELP = YES CHM_FILE = HHC_LOCATION = GENERATE_CHI = NO CHM_INDEX_ENCODING = BINARY_TOC = NO TOC_EXPAND = NO #GENERATE_QHP = NO #QCH_FILE = #QHP_NAMESPACE = im.swift.Swiften #QHP_VIRTUAL_FOLDER = doc #QHG_LOCATION = DISABLE_INDEX = NO ENUM_VALUES_PER_LINE = 4 GENERATE_TREEVIEW = YES TREEVIEW_WIDTH = 250 FORMULA_FONTSIZE = 10 #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- GENERATE_LATEX = NO LATEX_OUTPUT = LATEX_CMD_NAME = latex MAKEINDEX_CMD_NAME = makeindex COMPACT_LATEX = NO PAPER_TYPE = a4wide EXTRA_PACKAGES = LATEX_HEADER = PDF_HYPERLINKS = YES USE_PDFLATEX = NO LATEX_BATCHMODE = NO LATEX_HIDE_INDICES = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- GENERATE_RTF = NO RTF_OUTPUT = COMPACT_RTF = NO RTF_HYPERLINKS = NO RTF_STYLESHEET_FILE = RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- GENERATE_MAN = NO MAN_OUTPUT = MAN_EXTENSION = .3 MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- GENERATE_XML = NO XML_OUTPUT = xml XML_SCHEMA = XML_DTD = XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- GENERATE_PERLMOD = NO PERLMOD_LATEX = NO PERLMOD_PRETTY = YES PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- ENABLE_PREPROCESSING = YES MACRO_EXPANSION = YES EXPAND_ONLY_PREDEF = YES SEARCH_INCLUDES = YES INCLUDE_PATH = INCLUDE_FILE_PATTERNS = PREDEFINED = EXPAND_AS_DEFINED = SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- TAGFILES = GENERATE_TAGFILE = ALLEXTERNALS = NO EXTERNAL_GROUPS = YES PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- CLASS_DIAGRAMS = YES MSCGEN_PATH = HIDE_UNDOC_RELATIONS = YES HAVE_DOT = YES DOT_FONTNAME = FreeSans DOT_FONTPATH = CLASS_GRAPH = YES COLLABORATION_GRAPH = YES GROUP_GRAPHS = YES UML_LOOK = NO TEMPLATE_RELATIONS = YES INCLUDE_GRAPH = NO INCLUDED_BY_GRAPH = NO CALL_GRAPH = NO CALLER_GRAPH = NO GRAPHICAL_HIERARCHY = YES DIRECTORY_GRAPH = NO DOT_IMAGE_FORMAT = png DOT_PATH = DOTFILE_DIRS = DOT_GRAPH_MAX_NODES = 50 MAX_DOT_GRAPH_DEPTH = 0 DOT_TRANSPARENT = NO DOT_MULTI_TARGETS = NO GENERATE_LEGEND = YES DOT_CLEANUP = YES #--------------------------------------------------------------------------- # Configuration::additions related to the search engine #--------------------------------------------------------------------------- SEARCHENGINE = YES swift-im-2.0+dev6/Documentation/BuildingGenerics.txt0000644000175000017500000000021512227051773022447 0ustar kismithkismithTo cause scons to search for dependencies instead of using cached results, add force-configure=1 to the commandline: scons force-configure=1 swift-im-2.0+dev6/Documentation/SwiftenDevelopersGuide/0000755000175000017500000000000012227051774023122 5ustar kismithkismithswift-im-2.0+dev6/Documentation/SwiftenDevelopersGuide/SConscript0000644000175000017500000000632712227051773025143 0ustar kismithkismithImport("env") env.Tool("DocBook", toolpath = ["#/BuildTools/DocBook/SCons"]) ################################################################################ # Code generation helper ################################################################################ import sys, re, os.path def generateDocBookCode(env, target, source) : # Strips empty lines from the beginning & end of a program def stripEmptyLines(program) : programLines = program.split('\n') newProgramLines = [] inProgram = False for line in programLines : if not re.match("^\s*$", line) or inProgram : inProgram = True newProgramLines.append(line) return '\n'.join(newProgramLines).rstrip() def createCallouts(program, calloutPrefix) : newProgramLines = [] calloutLines = [] nextID = 0 for line in program.split("\n") : # FIXME: Takes the largest match m = re.match(".*\/* \(\*\) (.*) \*/.*", line) if m : cobID = "cob-" + calloutPrefix + "-" + str(nextID) coID = "co-" + calloutPrefix + "-" + str(nextID) nextID += 1 line = re.sub("/\*.*\*/", "]]>%(text)s" % {"cobID": cobID, "coID": coID, "text": m.group(1)}) newProgramLines.append(line) callouts = "" + "\n".join(calloutLines) + "" if len(calloutLines) > 0 else "" return ("\n".join(newProgramLines), callouts) # Parse program filename = source[0].abspath filenameBase = os.path.basename(filename).replace(".cpp", "") inputfile = open(filename) program = "" programs = {} programName = "" inEllipsis = False for line in inputfile.readlines() : if inEllipsis : if "//..." in line : inEllipsis = False else : if line.startswith("/*") or line.startswith(" *") : continue if "//..." in line : inEllipsis = True line = line.replace("//...", "]]>…" + callouts + "" # Generate code output = open(target[0].abspath, 'w') output.write(document) output.close() ################################################################################ if "doc" in ARGUMENTS : env.DocBook("Swiften Developers Guide.xml") sources = [] for i in range(1, 7) : sources.append("Examples/EchoBot/EchoBot" + str(i) + ".cpp") sources.append("Examples/EchoBot/EchoBot0x.cpp") sources += ["Examples/EchoBot/" + i for i in ["EchoPayloadParserFactory.h", "EchoPayloadSerializer.h", "EchoPayload.h", "EchoComponent.cpp"]] for source in sources : env.Command(source + ".xml", source, Action(generateDocBookCode, cmdstr = "$GENCOMSTR")) SConscript(dirs = ["Examples"]) swift-im-2.0+dev6/Documentation/SwiftenDevelopersGuide/documentation.css0000644000175000017500000000074012227051773026505 0ustar kismithkismithhtml{ font-family: "Lucida Grande", "Segoe UI", sans-serif; color: #111; background: #EEE; font-size: 62.5%; } body{ width: 640px; float: left; margin: 20px; padding: 10px 30px; font-size: 1.3em; line-height: 1.5em; } a{ color: #669; } a:visited{ color: #444; } a:hover{ color: #000; } pre { background: #EEE; padding: 5px 20px 5px 20px; } p.remark { background: yellow; } div.toc { border-bottom: thin solid #AAA; } swift-im-2.0+dev6/Documentation/SwiftenDevelopersGuide/Examples/0000755000175000017500000000000012227051773024677 5ustar kismithkismithswift-im-2.0+dev6/Documentation/SwiftenDevelopersGuide/Examples/SConscript0000644000175000017500000000003712227051773026711 0ustar kismithkismithSConscript(dirs = ["EchoBot"]) swift-im-2.0+dev6/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/0000755000175000017500000000000012227051774026223 5ustar kismithkismithswift-im-2.0+dev6/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/SConscript0000644000175000017500000000146512227051773030242 0ustar kismithkismithImport("env") example_env = env.Clone() example_env.UseFlags(example_env["SWIFTEN_FLAGS"]) example_env.UseFlags(example_env["SWIFTEN_DEP_FLAGS"]) for i in range(1,7) : example_env.Program("EchoBot" + str(i), ["EchoBot" + str(i) + ".cpp"]) example_env.Program("EchoComponent", "EchoComponent.cpp") # C++0x cpp0x = False cpp0x_env = example_env.Clone() if env["PLATFORM"] == "win32" : if int(env["MSVS_VERSION"].split(".")[0]) >= 10 : cpp0x = True else : if env["CCVERSION"].split(".") >= ["4", "5", "0"] : # Temporarily disabling c++0x mode because of problems with boost::thread # on some platforms #cpp0x = True cpp0x_env.Replace(CXXFLAGS = [flag for flag in env["CXXFLAGS"] if flag != "-Werror"]) cpp0x_env.Append(CXXFLAGS = ["-std=c++0x"]) if cpp0x : cpp0x_env.Program("EchoBot0x", "EchoBot0x.cpp") swift-im-2.0+dev6/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoPayloadSerializer.h0000644000175000017500000000110512227051773032612 0ustar kismithkismith/* * 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 #include "EchoPayload.h" class EchoPayloadSerializer : public Swift::GenericPayloadSerializer { public: std::string serializePayload(boost::shared_ptr payload) const { XMLElement element("echo", "http://swift.im/protocol/echo"); element.addNode(XMLTextNode::ref(new XMLTextNode(payload->getMessage()))); return element.serialize(); } }; swift-im-2.0+dev6/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoBot2.cpp0000644000175000017500000000201412227051773030330 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include using namespace Swift; using namespace boost; Client* client; void handleConnected(); void handleMessageReceived(Message::ref message); int main(int, char**) { SimpleEventLoop eventLoop; BoostNetworkFactories networkFactories(&eventLoop); client = new Client("echobot@wonderland.lit", "mypass", &networkFactories); client->setAlwaysTrustCertificates(); client->onConnected.connect(&handleConnected); client->onMessageReceived.connect(bind(&handleMessageReceived, _1)); client->connect(); eventLoop.run(); delete client; return 0; } void handleConnected() { std::cout << "Connected" << std::endl; } void handleMessageReceived(Message::ref message) { // Echo back the incoming message message->setTo(message->getFrom()); message->setFrom(JID()); client->sendMessage(message); } swift-im-2.0+dev6/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoBot0x.cpp0000644000175000017500000000140312227051773030517 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include using namespace Swift; int main(int, char**) { // Set up the event loop and network classes SimpleEventLoop eventLoop; BoostNetworkFactories networkFactories(&eventLoop); Client client("echobot@wonderland.lit", "mypass", &networkFactories); client.setAlwaysTrustCertificates(); client.onConnected.connect([&] { std::cout << "Connected" << std::endl; }); client.onMessageReceived.connect([&] (Message::ref message) { message->setTo(message->getFrom()); message->setFrom(JID()); client.sendMessage(message); }); client.connect(); eventLoop.run(); return 0; } swift-im-2.0+dev6/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoBot6.cpp0000644000175000017500000000637412227051773030351 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ //... #include #include #include #include using namespace Swift; using namespace boost; //... #include "EchoPayload.h" #include "EchoPayloadParserFactory.h" #include "EchoPayloadSerializer.h" class EchoBot { public: EchoBot(NetworkFactories* networkFactories) { //... client = new Client("echobot@wonderland.lit", "mypass", networkFactories); client->setAlwaysTrustCertificates(); client->onConnected.connect(bind(&EchoBot::handleConnected, this)); client->onMessageReceived.connect( bind(&EchoBot::handleMessageReceived, this, _1)); client->onPresenceReceived.connect( bind(&EchoBot::handlePresenceReceived, this, _1)); tracer = new ClientXMLTracer(client); softwareVersionResponder = new SoftwareVersionResponder(client->getIQRouter()); softwareVersionResponder->setVersion("EchoBot", "1.0"); softwareVersionResponder->start(); //... client->addPayloadParserFactory(&echoPayloadParserFactory); client->addPayloadSerializer(&echoPayloadSerializer); //... client->connect(); //... } ~EchoBot() { client->removePayloadSerializer(&echoPayloadSerializer); client->removePayloadParserFactory(&echoPayloadParserFactory); //... softwareVersionResponder->stop(); delete softwareVersionResponder; delete tracer; delete client; //... } //... private: void handlePresenceReceived(Presence::ref presence) { // Automatically approve subscription requests if (presence->getType() == Presence::Subscribe) { Presence::ref response = Presence::create(); response->setTo(presence->getFrom()); response->setType(Presence::Subscribed); client->sendPresence(response); } } void handleConnected() { // Request the roster GetRosterRequest::ref rosterRequest = GetRosterRequest::create(client->getIQRouter()); rosterRequest->onResponse.connect( bind(&EchoBot::handleRosterReceived, this, _2)); rosterRequest->send(); } void handleRosterReceived(ErrorPayload::ref error) { if (error) { std::cerr << "Error receiving roster. Continuing anyway."; } // Send initial available presence client->sendPresence(Presence::create("Send me a message")); } //... void handleMessageReceived(Message::ref message) { //... // Echo back the incoming message message->setTo(message->getFrom()); message->setFrom(JID()); //... if (!message->getPayload()) { boost::shared_ptr echoPayload = boost::make_shared(); echoPayload->setMessage("This is an echoed message"); message->addPayload(echoPayload); client->sendMessage(message); } } //... //... private: //... Client* client; ClientXMLTracer* tracer; SoftwareVersionResponder* softwareVersionResponder; //... EchoPayloadParserFactory echoPayloadParserFactory; EchoPayloadSerializer echoPayloadSerializer; }; //... int main(int, char**) { SimpleEventLoop eventLoop; BoostNetworkFactories networkFactories(&eventLoop); EchoBot bot(&networkFactories); eventLoop.run(); return 0; } //... swift-im-2.0+dev6/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoPayloadParserFactory.h0000644000175000017500000000205012227051773033265 0ustar kismithkismith/* * 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 #include "EchoPayload.h" class EchoPayloadParser : public Swift::GenericPayloadParser { public: EchoPayloadParser() : currentDepth(0) {} void handleStartElement( const std::string& /* element */, const std::string& /* ns */, const AttributeMap&) { currentDepth++; } void handleEndElement(const std::string& /* element */, const std::string& /* ns */) { currentDepth--; if (currentDepth == 0) { getPayloadInternal()->setMessage(currentText); } } void handleCharacterData(const std::string& data) { currentText += data; } private: int currentDepth; std::string currentText; }; class EchoPayloadParserFactory : public Swift::GenericPayloadParserFactory { public: EchoPayloadParserFactory() : GenericPayloadParserFactory("echo", "http://swift.im/echo") {} }; swift-im-2.0+dev6/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoPayload.h0000644000175000017500000000072712227051773030571 0ustar kismithkismith/* * 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 //... class EchoPayload : public Swift::Payload { public: EchoPayload() {} const std::string& getMessage() const { return message; } void setMessage(const std::string& message) { this->message = message; } private: std::string message; }; swift-im-2.0+dev6/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoBot5.cpp0000644000175000017500000000501312227051773030335 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ //... #include #include #include using namespace Swift; using namespace boost; //... class EchoBot { public: EchoBot(NetworkFactories* networkFactories) { //... client = new Client("echobot@wonderland.lit", "mypass", networkFactories); client->setAlwaysTrustCertificates(); client->onConnected.connect(bind(&EchoBot::handleConnected, this)); client->onMessageReceived.connect( bind(&EchoBot::handleMessageReceived, this, _1)); client->onPresenceReceived.connect( bind(&EchoBot::handlePresenceReceived, this, _1)); tracer = new ClientXMLTracer(client); //... softwareVersionResponder = new SoftwareVersionResponder(client->getIQRouter()); softwareVersionResponder->setVersion("EchoBot", "1.0"); softwareVersionResponder->start(); //... client->connect(); //... } ~EchoBot() { softwareVersionResponder->stop(); delete softwareVersionResponder; //... delete tracer; delete client; //... } //... private: void handlePresenceReceived(Presence::ref presence) { // Automatically approve subscription requests if (presence->getType() == Presence::Subscribe) { Presence::ref response = Presence::create(); response->setTo(presence->getFrom()); response->setType(Presence::Subscribed); client->sendPresence(response); } } void handleConnected() { // Request the roster GetRosterRequest::ref rosterRequest = GetRosterRequest::create(client->getIQRouter()); rosterRequest->onResponse.connect( bind(&EchoBot::handleRosterReceived, this, _2)); rosterRequest->send(); } void handleRosterReceived(ErrorPayload::ref error) { if (error) { std::cerr << "Error receiving roster. Continuing anyway."; } // Send initial available presence client->sendPresence(Presence::create("Send me a message")); } void handleMessageReceived(Message::ref message) { // Echo back the incoming message message->setTo(message->getFrom()); message->setFrom(JID()); client->sendMessage(message); } //... private: //... Client* client; ClientXMLTracer* tracer; //... SoftwareVersionResponder* softwareVersionResponder; }; //... int main(int, char**) { SimpleEventLoop eventLoop; BoostNetworkFactories networkFactories(&eventLoop); EchoBot bot(&networkFactories); eventLoop.run(); return 0; } //... swift-im-2.0+dev6/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoBot1.cpp0000644000175000017500000000074112227051773030334 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include using namespace Swift; int main(int, char**) { SimpleEventLoop eventLoop; BoostNetworkFactories networkFactories(&eventLoop); Client client("echobot@wonderland.lit", "mypass", &networkFactories); client.setAlwaysTrustCertificates(); client.connect(); eventLoop.run(); return 0; } swift-im-2.0+dev6/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoComponent.cpp0000644000175000017500000000341412227051773031471 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include using namespace Swift; using namespace boost; class EchoComponent { public: EchoComponent(EventLoop* eventLoop, NetworkFactories* networkFactories) : jid("echo.wonderland.lit") { component = new Component(eventLoop, networkFactories, jid, "EchoSecret"); component->onConnected.connect(bind(&EchoComponent::handleConnected, this)); component->onMessageReceived.connect( bind(&EchoComponent::handleMessageReceived, this, _1)); component->onPresenceReceived.connect( bind(&EchoComponent::handlePresenceReceived, this, _1)); tracer = new ComponentXMLTracer(component); component->connect("wonderland.lit", 5347); } ~EchoComponent() { delete tracer; delete component; } private: void handlePresenceReceived(Presence::ref presence) { // Automatically approve subscription requests if (presence->getType() == Presence::Subscribe) { Presence::ref response = Presence::create(); response->setTo(presence->getFrom()); response->setType(Presence::Subscribed); component->sendPresence(response); } } void handleConnected() { } void handleMessageReceived(Message::ref message) { // Echo back the incoming message message->setTo(message->getFrom()); message->setFrom(jid); component->sendMessage(message); } private: JID jid; Component* component; ComponentXMLTracer* tracer; }; int main(int, char**) { SimpleEventLoop eventLoop; BoostNetworkFactories networkFactories(&eventLoop); EchoComponent bot(&eventLoop, &networkFactories); eventLoop.run(); return 0; } swift-im-2.0+dev6/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoBot4.cpp0000644000175000017500000000430612227051773030340 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ //... #include #include #include using namespace Swift; using namespace boost; //... class EchoBot { public: EchoBot(NetworkFactories* networkFactories) { //... client = new Client("echobot@wonderland.lit", "mypass", networkFactories); client->setAlwaysTrustCertificates(); client->onConnected.connect(bind(&EchoBot::handleConnected, this)); client->onMessageReceived.connect( bind(&EchoBot::handleMessageReceived, this, _1)); //... client->onPresenceReceived.connect( bind(&EchoBot::handlePresenceReceived, this, _1)); //... tracer = new ClientXMLTracer(client); client->connect(); //... } //... ~EchoBot() { delete tracer; delete client; } private: //... void handlePresenceReceived(Presence::ref presence) { // Automatically approve subscription requests if (presence->getType() == Presence::Subscribe) { Presence::ref response = Presence::create(); response->setTo(presence->getFrom()); response->setType(Presence::Subscribed); client->sendPresence(response); } } void handleConnected() { // Request the roster GetRosterRequest::ref rosterRequest = GetRosterRequest::create(client->getIQRouter()); rosterRequest->onResponse.connect( bind(&EchoBot::handleRosterReceived, this, _2)); rosterRequest->send(); } void handleRosterReceived(ErrorPayload::ref error) { if (error) { std::cerr << "Error receiving roster. Continuing anyway."; } // Send initial available presence client->sendPresence(Presence::create("Send me a message")); } //... void handleMessageReceived(Message::ref message) { // Echo back the incoming message message->setTo(message->getFrom()); message->setFrom(JID()); client->sendMessage(message); } private: Client* client; ClientXMLTracer* tracer; //... }; //... int main(int, char**) { SimpleEventLoop eventLoop; BoostNetworkFactories networkFactories(&eventLoop); EchoBot bot(&networkFactories); eventLoop.run(); return 0; } //... swift-im-2.0+dev6/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoBot3.cpp0000644000175000017500000000237312227051773030341 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include using namespace Swift; using namespace boost; class EchoBot { public: EchoBot(NetworkFactories* networkFactories) { client = new Client("echobot@wonderland.lit", "mypass", networkFactories); client->setAlwaysTrustCertificates(); client->onConnected.connect(bind(&EchoBot::handleConnected, this)); client->onMessageReceived.connect( bind(&EchoBot::handleMessageReceived, this, _1)); tracer = new ClientXMLTracer(client); client->connect(); } ~EchoBot() { delete tracer; delete client; } private: void handleConnected() { std::cout << "Connected" << std::endl; } void handleMessageReceived(Message::ref message) { // Echo back the incoming message message->setTo(message->getFrom()); message->setFrom(JID()); client->sendMessage(message); } private: Client* client; ClientXMLTracer* tracer; }; int main(int, char**) { SimpleEventLoop eventLoop; BoostNetworkFactories networkFactories(&eventLoop); EchoBot bot(&networkFactories); eventLoop.run(); return 0; } swift-im-2.0+dev6/Documentation/SwiftenDevelopersGuide/Swiften Developers Guide.xml0000644000175000017500000006207412227051773030402 0ustar kismithkismith Swiften Developer's Guide Introduction
Prerequisites We assume that the reader is familiar with the basics of the XMPP protocol. For an overview of the XMPP protocol and its workings, see XMPP: The Definitive Guide
Boost Swiften makes heavy use of Boost (http://boost.org) libraries, including Signal, Bind, Optional, and Smart Pointers. We introduce the basic usage of these libraries in our API throughout this manual. For detailed documentation, we refer to the Boost website.
Tutorial: Writing an Echo Bot In this chapter, we guide you through the Swiften API by building an example XMPP application: an EchoBot. This example program, taken from XMPP: The Definitive Guide , connects to an XMPP server, logs in, and responds to all incoming messages with the exact same message. We build up our application using Swiften's basic building blocks for XMPP development, to help get a good understanding of how Swiften fundamental classes work and can be extended. In the last stage of this example, we introduce some of Swiften's convenience classes for standard XMPP tasks such as roster management. Connecting to a server: Clients & event loops As a first step, we create an application that connects to a server. The code can be seen in . Connecting to a server The first thing this program does is construct an Event Loop. An event loop is a seemingly infinite loop that waits for external events (e.g. incoming network packets, timers being activated, input happening) to happen; when such an event comes in, it notifies interested parties of this event, and then continues listening for the next event. Since many application frameworks (such as Qt, GLib, Cocoa) use their own event loop, Swiften comes prepackaged with classes that integrate with these event loops. These classes can be found in Swiften/EventLoop. In this example, however, we don't use such a framework, so we use Swiften's own SimpleEventLoop. This class is used by simply instantiating it at the beginning of the application, and calling run() after everything is set up, which will go into an infinite loop. Apart from constructing, passing, and (if necessary) starting the event loop, you will probably have no other contact with it in the rest of the application. Another prerequisite of Swiften's classes is an implementation of network interaction, provided through the NetworkFactories class. Swiften comes with a Boost-based network implementation, implemented in BoostNetworkFactories. As with the event loop, you probably will have no need to interact with this class apart from constructing it. Swiften's central class for implementing XMPP applications is Client. This class handles all the interaction with the XMPP network. After constructing it with the JID and password with which we want to connect, we call connect() to instruct the client to connect to the XMPP server with the given credentials. Note that this call returns immediately; it is only when starting the event loop that network the actual connection process will start. The call to setAlwaysTrustCertificates() before connecting avoids checks for certificate validity; this is convenient for the examples in this guide (because not all servers have trusted certificates), but for production software, you should not call this. Building EchoBot To build your application, you will need to set up your build environment to use the correct include and library paths for Swiften, and link against the Swiften library. This depends on both the compiler you are using, and the flags you used to build Swiften. To get the list of compiler options, Swiften comes with a program swiften-config (located in Swiften/Config in the Swiften tree). Calling this with the --libs option will return the list of link flags, whereas calling it with the --cflags option will return the list of C(++) compiler flags. Reacting to events: Signals, Slots & Bind Up to this point, our client doesn't do anything useful. In this section, we make the client react to XMPP events. The code can be seen in . Reacting to events: Notify whenever the client is connected to the network, and echo back incoming messages A first thing we want to do is print out a message when the client is connected to the server. Swiften uses the signal/slot paradigm for notifying interested parties of events. A signal is an object representing a type of event. For example, Client has an onConnected signal for notifying whenever the client is connected to the network. If you are interested in a particular signal, you connect a slot to the signal. A slot represents a callback that will be called whenever a signal is emitted. Since we want to print out a message whenever we're connected to the network, we connect to the client's signal, and tell it to call handleConnected (which prints out a message): client->onConnected.connect(&handleConnected) Another event we're interested in is whenever a message comes in. For this purpose, Client provides a signal called onMessageReceived. The major difference with the previous onConnected signal is that this signal also can provide extra information to the callback: the actual message received. A signal can provide this extra information through one or more arguments, which will be passed to the slot's parameters. To be able to handle parameters to slots, there needs to be a more general representation of callbacks than just function pointers. This is where Boost's bind comes in: bind provides a way to construct functors (callbacks, slots, …), by combining function pointers and parameter values. For example, to connect the signal to our slot, we call: client->onMessageReceived.connect(bind(&handleMessageReceived, _1)) This is essentially saying: when the onMessageReceived signal is emitted, call handleMessageReceived, and pass it the first parameter provided by the slot (which, in this case, is the actual message received). The implementation of handleMessageReceived should be straightforward: put the To address in place of the From address, and send the message to the server. One thing to note is that Message::ref represents a shared pointer to a Message stanza. Shared pointers behave the same as regular pointers, except that, when the last copy of the pointer goes away, the object it points to is deleted as well. Message::ref is in fact a typedef for boost::shared_ptr<Message>. Although Swiften tends to prefer the use of the ::ref notation, you will see both forms used intermixed. If you use a C++ compiler that supports C++0x lambda expressions (such as GCC 4.5 or Microsoft Visual Studio 2010), you can write this example in a more concise way, as illustrated in . However, for the remainder of this guide, we will not use this functionality. EchoBot using C++0x lambda expressions. This is currently only possible with a limited set of C++compilers. Before moving on to the next step, we are going to rearrange our code from a bit, to make it a bit cleaner. Instead of using global variables, we are going to create an EchoBot class with the current code in it. The resulting code can be found in . Creating an EchoBot class The important thing to consider in this step are the changes to the signal connections. Since we are now passing member variables of a class to the signal, we need to use bind to pass in the actual object on which this member variable is called as the first parameter. The only thing we added to this version is the ClientXMLTracer. This class will dump all incoming and outgoing XMPP messages to the console, which can be handy for debugging our bot. Presence Management: Requests The current version of our EchoBot does what it is supposed to do: it answers all incoming messages. However, although users can add the bot to their contact list, they will not see when it is online, since the bot doesn't do any presence handling yet. In this section, we explain the different steps involved in adding presence management, resulting in the code in . Adding presence management: Requesting the initial roster, and auto-approving incoming subscription requests. First of all, our bot needs to listen to incoming subscription requests from users who want to add it to their roster, and automatically approve them. This is done by connecting to the onPresenceReceived signal, checking whether the incoming presence is a subscription request, and if so, respond to it with an approval (in handlePresenceReceived). The first version of the XMPP protocol states that a client will not get any presence subscriptions until it requests the roster. To make sure we handle this, we want to make sure our bot requests the roster at login. After getting the onConnected signal, we therefore send a request to retrieve the roster. Swiften's Request classes correspond to XMPP IQ Get or Set actions. Swiften provides a set of built-in request classes for the most common tasks in Swiften/Queries/Requests, and can be easily extended to use add your own (see ). Requests have an onResponse signal, which is emitted when a response comes in. This signal has 2 parameters: the actual response data (the Payload), and an optional error payload in case there was an error executing the request. To use a Request class, you construct it with the correct parameters, connect to the onResponse signal, and then send the request by calling send() on it. In this case, we're not interested in the actual payload of the response (passed as the first parameter), so we pass it a slot with only the second parameter (the error payload). When we get the roster back, we send initial presence to all our subscribers, announcing them we're online. Publishing version information: Responders Most XMPP clients have support for querying software version information of a client through . These clients send out an IQ-Get request to an entity, which responds with the requested information. We would like our bot to listen to these requests, and respond with the correct information. Swiften uses Responder classes for the purpose of responding to IQ requests, and are therefore the dual of the Request clients discussed in the previous section. Adding presence management: Requesting the initial roster, and auto-approving incoming subscription requests. Using SoftwareVersionResponder is pretty straightforward, as can be seen in : simply construct the responder, set the correct parameters, call start(), and it will automatically respond to the incoming requests. Other Responder classes may provide signals to notify of incoming requests, or may have some other behavior. For a detailed explanation of responders, see . Extending Swiften with new payloads: Payloads, Parsers, and Serializers Swiften uses abstract datastructures for all the data that is received and sent over the XMPP network. The declaration of these datastructures can all be found in Swiften/Elements. For representing the XMPP stanzas, Swiften uses the Message, Presence, and IQ classes. Each stanza can have an arbitrary amount of child payloads, represented by the Payload class. A payload typically corresponds to a (namespaced) child XML element of a stanza; for example, the <query xmlns="jabber:iq:roster"/> element used for managing the roster is represented as a RosterPayload. If you want to extend Swiften with your own XMPP extension, you will first need to create a payload for this extension. For example, suppose we want to reate an extension for use in our Echo bot that contains a special textual message, and add this to all our outgoing messages, we create the EchoPayload illustrated in . We can then append or retrieve this payload from the stanzas using Stanza::getPayload() and Stanza::addPayload(). For example, the version of our bot in checks whether an incoming message contains the EchoPayload, and if not, echoes back the message, and adds an extension to the message with a descriptive text. Extending Swiften with a new payload: <literal>EchoPayload</literal> Adding a custom extension: Using a custom element, and registering a parser (factory) and serializer for the element. However, having the element is not enough; Swiften also needs to know how to extract this payload from the incoming stanzas, and know how to send it on outgoing stanzas. In order to do this, Swiften uses XML parsers and serializers for the payload. We therefore need to create a parser and serializer for our new payload, and register it with Client. Serializers are implemented as subclasses from PayloadSerializer, and provide the basic methods canSerialize() and serialize(). The serializer is registered using Client::addPayloadSerializer() (and unregistered using Client::removePayloadSerializer()). Parsers consist of 2 parts: a subclass of PayloadParser, which parses incoming XML in an event-driven way and builds up the payload, and a subclass of PayloadParserFactory, which is responsible for detecting whether a given parser can parse an incoming element, and creates a parser. The parser factory is registered with the client using Client::addPayloadParserFactory() (and unregistered using Client::removePayloadParserFactory()). Although you can subclass the base classes for parsers and serializers directly, Swiften comes with utility classes that contain common functionality for parsers and serializers. For example, for our EchoBot, the parser and serializer using these utility classes is shown in and respectively. Registration of the parser and serializer is shown in the constructor of our EchoBot in . The parser and parser factory for <literal>EchoPayload</literal> The serializer for <literal>EchoPayload</literal> If you want to create your own parser and serializers, you can look at the built-in parsers and serializers in the Swiften library, located in Swiften/Parser/PayloadParsers and Swiften/Serializer/PayloadSerializers. Extending Swiften with new queries and responders and explained that Swiften provides Requests and Responder classes for querying or responding to queries of specific payloads. If you extend Swiften with your own payloads, you can use these to create your own Request or Responder subclasses. Swiften also provides convenience classes such as GenericRequest, GetResponder and SetResponder for creating your requests and responders for your custom payloads. Using Swiften's convenience classes Swiften comes with flavors of the Client class: CoreClient, which implements only the basics of connecting to the XMPP server, without any built-in responders. If you want to build a client from scratch, this class is probably your preferred Swiften interface. However, most users of Swiften will not want to bother with explicitly instantiating responders to basic functionality such as software version information etc., and will want to have the convenience of built-in responders and utility classes. In this case, you can use the Client class (a subclass of CoreClient, which implements most of the common XMPP client functionality, including roster and subscription management, VCards, Avatars, Service Discovery, Multi-User Chats, ... Writing server components Swiften also provides classes for creating server components. The Component class has a similar interface as Client, but uses the component protocol to connect with the XMPP server. Except for a few classes, the same techniques and classes for writing clients can be applied to write components. illustrates how we would build a component version of our Echo bot. EchoBot as a server component Bibliography XMPP-TDG <ulink url="http://oreilly.com/catalog/9780596157197/">XMPP: The Definitive Guide</ulink> Peter Saint-Andre Kevin Smith Remko Tronçon XEP-0092 <ulink url='http://www.xmpp.org/extensions/xep-0092.html'>Software Version</ulink> Peter Saint-Andre
swift-im-2.0+dev6/Documentation/TranslatingSwift.txt0000644000175000017500000000542112227051773022541 0ustar kismithkismithTranslating Swift ----------------- Below, you will find instructions on how to translate Swift. A prerequisite is to have Qt (with Qt Linguist installed). Starting a new translation -------------------------- - If you have a source tree checkout, run the following command to generate a clean translation template 'swift.ts': scons update_translations=1 Swift/Translations/swift.ts If you don't have a source tree checkout, download swift_ts from http://swift.im/translations/master - Rename the new 'swift.ts' file to swift_.ts, e.g. "swift_nl.ts" for Dutch - Open the file in Qt Linguist, and translate the phrases. For more information, see http://doc.qt.nokia.com/latest/linguist-manual.html - Translate the Swift/resources/swift.desktop entry. - Submit your finished translation file to the Swift development team. Updating an existing translation -------------------------------- - If you have a source tree checkout, run the following command to update an existing translation template: scons update_translations=1 Swift/Translations/ - If you don't have a source tree checkout, get the latest version of your translation from http://swift.im/translations/master Testing your translation ------------------------ You can choose to manually test your translation (doesn't require a development environment for Swift), or automatically: - Manually: - In Linguist, select "File->Release". This will create a file called .qm. - Put .qm in the translations dir - On Windows, in the 'translations/' subdir of the installed package - On Mac OS X, use "Show Package Contents" on Swift.app, and put the translation file in Contents/Resources/translations - On Linux, in /usr/share/swift/translations - Automatically (with source tree checkout): - Ensure your translation file is in Swift/Translations/swift_.ts - Build Swift (for more details, see the building documentation): scons Swift This will build translation modules (.qm) for your translation sources, and put them in the right place. Finally, run Swift. If your system isn't set up to use your language, you can override the language: - For Swift compiled with Qt < 4.8: Set the LANG environment variable to the name of your language before running Swift. For example: set LANG=nl (on Windows, or, alternatively, edit the User Environment Variables in Control Panel) export LANG=nl (on Linux or Mac OS X, in Bash) - For Swift compiled with Qt >= 4.8: Use the '--language' parameter for Swift: For example: Swift.exe --language nl (on Windows, from cmd or by editing the shortcut) open Swift.app --args --language nl (on Mac OS X, from Terminal) swift-im --language nl (on Linux) swift-im-2.0+dev6/3rdParty/0000755000175000017500000000000012227051774015373 5ustar kismithkismithswift-im-2.0+dev6/3rdParty/SQLiteAsync/0000755000175000017500000000000012227051774017532 5ustar kismithkismithswift-im-2.0+dev6/3rdParty/SQLiteAsync/SConscript0000644000175000017500000000116312227051773021544 0ustar kismithkismithImport("env") if env.get("SQLITE_ASYNC_BUNDLED", False) : ################################################################################ # Flags ################################################################################ if env["SCONS_STAGE"] == "flags" : env["SQLITE_ASYNC_FLAGS"] = { "CPPPATH": [Dir(".")], "LIBPATH": [Dir(".")], } if env["SCONS_STAGE"] == "build" : myenv = env.Clone() myenv.Replace(CCFLAGS = [flag for flag in env["CCFLAGS"] if flag not in ["-W", "-Wall"]]) myenv.UseFlags(env.get("SQLITE_FLAGS", {})) env["SQLITE_ASYNC_OBJECTS"] = myenv.SwiftenObject(["sqlite3async.c"]) swift-im-2.0+dev6/3rdParty/LibMiniUPnPc/0000755000175000017500000000000012227051774017624 5ustar kismithkismithswift-im-2.0+dev6/3rdParty/LibMiniUPnPc/SConscript0000644000175000017500000000412112227051773021633 0ustar kismithkismithImport(["env", "conf_env"]) if env.get("LIBMINIUPNPC_BUNDLED", False) : ################################################################################ # Module flags ################################################################################ if env["SCONS_STAGE"] == "flags" : env["LIBMINIUPNPC_FLAGS"] = { "CPPPATH": [Dir("src/miniupnpc")], "LIBPATH": [Dir(".")], "INTERNAL_CPPDEFINES": ["STATICLIB"], } #if env["PLATFORM"] == "win32" : # env["LIBIDN_FLAGS"]["CPPPATH"] += [Dir("stubs/win32")] # if env["MSVC_VERSION"][:3] == "9.0" : # env["LIBIDN_FLAGS"]["CPPPATH"] += [Dir("stubs/win32/VC2008")] ################################################################################ # Build ################################################################################ if env["SCONS_STAGE"] == "build" : myenv = env.Clone() myenv.Append(CPPPATH = ["src"]) # Remove warn flags myenv.Replace(CCFLAGS = [flag for flag in env["CCFLAGS"] if flag not in ["-W", "-Wall"]]) myenv.Append(CCFLAGS = ["-DNDEBUG", "-DSTATICLIB"]) if myenv["PLATFORM"] != "win32": myenv.Append(CCFLAGS = ["-DMINIUPNPC_SET_SOCKET_TIMEOUT"]) if myenv["PLATFORM"] == "darwin": myenv.Append(CCFLAGS = ["-DMACOSX", "-D_DARWIN_C_SOURCE"]) if myenv["PLATFORM"] == "win32": myenv.Append(CCFLAGS = ["-DWIN32", "-D_WIN32_WINNT=0x0501"]) myenv.WriteVal("src/miniupnpc/miniupnpcstrings.h", myenv.Value( """ #ifndef __MINIUPNPCSTRINGS_H__ #define __MINIUPNPCSTRINGS_H__ #define OS_STRING "$OS_STRING" #define MINIUPNPC_VERSION_STRING "1.5" #endif """.replace("$OS_STRING", myenv["PLATFORM"]))) env["LIBMINIUPNPC_OBJECTS"] = myenv.SwiftenObject([ "src/miniupnpc/igd_desc_parse.c", "src/miniupnpc/miniupnpc.c", "src/miniupnpc/minixml.c", "src/miniupnpc/minisoap.c", "src/miniupnpc/minissdpc.c", "src/miniupnpc/miniwget.c", #"src/miniupnpc/upnpc.c", "src/miniupnpc/upnpcommands.c", "src/miniupnpc/upnpreplyparse.c", "src/miniupnpc/upnperrors.c", "src/miniupnpc/connecthostport.c", "src/miniupnpc/portlistingparse.c", "src/miniupnpc/receivedata.c" ]) swift-im-2.0+dev6/3rdParty/LibNATPMP/0000755000175000017500000000000012227051774017021 5ustar kismithkismithswift-im-2.0+dev6/3rdParty/LibNATPMP/SConscript0000644000175000017500000000311612227051773021033 0ustar kismithkismithImport(["env", "conf_env"]) if env.get("LIBNATPMP_BUNDLED", False) : ################################################################################ # Module flags ################################################################################ if env["SCONS_STAGE"] == "flags" : env["LIBNATPMP_FLAGS"] = { "CPPPATH": [Dir("src/libnatpmp")], "LIBPATH": [Dir(".")], "INTERNAL_CPPDEFINES": ["STATICLIB"], } #if env["PLATFORM"] == "win32" : # env["LIBIDN_FLAGS"]["CPPPATH"] += [Dir("stubs/win32")] # if env["MSVC_VERSION"][:3] == "9.0" : # env["LIBIDN_FLAGS"]["CPPPATH"] += [Dir("stubs/win32/VC2008")] ################################################################################ # Build ################################################################################ if env["SCONS_STAGE"] == "build" : myenv = env.Clone() myenv.Append(CPPPATH = ["src"]) # Remove warn flags myenv.Replace(CCFLAGS = [flag for flag in env["CCFLAGS"] if flag not in ["-W", "-Wall"]]) myenv.Append(CCFLAGS = ["-DNDEBUG", "-DSTATICLIB"]) #if myenv["PLATFORM"] != "win32": # myenv.Append(CCFLAGS = ["-DMINIUPNPC_SET_SOCKET_TIMEOUT"]) if myenv["PLATFORM"] == "darwin": myenv.Append(CCFLAGS = ["-DMACOSX", "-D_DARWIN_C_SOURCE"]) if myenv["PLATFORM"] == "win32": myenv.Append(CCFLAGS = ["-DWIN32"]) src_files = [ "src/libnatpmp/getgateway.c", "src/libnatpmp/natpmp.c", "src/libnatpmp/natpmpc.c", ] if myenv["PLATFORM"] == "win32": src_files += ["src/libnatpmp/wingettimeofday.c"] env["LIBNATPMP_OBJECTS"] = myenv.SwiftenObject(src_files) swift-im-2.0+dev6/3rdParty/Lua/0000755000175000017500000000000012227051774016114 5ustar kismithkismithswift-im-2.0+dev6/3rdParty/Lua/SConscript0000644000175000017500000000406512227051773020132 0ustar kismithkismithImport(["env", "conf_env"]) if env.get("LUA_BUNDLED", False) : ################################################################################ # Module flags ################################################################################ if env["SCONS_STAGE"] == "flags" : cppdefines = [] if not env["optimize"] : cppdefines.append("LUA_USE_APICHECK") if env["PLATFORM"] == "win32" : pass elif env["PLATFORM"] == "darwin" : cppdefines += ["LUA_USE_POSIX", "LUA_USE_DLOPEN"] else : cppdefines += ["LUA_USE_POSIX", "LUA_USE_DLOPEN"] env["LUA_FLAGS"] = { "CPPDEFINES": cppdefines, "CPPPATH": [Dir("src")], "LIBPATH": [Dir(".")], "LIBS": ["Swiften_Lua"], } ################################################################################ # Build ################################################################################ if env["SCONS_STAGE"] == "build" : myenv = env.Clone() if env["PLATFORM"] == "win32" : myenv.Append(CFLAGS = ["/TP"]) else : myenv.Append(CFLAGS = ["-x", "c++"]) # Remove warn flags myenv.Replace(CCFLAGS = [flag for flag in env["CCFLAGS"] if flag not in ["-W", "-Wall"]]) myenv.MergeFlags(myenv["LUA_FLAGS"]) myenv.StaticLibrary("Swiften_Lua", [ "src/lapi.c", "src/lcode.c", "src/ldebug.c", "src/ldo.c", "src/ldump.c", "src/lfunc.c", "src/lgc.c", "src/llex.c", "src/lmem.c", "src/lobject.c", "src/lopcodes.c", "src/lparser.c", "src/lstate.c", "src/lstring.c", "src/ltable.c", "src/ltm.c", "src/lundump.c", "src/lvm.c", "src/lzio.c", "src/lauxlib.c", "src/lbaselib.c", "src/ldblib.c", "src/liolib.c", "src/lmathlib.c", "src/loslib.c", "src/ltablib.c", "src/lstrlib.c", "src/loadlib.c", ]) lua_env = myenv.Clone() lua_env.MergeFlags(lua_env["LUA_FLAGS"]) if lua_env.get("HAVE_READLINE", False) : lua_env.Append(CPPDEFINES = ["LUA_USE_READLINE"]) lua_env.MergeFlags(lua_env["READLINE_FLAGS"]) lua_env.MergeFlags(myenv["PLATFORM_FLAGS"]) lua_env.Program("lua", [ "src/linit.c", "src/lua.c", ]) swift-im-2.0+dev6/3rdParty/ZLib/0000755000175000017500000000000012227051774016233 5ustar kismithkismithswift-im-2.0+dev6/3rdParty/ZLib/SConscript0000644000175000017500000000076712227051773020256 0ustar kismithkismithImport("env") if env.get("ZLIB_BUNDLED", False) : if env["SCONS_STAGE"] == "flags" : env["ZLIB_FLAGS"] = { "CPPPATH": [Dir("src")], "LIBPATH": [Dir(".")], } if env["SCONS_STAGE"] == "build" : env["ZLIB_OBJECTS"] = env.SwiftenObject([ "src/adler32.c", "src/compress.c", "src/crc32.c", "src/deflate.c", "src/gzio.c", "src/infback.c", "src/inffast.c", "src/inflate.c", "src/inftrees.c", "src/trees.c", "src/uncompr.c", "src/zutil.c" ]) swift-im-2.0+dev6/3rdParty/Expat/0000755000175000017500000000000012227051774016454 5ustar kismithkismithswift-im-2.0+dev6/3rdParty/Expat/SConscript0000644000175000017500000000116512227051773020470 0ustar kismithkismithImport(["env", "conf_env"]) if env.get("EXPAT_BUNDLED", False) : if env["SCONS_STAGE"] == "flags" : env["EXPAT_FLAGS"] = { "CPPDEFINES": ["XML_STATIC"], "CPPPATH": [Dir("src")], "LIBPATH": [Dir(".")], } if env["SCONS_STAGE"] == "build" : myenv = env.Clone() myenv.Append(CPPDEFINES = ["XML_STATIC", "HAVE_EXPAT_CONFIG_H"]) myenv.Append(CPPPATH = [".", "src"]) conf = Configure(conf_env) if conf.CheckFunc('memmove') : myenv.Append(CPPDEFINES = ["HAVE_MEMMOVE"]) conf.Finish() env["EXPAT_OBJECTS"] = myenv.SwiftenObject([ "src/xmltok.c", "src/xmlparse.c", "src/xmlrole.c" ]) swift-im-2.0+dev6/3rdParty/OpenSSL/0000755000175000017500000000000012227051774016656 5ustar kismithkismithswift-im-2.0+dev6/3rdParty/OpenSSL/SConscript0000644000175000017500000007213012227051773020672 0ustar kismithkismithImport("env") openssl_bundle_dir = "openssl" if env.get("OPENSSL_BUNDLED", False) : if env["SCONS_STAGE"] == "flags" : env["OPENSSL_FLAGS"] = { "CPPPATH": [Dir(openssl_bundle_dir + "/include")], "LIBPATH": [Dir(".")], "LIBS": ["ssl", "crypto"] } if env["SCONS_STAGE"] == "build" : openssl_env = env.Clone() openssl_env["OPENSSL_BUNDLE_DIR"] = openssl_bundle_dir openssl_env.Append(CPPPATH = [ "$OPENSSL_BUNDLE_DIR", "$OPENSSL_BUNDLE_DIR/crypto", "$OPENSSL_BUNDLE_DIR/crypto/asn1", "$OPENSSL_BUNDLE_DIR/crypto/evp", "$OPENSSL_BUNDLE_DIR/include" ]) openssl_env.Append(CPPDEFINES = ["OPENSSL_THREADS", "_REENTRANT", "DSO_DLFCN", "HAVE_DLFCN_H", "L_ENDIAN"]) openssl_env.StaticLibrary("crypto", [ "$OPENSSL_BUNDLE_DIR/crypto/cryptlib.c", "$OPENSSL_BUNDLE_DIR/crypto/mem.c", "$OPENSSL_BUNDLE_DIR/crypto/mem_dbg.c", "$OPENSSL_BUNDLE_DIR/crypto/cversion.c", "$OPENSSL_BUNDLE_DIR/crypto/ex_data.c", "$OPENSSL_BUNDLE_DIR/crypto/cpt_err.c", "$OPENSSL_BUNDLE_DIR/crypto/ebcdic.c", "$OPENSSL_BUNDLE_DIR/crypto/uid.c", "$OPENSSL_BUNDLE_DIR/crypto/o_time.c", "$OPENSSL_BUNDLE_DIR/crypto/o_str.c", "$OPENSSL_BUNDLE_DIR/crypto/o_dir.c", "$OPENSSL_BUNDLE_DIR/crypto/mem_clr.c", "$OPENSSL_BUNDLE_DIR/crypto/objects/o_names.c", "$OPENSSL_BUNDLE_DIR/crypto/objects/obj_dat.c", "$OPENSSL_BUNDLE_DIR/crypto/objects/obj_lib.c", "$OPENSSL_BUNDLE_DIR/crypto/objects/obj_err.c", "$OPENSSL_BUNDLE_DIR/crypto/objects/obj_xref.c", "$OPENSSL_BUNDLE_DIR/crypto/md4/md4_dgst.c", "$OPENSSL_BUNDLE_DIR/crypto/md4/md4_one.c", "$OPENSSL_BUNDLE_DIR/crypto/md5/md5_dgst.c", "$OPENSSL_BUNDLE_DIR/crypto/md5/md5_one.c", "$OPENSSL_BUNDLE_DIR/crypto/sha/sha_dgst.c", "$OPENSSL_BUNDLE_DIR/crypto/sha/sha1dgst.c", "$OPENSSL_BUNDLE_DIR/crypto/sha/sha_one.c", "$OPENSSL_BUNDLE_DIR/crypto/sha/sha1_one.c", "$OPENSSL_BUNDLE_DIR/crypto/sha/sha256.c", "$OPENSSL_BUNDLE_DIR/crypto/sha/sha512.c", "$OPENSSL_BUNDLE_DIR/crypto/mdc2/mdc2dgst.c", "$OPENSSL_BUNDLE_DIR/crypto/mdc2/mdc2_one.c", "$OPENSSL_BUNDLE_DIR/crypto/hmac/hmac.c", "$OPENSSL_BUNDLE_DIR/crypto/hmac/hm_ameth.c", "$OPENSSL_BUNDLE_DIR/crypto/hmac/hm_pmeth.c", "$OPENSSL_BUNDLE_DIR/crypto/ripemd/rmd_dgst.c", "$OPENSSL_BUNDLE_DIR/crypto/ripemd/rmd_one.c", "$OPENSSL_BUNDLE_DIR/crypto/whrlpool/wp_dgst.c", "$OPENSSL_BUNDLE_DIR/crypto/whrlpool/wp_block.c", "$OPENSSL_BUNDLE_DIR/crypto/des/set_key.c", "$OPENSSL_BUNDLE_DIR/crypto/des/ecb_enc.c", "$OPENSSL_BUNDLE_DIR/crypto/des/cbc_enc.c", "$OPENSSL_BUNDLE_DIR/crypto/des/ecb3_enc.c", "$OPENSSL_BUNDLE_DIR/crypto/des/cfb64enc.c", "$OPENSSL_BUNDLE_DIR/crypto/des/cfb64ede.c", "$OPENSSL_BUNDLE_DIR/crypto/des/cfb_enc.c", "$OPENSSL_BUNDLE_DIR/crypto/des/ofb64ede.c", "$OPENSSL_BUNDLE_DIR/crypto/des/enc_read.c", "$OPENSSL_BUNDLE_DIR/crypto/des/enc_writ.c", "$OPENSSL_BUNDLE_DIR/crypto/des/ofb64enc.c", "$OPENSSL_BUNDLE_DIR/crypto/des/ofb_enc.c", "$OPENSSL_BUNDLE_DIR/crypto/des/str2key.c", "$OPENSSL_BUNDLE_DIR/crypto/des/pcbc_enc.c", "$OPENSSL_BUNDLE_DIR/crypto/des/qud_cksm.c", "$OPENSSL_BUNDLE_DIR/crypto/des/rand_key.c", "$OPENSSL_BUNDLE_DIR/crypto/des/des_enc.c", "$OPENSSL_BUNDLE_DIR/crypto/des/fcrypt_b.c", "$OPENSSL_BUNDLE_DIR/crypto/des/fcrypt.c", "$OPENSSL_BUNDLE_DIR/crypto/des/xcbc_enc.c", "$OPENSSL_BUNDLE_DIR/crypto/des/rpc_enc.c", "$OPENSSL_BUNDLE_DIR/crypto/des/cbc_cksm.c", "$OPENSSL_BUNDLE_DIR/crypto/des/ede_cbcm_enc.c", "$OPENSSL_BUNDLE_DIR/crypto/des/des_old.c", "$OPENSSL_BUNDLE_DIR/crypto/des/des_old2.c", "$OPENSSL_BUNDLE_DIR/crypto/des/read2pwd.c", "$OPENSSL_BUNDLE_DIR/crypto/aes/aes_misc.c", "$OPENSSL_BUNDLE_DIR/crypto/aes/aes_ecb.c", "$OPENSSL_BUNDLE_DIR/crypto/aes/aes_cfb.c", "$OPENSSL_BUNDLE_DIR/crypto/aes/aes_ofb.c", "$OPENSSL_BUNDLE_DIR/crypto/aes/aes_ctr.c", "$OPENSSL_BUNDLE_DIR/crypto/aes/aes_ige.c", "$OPENSSL_BUNDLE_DIR/crypto/aes/aes_wrap.c", "$OPENSSL_BUNDLE_DIR/crypto/aes/aes_core.c", "$OPENSSL_BUNDLE_DIR/crypto/aes/aes_cbc.c", "$OPENSSL_BUNDLE_DIR/crypto/rc2/rc2_ecb.c", "$OPENSSL_BUNDLE_DIR/crypto/rc2/rc2_skey.c", "$OPENSSL_BUNDLE_DIR/crypto/rc2/rc2_cbc.c", "$OPENSSL_BUNDLE_DIR/crypto/rc2/rc2cfb64.c", "$OPENSSL_BUNDLE_DIR/crypto/rc2/rc2ofb64.c", "$OPENSSL_BUNDLE_DIR/crypto/rc4/rc4_enc.c", "$OPENSSL_BUNDLE_DIR/crypto/rc4/rc4_skey.c", "$OPENSSL_BUNDLE_DIR/crypto/idea/i_cbc.c", "$OPENSSL_BUNDLE_DIR/crypto/idea/i_cfb64.c", "$OPENSSL_BUNDLE_DIR/crypto/idea/i_ofb64.c", "$OPENSSL_BUNDLE_DIR/crypto/idea/i_ecb.c", "$OPENSSL_BUNDLE_DIR/crypto/idea/i_skey.c", "$OPENSSL_BUNDLE_DIR/crypto/bf/bf_skey.c", "$OPENSSL_BUNDLE_DIR/crypto/bf/bf_ecb.c", "$OPENSSL_BUNDLE_DIR/crypto/bf/bf_enc.c", "$OPENSSL_BUNDLE_DIR/crypto/bf/bf_cfb64.c", "$OPENSSL_BUNDLE_DIR/crypto/bf/bf_ofb64.c", "$OPENSSL_BUNDLE_DIR/crypto/cast/c_skey.c", "$OPENSSL_BUNDLE_DIR/crypto/cast/c_ecb.c", "$OPENSSL_BUNDLE_DIR/crypto/cast/c_enc.c", "$OPENSSL_BUNDLE_DIR/crypto/cast/c_cfb64.c", "$OPENSSL_BUNDLE_DIR/crypto/cast/c_ofb64.c", "$OPENSSL_BUNDLE_DIR/crypto/camellia/cmll_ecb.c", "$OPENSSL_BUNDLE_DIR/crypto/camellia/cmll_ofb.c", "$OPENSSL_BUNDLE_DIR/crypto/camellia/cmll_cfb.c", "$OPENSSL_BUNDLE_DIR/crypto/camellia/cmll_ctr.c", "$OPENSSL_BUNDLE_DIR/crypto/camellia/camellia.c", "$OPENSSL_BUNDLE_DIR/crypto/camellia/cmll_misc.c", "$OPENSSL_BUNDLE_DIR/crypto/camellia/cmll_cbc.c", "$OPENSSL_BUNDLE_DIR/crypto/seed/seed.c", "$OPENSSL_BUNDLE_DIR/crypto/seed/seed_ecb.c", "$OPENSSL_BUNDLE_DIR/crypto/seed/seed_cbc.c", "$OPENSSL_BUNDLE_DIR/crypto/seed/seed_cfb.c", "$OPENSSL_BUNDLE_DIR/crypto/seed/seed_ofb.c", "$OPENSSL_BUNDLE_DIR/crypto/modes/cbc128.c", "$OPENSSL_BUNDLE_DIR/crypto/modes/ctr128.c", "$OPENSSL_BUNDLE_DIR/crypto/modes/cts128.c", "$OPENSSL_BUNDLE_DIR/crypto/modes/cfb128.c", "$OPENSSL_BUNDLE_DIR/crypto/modes/ofb128.c", "$OPENSSL_BUNDLE_DIR/crypto/bn/bn_add.c", "$OPENSSL_BUNDLE_DIR/crypto/bn/bn_div.c", "$OPENSSL_BUNDLE_DIR/crypto/bn/bn_exp.c", "$OPENSSL_BUNDLE_DIR/crypto/bn/bn_lib.c", "$OPENSSL_BUNDLE_DIR/crypto/bn/bn_ctx.c", "$OPENSSL_BUNDLE_DIR/crypto/bn/bn_mul.c", "$OPENSSL_BUNDLE_DIR/crypto/bn/bn_mod.c", "$OPENSSL_BUNDLE_DIR/crypto/bn/bn_print.c", "$OPENSSL_BUNDLE_DIR/crypto/bn/bn_rand.c", "$OPENSSL_BUNDLE_DIR/crypto/bn/bn_shift.c", "$OPENSSL_BUNDLE_DIR/crypto/bn/bn_word.c", "$OPENSSL_BUNDLE_DIR/crypto/bn/bn_blind.c", "$OPENSSL_BUNDLE_DIR/crypto/bn/bn_kron.c", "$OPENSSL_BUNDLE_DIR/crypto/bn/bn_sqrt.c", "$OPENSSL_BUNDLE_DIR/crypto/bn/bn_gcd.c", "$OPENSSL_BUNDLE_DIR/crypto/bn/bn_prime.c", "$OPENSSL_BUNDLE_DIR/crypto/bn/bn_err.c", "$OPENSSL_BUNDLE_DIR/crypto/bn/bn_sqr.c", "$OPENSSL_BUNDLE_DIR/crypto/bn/bn_asm.c", "$OPENSSL_BUNDLE_DIR/crypto/bn/bn_recp.c", "$OPENSSL_BUNDLE_DIR/crypto/bn/bn_mont.c", "$OPENSSL_BUNDLE_DIR/crypto/bn/bn_mpi.c", "$OPENSSL_BUNDLE_DIR/crypto/bn/bn_exp2.c", "$OPENSSL_BUNDLE_DIR/crypto/bn/bn_gf2m.c", "$OPENSSL_BUNDLE_DIR/crypto/bn/bn_nist.c", "$OPENSSL_BUNDLE_DIR/crypto/bn/bn_depr.c", "$OPENSSL_BUNDLE_DIR/crypto/bn/bn_const.c", "$OPENSSL_BUNDLE_DIR/crypto/ec/ec_lib.c", "$OPENSSL_BUNDLE_DIR/crypto/ec/ecp_smpl.c", "$OPENSSL_BUNDLE_DIR/crypto/ec/ecp_mont.c", "$OPENSSL_BUNDLE_DIR/crypto/ec/ecp_nist.c", "$OPENSSL_BUNDLE_DIR/crypto/ec/ec_cvt.c", "$OPENSSL_BUNDLE_DIR/crypto/ec/ec_mult.c", "$OPENSSL_BUNDLE_DIR/crypto/ec/ec_err.c", "$OPENSSL_BUNDLE_DIR/crypto/ec/ec_curve.c", "$OPENSSL_BUNDLE_DIR/crypto/ec/ec_check.c", "$OPENSSL_BUNDLE_DIR/crypto/ec/ec_print.c", "$OPENSSL_BUNDLE_DIR/crypto/ec/ec_asn1.c", "$OPENSSL_BUNDLE_DIR/crypto/ec/ec_key.c", "$OPENSSL_BUNDLE_DIR/crypto/ec/ec2_smpl.c", "$OPENSSL_BUNDLE_DIR/crypto/ec/ec2_mult.c", "$OPENSSL_BUNDLE_DIR/crypto/ec/ec_ameth.c", "$OPENSSL_BUNDLE_DIR/crypto/ec/ec_pmeth.c", "$OPENSSL_BUNDLE_DIR/crypto/ec/eck_prn.c", "$OPENSSL_BUNDLE_DIR/crypto/rsa/rsa_eay.c", "$OPENSSL_BUNDLE_DIR/crypto/rsa/rsa_gen.c", "$OPENSSL_BUNDLE_DIR/crypto/rsa/rsa_lib.c", "$OPENSSL_BUNDLE_DIR/crypto/rsa/rsa_sign.c", "$OPENSSL_BUNDLE_DIR/crypto/rsa/rsa_saos.c", "$OPENSSL_BUNDLE_DIR/crypto/rsa/rsa_err.c", "$OPENSSL_BUNDLE_DIR/crypto/rsa/rsa_pk1.c", "$OPENSSL_BUNDLE_DIR/crypto/rsa/rsa_ssl.c", "$OPENSSL_BUNDLE_DIR/crypto/rsa/rsa_none.c", "$OPENSSL_BUNDLE_DIR/crypto/rsa/rsa_oaep.c", "$OPENSSL_BUNDLE_DIR/crypto/rsa/rsa_chk.c", "$OPENSSL_BUNDLE_DIR/crypto/rsa/rsa_null.c", "$OPENSSL_BUNDLE_DIR/crypto/rsa/rsa_pss.c", "$OPENSSL_BUNDLE_DIR/crypto/rsa/rsa_x931.c", "$OPENSSL_BUNDLE_DIR/crypto/rsa/rsa_asn1.c", "$OPENSSL_BUNDLE_DIR/crypto/rsa/rsa_depr.c", "$OPENSSL_BUNDLE_DIR/crypto/rsa/rsa_ameth.c", "$OPENSSL_BUNDLE_DIR/crypto/rsa/rsa_prn.c", "$OPENSSL_BUNDLE_DIR/crypto/rsa/rsa_pmeth.c", "$OPENSSL_BUNDLE_DIR/crypto/dsa/dsa_gen.c", "$OPENSSL_BUNDLE_DIR/crypto/dsa/dsa_key.c", "$OPENSSL_BUNDLE_DIR/crypto/dsa/dsa_lib.c", "$OPENSSL_BUNDLE_DIR/crypto/dsa/dsa_asn1.c", "$OPENSSL_BUNDLE_DIR/crypto/dsa/dsa_vrf.c", "$OPENSSL_BUNDLE_DIR/crypto/dsa/dsa_sign.c", "$OPENSSL_BUNDLE_DIR/crypto/dsa/dsa_err.c", "$OPENSSL_BUNDLE_DIR/crypto/dsa/dsa_ossl.c", "$OPENSSL_BUNDLE_DIR/crypto/dsa/dsa_depr.c", "$OPENSSL_BUNDLE_DIR/crypto/dsa/dsa_ameth.c", "$OPENSSL_BUNDLE_DIR/crypto/dsa/dsa_pmeth.c", "$OPENSSL_BUNDLE_DIR/crypto/dsa/dsa_prn.c", "$OPENSSL_BUNDLE_DIR/crypto/ecdsa/ecs_lib.c", "$OPENSSL_BUNDLE_DIR/crypto/ecdsa/ecs_asn1.c", "$OPENSSL_BUNDLE_DIR/crypto/ecdsa/ecs_ossl.c", "$OPENSSL_BUNDLE_DIR/crypto/ecdsa/ecs_sign.c", "$OPENSSL_BUNDLE_DIR/crypto/ecdsa/ecs_vrf.c", "$OPENSSL_BUNDLE_DIR/crypto/ecdsa/ecs_err.c", "$OPENSSL_BUNDLE_DIR/crypto/dh/dh_asn1.c", "$OPENSSL_BUNDLE_DIR/crypto/dh/dh_gen.c", "$OPENSSL_BUNDLE_DIR/crypto/dh/dh_key.c", "$OPENSSL_BUNDLE_DIR/crypto/dh/dh_lib.c", "$OPENSSL_BUNDLE_DIR/crypto/dh/dh_check.c", "$OPENSSL_BUNDLE_DIR/crypto/dh/dh_err.c", "$OPENSSL_BUNDLE_DIR/crypto/dh/dh_depr.c", "$OPENSSL_BUNDLE_DIR/crypto/dh/dh_ameth.c", "$OPENSSL_BUNDLE_DIR/crypto/dh/dh_pmeth.c", "$OPENSSL_BUNDLE_DIR/crypto/dh/dh_prn.c", "$OPENSSL_BUNDLE_DIR/crypto/ecdh/ech_lib.c", "$OPENSSL_BUNDLE_DIR/crypto/ecdh/ech_ossl.c", "$OPENSSL_BUNDLE_DIR/crypto/ecdh/ech_key.c", "$OPENSSL_BUNDLE_DIR/crypto/ecdh/ech_err.c", "$OPENSSL_BUNDLE_DIR/crypto/dso/dso_dl.c", "$OPENSSL_BUNDLE_DIR/crypto/dso/dso_dlfcn.c", "$OPENSSL_BUNDLE_DIR/crypto/dso/dso_err.c", "$OPENSSL_BUNDLE_DIR/crypto/dso/dso_lib.c", "$OPENSSL_BUNDLE_DIR/crypto/dso/dso_null.c", "$OPENSSL_BUNDLE_DIR/crypto/dso/dso_openssl.c", "$OPENSSL_BUNDLE_DIR/crypto/dso/dso_win32.c", "$OPENSSL_BUNDLE_DIR/crypto/dso/dso_vms.c", "$OPENSSL_BUNDLE_DIR/crypto/dso/dso_beos.c", "$OPENSSL_BUNDLE_DIR/crypto/engine/eng_err.c", "$OPENSSL_BUNDLE_DIR/crypto/engine/eng_lib.c", "$OPENSSL_BUNDLE_DIR/crypto/engine/eng_list.c", "$OPENSSL_BUNDLE_DIR/crypto/engine/eng_init.c", "$OPENSSL_BUNDLE_DIR/crypto/engine/eng_ctrl.c", "$OPENSSL_BUNDLE_DIR/crypto/engine/eng_table.c", "$OPENSSL_BUNDLE_DIR/crypto/engine/eng_pkey.c", "$OPENSSL_BUNDLE_DIR/crypto/engine/eng_fat.c", "$OPENSSL_BUNDLE_DIR/crypto/engine/eng_all.c", "$OPENSSL_BUNDLE_DIR/crypto/engine/tb_rsa.c", "$OPENSSL_BUNDLE_DIR/crypto/engine/tb_dsa.c", "$OPENSSL_BUNDLE_DIR/crypto/engine/tb_ecdsa.c", "$OPENSSL_BUNDLE_DIR/crypto/engine/tb_dh.c", "$OPENSSL_BUNDLE_DIR/crypto/engine/tb_ecdh.c", "$OPENSSL_BUNDLE_DIR/crypto/engine/tb_rand.c", "$OPENSSL_BUNDLE_DIR/crypto/engine/tb_store.c", "$OPENSSL_BUNDLE_DIR/crypto/engine/tb_cipher.c", "$OPENSSL_BUNDLE_DIR/crypto/engine/tb_digest.c", "$OPENSSL_BUNDLE_DIR/crypto/engine/tb_pkmeth.c", "$OPENSSL_BUNDLE_DIR/crypto/engine/tb_asnmth.c", "$OPENSSL_BUNDLE_DIR/crypto/engine/eng_openssl.c", "$OPENSSL_BUNDLE_DIR/crypto/engine/eng_cnf.c", "$OPENSSL_BUNDLE_DIR/crypto/engine/eng_dyn.c", "$OPENSSL_BUNDLE_DIR/crypto/engine/eng_cryptodev.c", "$OPENSSL_BUNDLE_DIR/crypto/buffer/buffer.c", "$OPENSSL_BUNDLE_DIR/crypto/buffer/buf_err.c", "$OPENSSL_BUNDLE_DIR/crypto/bio/bio_lib.c", "$OPENSSL_BUNDLE_DIR/crypto/bio/bio_cb.c", "$OPENSSL_BUNDLE_DIR/crypto/bio/bio_err.c", "$OPENSSL_BUNDLE_DIR/crypto/bio/bss_mem.c", "$OPENSSL_BUNDLE_DIR/crypto/bio/bss_null.c", "$OPENSSL_BUNDLE_DIR/crypto/bio/bss_fd.c", "$OPENSSL_BUNDLE_DIR/crypto/bio/bss_file.c", "$OPENSSL_BUNDLE_DIR/crypto/bio/bss_sock.c", "$OPENSSL_BUNDLE_DIR/crypto/bio/bss_conn.c", "$OPENSSL_BUNDLE_DIR/crypto/bio/bf_null.c", "$OPENSSL_BUNDLE_DIR/crypto/bio/bf_buff.c", "$OPENSSL_BUNDLE_DIR/crypto/bio/b_print.c", "$OPENSSL_BUNDLE_DIR/crypto/bio/b_dump.c", "$OPENSSL_BUNDLE_DIR/crypto/bio/b_sock.c", "$OPENSSL_BUNDLE_DIR/crypto/bio/bss_acpt.c", "$OPENSSL_BUNDLE_DIR/crypto/bio/bf_nbio.c", "$OPENSSL_BUNDLE_DIR/crypto/bio/bss_log.c", "$OPENSSL_BUNDLE_DIR/crypto/bio/bss_bio.c", "$OPENSSL_BUNDLE_DIR/crypto/bio/bss_dgram.c", "$OPENSSL_BUNDLE_DIR/crypto/stack/stack.c", "$OPENSSL_BUNDLE_DIR/crypto/lhash/lhash.c", "$OPENSSL_BUNDLE_DIR/crypto/lhash/lh_stats.c", "$OPENSSL_BUNDLE_DIR/crypto/rand/md_rand.c", "$OPENSSL_BUNDLE_DIR/crypto/rand/randfile.c", "$OPENSSL_BUNDLE_DIR/crypto/rand/rand_lib.c", "$OPENSSL_BUNDLE_DIR/crypto/rand/rand_err.c", "$OPENSSL_BUNDLE_DIR/crypto/rand/rand_egd.c", "$OPENSSL_BUNDLE_DIR/crypto/rand/rand_win.c", "$OPENSSL_BUNDLE_DIR/crypto/rand/rand_unix.c", "$OPENSSL_BUNDLE_DIR/crypto/rand/rand_os2.c", "$OPENSSL_BUNDLE_DIR/crypto/rand/rand_nw.c", "$OPENSSL_BUNDLE_DIR/crypto/err/err.c", "$OPENSSL_BUNDLE_DIR/crypto/err/err_all.c", "$OPENSSL_BUNDLE_DIR/crypto/err/err_prn.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/encode.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/digest.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/evp_enc.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/evp_key.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/evp_acnf.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/e_des.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/e_bf.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/e_idea.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/e_des3.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/e_camellia.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/e_rc4.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/e_aes.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/names.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/e_seed.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/e_xcbc_d.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/e_rc2.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/e_cast.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/e_rc5.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/m_null.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/m_md2.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/m_md4.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/m_md5.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/m_sha.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/m_sha1.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/m_wp.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/m_dss.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/m_dss1.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/m_mdc2.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/m_ripemd.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/m_ecdsa.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/p_open.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/p_seal.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/p_sign.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/p_verify.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/p_lib.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/p_enc.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/p_dec.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/bio_md.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/bio_b64.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/bio_enc.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/evp_err.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/e_null.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/c_all.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/c_allc.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/c_alld.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/evp_lib.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/bio_ok.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/evp_pkey.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/evp_pbe.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/p5_crpt.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/p5_crpt2.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/e_old.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/pmeth_lib.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/pmeth_fn.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/pmeth_gn.c", "$OPENSSL_BUNDLE_DIR/crypto/evp/m_sigver.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/a_object.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/a_bitstr.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/a_utctm.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/a_gentm.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/a_time.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/a_int.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/a_octet.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/a_print.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/a_type.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/a_set.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/a_dup.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/a_d2i_fp.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/a_i2d_fp.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/a_enum.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/a_utf8.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/a_sign.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/a_digest.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/a_verify.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/a_mbstr.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/a_strex.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/x_algor.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/x_val.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/x_pubkey.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/x_sig.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/x_req.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/x_attrib.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/x_bignum.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/x_long.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/x_name.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/x_x509.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/x_x509a.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/x_crl.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/x_info.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/x_spki.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/nsseq.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/x_nx509.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/d2i_pu.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/d2i_pr.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/i2d_pu.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/i2d_pr.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/t_req.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/t_x509.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/t_x509a.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/t_crl.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/t_pkey.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/t_spki.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/t_bitst.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/tasn_new.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/tasn_fre.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/tasn_enc.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/tasn_dec.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/tasn_utl.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/tasn_typ.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/tasn_prn.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/ameth_lib.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/f_int.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/f_string.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/n_pkey.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/f_enum.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/x_pkey.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/a_bool.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/x_exten.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/bio_asn1.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/bio_ndef.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/asn_mime.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/asn1_gen.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/asn1_par.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/asn1_lib.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/asn1_err.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/a_bytes.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/a_strnid.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/evp_asn1.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/asn_pack.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/p5_pbe.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/p5_pbev2.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/p8_pkey.c", "$OPENSSL_BUNDLE_DIR/crypto/asn1/asn_moid.c", "$OPENSSL_BUNDLE_DIR/crypto/pem/pem_sign.c", "$OPENSSL_BUNDLE_DIR/crypto/pem/pem_seal.c", "$OPENSSL_BUNDLE_DIR/crypto/pem/pem_info.c", "$OPENSSL_BUNDLE_DIR/crypto/pem/pem_lib.c", "$OPENSSL_BUNDLE_DIR/crypto/pem/pem_all.c", "$OPENSSL_BUNDLE_DIR/crypto/pem/pem_err.c", "$OPENSSL_BUNDLE_DIR/crypto/pem/pem_x509.c", "$OPENSSL_BUNDLE_DIR/crypto/pem/pem_xaux.c", "$OPENSSL_BUNDLE_DIR/crypto/pem/pem_oth.c", "$OPENSSL_BUNDLE_DIR/crypto/pem/pem_pk8.c", "$OPENSSL_BUNDLE_DIR/crypto/pem/pem_pkey.c", "$OPENSSL_BUNDLE_DIR/crypto/pem/pvkfmt.c", "$OPENSSL_BUNDLE_DIR/crypto/x509/x509_def.c", "$OPENSSL_BUNDLE_DIR/crypto/x509/x509_d2.c", "$OPENSSL_BUNDLE_DIR/crypto/x509/x509_r2x.c", "$OPENSSL_BUNDLE_DIR/crypto/x509/x509_cmp.c", "$OPENSSL_BUNDLE_DIR/crypto/x509/x509_obj.c", "$OPENSSL_BUNDLE_DIR/crypto/x509/x509_req.c", "$OPENSSL_BUNDLE_DIR/crypto/x509/x509spki.c", "$OPENSSL_BUNDLE_DIR/crypto/x509/x509_vfy.c", "$OPENSSL_BUNDLE_DIR/crypto/x509/x509_set.c", "$OPENSSL_BUNDLE_DIR/crypto/x509/x509cset.c", "$OPENSSL_BUNDLE_DIR/crypto/x509/x509rset.c", "$OPENSSL_BUNDLE_DIR/crypto/x509/x509_err.c", "$OPENSSL_BUNDLE_DIR/crypto/x509/x509name.c", "$OPENSSL_BUNDLE_DIR/crypto/x509/x509_v3.c", "$OPENSSL_BUNDLE_DIR/crypto/x509/x509_ext.c", "$OPENSSL_BUNDLE_DIR/crypto/x509/x509_att.c", "$OPENSSL_BUNDLE_DIR/crypto/x509/x509type.c", "$OPENSSL_BUNDLE_DIR/crypto/x509/x509_lu.c", "$OPENSSL_BUNDLE_DIR/crypto/x509/x_all.c", "$OPENSSL_BUNDLE_DIR/crypto/x509/x509_txt.c", "$OPENSSL_BUNDLE_DIR/crypto/x509/x509_trs.c", "$OPENSSL_BUNDLE_DIR/crypto/x509/by_file.c", "$OPENSSL_BUNDLE_DIR/crypto/x509/by_dir.c", "$OPENSSL_BUNDLE_DIR/crypto/x509/x509_vpm.c", "$OPENSSL_BUNDLE_DIR/crypto/x509v3/v3_bcons.c", "$OPENSSL_BUNDLE_DIR/crypto/x509v3/v3_bitst.c", "$OPENSSL_BUNDLE_DIR/crypto/x509v3/v3_conf.c", "$OPENSSL_BUNDLE_DIR/crypto/x509v3/v3_extku.c", "$OPENSSL_BUNDLE_DIR/crypto/x509v3/v3_ia5.c", "$OPENSSL_BUNDLE_DIR/crypto/x509v3/v3_lib.c", "$OPENSSL_BUNDLE_DIR/crypto/x509v3/v3_prn.c", "$OPENSSL_BUNDLE_DIR/crypto/x509v3/v3_utl.c", "$OPENSSL_BUNDLE_DIR/crypto/x509v3/v3err.c", "$OPENSSL_BUNDLE_DIR/crypto/x509v3/v3_genn.c", "$OPENSSL_BUNDLE_DIR/crypto/x509v3/v3_alt.c", "$OPENSSL_BUNDLE_DIR/crypto/x509v3/v3_skey.c", "$OPENSSL_BUNDLE_DIR/crypto/x509v3/v3_akey.c", "$OPENSSL_BUNDLE_DIR/crypto/x509v3/v3_pku.c", "$OPENSSL_BUNDLE_DIR/crypto/x509v3/v3_int.c", "$OPENSSL_BUNDLE_DIR/crypto/x509v3/v3_enum.c", "$OPENSSL_BUNDLE_DIR/crypto/x509v3/v3_sxnet.c", "$OPENSSL_BUNDLE_DIR/crypto/x509v3/v3_cpols.c", "$OPENSSL_BUNDLE_DIR/crypto/x509v3/v3_crld.c", "$OPENSSL_BUNDLE_DIR/crypto/x509v3/v3_purp.c", "$OPENSSL_BUNDLE_DIR/crypto/x509v3/v3_info.c", "$OPENSSL_BUNDLE_DIR/crypto/x509v3/v3_ocsp.c", "$OPENSSL_BUNDLE_DIR/crypto/x509v3/v3_akeya.c", "$OPENSSL_BUNDLE_DIR/crypto/x509v3/v3_pmaps.c", "$OPENSSL_BUNDLE_DIR/crypto/x509v3/v3_pcons.c", "$OPENSSL_BUNDLE_DIR/crypto/x509v3/v3_ncons.c", "$OPENSSL_BUNDLE_DIR/crypto/x509v3/v3_pcia.c", "$OPENSSL_BUNDLE_DIR/crypto/x509v3/v3_pci.c", "$OPENSSL_BUNDLE_DIR/crypto/x509v3/pcy_cache.c", "$OPENSSL_BUNDLE_DIR/crypto/x509v3/pcy_node.c", "$OPENSSL_BUNDLE_DIR/crypto/x509v3/pcy_data.c", "$OPENSSL_BUNDLE_DIR/crypto/x509v3/pcy_map.c", "$OPENSSL_BUNDLE_DIR/crypto/x509v3/pcy_tree.c", "$OPENSSL_BUNDLE_DIR/crypto/x509v3/pcy_lib.c", "$OPENSSL_BUNDLE_DIR/crypto/x509v3/v3_asid.c", "$OPENSSL_BUNDLE_DIR/crypto/x509v3/v3_addr.c", "$OPENSSL_BUNDLE_DIR/crypto/conf/conf_err.c", "$OPENSSL_BUNDLE_DIR/crypto/conf/conf_lib.c", "$OPENSSL_BUNDLE_DIR/crypto/conf/conf_api.c", "$OPENSSL_BUNDLE_DIR/crypto/conf/conf_def.c", "$OPENSSL_BUNDLE_DIR/crypto/conf/conf_mod.c", "$OPENSSL_BUNDLE_DIR/crypto/conf/conf_mall.c", "$OPENSSL_BUNDLE_DIR/crypto/conf/conf_sap.c", "$OPENSSL_BUNDLE_DIR/crypto/txt_db/txt_db.c", "$OPENSSL_BUNDLE_DIR/crypto/pkcs7/pk7_asn1.c", "$OPENSSL_BUNDLE_DIR/crypto/pkcs7/pk7_lib.c", "$OPENSSL_BUNDLE_DIR/crypto/pkcs7/pkcs7err.c", "$OPENSSL_BUNDLE_DIR/crypto/pkcs7/pk7_doit.c", "$OPENSSL_BUNDLE_DIR/crypto/pkcs7/pk7_smime.c", "$OPENSSL_BUNDLE_DIR/crypto/pkcs7/pk7_attr.c", "$OPENSSL_BUNDLE_DIR/crypto/pkcs7/pk7_mime.c", "$OPENSSL_BUNDLE_DIR/crypto/pkcs7/bio_pk7.c", "$OPENSSL_BUNDLE_DIR/crypto/pkcs12/p12_add.c", "$OPENSSL_BUNDLE_DIR/crypto/pkcs12/p12_asn.c", "$OPENSSL_BUNDLE_DIR/crypto/pkcs12/p12_attr.c", "$OPENSSL_BUNDLE_DIR/crypto/pkcs12/p12_crpt.c", "$OPENSSL_BUNDLE_DIR/crypto/pkcs12/p12_crt.c", "$OPENSSL_BUNDLE_DIR/crypto/pkcs12/p12_decr.c", "$OPENSSL_BUNDLE_DIR/crypto/pkcs12/p12_init.c", "$OPENSSL_BUNDLE_DIR/crypto/pkcs12/p12_key.c", "$OPENSSL_BUNDLE_DIR/crypto/pkcs12/p12_kiss.c", "$OPENSSL_BUNDLE_DIR/crypto/pkcs12/p12_mutl.c", "$OPENSSL_BUNDLE_DIR/crypto/pkcs12/p12_utl.c", "$OPENSSL_BUNDLE_DIR/crypto/pkcs12/p12_npas.c", "$OPENSSL_BUNDLE_DIR/crypto/pkcs12/pk12err.c", "$OPENSSL_BUNDLE_DIR/crypto/pkcs12/p12_p8d.c", "$OPENSSL_BUNDLE_DIR/crypto/pkcs12/p12_p8e.c", "$OPENSSL_BUNDLE_DIR/crypto/comp/comp_lib.c", "$OPENSSL_BUNDLE_DIR/crypto/comp/comp_err.c", "$OPENSSL_BUNDLE_DIR/crypto/comp/c_rle.c", "$OPENSSL_BUNDLE_DIR/crypto/comp/c_zlib.c", "$OPENSSL_BUNDLE_DIR/crypto/ocsp/ocsp_asn.c", "$OPENSSL_BUNDLE_DIR/crypto/ocsp/ocsp_ext.c", "$OPENSSL_BUNDLE_DIR/crypto/ocsp/ocsp_ht.c", "$OPENSSL_BUNDLE_DIR/crypto/ocsp/ocsp_lib.c", "$OPENSSL_BUNDLE_DIR/crypto/ocsp/ocsp_cl.c", "$OPENSSL_BUNDLE_DIR/crypto/ocsp/ocsp_srv.c", "$OPENSSL_BUNDLE_DIR/crypto/ocsp/ocsp_prn.c", "$OPENSSL_BUNDLE_DIR/crypto/ocsp/ocsp_vfy.c", "$OPENSSL_BUNDLE_DIR/crypto/ocsp/ocsp_err.c", "$OPENSSL_BUNDLE_DIR/crypto/ui/ui_err.c", "$OPENSSL_BUNDLE_DIR/crypto/ui/ui_lib.c", "$OPENSSL_BUNDLE_DIR/crypto/ui/ui_openssl.c", "$OPENSSL_BUNDLE_DIR/crypto/ui/ui_util.c", "$OPENSSL_BUNDLE_DIR/crypto/ui/ui_compat.c", "$OPENSSL_BUNDLE_DIR/crypto/krb5/krb5_asn.c", "$OPENSSL_BUNDLE_DIR/crypto/cms/cms_lib.c", "$OPENSSL_BUNDLE_DIR/crypto/cms/cms_asn1.c", "$OPENSSL_BUNDLE_DIR/crypto/cms/cms_att.c", "$OPENSSL_BUNDLE_DIR/crypto/cms/cms_io.c", "$OPENSSL_BUNDLE_DIR/crypto/cms/cms_smime.c", "$OPENSSL_BUNDLE_DIR/crypto/cms/cms_err.c", "$OPENSSL_BUNDLE_DIR/crypto/cms/cms_sd.c", "$OPENSSL_BUNDLE_DIR/crypto/cms/cms_dd.c", "$OPENSSL_BUNDLE_DIR/crypto/cms/cms_cd.c", "$OPENSSL_BUNDLE_DIR/crypto/cms/cms_env.c", "$OPENSSL_BUNDLE_DIR/crypto/cms/cms_enc.c", "$OPENSSL_BUNDLE_DIR/crypto/cms/cms_ess.c", "$OPENSSL_BUNDLE_DIR/crypto/pqueue/pqueue.c", "$OPENSSL_BUNDLE_DIR/crypto/ts/ts_err.c", "$OPENSSL_BUNDLE_DIR/crypto/ts/ts_req_utils.c", "$OPENSSL_BUNDLE_DIR/crypto/ts/ts_req_print.c", "$OPENSSL_BUNDLE_DIR/crypto/ts/ts_rsp_utils.c", "$OPENSSL_BUNDLE_DIR/crypto/ts/ts_rsp_print.c", "$OPENSSL_BUNDLE_DIR/crypto/ts/ts_rsp_sign.c", "$OPENSSL_BUNDLE_DIR/crypto/ts/ts_rsp_verify.c", "$OPENSSL_BUNDLE_DIR/crypto/ts/ts_verify_ctx.c", "$OPENSSL_BUNDLE_DIR/crypto/ts/ts_lib.c", "$OPENSSL_BUNDLE_DIR/crypto/ts/ts_conf.c", "$OPENSSL_BUNDLE_DIR/crypto/ts/ts_asn1.c", ]) openssl_env.StaticLibrary("ssl", [ "$OPENSSL_BUNDLE_DIR/ssl/s2_meth.c", "$OPENSSL_BUNDLE_DIR/ssl/s2_srvr.c", "$OPENSSL_BUNDLE_DIR/ssl/s2_clnt.c", "$OPENSSL_BUNDLE_DIR/ssl/s2_lib.c", "$OPENSSL_BUNDLE_DIR/ssl/s2_enc.c", "$OPENSSL_BUNDLE_DIR/ssl/s2_pkt.c", "$OPENSSL_BUNDLE_DIR/ssl/s3_meth.c", "$OPENSSL_BUNDLE_DIR/ssl/s3_srvr.c", "$OPENSSL_BUNDLE_DIR/ssl/s3_clnt.c", "$OPENSSL_BUNDLE_DIR/ssl/s3_lib.c", "$OPENSSL_BUNDLE_DIR/ssl/s3_enc.c", "$OPENSSL_BUNDLE_DIR/ssl/s3_pkt.c", "$OPENSSL_BUNDLE_DIR/ssl/s3_both.c", "$OPENSSL_BUNDLE_DIR/ssl/s23_meth.c", "$OPENSSL_BUNDLE_DIR/ssl/s23_srvr.c", "$OPENSSL_BUNDLE_DIR/ssl/s23_clnt.c", "$OPENSSL_BUNDLE_DIR/ssl/s23_lib.c", "$OPENSSL_BUNDLE_DIR/ssl/s23_pkt.c", "$OPENSSL_BUNDLE_DIR/ssl/t1_meth.c", "$OPENSSL_BUNDLE_DIR/ssl/t1_srvr.c", "$OPENSSL_BUNDLE_DIR/ssl/t1_clnt.c", "$OPENSSL_BUNDLE_DIR/ssl/t1_lib.c", "$OPENSSL_BUNDLE_DIR/ssl/t1_enc.c", "$OPENSSL_BUNDLE_DIR/ssl/d1_meth.c", "$OPENSSL_BUNDLE_DIR/ssl/d1_srvr.c", "$OPENSSL_BUNDLE_DIR/ssl/d1_clnt.c", "$OPENSSL_BUNDLE_DIR/ssl/d1_lib.c", "$OPENSSL_BUNDLE_DIR/ssl/d1_pkt.c", "$OPENSSL_BUNDLE_DIR/ssl/d1_both.c", "$OPENSSL_BUNDLE_DIR/ssl/d1_enc.c", "$OPENSSL_BUNDLE_DIR/ssl/ssl_lib.c", "$OPENSSL_BUNDLE_DIR/ssl/ssl_err2.c", "$OPENSSL_BUNDLE_DIR/ssl/ssl_cert.c", "$OPENSSL_BUNDLE_DIR/ssl/ssl_sess.c", "$OPENSSL_BUNDLE_DIR/ssl/ssl_ciph.c", "$OPENSSL_BUNDLE_DIR/ssl/ssl_stat.c", "$OPENSSL_BUNDLE_DIR/ssl/ssl_rsa.c", "$OPENSSL_BUNDLE_DIR/ssl/ssl_asn1.c", "$OPENSSL_BUNDLE_DIR/ssl/ssl_txt.c", "$OPENSSL_BUNDLE_DIR/ssl/ssl_algs.c", "$OPENSSL_BUNDLE_DIR/ssl/bio_ssl.c", "$OPENSSL_BUNDLE_DIR/ssl/ssl_err.c", "$OPENSSL_BUNDLE_DIR/ssl/kssl.c", "$OPENSSL_BUNDLE_DIR/ssl/t1_reneg.c", ]) openssl_env.WriteVal("$OPENSSL_BUNDLE_DIR/crypto/buildinf.h", openssl_env.Value("#define CFLAGS \"\"\n#define PLATFORM \"-\"\n#define DATE \"-\"")) swift-im-2.0+dev6/3rdParty/SQLite/0000755000175000017500000000000012227051774016534 5ustar kismithkismithswift-im-2.0+dev6/3rdParty/SQLite/SConscript0000644000175000017500000000111612227051773020544 0ustar kismithkismithImport("env") if env.get("SQLITE_BUNDLED", False) : ################################################################################ # Flags ################################################################################ if env["SCONS_STAGE"] == "flags" : env["SQLITE_FLAGS"] = { "CPPPATH": [Dir(".")], "LIBPATH": [Dir(".")], } if env["SCONS_STAGE"] == "build" : myenv = env.Clone() myenv.Replace(CCFLAGS = [flag for flag in env["CCFLAGS"] if flag not in ["-W", "-Wall"]]) myenv.Append(CPPPATH = ["."]) env["SQLITE_OBJECTS"] = myenv.SwiftenObject(["sqlite3.c"]) swift-im-2.0+dev6/3rdParty/CppUnit/0000755000175000017500000000000012227051774016755 5ustar kismithkismithswift-im-2.0+dev6/3rdParty/CppUnit/SConscript0000644000175000017500000000436312227051773020774 0ustar kismithkismithImport("env") if env["TEST"] : if env["PLATFORM"] == "win32" : cppflags = ["/I" + Dir("src/include").abspath] else : cppflags = [("-isystem", Dir("src/include").abspath)] ################################################################################ # Module flags ################################################################################ if env["SCONS_STAGE"] == "flags" : env["CPPUNIT_FLAGS"] = { "CPPPATH": [env.Dir(".")], "CPPFLAGS": cppflags, "LIBPATH": [env.Dir(".")], "LIBS": ["Swiften_CppUnit"] } ################################################################################ # Build ################################################################################ if env["SCONS_STAGE"] == "build" : myenv = env.Clone() myenv.Replace(CXXFLAGS = [flag for flag in env["CXXFLAGS"] if not flag.startswith("-W")]) myenv.Append(CPPPATH = ["src/include", "."]) sources = [ "src/src/cppunit/TextTestRunner.cpp", "src/src/cppunit/TextTestProgressListener.cpp", "src/src/cppunit/BriefTestProgressListener.cpp", "src/src/cppunit/TextOutputter.cpp", "src/src/cppunit/XmlOutputter.cpp", "src/src/cppunit/XmlElement.cpp", "src/src/cppunit/XmlDocument.cpp", "src/src/cppunit/StringTools.cpp", "src/src/cppunit/DefaultProtector.cpp", "src/src/cppunit/Protector.cpp", "src/src/cppunit/ProtectorChain.cpp", "src/src/cppunit/SynchronizedObject.cpp", "src/src/cppunit/SourceLine.cpp", "src/src/cppunit/TestRunner.cpp", "src/src/cppunit/TestFactoryRegistry.cpp", "src/src/cppunit/TestSuite.cpp", "src/src/cppunit/TestSuiteBuilderContext.cpp", "src/src/cppunit/TestResult.cpp", "src/src/cppunit/TestResultCollector.cpp", "src/src/cppunit/TestSuccessListener.cpp", "src/src/cppunit/TestComposite.cpp", "src/src/cppunit/TestCase.cpp", "src/src/cppunit/TestFailure.cpp", "src/src/cppunit/TestLeaf.cpp", "src/src/cppunit/TestNamer.cpp", "src/src/cppunit/Asserter.cpp", "src/src/cppunit/TypeInfoHelper.cpp", "src/src/cppunit/Exception.cpp", "src/src/cppunit/Message.cpp", "src/src/cppunit/AdditionalMessage.cpp", "src/src/cppunit/Test.cpp", "src/src/cppunit/TestPath.cpp" ] myenv.StaticLibrary("Swiften_CppUnit", sources) swift-im-2.0+dev6/3rdParty/Snarl/0000755000175000017500000000000012227051774016452 5ustar kismithkismithswift-im-2.0+dev6/3rdParty/Snarl/SConscript0000644000175000017500000000111412227051773020460 0ustar kismithkismithImport("env") ################################################################################ # Flags ################################################################################ if env.get("HAVE_SNARL", False) : if env["SCONS_STAGE"] == "flags" : env["SNARL_FLAGS"] = { "CPPPATH": [Dir(".")], "LIBPATH": [Dir(".")], "LIBS": ["Snarl"], } elif env["SCONS_STAGE"] == "build" : myenv = env.Clone() myenv.Replace(CCFLAGS = [flag for flag in env["CCFLAGS"] if flag not in ["-W", "-Wall"]]) myenv.StaticLibrary("Snarl", ["SnarlInterface.cpp"], CPPPATH = ["."]) swift-im-2.0+dev6/3rdParty/Boost/0000755000175000017500000000000012227051774016461 5ustar kismithkismithswift-im-2.0+dev6/3rdParty/Boost/SConscript0000644000175000017500000001322512227051773020475 0ustar kismithkismithImport("env") # FIXME: Remove this when Boost UUID is present on most distros if env.get("BOOST_BUNDLED_UUID_ONLY", False) : # Cherry pick UUID out of the 3rdParty dir, install it in a separate # dir, and use this as an include path. if env["SCONS_STAGE"] == "flags" : if env["PLATFORM"] == "win32" : uuid_cppflags = ["/I" + Dir("uuid").abspath] else : uuid_cppflags = [("-isystem", Dir("uuid").abspath)] env["BOOST_FLAGS"]["CPPFLAGS"] = env["BOOST_FLAGS"].get("CPPFLAGS", []) + uuid_cppflags elif env["SCONS_STAGE"] == "build" : env.Install("uuid/boost", "src/boost/uuid") elif env.get("BOOST_BUNDLED", False) : ################################################################################ # Common ################################################################################ cppdefines = ["BOOST_ALL_NO_LIB", "BOOST_SYSTEM_NO_DEPRECATED"] #if env["PLATFORM"] == "darwin" : # cppdefines += ["BOOST_ASIO_DISABLE_KQUEUE"] if env["PLATFORM"] == "win32" : cppflags = ["/I" + Dir("#/3rdParty/Boost/src").abspath] else : cppflags = [("-isystem", Dir("#/3rdParty/Boost/src").abspath)] ################################################################################ # Flags ################################################################################ if env["SCONS_STAGE"] == "flags" : env["BOOST_FLAGS"] = { "CPPFLAGS": cppflags, "CPPDEFINES": cppdefines, "LIBPATH": [Dir(".")], "LIBS": ["Swiften_Boost"] } if env["PLATFORM"] == "win32" : env["BOOST_FLAGS"]["CPPDEFINES"] += [("_WIN32_WINNT", "0x0501")] if env["PLATFORM"] == "cygwin" : env["BOOST_FLAGS"]["CPPDEFINES"] += ["__USE_W32_SOCKETS"] ################################################################################ # Build ################################################################################ if env["SCONS_STAGE"] == "build" : myenv = env.Clone() myenv.Replace(CXXFLAGS = [flag for flag in env["CXXFLAGS"] if not flag.startswith("-W")]) sources = [ "src/libs/date_time/src/gregorian/date_generators.cpp", "src/libs/date_time/src/gregorian/greg_month.cpp", "src/libs/date_time/src/gregorian/greg_weekday.cpp", "src/libs/date_time/src/gregorian/gregorian_types.cpp", "src/libs/date_time/src/posix_time/posix_time_types.cpp", "src/libs/system/src/error_code.cpp", "src/libs/thread/src/tss_null.cpp", "src/libs/signals/src/connection.cpp", "src/libs/signals/src/named_slot_map.cpp", "src/libs/signals/src/signal_base.cpp", "src/libs/signals/src/slot.cpp", "src/libs/signals/src/trackable.cpp", "src/libs/filesystem/v3/src/codecvt_error_category.cpp", "src/libs/filesystem/v3/src/operations.cpp", "src/libs/filesystem/v3/src/path.cpp", "src/libs/filesystem/v3/src/path_traits.cpp", "src/libs/filesystem/v3/src/portability.cpp", "src/libs/filesystem/v3/src/unique_path.cpp", "src/libs/filesystem/v3/src/windows_file_codecvt.cpp", # "src/libs/filesystem/v2/src/v2_operations.cpp", # "src/libs/filesystem/v2/src/v2_path.cpp", # "src/libs/filesystem/v2/src/v2_portability.cpp", "src/libs/filesystem/v3/src/filesystem_utf8_codecvt_facet.cpp", "src/libs/regex/src/c_regex_traits.cpp", "src/libs/regex/src/cpp_regex_traits.cpp", "src/libs/regex/src/cregex.cpp", "src/libs/regex/src/fileiter.cpp", "src/libs/regex/src/icu.cpp", "src/libs/regex/src/instances.cpp", "src/libs/regex/src/posix_api.cpp", "src/libs/regex/src/regex.cpp", "src/libs/regex/src/regex_debug.cpp", "src/libs/regex/src/regex_raw_buffer.cpp", "src/libs/regex/src/regex_traits_defaults.cpp", "src/libs/regex/src/static_mutex.cpp", "src/libs/regex/src/w32_regex_traits.cpp", "src/libs/regex/src/wc_regex_traits.cpp", "src/libs/regex/src/wide_posix_api.cpp", "src/libs/regex/src/winstances.cpp", "src/libs/regex/src/usinstances.cpp", "src/libs/program_options/src/cmdline.cpp", "src/libs/program_options/src/config_file.cpp", "src/libs/program_options/src/convert.cpp", "src/libs/program_options/src/options_description.cpp", "src/libs/program_options/src/positional_options.cpp", "src/libs/program_options/src/split.cpp", "src/libs/program_options/src/program_options_utf8_codecvt_facet.cpp", "src/libs/program_options/src/value_semantic.cpp", "src/libs/program_options/src/variables_map.cpp", "src/libs/program_options/src/winmain.cpp"] if env["PLATFORM"] != "darwin" or env["target"] == "native" : sources += [ "src/libs/program_options/src/parsers.cpp", ] if env["PLATFORM"] != "win32" : sources += [ "src/libs/thread/src/pthread/once.cpp", "src/libs/thread/src/pthread/thread.cpp"] else : sources += [ "win32_stubs.cpp", "src/libs/thread/src/win32/thread.cpp", "src/libs/thread/src/win32/tss_dll.cpp", "src/libs/thread/src/win32/tss_pe.cpp"] myenv.MergeFlags(myenv["BOOST_FLAGS"]) myenv.StaticLibrary("Swiften_Boost", sources) if ARGUMENTS.get("BOOST_BUILD_BCP") or env.GetOption("clean") : bcp_env = myenv.Clone() bcp_env.MergeFlags(bcp_env["BOOST_FLAGS"]) bcp_env.Append(CPPPATH = ["src/tools/bcp"]) bcp_env.Replace(CPPDEFINES = [flag for flag in bcp_env["CPPDEFINES"] if flag[0] != "BOOST_FILESYSTEM_VERSION"]) bcp_env.Program("bcp", [ "src/tools/bcp/add_path.cpp", "src/tools/bcp/add_dependent_lib.cpp", "src/tools/bcp/bcp_imp.cpp", "src/tools/bcp/copy_path.cpp", "src/tools/bcp/file_types.cpp", "src/tools/bcp/fileview.cpp", "src/tools/bcp/licence_info.cpp", "src/tools/bcp/output_licence_info.cpp", "src/tools/bcp/path_operations.cpp", "src/tools/bcp/scan_cvs_path.cpp", "src/tools/bcp/scan_licence.cpp", "bcp.cpp", ]) swift-im-2.0+dev6/3rdParty/Breakpad/0000755000175000017500000000000012227051774017104 5ustar kismithkismithswift-im-2.0+dev6/3rdParty/Breakpad/SConscript0000644000175000017500000000607312227051773021123 0ustar kismithkismithImport("env") # Currently disabling breakpad for all platforms except windows if env["PLATFORM"] == "win32" : ################################################################################ # Module flags ################################################################################ if env["SCONS_STAGE"] == "flags" : env["HAVE_BREAKPAD"] = True env["BREAKPAD_FLAGS"] = { "CPPPATH": [env.Dir("src")], "CPPFLAGS": [], "LIBPATH": [env.Dir(".")], "LIBS": ["Swift_BreakPad"] } ################################################################################ # Build ################################################################################ if env["SCONS_STAGE"] == "build" : myenv = env.Clone() myenv.Replace(CXXFLAGS = [flag for flag in env["CXXFLAGS"] if not flag.startswith("-W")]) myenv.Append(CPPPATH = ["src"]) lib_sources = [] dumpsyms_sources = [] common_sources = [] if myenv["PLATFORM"] == "win32" : myenv.Append(CPPDEFINES = ["UNICODE"]) lib_sources += [ "src/client/windows/handler/exception_handler.cc", "src/client/windows/crash_generation/crash_generation_client.cc", "src/common/windows/guid_string.cc", ] if myenv["PLATFORM"] == "darwin" or myenv["PLATFORM"] == "linux" : lib_sources += [ ] common_sources += [ "src/common/md5.cc", ] if myenv["PLATFORM"] == "darwin" : myenv.Append(CPPDEFINES = ["HAVE_MACH_O_NLIST_H"]) lib_sources += [ "src/client/mac/handler/exception_handler.cc", "src/client/mac/handler/minidump_generator.cc", "src/client/mac/handler/dynamic_images.cc", "src/client/mac/handler/breakpad_nlist_64.cc", "src/client/mac/crash_generation/crash_generation_client.cc", "src/common/mac/MachIPC.mm", "src/common/mac/string_utilities.cc", "src/common/mac/bootstrap_compat.cc", "src/client/minidump_file_writer.cc", "src/common/string_conversion.cc", "src/common/convert_UTF.c", ] common_sources += [ "src/common/mac/macho_id.cc", "src/common/mac/macho_walker.cc", "src/common/mac/file_id.cc", "src/common/mac/macho_utilities.cc", ] dumpsyms_sources += [ "src/tools/mac/dump_syms/dump_syms_tool.mm", "src/common/mac/dump_syms.mm", "src/common/mac/macho_reader.cc", "src/common/dwarf/bytereader.cc", "src/common/dwarf/dwarf2reader.cc", "src/common/dwarf/dwarf2diehandler.cc", "src/common/dwarf_line_to_module.cc", "src/common/dwarf_cfi_to_module.cc", "src/common/dwarf_cu_to_module.cc", "src/common/stabs_to_module.cc", "src/common/stabs_reader.cc", "src/common/module.cc", "src/common/language.cc", ] if myenv["PLATFORM"] == "linux" : dumpsyms_sources += ["src/tools/linux/dump_syms/dump_syms.cc"] common_objects = myenv.StaticObject(common_sources) myenv.StaticLibrary("Swift_BreakPad", lib_sources + common_objects) #if myenv["PLATFORM"] == "darwin" or myenv["PLATFORM"] == "linux" : # myenv.Program("dump_syms", dumpsyms_sources + common_objects) else : if env["SCONS_STAGE"] == "flags" : env["HAVE_BREAKPAD"] = False env["BREAKPAD_FLAGS"] = {} swift-im-2.0+dev6/3rdParty/LibIDN/0000755000175000017500000000000012227051774016434 5ustar kismithkismithswift-im-2.0+dev6/3rdParty/LibIDN/SConscript0000644000175000017500000000354612227051773020455 0ustar kismithkismithImport(["env", "conf_env"]) if env.get("LIBIDN_BUNDLED", False) : ################################################################################ # Module flags ################################################################################ if env["SCONS_STAGE"] == "flags" : env["LIBIDN_FLAGS"] = { "CPPDEFINES": ["IDNA_STATIC"], "CPPPATH": [Dir("src")], } if env["PLATFORM"] == "win32" : env["LIBIDN_FLAGS"]["CPPPATH"] += [Dir("stubs/win32")] if env["MSVC_VERSION"][:3] == "9.0" : env["LIBIDN_FLAGS"]["CPPPATH"] += [Dir("stubs/win32/VC2008")] ################################################################################ # Build ################################################################################ if env["SCONS_STAGE"] == "build" : myenv = env.Clone() # Remove warn flags myenv.Replace(CCFLAGS = [flag for flag in env["CCFLAGS"] if flag not in ["-W", "-Wall"]]) # Check for strcasecmp() or replacement conf = Configure(conf_env) if not conf.CheckFunc('strcasecmp') : if conf.CheckFunc("stricmp") : myenv.Append(CPPDEFINES = [("strcasecmp", "stricmp")]) else : print "Error: Cannot find strcasecmp() or stricmp()" Exit(1) if not conf.CheckFunc('strncasecmp') : if conf.CheckFunc("strnicmp") : myenv.Append(CPPDEFINES = [("strncasecmp", "strnicmp")]) else : print "Error: Cannot find strncasecmp() or strnicmp()" Exit(1) conf.Finish() myenv.Append(CPPDEFINES = "IDNA_STATIC") myenv.Append(CPPPATH = ["src", "stubs"]) if myenv["PLATFORM"] == "win32" : myenv.Append(CPPPATH = "stubs/win32") if myenv["MSVC_VERSION"][:3] == "9.0" : myenv.Append(CPPPATH = "stubs/win32/VC2008") env["LIBIDN_OBJECTS"] = myenv.SwiftenObject([ "src/stringprep.c", "src/profiles.c", "src/rfc3454.c", "src/punycode.c", "src/idna.c", "src/toutf8.c", "src/nfkc.c" ]) swift-im-2.0+dev6/.project.vimrc0000644000175000017500000000063212227051773016451 0ustar kismithkismith" Some useful VIM stuff for developing Swift " " Add the following in your ~/.vimrc to automatically load this when " you open vim in the toplevel directory: " " if filereadable(".project.vimrc") " source .project.vimrc " endif " " CPPUnit QuicFix support set errorformat+=%E%.%#\ test:\ %.%#line:\ %l\ %f,%Z%m " SCons support set makeprg=python\ 3rdParty/SCons/scons.py\ check=1 set noexpandtab swift-im-2.0+dev6/COPYING.thirdparty0000644000175000017500000010526312227051773017115 0ustar kismithkismithSwift - An XMPP Client Copyright (C) 2009-2012 Kevin Smith and Remko Tronçon ================ Third-party code ================ These projects contain source code from third parties: - Patches from contributors - Google Breakpad - Boost - Expat - ZLib - LibIDN The licenses under which these contributions and libraries are released are listed below. ============ Contributors ============ These projects contain some code contributed under the BSD License, under the following terms: --- START OF BSD LICENSE Copyright (c) 2011, Arnt Gulbrandsen Copyright (c) 2011, Thilo Cestonaro Copyright (c) 2011, Vlad Voicu Copyright (c) 2011, Jan Kaluza Copyright (c) 2011, Tobias Markmann Copyright (c) 2011, Soren Dreijer Copyright (c) 2012, Vitaly Takmazov Copyright (c) 2012, Pavol Babincak All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of their organizations nor the names of the contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --- END OF BSD LICENSE --- START OF SIMPLIFIED BSD LICENSE Copyright (c) 2012 Isode Limited, London, England. Copyright (c) 2012 Yoann Blein. Copyright (c) 2012 Mateusz Piekos. Copyright (c) 2012 Catalin Badea. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WA RRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --- END OF SIMPLIFIED BSD LICENSE =============== Google Breakpad =============== Copyright (c) 2006, Google Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ===== Boost ===== Boost Software License - Version 1.0 - August 17th, 2003 Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ===== Expat ===== Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd and Clark Cooper Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006 Expat maintainers. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ==== ZLib ==== Copyright (C) 1995-2005 Jean-loup Gailly and Mark Adler This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. Jean-loup Gailly Mark Adler jloup@gzip.org madler@alumni.caltech.edu The data format used by the zlib library is described by RFCs (Request for Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). ====== LibIDN ====== GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! swift-im-2.0+dev6/SConstruct0000644000175000017500000000032512227051773015714 0ustar kismithkismithvariant_dir = SConscript("BuildTools/SCons/SConscript.boot") if variant_dir : SConscript("BuildTools/SCons/SConstruct", variant_dir = variant_dir, src_dir = ".") else : SConscript("BuildTools/SCons/SConstruct") swift-im-2.0+dev6/Swiftob/0000755000175000017500000000000012227051774015300 5ustar kismithkismithswift-im-2.0+dev6/Swiftob/MUCs.h0000644000175000017500000000241512227051774016262 0ustar kismithkismith/* * Copyright (c) 2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include #include #include namespace Swift { class Client; class MUC; } class Storage; using namespace Swift; class MUCs { public: MUCs(Client* client, Storage* storage); void join(const JID& room, boost::signal::slot_type successCallback, boost::function failureCallback); void part(const JID& room); bool contains(const JID& room); MUC::ref getMUC(const JID& room); const std::string& getDefaultNick() const {return defaultNick_;} bool setDefaultNick(const std::string& nick); private: void handleConnected(); void handleJoinFailed(const JID& room, ErrorPayload::ref error, boost::function failureCallback); void handleInitialJoinSuccess(); void handleInitialJoinFailure(const std::string&); void save(); private: MUCRegistry registry_; std::map mucs_; Client* client_; Storage* storage_; std::string defaultNick_; }; swift-im-2.0+dev6/Swiftob/SConscript0000644000175000017500000000061412227051774017313 0ustar kismithkismithImport("env") if env["SCONS_STAGE"] == "build": myenv = env.Clone() myenv.MergeFlags(myenv.get("LUA_FLAGS", {})) myenv.MergeFlags(myenv["SWIFTEN_FLAGS"]) myenv.MergeFlags(myenv["SWIFTEN_DEP_FLAGS"]) sources = [ "linit.cpp", "Swiftob.cpp", "Users.cpp", "Commands.cpp", "MUCs.cpp", "Storage.cpp", "LuaCommands.cpp", "main.cpp" ] swiftob = myenv.Program("swiftob", sources) swift-im-2.0+dev6/Swiftob/scripts/0000755000175000017500000000000012227051774016767 5ustar kismithkismithswift-im-2.0+dev6/Swiftob/scripts/echo.lua0000644000175000017500000000024712227051774020413 0ustar kismithkismithfunction echo_message(command, params, message) swiftob_reply_to(message, params) end swiftob_register_command("echo", "Anyone", "What did you say?", echo_message)swift-im-2.0+dev6/Swiftob/scripts/irssiLogs.lua0000644000175000017500000000077012227051774021454 0ustar kismithkismithfunction irssi_log_to_file(muc, line) filename = muc:gsub("@", "_at_") filename = filename:gsub("%.%.", "_") filename = filename:gsub("/", "_") filename = filename:gsub("\\", "_") file = io.open(filename, "a+") if file == nil then io.close() else file:write(line) file:write("\n") file:close() end end function irssi_log_message(body, muc, nick, message) time = os.date("%H:%M") irssi_log_to_file(muc, time.." <"..nick.."> "..body) end swiftob_register_listener(irssi_log_message) swift-im-2.0+dev6/Swiftob/scripts/logAllMessages.lua0000644000175000017500000000026212227051774022374 0ustar kismithkismithfunction log_a_message(body, muc, nick, message) print("Received line from '" .. nick .. "' in '" .. muc .. "':") print(body) end swiftob_register_listener(log_a_message) swift-im-2.0+dev6/Swiftob/scripts/badWords.lua0000644000175000017500000000062212227051774021237 0ustar kismithkismithfunction bad_words(body, muc, nick, message) words = {"sbwriel"} print("Received line from '" .. nick .. "' in '" .. muc .. "':") print(body) for _, word in pairs(words) do if string.len(string.match(body, word)) > 0 then --swiftob_reply_to(message, "Kicking "..nick.." for bad word "..word) swiftob_muc_kick(muc, nick) end end end swiftob_register_listener(bad_words) swift-im-2.0+dev6/Swiftob/scripts/eval.lua0000644000175000017500000000070212227051774020420 0ustar kismithkismith function eval_command(command, params, message) assert(loadstring(params))() swiftob_reply_to(message, "done") end function evalr_command(command, params, message) result = assert(loadstring(params))() swiftob_reply_to(message, "" .. result) end swiftob_register_command("eval", "Owner", "Evaluate an expression", eval_command) swiftob_register_command("evalr", "Owner", "Evaluate an expression and return the result", evalr_command) swift-im-2.0+dev6/Swiftob/scripts/version.lua0000644000175000017500000000175012227051774021162 0ustar kismithkismithfunction friendly_version(version) result = version['name'] if version['version'] ~= nil and version['version'] ~= "" then result = result.." version "..version['version'] end if version['os'] ~= nil and version['os'] ~= "" then result = result .." on "..version['os'] end return result end function version_command(command, params, message) jid = swiftob_muc_input_to_jid(params, message['from']) if jid == nil then else swiftob_get_software_version({ to=jid, timeout=10, success_callback=function(result) swiftob_reply_to(message, params.." is running "..friendly_version(result)) end, failure_callback=function(error) swiftob_reply_to(message, "Error getting version from "..params..": "..error) end, timeout_callback=function() swiftob_reply_to(message, "Timeout waiting for version from "..params) end}) end end swiftob_register_command("version", "Anyone", "Ask for someone's version", version_command)swift-im-2.0+dev6/Swiftob/scripts/agenda.lua0000644000175000017500000000505412227051774020715 0ustar kismithkismithagendas = {} currents = {} function full_agenda(from) fullagenda = {} fullagenda[1] = "Roll call" fullagenda[2] = "Agenda bashing" for i, v in ipairs(agendas[from]) do table.insert(fullagenda, v) end table.insert(fullagenda, "Date of next meeting") table.insert(fullagenda, "Any other business") return fullagenda end function agenda_full_command(command, params, message) from = message['frombare'] ensure_loaded(from) agenda = agendas[from] fullagenda = full_agenda(from) reply = "" for i, v in ipairs(fullagenda) do reply = reply..i..") "..v.."\n" end reply = reply.."Fini" swiftob_reply_to(message, reply) end function agenda_append_command(command, params, message) from = message['frombare'] agenda_append(from, params) agenda_save(from) swiftob_reply_to(message, "Done.") end function agenda_up_command(command, params, message) from = message['frombare'] ensure_loaded(from) up = tonumber(params) if up == nil then up = 1 end currents[from] = currents[from] + up if currents[from] <= 0 then currents[from] = 1 end item = full_agenda(from)[currents[from]] if item == nil then item = "Fini." end reply = currents[from]..") "..item swiftob_reply_to(message, reply) end function agenda_clear_command(command, params, message) from = message['frombare'] agendas[from] = {} agenda_save(from) swiftob_reply_to(message, "Done.") end function agenda_save(from) agenda = agendas[from] swiftob_store_setting("count@@@"..from, #agenda) for i, v in ipairs(agenda) do swiftob_store_setting(i.."@@@"..from, v) end end function ensure_loaded(from) if agendas[from] == nil then agenda_load(from) end end function agenda_load(from) agendas[from] = {} currents[from] = 0 num_items = tonumber(swiftob_get_setting("count@@@"..from)) if num_items == nil then num_items = 0 end for i = 1, num_items do agenda_append(from, swiftob_get_setting(i.."@@@"..from)) end end function agenda_append(from, item) ensure_loaded(from) agenda = agendas[from] table.insert(agenda, item) agendas[from] = agenda end swiftob_register_command("agenda", "Anyone", "print the full agenda", agenda_full_command) swiftob_register_command("agendaappend", "Owner", "append an item to the agenda", agenda_append_command) swiftob_register_command("agendaclear", "Owner", "clear the agenda", agenda_clear_command) swiftob_register_command("agendaup", "Owner", "Moves the current counter by n, and returns the current agenda item", agenda_up_command) swift-im-2.0+dev6/Swiftob/Commands.h0000644000175000017500000000616412227051774017221 0ustar kismithkismith/* * Copyright (c) 2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include #include #include namespace Swift { class Client; } class Storage; class Commands { typedef boost::function ListenerCallback; public: enum RoleList {Anyone, Owner}; public: class Command { public: Command(RoleList allowedBy, const std::string& description) : allowedBy_(allowedBy), description_(description) { } virtual ~Command() {}; boost::signal onReceived; RoleList getAllowedBy() {return allowedBy_;} std::string getDescription() {return description_;} private: RoleList allowedBy_; std::string description_; }; public: Commands(Users* users, Swift::Client* client, Storage* storage, MUCs* mucs); ~Commands(); bool hasCommand(const std::string&); bool runCommand(const std::string& command, const std::string& params, Swift::Message::ref message); void runListeners(Swift::Message::ref message); void replyTo(Swift::Message::ref source, std::string replyBody, bool outOfMUC = false); void registerCommand(const std::string& name, RoleList roles, const std::string& description, boost::function callback); void registerListener(ListenerCallback); void resetCommands(); void setRehashError(const std::string& error); public: boost::signal onReset; boost::signal onRestartRequested; private: void clearCommands(); bool roleIn(const Users::User::Role userRole, RoleList roles); void handleQuitCommand(const std::string& command, const std::string& params, Swift::Message::ref message); void handleHelpCommand(const std::string& command, const std::string& params, Swift::Message::ref message); void handleJoinCommand(const std::string& /*command*/, const std::string& params, Swift::Message::ref message); void handleJoinCommandSuccess(const Swift::JID& room, Swift::Message::ref message); void handleJoinCommandFailure(const Swift::JID& room, const std::string& error, Swift::Message::ref message); void handlePartCommand(const std::string& /*command*/, const std::string& params, Swift::Message::ref message); void handleRehashCommand(const std::string& command, const std::string& params, Swift::Message::ref message); void handleRestartCommand(const std::string& command, const std::string& params, Swift::Message::ref message); void handleChangeNick(const std::string& command, const std::string& params, Swift::Message::ref message); void handleChangeOwner(const std::string& command, const std::string& params, Swift::Message::ref message); private: std::map commands_; std::vector listeners_; Users* users_; Swift::Client* client_; Storage* storage_; MUCs* mucs_; std::string rehashError_; }; swift-im-2.0+dev6/Swiftob/LuaCommands.h0000644000175000017500000000576612227051774017672 0ustar kismithkismith/* * Copyright (c) 2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Swift; /** * Yes, there's an odd naming scheme going on here for methods. * normalCamelCase methods are methods called from C++ * lower_case_methods are exposed to Lua through wrappers and * l_lower_case_functions are functions directly exposed (often calling the lower_case_methods). */ class LuaCommands { public: class LuaCommand : public Commands::Command, boost::noncopyable { public: /** Takes ownership of lua and storage.*/ LuaCommand(Commands::RoleList allowedBy, const std::string& description, lua_State* lua, Storage* storage) : Command(allowedBy, description), lua_(lua), storage_(storage) { } virtual ~LuaCommand() { lua_close(lua_); delete storage_; }; private: lua_State* lua_; Storage* storage_; }; class Callbacks { public: Callbacks(int success, int failure, int timeout) : success(success), failure(failure), timeout(timeout) {}; int success; int failure; int timeout; void erase(lua_State *L) { lua_pushnil(L); lua_rawseti(L, LUA_REGISTRYINDEX, success); lua_pushnil(L); lua_rawseti(L, LUA_REGISTRYINDEX, failure); lua_pushnil(L); lua_rawseti(L, LUA_REGISTRYINDEX, timeout); } }; LuaCommands(Commands* commands, const std::string& path, Client* client, TimerFactory* timerFactory, MUCs* mucs); /* Public but aren't really part of the API */ void handleLuaCommand(int callbackIndex, lua_State* L, const std::string& command, const std::string& params, Message::ref message); void handleLuaListener(int callbackIndex, lua_State* L, Message::ref message); Commands* getCommands() {return commands_;} int get_software_version(lua_State *L); int muc_input_to_jid(lua_State *L); int store_setting(lua_State *L); int get_setting(lua_State *L); int muc_kick(lua_State *L); static LuaCommands* commandsFromLua(lua_State *L); static Storage* storageFromLua(lua_State *L); private: void registerCommands(); void loadScript(boost::filesystem::path filePath); void messageOntoStack(Message::ref message, lua_State* L); void handleSoftwareVersionResponse(boost::shared_ptr version, ErrorPayload::ref error, bool timeout, GetSoftwareVersionRequest::ref request, Timer::ref timer, lua_State* L, Callbacks callbacks); private: std::string path_; boost::filesystem::path scriptsPath_; Commands* commands_; MUCs* mucs_; Client* client_; TimerFactory* timerFactory_; }; swift-im-2.0+dev6/Swiftob/Swiftob.cpp0000644000175000017500000001111212227051774017415 0ustar kismithkismith/* * Copyright (c) 2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include po::options_description Swiftob::getOptionsDescription() { po::options_description result("Options"); result.add_options() ("path", po::value(), "Configuration folder") ("help", "produce help message") ("init", "Reset everything (Really, everything, be careful, you only want to use this on first run).") ("jid", po::value(), "JID to use") ("password", po::value(), "password") ("initial-owner", po::value(), "Initial bot owner (JID)") ; return result; } Swiftob::Swiftob(const po::variables_map& options) : options_(options), networkFactories_(&eventLoop_), quitting_(false) { path_ = options["path"].as(); client_ = new Swift::Client(Swift::JID(options["jid"].as()), options["password"].as(), &networkFactories_); storage_ = new Storage(boost::filesystem::path(path_) / "settings.txt"); mucs_ = NULL; users_ = NULL; commands_ = NULL; lua_ = NULL; init(); client_->onConnected.connect(boost::bind(&Swiftob::handleConnected, this)); client_->onDisconnected.connect(boost::bind(&Swiftob::handleDisconnected, this, _1)); client_->onMessageReceived.connect(boost::bind(&Swiftob::handleMessageReceived, this, _1)); if (options_.count("init") > 0) { } else if (options_.count("jid") > 0 || options_.count("password") > 0 || options_.count("initial-owner") == 0) { std::cout << "Ignoring initial config options without --initial" << std::endl; } client_->setAlwaysTrustCertificates(); client_->setSoftwareVersion("Swiftob", "pregit", ""); client_->connect(); eventLoop_.run(); } void Swiftob::init() { delete mucs_; mucs_ = new MUCs(client_, storage_); delete users_; users_ = new Users(client_, mucs_); delete commands_; commands_ = new Commands(users_, client_, storage_, mucs_); commands_->onRestartRequested.connect(boost::bind(&Swiftob::handleRestartRequested, this)); delete lua_; lua_ = new LuaCommands(commands_, path_, client_, networkFactories_.getTimerFactory(), mucs_); } void Swiftob::handleRestartRequested() { client_->disconnect(); init(); } void Swiftob::handleConnected() { std::cout << "Connected" << std::endl; if (options_.count("init") > 0) {}{ /* FIXME: Not ready for persistence yet*/ users_->clearAll(); users_->addUser(Users::User(Swift::JID(options_["initial-owner"].as()), Users::User::Owner)); } Swift::Presence::ref presence(new Swift::Presence()); presence->setStatus("Online and botty"); client_->getPresenceSender()->sendPresence(presence); } void Swiftob::handleDisconnected(const boost::optional& /*error*/) { std::cout << "Disconnected" << std::endl; /* FIXME: check if last connect was more than a minute ago. If so, go ahead and connect, if not then wait a minute before connecting.*/ if (quitting_) { eventLoop_.stop(); } else { client_->connect(); } } void Swiftob::handleMessageReceived(Swift::Message::ref message) { Swift::Message::Type type = message->getType(); if (type == Swift::Message::Error || type == Swift::Message::Headline) { std::cout << "Ignoring typed message" << std::endl; return; } std::string body = message->getBody(); std::cout << "Got message with body " << body << std::endl; if (body.size() == 0) { std::cout << "Not handling empty body" << std::endl; return; } /* Run through any full-message listeners */ commands_->runListeners(message); /*Convert body into !command if it's not a MUC, and it misses the bang*/ std::string bangBody(body); if (type != Swift::Message::Groupchat && body[0] != '!') { bangBody = "!" + body; } std::cout << "After banging, body is " << bangBody << std::endl; std::pair split = Swift::String::getSplittedAtFirst(bangBody, ' '); std::string commandName(split.first); commandName = Swift::String::getSplittedAtFirst(commandName, '!').second; /*FIXME: remove leading bang in commandName*/ if (commands_->hasCommand(commandName)) { std::cout << "Matched command " << commandName << std::endl; commands_->runCommand(commandName, split.second, message); } } Swiftob::~Swiftob() { delete lua_; delete commands_; delete storage_; delete users_; delete mucs_; delete client_; } int Swiftob::exec() { return 0; } swift-im-2.0+dev6/Swiftob/Users.cpp0000644000175000017500000000204012227051774017101 0ustar kismithkismith/* * Copyright (c) 2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include Users::Users(Client* client, MUCs* mucs) { client_ = client; mucs_ = mucs; } /* TODO: Store in roster */ void Users::clearAll() { users_.clear(); } void Users::addUser(const User& user) { users_.push_back(user); } Users::User::Role Users::getRoleForSender(Message::ref message) { JID jid = message->getFrom(); MUC::ref muc = mucs_->getMUC(message->getFrom().toBare()); if (muc && muc->hasOccupant(message->getFrom().getResource())) { MUCOccupant occupant = muc->getOccupant(message->getFrom().getResource()); if (occupant.getRealJID()) { jid = occupant.getRealJID().get(); } } foreach (User user, users_) { if (user.getJID().equals(jid.toBare(), JID::WithoutResource)) { return user.getRole(); } } return User::Unknown; } swift-im-2.0+dev6/Swiftob/Swiftob.h0000644000175000017500000000246512227051774017075 0ustar kismithkismith/* * Copyright (c) 2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include #include #include #include #include #include #include #include namespace po = boost::program_options; class Users; class Storage; class Swiftob { public: Swiftob(const po::variables_map& options); static po::options_description getOptionsDescription(); int exec(); ~Swiftob(); private: void init(); void handleRestartRequested(); void handleConnected(); void handleDisconnected(const boost::optional&); void handleMessageReceived(Swift::Message::ref); private: const po::variables_map options_; Swift::SimpleEventLoop eventLoop_; Swift::BoostNetworkFactories networkFactories_; Commands* commands_; LuaCommands* lua_; Storage* storage_; MUCs* mucs_; bool quitting_; Users* users_; std::string path_; Swift::Client* client_; }; swift-im-2.0+dev6/Swiftob/linit.cpp0000644000175000017500000000004712227051774017124 0ustar kismithkismith#include "../3rdParty/Lua/src/linit.c" swift-im-2.0+dev6/Swiftob/LuaCommands.cpp0000644000175000017500000003434112227051774020214 0ustar kismithkismith/* * Copyright (c) 2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include #include #define LUA_COMMANDS "__Lua_Commands" #define STORAGE "__Storage" LuaCommands::LuaCommands(Commands* commands, const std::string& path, Client* client, TimerFactory* timerFactory, MUCs* mucs) : path_(path), scriptsPath_(boost::filesystem::path(path_) / "scripts") { commands_ = commands; client_ = client; timerFactory_ = timerFactory; mucs_ = mucs; commands_->onReset.connect(boost::bind(&LuaCommands::registerCommands, this)); registerCommands(); } void LuaCommands::registerCommands() { std::cout << "Trying to load all scripts in " << scriptsPath_ << std::endl; if (boost::filesystem::exists(scriptsPath_) && boost::filesystem::is_directory(scriptsPath_)) { std::vector files; copy(boost::filesystem::directory_iterator(scriptsPath_), boost::filesystem::directory_iterator(), std::back_inserter(files)); foreach (boost::filesystem::path file, files) { if (boost::filesystem::is_regular_file(file) && file.extension() == ".lua") { loadScript(file); } } } } static int l_register_listener(lua_State *L) { LuaCommands* commands = NULL; lua_getfield(L, LUA_REGISTRYINDEX, LUA_COMMANDS); commands = static_cast(lua_touserdata(L, -1)); lua_pop(L, 1); if (!lua_isfunction(L, 1)) { return luaL_error(L, "register_listener parameter must be a callback function"); } lua_pushvalue(L, 1); int callbackIndex = luaL_ref(L, LUA_REGISTRYINDEX); lua_pop(L, 1); commands->getCommands()->registerListener(boost::bind(&LuaCommands::handleLuaListener, commands, callbackIndex, L, _1)); return 0; } static int l_register_command(lua_State *L) { LuaCommands* commands = NULL; lua_getfield(L, LUA_REGISTRYINDEX, LUA_COMMANDS); commands = static_cast(lua_touserdata(L, -1)); lua_pop(L, 1); if (!lua_isfunction(L, 4)) { return luaL_error(L, "register_command callback parameter must be a function"); } //luaL_ref callback(lua_to(L, 4)); lua_pushvalue(L, 4); int callbackIndex = luaL_ref(L, LUA_REGISTRYINDEX); lua_pop(L, 1); if (!lua_isstring(L, 3)) { return luaL_error(L, "register_command description parameter must be a string"); } std::string description(lua_tostring(L, 3)); lua_pop(L, 1); if (!lua_isstring(L, 2)) { return luaL_error(L, "register_command allowed roles parameter must be a string"); } std::string roleString(lua_tostring(L, 2)); lua_pop(L, 1); Commands::RoleList roleList = Commands::Owner; if (roleString == "Owner") { roleList = Commands::Owner; } else if (roleString == "Anyone") { roleList = Commands::Anyone; } else { return luaL_error(L, "register_command allowed roles parameter has illegal value"); } if (!lua_isstring(L, 1)) { return luaL_error(L, "register_command command name parameter must be a string"); } std::string name(lua_tostring(L, 1)); lua_pop(L, 1); std::cout << "Registering lua command '" << name << "' for '" << roleString << "' with callback index " << callbackIndex << std::endl; commands->getCommands()->registerCommand(name, roleList, description, boost::bind(&LuaCommands::handleLuaCommand, commands, callbackIndex, L, _1, _2, _3)); return 0; } static std::string luatable_asstring(lua_State *L, const char* key) { lua_getfield(L, -1, key); const char* valueChars = lua_tostring(L, -1); std::string value(valueChars != NULL ? valueChars : ""); lua_pop(L, 1); return value; } static int luatable_asint(lua_State *L, const char* key) { lua_getfield(L, -1, key); int value = lua_tointeger(L, -1); lua_pop(L, 1); return value; } static int luatable_asfunction(lua_State *L, const char* key) { lua_getfield(L, -1, key); int callbackIndex = luaL_ref(L, LUA_REGISTRYINDEX); return callbackIndex; } static Message::ref messageFromTable(lua_State *L) { Message::ref message(new Message()); message->setFrom(JID(luatable_asstring(L, "from"))); message->setBody(luatable_asstring(L, "body")); Message::Type type = Message::Normal; std::string typeString(luatable_asstring(L, "type")); if (typeString == "normal") { type = Message::Normal; } else if (typeString == "chat") { type = Message::Chat; } else if (typeString == "groupchat") { type = Message::Groupchat; } else if (typeString == "error") { type = Message::Error; } else if (typeString == "headline") { type = Message::Headline; } else { return Message::ref(); } message->setType(type); return message; } LuaCommands* LuaCommands::commandsFromLua(lua_State *L) { LuaCommands* commands = NULL; lua_getfield(L, LUA_REGISTRYINDEX, LUA_COMMANDS); commands = static_cast(lua_touserdata(L, -1)); lua_pop(L, 1); return commands; } Storage* LuaCommands::storageFromLua(lua_State *L) { Storage* storage = NULL; lua_getfield(L, LUA_REGISTRYINDEX, STORAGE); storage = static_cast(lua_touserdata(L, -1)); lua_pop(L, 1); return storage; } static int l_reply_to(lua_State *L) { LuaCommands* commands = LuaCommands::commandsFromLua(L); if (!lua_isboolean(L, 3) && lua_gettop(L) > 2) { return luaL_error(L, "reply_to parameter 3 must be boolean if present"); } bool outOfMUC = lua_toboolean(L, 3); if (lua_gettop(L) == 3) { lua_pop(L, 1); } if (!lua_isstring(L, 2)) { return luaL_error(L, "reply_to body parameter must be a string"); } std::string body(lua_tostring(L, 2)); lua_pop(L, 1); if (!lua_istable(L, 1)) { return luaL_error(L, "reply_to message parameter must be a table"); } lua_pushvalue(L, 1); Message::ref message(messageFromTable(L)); if (!message) { return luaL_error(L, "message parameter invalid"); } commands->getCommands()->replyTo(message, body, outOfMUC); lua_pop(L, 1); return 0; } static int l_muc_input_to_jid(lua_State *L) { LuaCommands* commands = LuaCommands::commandsFromLua(L); return commands->muc_input_to_jid(L); } int LuaCommands::muc_input_to_jid(lua_State *L) { if (!lua_isstring(L, 2)) { return luaL_error(L, "must pass a string to muc_input_to_jid p2"); } std::string source = lua_tostring(L, 2); JID sourceJID(source); lua_pop(L, 1); if (!lua_isstring(L, 1)) { return luaL_error(L, "must pass a string to muc_input_to_jid p1"); } std::string input = lua_tostring(L, 1); lua_pop(L, 1); JID result(input); if (mucs_->contains(sourceJID.toBare())) { if (result.isBare() && result.getNode().empty()) { if (mucs_->getMUC(sourceJID.toBare())->hasOccupant(input)) { result = JID(sourceJID.getNode(), sourceJID.getDomain(), input); } } } lua_pushstring(L, result.isValid() ? result.toString().c_str() : ""); return 1; } void LuaCommands::handleSoftwareVersionResponse(boost::shared_ptr version, ErrorPayload::ref error, bool timeout, GetSoftwareVersionRequest::ref request, Timer::ref timer, lua_State* L, Callbacks callbacks) { request->onResponse.disconnect_all_slots(); timer->onTick.disconnect_all_slots(); timer->stop(); int callback = callbacks.failure; int stackCount = 0; if (timeout) { callback = callbacks.timeout; } else if (version) { callback = callbacks.success; } lua_rawgeti(L, LUA_REGISTRYINDEX, callback); if (error) { lua_pushstring(L, error->getText().empty() ? "No error text" : error->getText().c_str()); stackCount++; } else if (version) { lua_createtable(L, 0, 3); lua_pushstring(L, version->getName().c_str()); lua_setfield(L, -2, "name"); lua_pushstring(L, version->getVersion().c_str()); lua_setfield(L, -2, "version"); lua_pushstring(L, version->getOS().c_str()); lua_setfield(L, -2, "os"); stackCount++; } else { lua_pushliteral(L, "Missing payload"); stackCount++; } int result = lua_pcall(L, stackCount, 0, 0); if (result != 0) { std::string error(lua_tostring(L, -1)); lua_pop(L, 1); std::cout << error << std::endl; callbacks.erase(L); luaL_error(L, error.c_str()); } else { callbacks.erase(L); } } static int l_get_software_version(lua_State *L) { LuaCommands* commands = LuaCommands::commandsFromLua(L); return commands->get_software_version(L); } int LuaCommands::get_software_version(lua_State *L) { if (!lua_istable(L, 1)) { return luaL_error(L, "get_software_version requires a table parameter."); } lua_pushvalue(L, 1); JID to(luatable_asstring(L, "to")); if (!to.isValid()) { return luaL_error(L, "invalid JID."); } int timeout = luatable_asint(L, "timeout"); if (timeout == 0) { return luaL_error(L, "invalid timeout."); } int successCallback = luatable_asfunction(L, "success_callback"); int failureCallback = luatable_asfunction(L, "failure_callback"); int timeoutCallback = luatable_asfunction(L, "timeout_callback"); GetSoftwareVersionRequest::ref request = GetSoftwareVersionRequest::create(to, client_->getIQRouter()); Timer::ref timer = timerFactory_->createTimer(timeout * 1000); Callbacks callbacks(successCallback, failureCallback, timeoutCallback); request->onResponse.connect(boost::bind(&LuaCommands::handleSoftwareVersionResponse, this, _1, _2, false, request, timer, L, callbacks)); boost::shared_ptr fakePayload; ErrorPayload::ref fakeError; timer->onTick.connect(boost::bind(&LuaCommands::handleSoftwareVersionResponse, this, fakePayload, fakeError, true, request, timer, L, callbacks)); timer->start(); request->send(); return 1; } int LuaCommands::muc_kick(lua_State *L) { if (!lua_isstring(L, 2)) { return luaL_error(L, "muc_kick requires a nick to kick"); } std::string nick = lua_tostring(L, 2); if (!lua_isstring(L, 1)) { return luaL_error(L, "muc_kick requires a muc to kick from"); } JID mucJID(lua_tostring(L, 1)); MUC::ref muc = mucs_->getMUC(mucJID); muc->kickOccupant(JID(mucJID.getNode(), mucJID.getDomain(), nick)); return 0; } static int l_muc_kick(lua_State *L) { LuaCommands* commands = LuaCommands::commandsFromLua(L); return commands->muc_kick(L); } static int l_store_setting(lua_State *L) { return LuaCommands::commandsFromLua(L)->store_setting(L); } static int l_get_setting(lua_State *L) { return LuaCommands::commandsFromLua(L)->get_setting(L); } int LuaCommands::store_setting(lua_State *L) { if (!lua_isstring(L, 2) || !lua_isstring(L, 1)) { return luaL_error(L, "both setting and key must be strings"); } std::string value(lua_tostring(L, 2)); std::string key(lua_tostring(L, 1)); lua_pop(L, 2); storageFromLua(L)->saveSetting(key, value); return 0; } int LuaCommands::get_setting(lua_State *L) { if (!lua_isstring(L, 1)) { return luaL_error(L, "key must be a string"); } std::string key(lua_tostring(L, 1)); lua_pop(L, 1); lua_pushstring(L, storageFromLua(L)->getSetting(key).c_str()); return 1; } void LuaCommands::handleLuaListener(int callbackIndex, lua_State* L, Swift::Message::ref message) { lua_rawgeti(L, LUA_REGISTRYINDEX, callbackIndex); lua_pushstring(L, message->getBody().c_str()); lua_pushstring(L, message->getFrom().toBare().toString().c_str()); lua_pushstring(L, message->getFrom().getResource().c_str()); messageOntoStack(message, L); int result = lua_pcall(L, 4, 0, 0); if (result != 0) { std::string error(lua_tostring(L, -1)); lua_pop(L, 1); error = "Listener failed: " + error; std::cout << error << std::endl; } } void LuaCommands::handleLuaCommand(int callbackIndex, lua_State* L, const std::string& command, const std::string& params, Swift::Message::ref message) { lua_rawgeti(L, LUA_REGISTRYINDEX, callbackIndex); lua_pushstring(L, command.c_str()); lua_pushstring(L, params.c_str()); messageOntoStack(message, L); int result = lua_pcall(L, 3, 0, 0); if (result != 0) { std::string error(lua_tostring(L, -1)); lua_pop(L, 1); error = "Command '" + command + "' failed: " + error; std::cout << error << std::endl; commands_->replyTo(message, error, false); } } void LuaCommands::messageOntoStack(Swift::Message::ref message, lua_State* L) { lua_createtable(L, 0, 4); std::string typeString; switch (message->getType()) { case Message::Chat : typeString = "chat";break; case Message::Groupchat : typeString = "groupchat";break; case Message::Normal : typeString = "normal";break; case Message::Error : typeString = "error";break; case Message::Headline : typeString = "headline";break; } lua_pushstring(L, typeString.c_str()); lua_setfield(L, -2, "type"); lua_pushstring(L, message->getFrom().toString().c_str()); lua_setfield(L, -2, "from"); lua_pushstring(L, message->getFrom().toBare().toString().c_str()); lua_setfield(L, -2, "frombare"); lua_pushstring(L, message->getTo().toString().c_str()); lua_setfield(L, -2, "to"); lua_pushstring(L, message->getBody().c_str()); lua_setfield(L, -2, "body"); } void LuaCommands::loadScript(boost::filesystem::path filePath) { std::cout << "Trying to load file from " << filePath << std::endl; lua_State* lua = lua_open(); luaL_openlibs(lua); lua_pushlightuserdata(lua, this); lua_setfield(lua, LUA_REGISTRYINDEX, LUA_COMMANDS); #if BOOST_FILESYSTEM_VERSION == 2 // TODO: Delete this when boost 1.44 becomes a minimum requirement, and we no longer need v2 std::string filename = filePath.filename(); #else std::string filename = filePath.filename().string(); #endif filename += ".storage"; boost::filesystem::path storagePath(boost::filesystem::path(path_) / filename); Storage* storage = new Storage(storagePath); lua_pushlightuserdata(lua, storage); lua_setfield(lua, LUA_REGISTRYINDEX, STORAGE); lua_register(lua, "swiftob_register_command", &l_register_command); lua_register(lua, "swiftob_register_listener", &l_register_listener); lua_register(lua, "swiftob_reply_to", &l_reply_to); lua_register(lua, "swiftob_get_software_version", &l_get_software_version); lua_register(lua, "swiftob_muc_input_to_jid", &l_muc_input_to_jid); lua_register(lua, "swiftob_store_setting", &l_store_setting); lua_register(lua, "swiftob_get_setting", &l_get_setting); lua_register(lua, "swiftob_muc_kick", &l_muc_kick); int fileLoaded = luaL_dofile(lua, filePath.string().c_str()); if (fileLoaded == 0 ) { std::cout << "Loaded" << std::endl; } else { const char* error = lua_tostring(lua, -1); std::cout << "Error: " << error << std::endl; lua_pop(lua, -1); } } swift-im-2.0+dev6/Swiftob/Storage.cpp0000644000175000017500000000256712227051774017422 0ustar kismithkismith/* * Copyright (c) 2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include typedef std::pair Strings; Storage::Storage(const std::string& path) : settingsPath_(boost::filesystem::path(path)) { load(); } Storage::Storage(const boost::filesystem::path& path) : settingsPath_(path) { load(); } void Storage::load() { if (boost::filesystem::exists(settingsPath_)) { Swift::ByteArray data; Swift::readByteArrayFromFile(data, settingsPath_.string()); foreach (std::string line, Swift::String::split(Swift::byteArrayToString(data), '\n')) { std::pair pair = Swift::String::getSplittedAtFirst(line, '\t'); settings_[pair.first] = pair.second; } } } void Storage::saveSetting(const std::string& setting, const std::string& value) { settings_[setting] = value; std::string settingsString; foreach(Strings pair, settings_) { settingsString += pair.first + '\t' + pair.second + '\n'; } boost::filesystem::ofstream file(settingsPath_); file << settingsString; file.close(); } std::string Storage::getSetting(const std::string& setting) { return settings_[setting]; } swift-im-2.0+dev6/Swiftob/MUCs.cpp0000644000175000017500000000745012227051774016621 0ustar kismithkismith/* * Copyright (c) 2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #define MUC_LIST_SETTING "muc_list" #define NICK "default_nick" typedef std::pair JIDMUCPair; MUCs::MUCs(Client* client, Storage* storage) : defaultNick_("Kanchil+") { client_ = client; storage_ = storage; std::string storedNick = storage_->getSetting(NICK); if (!storedNick.empty()) { defaultNick_ = storedNick; } client_->onConnected.connect(boost::bind(&MUCs::handleConnected, this)); } void MUCs::handleConnected() { foreach (std::string room, String::split(storage_->getSetting(MUC_LIST_SETTING), ' ')) { join(JID(room), boost::bind(&MUCs::handleInitialJoinSuccess, this), boost::bind(&MUCs::handleInitialJoinFailure, this, _1)); } } void MUCs::handleInitialJoinSuccess() { } void MUCs::handleInitialJoinFailure(const std::string&) { } void MUCs::join(const JID& room, boost::signal::slot_type successCallback, boost::function failureCallback) { if (contains(room)) { failureCallback("Already in room"); } mucs_[room] = client_->getMUCManager()->createMUC(room); mucs_[room]->onJoinComplete.connect(successCallback); mucs_[room]->onJoinFailed.connect(boost::bind(&MUCs::handleJoinFailed, this, room, _1, failureCallback)); mucs_[room]->joinWithContextSince(defaultNick_, boost::posix_time::microsec_clock::universal_time()); save(); } void MUCs::part(const JID& room) { if (!contains(room)) { return; } mucs_[room]->part(); } bool MUCs::contains(const JID& room) { return mucs_.find(room) != mucs_.end(); } void MUCs::handleJoinFailed(const JID& muc, ErrorPayload::ref error, boost::function failureCallback) { std::string errorMessage("Couldn't join room"); if (error) { switch (error->getCondition()) { case ErrorPayload::Conflict: // rejoinNick = nick_ + "_"; errorMessage += ": Nickname in use"; // errorMessage = str(format("Unable to enter this room as %1%, retrying as %2%") % nick_ % rejoinNick); break; case ErrorPayload::JIDMalformed: errorMessage += ": "; errorMessage += "No nickname specified"; break; case ErrorPayload::NotAuthorized: errorMessage += ": "; errorMessage += "A password needed"; break; case ErrorPayload::RegistrationRequired: errorMessage += ": "; errorMessage += "Only members may enter"; break; case ErrorPayload::Forbidden: errorMessage += ": "; errorMessage += "You are banned from the room"; break; case ErrorPayload::ServiceUnavailable: errorMessage += ": "; errorMessage += "The room is full"; break; case ErrorPayload::ItemNotFound: errorMessage += ": "; errorMessage += "The room does not exist"; break; default: break; } if (!error->getText().empty()) { errorMessage += " - " + error->getText(); } } mucs_.erase(muc); failureCallback(errorMessage); } void MUCs::save() { std::string concat; foreach (JIDMUCPair pair, mucs_) { concat += pair.first.toString() + " "; } storage_->saveSetting(MUC_LIST_SETTING, concat); } MUC::ref MUCs::getMUC(const JID& room) { return (mucs_.find(room) != mucs_.end()) ? mucs_[room] : MUC::ref(); } bool MUCs::setDefaultNick(const std::string& nick) { JID testJID("alice", "wonderland.lit", nick); if (testJID.isValid()) { defaultNick_ = testJID.getResource(); storage_->saveSetting(NICK, defaultNick_); return true; } return false; } swift-im-2.0+dev6/Swiftob/main.cpp0000644000175000017500000000212712227051774016732 0ustar kismithkismith/* * Copyright (c) 2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include "Swiftob.h" int main(int argc, char* argv[]) { boost::program_options::options_description desc = Swiftob::getOptionsDescription(); boost::program_options::variables_map vm; try { boost::program_options::store(boost::program_options::parse_command_line(argc, argv, desc), vm); } catch (const boost::program_options::unknown_option& option) { #if BOOST_VERSION >= 104200 std::cout << "Ignoring unknown option " << option.get_option_name() << " but continuing." << std::endl; #else std::cout << "Error: " << option.what() << " (continuing)" << std::endl; #endif } boost::program_options::notify(vm); if (vm.count("help") > 0) { std::cout << desc << "\n"; return 1; } Swiftob bot(vm); int result = bot.exec(); return result; } swift-im-2.0+dev6/Swiftob/Users.h0000644000175000017500000000163112227051774016553 0ustar kismithkismith/* * Copyright (c) 2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include namespace Swift { class Client; } class MUCs; using namespace Swift; class Users { public: class User { public: /* If you add a role here, edit the role lists in Commands.cpp*/ enum Role {Unknown, Owner}; User(const JID& jid, Role role) : jid_(jid), role_(role) {} Role getRole() {return role_;} Swift::JID getJID() {return jid_;} private: Swift::JID jid_; Role role_; }; public: Users(Client* client, MUCs* mucs); void clearAll(); void addUser(const User& user); User::Role getRoleForSender(Message::ref message); private: std::vector users_; Client* client_; MUCs* mucs_; }; swift-im-2.0+dev6/Swiftob/Commands.cpp0000644000175000017500000001745112227051774017555 0ustar kismithkismith/* * Copyright (c) 2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include typedef std::pair NamedCommand; Commands::Commands(Users* users, Swift::Client* client, Storage* storage, MUCs* mucs) { users_ = users; client_ = client; mucs_ = mucs; storage_ = storage; resetCommands(); } Commands::~Commands() { clearCommands(); } void Commands::clearCommands() { foreach (NamedCommand command, commands_) { delete command.second; } commands_.clear(); } void Commands::resetCommands() { clearCommands(); registerCommand("quit", Owner, "Quit the bot", boost::bind(&Commands::handleQuitCommand, this, _1, _2, _3)); registerCommand("help", Anyone, "Get help", boost::bind(&Commands::handleHelpCommand, this, _1, _2, _3)); registerCommand("join", Owner, "Join a MUC", boost::bind(&Commands::handleJoinCommand, this, _1, _2, _3)); registerCommand("part", Owner, "Leave a MUC", boost::bind(&Commands::handlePartCommand, this, _1, _2, _3)); registerCommand("rehash", Owner, "Reload scripts", boost::bind(&Commands::handleRehashCommand, this, _1, _2, _3)); registerCommand("restart", Owner, "Restart bot", boost::bind(&Commands::handleRestartCommand, this, _1, _2, _3)); registerCommand("nick", Owner, "Change nick (requires restart)", boost::bind(&Commands::handleChangeNick, this, _1, _2, _3)); //registerCommand("owner", Owner, "Change owner settinsg", boost::bind(&Commands::handleChangeOwner, this, _1, _2, _3)); onReset(); } void Commands::registerCommand(const std::string& name, RoleList roles, const std::string& description, boost::function callback) { Command* command = new Command(roles, description); commands_[name] = command; command->onReceived.connect(callback); } void Commands::registerListener(ListenerCallback listener) { listeners_.push_back(listener); } bool Commands::hasCommand(const std::string& name) { return commands_.find(name) != commands_.end(); } bool Commands::runCommand(const std::string& name, const std::string& params, Swift::Message::ref message) { Users::User::Role userRole = users_->getRoleForSender(message); Command* command = commands_[name]; if (roleIn(userRole, command->getAllowedBy())) { command->onReceived(name, params, message); return true; } else { replyTo(message, "You may not run this command", true); } return false; } void Commands::runListeners(Swift::Message::ref message) { foreach (ListenerCallback listener, listeners_) { listener(message); } } bool Commands::roleIn(const Users::User::Role userRole, RoleList roleList) { switch (roleList) { case Owner : return userRole == Users::User::Owner; case Anyone : return true; } std::cerr << "Unrecognised role list" << std::endl; return false; } void Commands::handleChangeNick(const std::string& /*command*/, const std::string& params, Swift::Message::ref message) { std::string nick(params); boost::algorithm::trim(nick); if (nick.empty()) { replyTo(message, "Current nick is '" + mucs_->getDefaultNick() + "'. Run the command with a new nick to change it."); } else { if (mucs_->setDefaultNick(params)) { replyTo(message, "Default nick now set to '" + nick + "' - restart the bot for this to take effect."); } else { replyTo(message, "Can't set invalid nick '" + nick + "'."); } } } void Commands::handleChangeOwner(const std::string& /*command*/, const std::string& /*params*/, Swift::Message::ref /*message*/) { /* Oh, right. I don't have user persistence coded yet. * Probably not worth doing this until I have.*/ } void Commands::handleQuitCommand(const std::string& /*command*/, const std::string& /*params*/, Swift::Message::ref message) { replyTo(message, "Shutting down"); std::cout << "Quitting at the behest of " << message->getFrom().toString() << std::endl; exit(0); } void Commands::setRehashError(const std::string& error) { if (!rehashError_.empty()) { rehashError_ += "; "; } rehashError_ += error; } void Commands::handleRehashCommand(const std::string& /*command*/, const std::string& /*params*/, Swift::Message::ref message) { rehashError_ = ""; replyTo(message, "Rehashing now."); std::cout << "Rehashing at the behest of " << message->getFrom().toString() << std::endl; resetCommands(); listeners_.clear(); if (rehashError_.empty()) { replyTo(message, "Rehash complete"); } else { replyTo(message, "I have suffered a tremendous failure: " + rehashError_); } } void Commands::handleRestartCommand(const std::string& /*command*/, const std::string& /*params*/, Swift::Message::ref message) { rehashError_ = ""; replyTo(message, "Restarting now."); std::cout << "Restarting at the behest of " << message->getFrom().toString() << std::endl; onRestartRequested(); } void Commands::handleJoinCommand(const std::string& /*command*/, const std::string& params, Swift::Message::ref message) { Swift::JID room(params); if (!room.isValid() || !room.getResource().empty() || room.getNode().empty()) { replyTo(message, "Can't join " + room.toString() + ", not a valid room JID."); return; } if (mucs_->contains(room)) { replyTo(message, "I'm already (trying to be?) in " + room.toString() + "."); return; } replyTo(message, "Trying to join " + room.toString() + "."); mucs_->join(room, boost::bind(&Commands::handleJoinCommandSuccess, this, room, message), boost::bind(&Commands::handleJoinCommandFailure, this, room, _1, message)); } void Commands::handlePartCommand(const std::string& /*command*/, const std::string& params, Swift::Message::ref message) { Swift::JID room(params); if (!room.isValid() || !room.getResource().empty() || room.getNode().empty()) { replyTo(message, "Can't leave " + room.toString() + ", not a valid room JID."); return; } if (mucs_->contains(room)) { replyTo(message, "I'm not in " + room.toString() + "."); return; } replyTo(message, "Leaving " + room.toString() + "."); mucs_->part(room); } void Commands::handleJoinCommandSuccess(const Swift::JID& room, Swift::Message::ref message) { replyTo(message, "Joined " + room.toString()); } void Commands::handleJoinCommandFailure(const Swift::JID& room, const std::string& error, Swift::Message::ref message) { replyTo(message, "Join to " + room.toString() + "failed. " + error); } void Commands::handleHelpCommand(const std::string& /*command*/, const std::string& /*params*/, Swift::Message::ref message) { Users::User::Role userRole = users_->getRoleForSender(message); std::string result("Available commands:"); std::string bang = message->getType() == Swift::Message::Groupchat ? "\n!" : "\n"; foreach (NamedCommand pair, commands_) { if (roleIn(userRole, pair.second->getAllowedBy())) { result += bang + pair.first + " - " + pair.second->getDescription(); } } replyTo(message, result, true); } /** * \param outOfMUC Reply to the sender directly, don't spam MUCs with the reply */ void Commands::replyTo(Swift::Message::ref source, std::string replyBody, bool outOfMUC) { Swift::Message::ref reply(new Swift::Message()); Swift::Message::Type type = source->getType(); reply->setType(type); reply->setBody(type == Swift::Message::Groupchat ? source->getFrom().getResource() + ": " + replyBody : replyBody); Swift::JID to = source->getFrom(); if (type == Swift::Message::Groupchat) { if (outOfMUC) { reply->setType(Swift::Message::Chat); } else { to = to.toBare(); } } reply->setTo(to); if (client_->isAvailable()) { client_->sendMessage(reply); } else { std::cout << "Dropping '" + reply->getBody() + "' -> " + reply->getTo().toString() + " on the floor due to missing connection." << std::endl; } } swift-im-2.0+dev6/Swiftob/Storage.h0000644000175000017500000000122412227051774017054 0ustar kismithkismith/* * Copyright (c) 2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include class Storage { public: Storage(const std::string& path); Storage(const boost::filesystem::path& path); void saveSetting(const std::string& setting, const std::string& value); std::string getSetting(const std::string& setting); private: void load(); std::map settings_; std::string path_; boost::filesystem::path settingsPath_; }; swift-im-2.0+dev6/COPYING0000644000175000017500000000045712227051773014723 0ustar kismithkismithSwift - An XMPP Client Copyright (C) 2009-2012 Kevin Smith and Remko Tronçon The license under which these products are made available is provided in the COPYING.gpl file. These products also use third-party code and contributions, the licenses for which are provided in the COPYING.thirdparty file. swift-im-2.0+dev6/scons.bat0000644000175000017500000000005512227051774015500 0ustar kismithkismith@echo off python 3rdParty/SCons/scons.py %* swift-im-2.0+dev6/Swiften/0000755000175000017500000000000012227051774015302 5ustar kismithkismithswift-im-2.0+dev6/Swiften/Session/0000755000175000017500000000000012227051774016725 5ustar kismithkismithswift-im-2.0+dev6/Swiften/Session/BasicSessionStream.h0000644000175000017500000000525312227051774022644 0ustar kismithkismith/* * 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 #include #include #include #include #include namespace Swift { class TLSContextFactory; class TLSLayer; class TimerFactory; class WhitespacePingLayer; class PayloadParserFactoryCollection; class PayloadSerializerCollection; class StreamStack; class XMPPLayer; class ConnectionLayer; class CompressionLayer; class XMLParserFactory; class BasicSessionStream : public SessionStream { public: BasicSessionStream( StreamType streamType, boost::shared_ptr connection, PayloadParserFactoryCollection* payloadParserFactories, PayloadSerializerCollection* payloadSerializers, TLSContextFactory* tlsContextFactory, TimerFactory* whitespacePingLayerFactory, XMLParserFactory* xmlParserFactory ); ~BasicSessionStream(); virtual void close(); virtual bool isOpen(); virtual void writeHeader(const ProtocolHeader& header); virtual void writeElement(boost::shared_ptr); virtual void writeFooter(); virtual void writeData(const std::string& data); virtual bool supportsZLibCompression(); virtual void addZLibCompression(); virtual bool supportsTLSEncryption(); virtual void addTLSEncryption(); virtual bool isTLSEncrypted(); virtual Certificate::ref getPeerCertificate() const; virtual std::vector getPeerCertificateChain() const; virtual boost::shared_ptr getPeerCertificateVerificationError() const; virtual ByteArray getTLSFinishMessage() const; virtual void setWhitespacePingEnabled(bool); virtual void resetXMPPParser(); private: void handleConnectionFinished(const boost::optional& error); void handleXMPPError(); void handleTLSConnected(); void handleTLSError(boost::shared_ptr); void handleStreamStartReceived(const ProtocolHeader&); void handleElementReceived(boost::shared_ptr); void handleDataRead(const SafeByteArray& data); void handleDataWritten(const SafeByteArray& data); private: bool available; boost::shared_ptr connection; TLSContextFactory* tlsContextFactory; TimerFactory* timerFactory; XMPPLayer* xmppLayer; ConnectionLayer* connectionLayer; CompressionLayer* compressionLayer; TLSLayer* tlsLayer; WhitespacePingLayer* whitespacePingLayer; StreamStack* streamStack; }; } swift-im-2.0+dev6/Swiften/Session/SessionStream.h0000644000175000017500000000506712227051774021705 0ustar kismithkismith/* * Copyright (c) 2010-2012 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include #include #include #include #include #include #include #include namespace Swift { class SWIFTEN_API SessionStream { public: class SessionStreamError : public Swift::Error { public: enum Type { ParseError, TLSError, InvalidTLSCertificateError, ConnectionReadError, ConnectionWriteError }; SessionStreamError(Type type) : type(type) {} Type type; }; SessionStream(): certificate() {} virtual ~SessionStream(); virtual void close() = 0; virtual bool isOpen() = 0; virtual void writeHeader(const ProtocolHeader& header) = 0; virtual void writeFooter() = 0; virtual void writeElement(boost::shared_ptr) = 0; virtual void writeData(const std::string& data) = 0; virtual bool supportsZLibCompression() = 0; virtual void addZLibCompression() = 0; virtual bool supportsTLSEncryption() = 0; virtual void addTLSEncryption() = 0; virtual bool isTLSEncrypted() = 0; virtual void setWhitespacePingEnabled(bool enabled) = 0; virtual void resetXMPPParser() = 0; void setTLSCertificate(CertificateWithKey::ref cert) { certificate = cert; } virtual bool hasTLSCertificate() { return certificate && !certificate->isNull(); } virtual Certificate::ref getPeerCertificate() const = 0; virtual std::vector getPeerCertificateChain() const = 0; virtual boost::shared_ptr getPeerCertificateVerificationError() const = 0; virtual ByteArray getTLSFinishMessage() const = 0; boost::signal onStreamStartReceived; boost::signal)> onElementReceived; boost::signal)> onClosed; boost::signal onTLSEncrypted; boost::signal onDataRead; boost::signal onDataWritten; protected: CertificateWithKey::ref getTLSCertificate() const { return certificate; } private: CertificateWithKey::ref certificate; }; } swift-im-2.0+dev6/Swiften/Session/BOSHSessionStream.h0000644000175000017500000000577112227051774022363 0ustar kismithkismith/* * Copyright (c) 2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include #include #include #include namespace Swift { class TimerFactory; class PayloadParserFactoryCollection; class PayloadSerializerCollection; class StreamStack; class XMPPLayer; class ConnectionLayer; class CompressionLayer; class XMLParserFactory; class TLSContextFactory; class EventLoop; class BOSHSessionStream : public SessionStream, public EventOwner, public boost::enable_shared_from_this { public: BOSHSessionStream( const URL& boshURL, PayloadParserFactoryCollection* payloadParserFactories, PayloadSerializerCollection* payloadSerializers, ConnectionFactory* connectionFactory, TLSContextFactory* tlsContextFactory, TimerFactory* whitespacePingLayerFactory, XMLParserFactory* xmlParserFactory, EventLoop* eventLoop, DomainNameResolver* resolver, const std::string& to, const URL& boshHTTPConnectProxyURL, const SafeString& boshHTTPConnectProxyAuthID, const SafeString& boshHTTPConnectProxyAuthPassword ); ~BOSHSessionStream(); virtual void close(); virtual bool isOpen(); virtual void writeHeader(const ProtocolHeader& header); virtual void writeElement(boost::shared_ptr); virtual void writeFooter(); virtual void writeData(const std::string& data); virtual bool supportsZLibCompression(); virtual void addZLibCompression(); virtual bool supportsTLSEncryption(); virtual void addTLSEncryption(); virtual bool isTLSEncrypted(); virtual Certificate::ref getPeerCertificate() const; virtual std::vector getPeerCertificateChain() const; virtual boost::shared_ptr getPeerCertificateVerificationError() const; virtual ByteArray getTLSFinishMessage() const; virtual void setWhitespacePingEnabled(bool); virtual void resetXMPPParser(); private: void handleXMPPError(); void handleStreamStartReceived(const ProtocolHeader&); void handleElementReceived(boost::shared_ptr); void handlePoolXMPPDataRead(const SafeByteArray& data); void handleXMPPLayerDataWritten(const SafeByteArray& data); void handlePoolSessionStarted(); void handlePoolBOSHDataRead(const SafeByteArray& data); void handlePoolBOSHDataWritten(const SafeByteArray& data); void handlePoolSessionTerminated(BOSHError::ref condition); private: void fakeStreamHeaderReceipt(); void fakeStreamFooterReceipt(BOSHError::ref error); private: BOSHConnectionPool* connectionPool; bool available; XMPPLayer* xmppLayer; ProtocolHeader streamHeader; EventLoop* eventLoop; bool firstHeader; }; } swift-im-2.0+dev6/Swiften/Session/BasicSessionStream.cpp0000644000175000017500000001504412227051774023176 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include #include namespace Swift { BasicSessionStream::BasicSessionStream( StreamType streamType, boost::shared_ptr connection, PayloadParserFactoryCollection* payloadParserFactories, PayloadSerializerCollection* payloadSerializers, TLSContextFactory* tlsContextFactory, TimerFactory* timerFactory, XMLParserFactory* xmlParserFactory) : available(false), connection(connection), tlsContextFactory(tlsContextFactory), timerFactory(timerFactory), compressionLayer(NULL), tlsLayer(NULL), whitespacePingLayer(NULL) { xmppLayer = new XMPPLayer(payloadParserFactories, payloadSerializers, xmlParserFactory, streamType); xmppLayer->onStreamStart.connect(boost::bind(&BasicSessionStream::handleStreamStartReceived, this, _1)); xmppLayer->onElement.connect(boost::bind(&BasicSessionStream::handleElementReceived, this, _1)); xmppLayer->onError.connect(boost::bind(&BasicSessionStream::handleXMPPError, this)); xmppLayer->onDataRead.connect(boost::bind(&BasicSessionStream::handleDataRead, this, _1)); xmppLayer->onWriteData.connect(boost::bind(&BasicSessionStream::handleDataWritten, this, _1)); connection->onDisconnected.connect(boost::bind(&BasicSessionStream::handleConnectionFinished, this, _1)); connectionLayer = new ConnectionLayer(connection); streamStack = new StreamStack(xmppLayer, connectionLayer); available = true; } BasicSessionStream::~BasicSessionStream() { delete compressionLayer; if (tlsLayer) { tlsLayer->onError.disconnect(boost::bind(&BasicSessionStream::handleTLSError, this)); tlsLayer->onConnected.disconnect(boost::bind(&BasicSessionStream::handleTLSConnected, this)); delete tlsLayer; } delete whitespacePingLayer; delete streamStack; connection->onDisconnected.disconnect(boost::bind(&BasicSessionStream::handleConnectionFinished, this, _1)); delete connectionLayer; xmppLayer->onStreamStart.disconnect(boost::bind(&BasicSessionStream::handleStreamStartReceived, this, _1)); xmppLayer->onElement.disconnect(boost::bind(&BasicSessionStream::handleElementReceived, this, _1)); xmppLayer->onError.disconnect(boost::bind(&BasicSessionStream::handleXMPPError, this)); xmppLayer->onDataRead.disconnect(boost::bind(&BasicSessionStream::handleDataRead, this, _1)); xmppLayer->onWriteData.disconnect(boost::bind(&BasicSessionStream::handleDataWritten, this, _1)); delete xmppLayer; } void BasicSessionStream::writeHeader(const ProtocolHeader& header) { assert(available); xmppLayer->writeHeader(header); } void BasicSessionStream::writeElement(boost::shared_ptr element) { assert(available); xmppLayer->writeElement(element); } void BasicSessionStream::writeFooter() { assert(available); xmppLayer->writeFooter(); } void BasicSessionStream::writeData(const std::string& data) { assert(available); xmppLayer->writeData(data); } void BasicSessionStream::close() { connection->disconnect(); } bool BasicSessionStream::isOpen() { return available; } bool BasicSessionStream::supportsTLSEncryption() { return tlsContextFactory && tlsContextFactory->canCreate(); } void BasicSessionStream::addTLSEncryption() { assert(available); tlsLayer = new TLSLayer(tlsContextFactory); if (hasTLSCertificate() && !tlsLayer->setClientCertificate(getTLSCertificate())) { onClosed(boost::make_shared(SessionStreamError::InvalidTLSCertificateError)); } else { streamStack->addLayer(tlsLayer); tlsLayer->onError.connect(boost::bind(&BasicSessionStream::handleTLSError, this, _1)); tlsLayer->onConnected.connect(boost::bind(&BasicSessionStream::handleTLSConnected, this)); tlsLayer->connect(); } } bool BasicSessionStream::isTLSEncrypted() { return tlsLayer; } Certificate::ref BasicSessionStream::getPeerCertificate() const { return tlsLayer->getPeerCertificate(); } std::vector BasicSessionStream::getPeerCertificateChain() const { return tlsLayer->getPeerCertificateChain(); } boost::shared_ptr BasicSessionStream::getPeerCertificateVerificationError() const { return tlsLayer->getPeerCertificateVerificationError(); } ByteArray BasicSessionStream::getTLSFinishMessage() const { return tlsLayer->getContext()->getFinishMessage(); } bool BasicSessionStream::supportsZLibCompression() { return true; } void BasicSessionStream::addZLibCompression() { compressionLayer = new CompressionLayer(); streamStack->addLayer(compressionLayer); } void BasicSessionStream::setWhitespacePingEnabled(bool enabled) { if (enabled) { if (!whitespacePingLayer) { whitespacePingLayer = new WhitespacePingLayer(timerFactory); streamStack->addLayer(whitespacePingLayer); } whitespacePingLayer->setActive(); } else if (whitespacePingLayer) { whitespacePingLayer->setInactive(); } } void BasicSessionStream::resetXMPPParser() { xmppLayer->resetParser(); } void BasicSessionStream::handleStreamStartReceived(const ProtocolHeader& header) { onStreamStartReceived(header); } void BasicSessionStream::handleElementReceived(boost::shared_ptr element) { onElementReceived(element); } void BasicSessionStream::handleXMPPError() { available = false; onClosed(boost::make_shared(SessionStreamError::ParseError)); } void BasicSessionStream::handleTLSConnected() { onTLSEncrypted(); } void BasicSessionStream::handleTLSError(boost::shared_ptr error) { available = false; onClosed(error); } void BasicSessionStream::handleConnectionFinished(const boost::optional& error) { available = false; if (error == Connection::ReadError) { onClosed(boost::make_shared(SessionStreamError::ConnectionReadError)); } else if (error) { onClosed(boost::make_shared(SessionStreamError::ConnectionWriteError)); } else { onClosed(boost::shared_ptr()); } } void BasicSessionStream::handleDataRead(const SafeByteArray& data) { onDataRead(data); } void BasicSessionStream::handleDataWritten(const SafeByteArray& data) { onDataWritten(data); } }; swift-im-2.0+dev6/Swiften/Session/Session.h0000644000175000017500000000562312227051774020527 0ustar kismithkismith/* * 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 #include #include #include #include #include #include #include #include #include namespace Swift { class ProtocolHeader; class StreamStack; class JID; class Element; class PayloadParserFactoryCollection; class PayloadSerializerCollection; class XMPPLayer; class XMLParserFactory; class SWIFTEN_API Session : public boost::enable_shared_from_this { public: enum SessionError { ConnectionReadError, ConnectionWriteError, XMLError, AuthenticationFailedError, NoSupportedAuthMechanismsError, UnexpectedElementError, ResourceBindError, SessionStartError, TLSError, ClientCertificateLoadError, ClientCertificateError }; Session( boost::shared_ptr connection, PayloadParserFactoryCollection* payloadParserFactories, PayloadSerializerCollection* payloadSerializers, XMLParserFactory* xmlParserFactory); virtual ~Session(); void startSession(); void finishSession(); void sendElement(boost::shared_ptr); const JID& getLocalJID() const { return localJID; } const JID& getRemoteJID() const { return remoteJID; } boost::signal)> onElementReceived; boost::signal&)> onSessionFinished; boost::signal onDataWritten; boost::signal onDataRead; protected: void setRemoteJID(const JID& j) { remoteJID = j; } void setLocalJID(const JID& j) { localJID = j; } void finishSession(const SessionError&); virtual void handleSessionStarted() {} virtual void handleSessionFinished(const boost::optional&) {} virtual void handleElement(boost::shared_ptr) = 0; virtual void handleStreamStart(const ProtocolHeader&) = 0; void initializeStreamStack(); XMPPLayer* getXMPPLayer() const { return xmppLayer; } StreamStack* getStreamStack() const { return streamStack; } void setFinished(); private: void handleDisconnected(const boost::optional& error); private: JID localJID; JID remoteJID; boost::shared_ptr connection; PayloadParserFactoryCollection* payloadParserFactories; PayloadSerializerCollection* payloadSerializers; XMLParserFactory* xmlParserFactory; XMPPLayer* xmppLayer; ConnectionLayer* connectionLayer; StreamStack* streamStack; bool finishing; }; } swift-im-2.0+dev6/Swiften/Session/SessionTracer.h0000644000175000017500000000076012227051774021665 0ustar kismithkismith/* * 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 #include #include namespace Swift { class SessionTracer { public: SessionTracer(boost::shared_ptr session); private: void printData(char direction, const SafeByteArray& data); boost::shared_ptr session; }; } swift-im-2.0+dev6/Swiften/Session/BOSHSessionStream.cpp0000644000175000017500000001712612227051774022713 0ustar kismithkismith/* * Copyright (c) 2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Swift { BOSHSessionStream::BOSHSessionStream( const URL& boshURL, PayloadParserFactoryCollection* payloadParserFactories, PayloadSerializerCollection* payloadSerializers, ConnectionFactory* connectionFactory, TLSContextFactory* tlsContextFactory, TimerFactory* timerFactory, XMLParserFactory* xmlParserFactory, EventLoop* eventLoop, DomainNameResolver* resolver, const std::string& to, const URL& boshHTTPConnectProxyURL, const SafeString& boshHTTPConnectProxyAuthID, const SafeString& boshHTTPConnectProxyAuthPassword) : available(false), eventLoop(eventLoop), firstHeader(true) { boost::mt19937 random; boost::uniform_int dist(0, (1LL<<53) - 1); random.seed(time(NULL)); unsigned long long initialRID = boost::variate_generator >(random, dist)(); connectionPool = new BOSHConnectionPool(boshURL, resolver, connectionFactory, xmlParserFactory, tlsContextFactory, timerFactory, eventLoop, to, initialRID, boshHTTPConnectProxyURL, boshHTTPConnectProxyAuthID, boshHTTPConnectProxyAuthPassword); connectionPool->onSessionTerminated.connect(boost::bind(&BOSHSessionStream::handlePoolSessionTerminated, this, _1)); connectionPool->onSessionStarted.connect(boost::bind(&BOSHSessionStream::handlePoolSessionStarted, this)); connectionPool->onXMPPDataRead.connect(boost::bind(&BOSHSessionStream::handlePoolXMPPDataRead, this, _1)); connectionPool->onBOSHDataRead.connect(boost::bind(&BOSHSessionStream::handlePoolBOSHDataRead, this, _1)); connectionPool->onBOSHDataWritten.connect(boost::bind(&BOSHSessionStream::handlePoolBOSHDataWritten, this, _1)); xmppLayer = new XMPPLayer(payloadParserFactories, payloadSerializers, xmlParserFactory, ClientStreamType); xmppLayer->onStreamStart.connect(boost::bind(&BOSHSessionStream::handleStreamStartReceived, this, _1)); xmppLayer->onElement.connect(boost::bind(&BOSHSessionStream::handleElementReceived, this, _1)); xmppLayer->onError.connect(boost::bind(&BOSHSessionStream::handleXMPPError, this)); xmppLayer->onWriteData.connect(boost::bind(&BOSHSessionStream::handleXMPPLayerDataWritten, this, _1)); available = true; } BOSHSessionStream::~BOSHSessionStream() { close(); connectionPool->onSessionTerminated.disconnect(boost::bind(&BOSHSessionStream::handlePoolSessionTerminated, this, _1)); connectionPool->onSessionStarted.disconnect(boost::bind(&BOSHSessionStream::handlePoolSessionStarted, this)); connectionPool->onXMPPDataRead.disconnect(boost::bind(&BOSHSessionStream::handlePoolXMPPDataRead, this, _1)); connectionPool->onBOSHDataRead.disconnect(boost::bind(&BOSHSessionStream::handlePoolBOSHDataRead, this, _1)); connectionPool->onBOSHDataWritten.disconnect(boost::bind(&BOSHSessionStream::handlePoolBOSHDataWritten, this, _1)); delete connectionPool; connectionPool = NULL; xmppLayer->onStreamStart.disconnect(boost::bind(&BOSHSessionStream::handleStreamStartReceived, this, _1)); xmppLayer->onElement.disconnect(boost::bind(&BOSHSessionStream::handleElementReceived, this, _1)); xmppLayer->onError.disconnect(boost::bind(&BOSHSessionStream::handleXMPPError, this)); xmppLayer->onWriteData.disconnect(boost::bind(&BOSHSessionStream::handleXMPPLayerDataWritten, this, _1)); delete xmppLayer; xmppLayer = NULL; } void BOSHSessionStream::handlePoolXMPPDataRead(const SafeByteArray& data) { xmppLayer->handleDataRead(data); } void BOSHSessionStream::writeElement(boost::shared_ptr element) { assert(available); xmppLayer->writeElement(element); } void BOSHSessionStream::writeFooter() { connectionPool->writeFooter(); } void BOSHSessionStream::writeData(const std::string& data) { assert(available); xmppLayer->writeData(data); } void BOSHSessionStream::close() { connectionPool->close(); } bool BOSHSessionStream::isOpen() { return available; } bool BOSHSessionStream::supportsTLSEncryption() { return false; } void BOSHSessionStream::addTLSEncryption() { assert(available); } bool BOSHSessionStream::isTLSEncrypted() { return false; } Certificate::ref BOSHSessionStream::getPeerCertificate() const { return Certificate::ref(); } std::vector BOSHSessionStream::getPeerCertificateChain() const { return std::vector(); } boost::shared_ptr BOSHSessionStream::getPeerCertificateVerificationError() const { return boost::shared_ptr(); } ByteArray BOSHSessionStream::getTLSFinishMessage() const { return ByteArray(); } bool BOSHSessionStream::supportsZLibCompression() { return false; } void BOSHSessionStream::addZLibCompression() { } void BOSHSessionStream::setWhitespacePingEnabled(bool /*enabled*/) { return; } void BOSHSessionStream::resetXMPPParser() { xmppLayer->resetParser(); } void BOSHSessionStream::handleStreamStartReceived(const ProtocolHeader& header) { onStreamStartReceived(header); } void BOSHSessionStream::handleElementReceived(boost::shared_ptr element) { onElementReceived(element); } void BOSHSessionStream::handleXMPPError() { available = false; onClosed(boost::make_shared(SessionStreamError::ParseError)); } void BOSHSessionStream::handlePoolSessionStarted() { fakeStreamHeaderReceipt(); } void BOSHSessionStream::handlePoolSessionTerminated(BOSHError::ref error) { eventLoop->postEvent(boost::bind(&BOSHSessionStream::fakeStreamFooterReceipt, this, error), shared_from_this()); } void BOSHSessionStream::writeHeader(const ProtocolHeader& header) { streamHeader = header; /*First time we're told to do this, don't (the sending of the initial header is handled on connect) On subsequent requests we should restart the stream the BOSH way. */ if (!firstHeader) { eventLoop->postEvent(boost::bind(&BOSHSessionStream::fakeStreamHeaderReceipt, this), shared_from_this()); eventLoop->postEvent(boost::bind(&BOSHConnectionPool::restartStream, connectionPool), shared_from_this()); } firstHeader = false; } void BOSHSessionStream::fakeStreamHeaderReceipt() { std::stringstream header; header << ""; xmppLayer->handleDataRead(createSafeByteArray(header.str())); } void BOSHSessionStream::fakeStreamFooterReceipt(BOSHError::ref error) { std::string footer(""); xmppLayer->handleDataRead(createSafeByteArray(footer)); onClosed(error); } void BOSHSessionStream::handleXMPPLayerDataWritten(const SafeByteArray& data) { eventLoop->postEvent(boost::bind(&BOSHConnectionPool::write, connectionPool, data), shared_from_this()); } void BOSHSessionStream::handlePoolBOSHDataRead(const SafeByteArray& data) { onDataRead(data); } void BOSHSessionStream::handlePoolBOSHDataWritten(const SafeByteArray& data) { onDataWritten(data); } }; swift-im-2.0+dev6/Swiften/Session/SessionTracer.cpp0000644000175000017500000000177512227051774022227 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include namespace Swift { SessionTracer::SessionTracer(boost::shared_ptr session) : session(session) { session->onDataRead.connect(boost::bind(&SessionTracer::printData, this, '<', _1)); session->onDataWritten.connect(boost::bind(&SessionTracer::printData, this, '>', _1)); } void SessionTracer::printData(char direction, const SafeByteArray& data) { std::cerr << direction << direction << " " << session->getLocalJID() << " "; for (unsigned int i = 0; i < 72 - session->getLocalJID().toString().size() - session->getRemoteJID().toString().size(); ++i) { std::cerr << direction; } std::cerr << " " << session->getRemoteJID()<< " " << direction << direction << std::endl; std::cerr << byteArrayToString(ByteArray(data.begin(), data.end())) << std::endl; } } swift-im-2.0+dev6/Swiften/Session/SessionStream.cpp0000644000175000017500000000040712227051774022231 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { SessionStream::~SessionStream() { } }; swift-im-2.0+dev6/Swiften/Session/Session.cpp0000644000175000017500000000541012227051774021054 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include namespace Swift { Session::Session( boost::shared_ptr connection, PayloadParserFactoryCollection* payloadParserFactories, PayloadSerializerCollection* payloadSerializers, XMLParserFactory* xmlParserFactory) : connection(connection), payloadParserFactories(payloadParserFactories), payloadSerializers(payloadSerializers), xmlParserFactory(xmlParserFactory), xmppLayer(NULL), connectionLayer(NULL), streamStack(0), finishing(false) { } Session::~Session() { delete streamStack; delete connectionLayer; delete xmppLayer; } void Session::startSession() { initializeStreamStack(); handleSessionStarted(); } void Session::finishSession() { if (finishing) { return; } finishing = true; if (xmppLayer) { xmppLayer->writeFooter(); } connection->disconnect(); } void Session::finishSession(const SessionError& /*error*/) { if (finishing) { return; } finishing = true; if (xmppLayer) { xmppLayer->writeFooter(); } connection->disconnect(); } void Session::initializeStreamStack() { xmppLayer = new XMPPLayer(payloadParserFactories, payloadSerializers, xmlParserFactory, ClientStreamType); xmppLayer->onStreamStart.connect( boost::bind(&Session::handleStreamStart, this, _1)); xmppLayer->onElement.connect(boost::bind(&Session::handleElement, this, _1)); xmppLayer->onError.connect( boost::bind(&Session::finishSession, this, XMLError)); xmppLayer->onDataRead.connect(boost::bind(boost::ref(onDataRead), _1)); xmppLayer->onWriteData.connect(boost::bind(boost::ref(onDataWritten), _1)); connection->onDisconnected.connect( boost::bind(&Session::handleDisconnected, this, _1)); connectionLayer = new ConnectionLayer(connection); streamStack = new StreamStack(xmppLayer, connectionLayer); } void Session::sendElement(boost::shared_ptr stanza) { xmppLayer->writeElement(stanza); } void Session::handleDisconnected(const boost::optional& connectionError) { connection->onDisconnected.disconnect( boost::bind(&Session::handleDisconnected, this, _1)); if (connectionError) { switch (*connectionError) { case Connection::ReadError: handleSessionFinished(ConnectionReadError); onSessionFinished(ConnectionReadError); break; case Connection::WriteError: handleSessionFinished(ConnectionWriteError); onSessionFinished(ConnectionWriteError); break; } } else { boost::optional error; handleSessionFinished(error); onSessionFinished(error); } } } swift-im-2.0+dev6/Swiften/Base/0000755000175000017500000000000012227051774016154 5ustar kismithkismithswift-im-2.0+dev6/Swiften/Base/SConscript0000644000175000017500000000057112227051774020171 0ustar kismithkismithImport("swiften_env") objects = swiften_env.SwiftenObject([ "ByteArray.cpp", "DateTime.cpp", "SafeByteArray.cpp", "Error.cpp", "Log.cpp", "Paths.cpp", "String.cpp", "IDGenerator.cpp", "SimpleIDGenerator.cpp", "RandomGenerator.cpp", "BoostRandomGenerator.cpp", "sleep.cpp", "URL.cpp", ]) swiften_env.Append(SWIFTEN_OBJECTS = [objects]) swift-im-2.0+dev6/Swiften/Base/BoostRandomGenerator.cpp0000644000175000017500000000117212227051774022757 0ustar kismithkismith/* * Copyright (c) 2012 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include namespace Swift { BoostRandomGenerator::BoostRandomGenerator() { // FIXME: Not a good seed generator.seed(static_cast(std::time(0))); } int BoostRandomGenerator::generateRandomInteger(int maximum) { boost::uniform_int<> distribution(0, maximum); return distribution(generator); } } swift-im-2.0+dev6/Swiften/Base/Error.h0000644000175000017500000000044012227051774017414 0ustar kismithkismith/* * 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 namespace Swift { class SWIFTEN_API Error { public: virtual ~Error(); }; }; swift-im-2.0+dev6/Swiften/Base/StartStopper.h0000644000175000017500000000061612227051774021002 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once namespace Swift { template class StartStopper { public: StartStopper(T* target) : target(target) { target->start(); } ~StartStopper() { target->stop(); } private: T* target; }; } swift-im-2.0+dev6/Swiften/Base/sleep.h0000644000175000017500000000041612227051774017436 0ustar kismithkismith/* * 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 namespace Swift { SWIFTEN_API void sleep(unsigned int msecs); } swift-im-2.0+dev6/Swiften/Base/sleep.cpp0000644000175000017500000000077012227051774017774 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include namespace Swift { void sleep(unsigned int msecs) { boost::xtime xt; #if BOOST_VERSION >= 105000 boost::xtime_get(&xt, boost::TIME_UTC_); #else boost::xtime_get(&xt, boost::TIME_UTC); #endif xt.nsec += msecs*1000000; boost::thread::sleep(xt); } } swift-im-2.0+dev6/Swiften/Base/SafeByteArray.h0000644000175000017500000000334412227051774021032 0ustar kismithkismith/* * 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 #include #include #include #include namespace Swift { typedef std::vector > SafeByteArray; inline SafeByteArray createSafeByteArray(const ByteArray& a) { return SafeByteArray(a.begin(), a.end()); } SWIFTEN_API SafeByteArray createSafeByteArray(const char* c); inline SafeByteArray createSafeByteArray(const std::string& s) { return SafeByteArray(s.begin(), s.end()); } inline boost::shared_ptr createSafeByteArrayRef(const std::string& s) { return boost::make_shared(s.begin(), s.end()); } inline SafeByteArray createSafeByteArray(char c) { return SafeByteArray(1, c); } inline SafeByteArray createSafeByteArray(const char* c, size_t n) { return SafeByteArray(c, c + n); } inline boost::shared_ptr createSafeByteArrayRef(const char* c, size_t n) { return boost::make_shared(c, c + n); } inline SafeByteArray createSafeByteArray(const unsigned char* c, size_t n) { return SafeByteArray(c, c + n); } inline boost::shared_ptr createSafeByteArrayRef(const unsigned char* c, size_t n) { return boost::make_shared(c, c + n); } /* WARNING! This breaks the safety of the data in the safe byte array. * Do not use in modes that require data safety. */ inline std::string safeByteArrayToString(const SafeByteArray& b) { return byteArrayToString(ByteArray(b.begin(), b.end())); } } swift-im-2.0+dev6/Swiften/Base/ByteArray.cpp0000644000175000017500000000223312227051774020562 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include namespace Swift { static const int BUFFER_SIZE = 4096; void readByteArrayFromFile(ByteArray& data, const std::string& file) { std::ifstream input(file.c_str(), std::ios_base::in|std::ios_base::binary); while (input.good()) { size_t oldSize = data.size(); data.resize(oldSize + BUFFER_SIZE); input.read(reinterpret_cast(&data[oldSize]), BUFFER_SIZE); data.resize(oldSize + input.gcount()); } input.close(); } std::vector createByteArray(const std::string& s) { return std::vector(s.begin(), s.end()); } std::vector createByteArray(const char* c) { std::vector data; while (*c) { data.push_back(static_cast(*c)); ++c; } return data; } std::string byteArrayToString(const ByteArray& b) { size_t i; for (i = b.size(); i > 0; --i) { if (b[i - 1] != 0) { break; } } return i > 0 ? std::string(reinterpret_cast(vecptr(b)), i) : ""; } } swift-im-2.0+dev6/Swiften/Base/IDGenerator.cpp0000644000175000017500000000104312227051774021021 0ustar kismithkismith/* * Copyright (c) 2010-2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include namespace Swift { IDGenerator::IDGenerator() { } std::string IDGenerator::generateID() { static boost::uuids::random_generator generator; return boost::lexical_cast(generator()); } } swift-im-2.0+dev6/Swiften/Base/URL.h0000644000175000017500000000307512227051774016774 0ustar kismithkismith/* * Copyright (c) 2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include namespace Swift { class SWIFTEN_API URL { public: URL() : scheme(""), user(""), password(""), host(""), path(""), empty(true) { } URL(const std::string& scheme, const std::string& host, int port, const std::string& path) : scheme(scheme), user(), password(), host(host), port(port), path(path), empty(false) { } URL(const std::string& scheme, const std::string& host, const std::string& path) : scheme(scheme), user(), password(), host(host), path(path), empty(false) { } /** * Whether the URL is empty. */ bool isEmpty() const { return empty; } /** * Scheme used for the URL (http, https etc.) */ const std::string& getScheme() const { return scheme; } /** * Hostname */ const std::string& getHost() const { return host; } /** * Port number */ boost::optional getPort() const { return port; } /** * Path */ const std::string& getPath() const { return path; } std::string toString() const; static int getPortOrDefaultPort(const URL& url); static URL fromString(const std::string&); static std::string unescape(const std::string&); private: std::string scheme; std::string user; std::string password; std::string host; boost::optional port; std::string path; bool empty; }; } swift-im-2.0+dev6/Swiften/Base/SafeString.h0000644000175000017500000000110012227051774020362 0ustar kismithkismith/* * 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 namespace Swift { class SafeString { public: SafeString(const SafeByteArray& data) : data(data) { } SafeString(const std::string& s) { data = createSafeByteArray(s); } SafeString(const char* s) { data = createSafeByteArray(s); } operator SafeByteArray () const { return data; } private: SafeByteArray data; }; } swift-im-2.0+dev6/Swiften/Base/Error.cpp0000644000175000017500000000035312227051774017752 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { Error::~Error() { } } swift-im-2.0+dev6/Swiften/Base/Paths.cpp0000644000175000017500000000276512227051774017751 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #if defined(SWIFTEN_PLATFORM_MACOSX) #include #elif defined(SWIFTEN_PLATFORM_LINUX) #include #elif defined(SWIFTEN_PLATFORM_WINDOWS) #include #endif #include namespace Swift { boost::filesystem::path Paths::getExecutablePath() { #if defined(SWIFTEN_PLATFORM_MACOSX) ByteArray path; uint32_t size = 4096; path.resize(size); if (_NSGetExecutablePath(const_cast(reinterpret_cast(vecptr(path))), &size) == 0) { return boost::filesystem::path(std::string(reinterpret_cast(vecptr(path)), path.size()).c_str()).parent_path(); } #elif defined(SWIFTEN_PLATFORM_LINUX) ByteArray path; path.resize(4096); ssize_t size = readlink("/proc/self/exe", reinterpret_cast(vecptr(path)), path.size()); if (size > 0) { path.resize(size); return boost::filesystem::path(std::string(reinterpret_cast(vecptr(path)), path.size()).c_str()).parent_path(); } #elif defined(SWIFTEN_PLATFORM_WINDOWS) ByteArray data; data.resize(2048); GetModuleFileName(NULL, reinterpret_cast(vecptr(data)), data.size()); return boost::filesystem::path(std::string(reinterpret_cast(vecptr(data)), data.size()).c_str()).parent_path(); #endif return boost::filesystem::path(); } } swift-im-2.0+dev6/Swiften/Base/foreach.h0000644000175000017500000000044712227051774017741 0ustar kismithkismith/* * 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 #undef foreach #define foreach BOOST_FOREACH #define reverse_foreach BOOST_REVERSE_FOREACH swift-im-2.0+dev6/Swiften/Base/IDGenerator.h0000644000175000017500000000052312227051774020470 0ustar kismithkismith/* * 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 #include namespace Swift { class SWIFTEN_API IDGenerator { public: IDGenerator(); std::string generateID(); }; } swift-im-2.0+dev6/Swiften/Base/DateTime.h0000644000175000017500000000114012227051774020015 0ustar kismithkismith/* * 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 #include namespace Swift { /** * Converts a date formatted according to XEP-0082 into a ptime * object (in UTC). */ SWIFTEN_API boost::posix_time::ptime stringToDateTime(const std::string& string); /** * Converts a UTC ptime object to a XEP-0082 formatted string. */ SWIFTEN_API std::string dateTimeToString(const boost::posix_time::ptime& time); } swift-im-2.0+dev6/Swiften/Base/API.h0000644000175000017500000000072712227051774016744 0ustar kismithkismith/* * Copyright (c) 2012 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #ifdef SWIFTEN_STATIC # define SWIFTEN_API #else # ifdef SWIFTEN_PLATFORM_WINDOWS # ifdef SWIFTEN_BUILDING # define SWIFTEN_API __declspec(dllexport) # else # define SWIFTEN_API __declspec(dllimport) # endif # else # define SWIFTEN_API # endif #endif swift-im-2.0+dev6/Swiften/Base/URL.cpp0000644000175000017500000001050412227051774017322 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include namespace Swift { int URL::getPortOrDefaultPort(const URL& url) { if (url.getPort()) { return *url.getPort(); } else if (url.getScheme() == "http") { return 80; } else if (url.getScheme() == "https") { return 443; } else { std::cerr << "Unknown scheme: " + url.getScheme() << std::endl; return 80; } } URL URL::fromString(const std::string& urlString) { size_t colonIndex = urlString.find(':'); if (colonIndex == std::string::npos) { return URL(); } std::string scheme = urlString.substr(0, colonIndex); // Authority if (urlString.size() > colonIndex + 2 && urlString[colonIndex+1] == '/' && urlString[colonIndex+2] == '/') { size_t authorityIndex = colonIndex + 3; size_t slashIndex = urlString.find('/', authorityIndex); std::string authority; std::string path; if (slashIndex == std::string::npos) { authority = urlString.substr(authorityIndex); path = ""; } else { authority = urlString.substr(authorityIndex, slashIndex - authorityIndex); path = unescape(urlString.substr(slashIndex)); } size_t atIndex = authority.find('@'); std::string userInfo; std::string hostAndPort; if (atIndex != std::string::npos) { userInfo = authority.substr(0, atIndex); hostAndPort = authority.substr(atIndex + 1); } else { userInfo = ""; hostAndPort = authority; } std::string host; boost::optional port; colonIndex = hostAndPort.find(':'); if (colonIndex != std::string::npos) { host = unescape(hostAndPort.substr(0, colonIndex)); try { port = boost::lexical_cast(hostAndPort.substr(colonIndex + 1)); } catch (const boost::bad_lexical_cast&) { return URL(); } } else { host = unescape(hostAndPort); } if (port) { return URL(scheme, host, *port, path); } else { return URL(scheme, host, path); } } else { // We don't support URLs without authorities yet return URL(); } } // FIXME: Escape non-ascii characters std::string URL::toString() const { if (empty) { return ""; } std::string result = scheme + "://"; if (!user.empty()) { result += user; if (!password.empty()) { result += ":" + password; } result += "@"; } result += host; if (port) { result += ":"; result += boost::lexical_cast(*port); } result += path; return result; } // Disabling this code for now, since GCC4.5+boost1.42 (on ubuntu) seems to // result in a bug. Replacing it with naive code. #if 0 // Should be in anonymous namespace, but older GCCs complain if we do that struct PercentEncodedCharacterFinder { template boost::iterator_range operator()(Iterator begin, Iterator end) { boost::iterator_range r = boost::first_finder("%")(begin, end); if (r.end() == end) { return r; } else { if (r.end() + 1 == end || r.end() + 2 == end) { throw std::runtime_error("Incomplete escape character"); } else { r.advance_end(2); return r; } } } }; struct PercentUnencodeFormatter { template std::string operator()(const FindResult& match) const { std::stringstream s; s << std::hex << std::string(match.begin() + 1, match.end()); unsigned int value; s >> value; if (s.fail() || s.bad()) { throw std::runtime_error("Invalid escape character"); } unsigned char charValue = static_cast(value); return std::string(reinterpret_cast(&charValue), 1); } }; std::string unescape(const std::string& s) { try { return boost::find_format_all_copy(s, PercentEncodedCharacterFinder(), PercentUnencodeFormatter()); } catch (const std::exception&) { return ""; } } #endif std::string URL::unescape(const std::string& str) { std::string result; for (size_t i = 0; i < str.size(); ++i) { if (str[i] == '%') { if (i + 3 < str.size()) { std::stringstream s; s << std::hex << str.substr(i+1, 2); unsigned int value; s >> value; if (s.fail() || s.bad()) { return ""; } unsigned char charValue = static_cast(value); result += std::string(reinterpret_cast(&charValue), 1); i += 2; } else { return ""; } } else { result += str[i]; } } return result; } } swift-im-2.0+dev6/Swiften/Base/BoostRandomGenerator.h0000644000175000017500000000071312227051774022424 0ustar kismithkismith/* * Copyright (c) 2012 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include namespace Swift { class BoostRandomGenerator : public RandomGenerator{ public: BoostRandomGenerator(); int generateRandomInteger(int max); private: boost::mt19937 generator; }; } swift-im-2.0+dev6/Swiften/Base/format.h0000644000175000017500000000100412227051774017610 0ustar kismithkismith/* * 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 #include namespace Swift { inline boost::format format(const std::string& s) { using namespace boost::io; try { boost::format fmter(s); fmter.exceptions(no_error_bits); return fmter; } catch (...) { std::cerr << "Error: Invalid translation: " << s << std::endl; throw; } } } swift-im-2.0+dev6/Swiften/Base/boost_bsignals.h0000644000175000017500000000115012227051774021332 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ // Work around for the boost::signals / Qt signals keyword clash. // Based on an example from Frank Hess, improved by Niels Dekker #pragma once #if defined(signals) && defined(Q_SIGNALS) && !defined(QT_MOC_CPP) #undef signals #define signals signals #endif #include namespace boost { namespace bsignals = signals; } #if defined(signals) && defined(Q_SIGNALS) && !defined(QT_MOC_CPP) #undef signals #define signals Q_SIGNALS #endif swift-im-2.0+dev6/Swiften/Base/SimpleIDGenerator.h0000644000175000017500000000060512227051774021643 0ustar kismithkismith/* * 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 #include namespace Swift { class SWIFTEN_API SimpleIDGenerator { public: SimpleIDGenerator(); std::string generateID(); private: std::string currentID; }; } swift-im-2.0+dev6/Swiften/Base/WindowsRegistry.h0000644000175000017500000000272412227051774021515 0ustar kismithkismith/* * Copyright (c) 2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include namespace Swift { class WindowsRegistry { public: static bool isFIPSEnabled() { char* pathForXP = "System\\CurrentControlSet\\Control\\Lsa"; char* pathSinceVista = "System\\CurrentControlSet\\Control\\Lsa\\FIPSAlgorithmPolicy"; char* keyForXP = "FIPSAlgorithmPolicy"; char* keySinceVista = "Enabled"; OSVERSIONINFO osvi; ZeroMemory(&osvi, sizeof(OSVERSIONINFO)); osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&osvi); char* keyForOS = osvi.dwMajorVersion < 6 ? keyForXP : keySinceVista; char* pathForOS = osvi.dwMajorVersion < 6 ? pathForXP : pathSinceVista; /* http://support.microsoft.com/kb/811833 */ /* http://msdn.microsoft.com/en-us/library/ms724911%28VS.85%29.aspx */ HKEY key; bool result = false; if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, pathForOS, 0, KEY_READ, &key) != ERROR_SUCCESS) { /* If we can't find the key that says we're FIPS, we're not FIPS */ return result; } DWORD keyType = REG_DWORD; DWORD data; DWORD length = sizeof(data); if (RegQueryValueEx(key, keyForOS, NULL, &keyType, (LPBYTE)&data, &length) == ERROR_SUCCESS) { result = data != 0; } RegCloseKey(key); return result; } }; } swift-im-2.0+dev6/Swiften/Base/String.h0000644000175000017500000000253212227051774017575 0ustar kismithkismith/* * 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 #include #include #include #define SWIFTEN_STRING_TO_CFSTRING(a) \ CFStringCreateWithBytes(NULL, reinterpret_cast(a.c_str()), a.size(), kCFStringEncodingUTF8, false) namespace Swift { namespace String { SWIFTEN_API std::vector getUnicodeCodePoints(const std::string&); SWIFTEN_API std::pair getSplittedAtFirst(const std::string&, char c); SWIFTEN_API std::vector split(const std::string&, char c); SWIFTEN_API void replaceAll(std::string&, char c, const std::string& s); inline bool beginsWith(const std::string& s, char c) { return s.size() > 0 && s[0] == c; } inline bool endsWith(const std::string& s, char c) { return s.size() > 0 && s[s.size()-1] == c; } std::string convertIntToHexString(int h); int convertHexStringToInt(const std::string& s); }; class SWIFTEN_API makeString { public: template makeString& operator<<(T const& v) { stream << v; return *this; } operator std::string() const { return stream.str(); } private: std::ostringstream stream; }; } swift-im-2.0+dev6/Swiften/Base/SimpleIDGenerator.cpp0000644000175000017500000000111112227051774022167 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Swiften/Base/SimpleIDGenerator.h" namespace Swift { SimpleIDGenerator::SimpleIDGenerator() { } std::string SimpleIDGenerator::generateID() { bool carry = true; size_t i = 0; while (carry && i < currentID.size()) { char c = currentID[i]; if (c >= 'z') { currentID[i] = 'a'; } else { currentID[i] = c+1; carry = false; } ++i; } if (carry) { currentID += 'a'; } return currentID; } } swift-im-2.0+dev6/Swiften/Base/Log.h0000644000175000017500000000123612227051774017050 0ustar kismithkismith/* * Copyright (c) 2010-2012 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include namespace Swift { extern SWIFTEN_API bool logging; namespace LogDetail { // Only here to be able to statically check the correctness of the severity levers namespace Severity { enum { debug, info, warning, error }; } } } #define SWIFT_LOG(severity) \ if (!Swift::logging) {} else (void) LogDetail::Severity::severity, std::cerr << "[" << #severity << "] " << __FILE__ << ":" << __LINE__ << " " << __FUNCTION__ << ": " swift-im-2.0+dev6/Swiften/Base/UnitTest/0000755000175000017500000000000012227051774017733 5ustar kismithkismithswift-im-2.0+dev6/Swiften/Base/UnitTest/DateTimeTest.cpp0000644000175000017500000000253512227051774023000 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include using namespace Swift; class DateTimeTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(DateTimeTest); CPPUNIT_TEST(testStringToDateTime_UTC); CPPUNIT_TEST(testStringToDateTime_WithTimezone); CPPUNIT_TEST(testDateTimeToString); CPPUNIT_TEST_SUITE_END(); public: void testStringToDateTime_UTC() { boost::posix_time::ptime time = stringToDateTime("1969-07-21T02:56:15Z"); CPPUNIT_ASSERT_EQUAL(std::string("1969-07-21T02:56:15"), boost::posix_time::to_iso_extended_string(time)); } void testStringToDateTime_WithTimezone() { boost::posix_time::ptime time = stringToDateTime("1969-07-20T21:56:15-05:00"); CPPUNIT_ASSERT_EQUAL(std::string("1969-07-21T02:56:15"), boost::posix_time::to_iso_extended_string(time)); } void testDateTimeToString() { boost::posix_time::ptime time = stringToDateTime("1969-07-20T21:56:15-05:00"); CPPUNIT_ASSERT_EQUAL(std::string("1969-07-21T02:56:15Z"), dateTimeToString(time)); } }; CPPUNIT_TEST_SUITE_REGISTRATION(DateTimeTest); swift-im-2.0+dev6/Swiften/Base/UnitTest/StringTest.cpp0000644000175000017500000000664412227051774022557 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include using namespace Swift; class StringTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(StringTest); CPPUNIT_TEST(testGetUnicodeCodePoints); CPPUNIT_TEST(testGetSplittedAtFirst); CPPUNIT_TEST(testGetSplittedAtFirst_CharacterAtBegin); CPPUNIT_TEST(testGetSplittedAtFirst_CharacterAtEnd); CPPUNIT_TEST(testGetSplittedAtFirst_NoSuchCharacter); CPPUNIT_TEST(testReplaceAll); CPPUNIT_TEST(testReplaceAll_LastChar); CPPUNIT_TEST(testReplaceAll_ConsecutiveChars); CPPUNIT_TEST(testReplaceAll_MatchingReplace); CPPUNIT_TEST(testSplit); CPPUNIT_TEST_SUITE_END(); public: void testGetUnicodeCodePoints() { std::string testling("$\xc2\xa2\xe2\x82\xac\xf4\x8a\xaf\x8d"); std::vector points = String::getUnicodeCodePoints(testling); CPPUNIT_ASSERT_EQUAL(0x24U, points[0]); CPPUNIT_ASSERT_EQUAL(0xA2U, points[1]); CPPUNIT_ASSERT_EQUAL(0x20ACU, points[2]); CPPUNIT_ASSERT_EQUAL(0x10ABCDU, points[3]); } void testGetSplittedAtFirst() { std::string testling("ab@cd@ef"); std::pair result = String::getSplittedAtFirst(testling, '@'); CPPUNIT_ASSERT_EQUAL(std::string("ab"), result.first); CPPUNIT_ASSERT_EQUAL(std::string("cd@ef"), result.second); } void testGetSplittedAtFirst_CharacterAtBegin() { std::string testling(" ab"); std::pair result = String::getSplittedAtFirst(testling, ' '); CPPUNIT_ASSERT(result.first.empty()); CPPUNIT_ASSERT_EQUAL(std::string("ab"), result.second); } void testGetSplittedAtFirst_CharacterAtEnd() { std::string testling("ab@"); std::pair result = String::getSplittedAtFirst(testling, '@'); CPPUNIT_ASSERT_EQUAL(std::string("ab"), result.first); CPPUNIT_ASSERT(result.second.empty()); } void testGetSplittedAtFirst_NoSuchCharacter() { std::string testling("ab"); std::pair result = String::getSplittedAtFirst(testling, '@'); CPPUNIT_ASSERT_EQUAL(std::string("ab"), result.first); CPPUNIT_ASSERT(result.second.empty()); } void testReplaceAll() { std::string testling("abcbd"); String::replaceAll(testling, 'b', "xyz"); CPPUNIT_ASSERT_EQUAL(std::string("axyzcxyzd"), testling); } void testReplaceAll_LastChar() { std::string testling("abc"); String::replaceAll(testling, 'c', "xyz"); CPPUNIT_ASSERT_EQUAL(std::string("abxyz"), testling); } void testReplaceAll_ConsecutiveChars() { std::string testling("abbc"); String::replaceAll(testling, 'b',"xyz"); CPPUNIT_ASSERT_EQUAL(std::string("axyzxyzc"), testling); } void testReplaceAll_MatchingReplace() { std::string testling("abc"); String::replaceAll(testling, 'b',"bbb"); CPPUNIT_ASSERT_EQUAL(std::string("abbbc"), testling); } void testSplit() { std::vector result = String::split("abc def ghi", ' '); CPPUNIT_ASSERT_EQUAL(3, static_cast(result.size())); CPPUNIT_ASSERT_EQUAL(std::string("abc"), result[0]); CPPUNIT_ASSERT_EQUAL(std::string("def"), result[1]); CPPUNIT_ASSERT_EQUAL(std::string("ghi"), result[2]); } }; CPPUNIT_TEST_SUITE_REGISTRATION(StringTest); swift-im-2.0+dev6/Swiften/Base/UnitTest/SimpleIDGeneratorTest.cpp0000644000175000017500000000161612227051774024620 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include "Swiften/Base/SimpleIDGenerator.h" using namespace Swift; class SimpleIDGeneratorTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(SimpleIDGeneratorTest); CPPUNIT_TEST(testGenerate); CPPUNIT_TEST_SUITE_END(); public: SimpleIDGeneratorTest() {} void setUp() { generatedIDs_.clear(); } void testGenerate() { SimpleIDGenerator testling; for (unsigned int i = 0; i < 26*4; ++i) { std::string id = testling.generateID(); CPPUNIT_ASSERT(generatedIDs_.insert(id).second); } } private: std::set generatedIDs_; }; CPPUNIT_TEST_SUITE_REGISTRATION(SimpleIDGeneratorTest); swift-im-2.0+dev6/Swiften/Base/UnitTest/IDGeneratorTest.cpp0000644000175000017500000000155212227051774023445 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include using namespace Swift; class IDGeneratorTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(IDGeneratorTest); CPPUNIT_TEST(testGenerate); CPPUNIT_TEST_SUITE_END(); public: IDGeneratorTest() {} void setUp() { generatedIDs_.clear(); } void testGenerate() { IDGenerator testling; for (unsigned int i = 0; i < 26*4; ++i) { std::string id = testling.generateID(); CPPUNIT_ASSERT(generatedIDs_.insert(id).second); } } private: std::set generatedIDs_; }; CPPUNIT_TEST_SUITE_REGISTRATION(IDGeneratorTest); swift-im-2.0+dev6/Swiften/Base/UnitTest/URLTest.cpp0000644000175000017500000000757612227051774021760 0ustar kismithkismith/* * Copyright (c) 2012 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include using namespace Swift; class URLTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(URLTest); CPPUNIT_TEST(testFromString); CPPUNIT_TEST(testFromString_WithoutPath); CPPUNIT_TEST(testFromString_WithRootPath); CPPUNIT_TEST(testFromString_WithPort); CPPUNIT_TEST(testFromString_WithPortOnePartPath); CPPUNIT_TEST(testFromString_WithPortWithoutPath); CPPUNIT_TEST(testFromString_WithUserInfo); CPPUNIT_TEST(testFromString_NonASCIIHost); CPPUNIT_TEST(testFromString_NonASCIIPath); CPPUNIT_TEST(testToString); CPPUNIT_TEST(testToString_WithPort); CPPUNIT_TEST_SUITE_END(); public: void testFromString() { URL url = URL::fromString("http://foo.bar/baz/bam"); CPPUNIT_ASSERT_EQUAL(std::string("http"), url.getScheme()); CPPUNIT_ASSERT_EQUAL(std::string("foo.bar"), url.getHost()); CPPUNIT_ASSERT(!url.getPort()); CPPUNIT_ASSERT_EQUAL(std::string("/baz/bam"), url.getPath()); } void testFromString_WithoutPath() { URL url = URL::fromString("http://foo.bar"); CPPUNIT_ASSERT_EQUAL(std::string("http"), url.getScheme()); CPPUNIT_ASSERT_EQUAL(std::string("foo.bar"), url.getHost()); CPPUNIT_ASSERT(!url.getPort()); CPPUNIT_ASSERT_EQUAL(std::string(""), url.getPath()); } void testFromString_WithRootPath() { URL url = URL::fromString("http://foo.bar/"); CPPUNIT_ASSERT_EQUAL(std::string("http"), url.getScheme()); CPPUNIT_ASSERT_EQUAL(std::string("foo.bar"), url.getHost()); CPPUNIT_ASSERT(!url.getPort()); CPPUNIT_ASSERT_EQUAL(std::string("/"), url.getPath()); } void testFromString_WithPort() { URL url = URL::fromString("http://foo.bar:1234/baz/bam"); CPPUNIT_ASSERT_EQUAL(std::string("http"), url.getScheme()); CPPUNIT_ASSERT_EQUAL(std::string("foo.bar"), url.getHost()); CPPUNIT_ASSERT_EQUAL(1234, *url.getPort()); CPPUNIT_ASSERT_EQUAL(std::string("/baz/bam"), url.getPath()); } void testFromString_WithPortOnePartPath() { URL url = URL::fromString("http://foo.bar:11440/http-bind/"); CPPUNIT_ASSERT_EQUAL(std::string("http"), url.getScheme()); CPPUNIT_ASSERT_EQUAL(std::string("foo.bar"), url.getHost()); CPPUNIT_ASSERT_EQUAL(11440, *url.getPort()); CPPUNIT_ASSERT_EQUAL(std::string("/http-bind/"), url.getPath()); } void testFromString_WithPortWithoutPath() { URL url = URL::fromString("http://foo.bar:1234"); CPPUNIT_ASSERT_EQUAL(std::string("http"), url.getScheme()); CPPUNIT_ASSERT_EQUAL(std::string("foo.bar"), url.getHost()); CPPUNIT_ASSERT_EQUAL(1234, *url.getPort()); CPPUNIT_ASSERT_EQUAL(std::string(""), url.getPath()); } void testFromString_WithUserInfo() { URL url = URL::fromString("http://user:pass@foo.bar/baz/bam"); CPPUNIT_ASSERT_EQUAL(std::string("http"), url.getScheme()); CPPUNIT_ASSERT_EQUAL(std::string("foo.bar"), url.getHost()); CPPUNIT_ASSERT_EQUAL(std::string("/baz/bam"), url.getPath()); } void testFromString_NonASCIIHost() { URL url = URL::fromString("http://www.tron%C3%A7on.be/baz/bam"); CPPUNIT_ASSERT_EQUAL(std::string("www.tron\xc3\xa7on.be"), url.getHost()); } void testFromString_NonASCIIPath() { URL url = URL::fromString("http://foo.bar/baz/tron%C3%A7on/bam"); CPPUNIT_ASSERT_EQUAL(std::string("/baz/tron\xc3\xa7on/bam"), url.getPath()); } void testToString() { CPPUNIT_ASSERT_EQUAL(std::string("http://foo.bar/baz/bam"), URL("http", "foo.bar", "/baz/bam").toString()); } void testToString_WithPort() { CPPUNIT_ASSERT_EQUAL(std::string("http://foo.bar:1234/baz/bam"), URL("http", "foo.bar", 1234, "/baz/bam").toString()); } }; CPPUNIT_TEST_SUITE_REGISTRATION(URLTest); swift-im-2.0+dev6/Swiften/Base/UnitTest/ByteArrayTest.cpp0000644000175000017500000000304012227051774023176 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include using namespace Swift; class ByteArrayTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(ByteArrayTest); CPPUNIT_TEST(testGetData_NoData); CPPUNIT_TEST(testToString); CPPUNIT_TEST(testToString_NullTerminated); CPPUNIT_TEST(testToString_TwoNullTerminated); CPPUNIT_TEST(testToString_AllNull); CPPUNIT_TEST_SUITE_END(); public: void testGetData_NoData() { ByteArray testling; CPPUNIT_ASSERT_EQUAL(reinterpret_cast(NULL), reinterpret_cast(vecptr(testling))); } void testToString() { ByteArray testling(createByteArray("abcde")); CPPUNIT_ASSERT_EQUAL(std::string("abcde"), byteArrayToString(testling)); } void testToString_NullTerminated() { ByteArray testling(createByteArray("abcde\0", 6)); CPPUNIT_ASSERT_EQUAL(std::string("abcde"), byteArrayToString(testling)); } void testToString_TwoNullTerminated() { ByteArray testling(createByteArray("abcde\0\0", 7)); CPPUNIT_ASSERT_EQUAL(std::string("abcde"), byteArrayToString(testling)); } void testToString_AllNull() { ByteArray testling(createByteArray("\0\0", 2)); CPPUNIT_ASSERT_EQUAL(std::string(""), byteArrayToString(testling)); } }; CPPUNIT_TEST_SUITE_REGISTRATION(ByteArrayTest); swift-im-2.0+dev6/Swiften/Base/BoostFilesystemVersion.h0000644000175000017500000000037212227051774023030 0ustar kismithkismith/* * Copyright (c) 2010-2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #ifndef BOOST_FILESYSTEM_VERSION #define BOOST_FILESYSTEM_VERSION 2 #endif swift-im-2.0+dev6/Swiften/Base/Concat.h0000644000175000017500000000674112227051774017544 0ustar kismithkismith/* * 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 { template C concat(const C& c1, const C& c2) { C result; result.resize(c1.size() + c2.size()); std::copy(c2.begin(), c2.end(), std::copy(c1.begin(), c1.end(), result.begin())); return result; } template C concat(const C& c1, const C& c2, const C& c3) { C result; result.resize(c1.size() + c2.size() + c3.size()); std::copy(c3.begin(), c3.end(), std::copy(c2.begin(), c2.end(), std::copy(c1.begin(), c1.end(), result.begin()))); return result; } template C concat(const C& c1, const C& c2, const C& c3, const C& c4) { C result; result.resize(c1.size() + c2.size() + c3.size() + c4.size()); std::copy(c4.begin(), c4.end(), std::copy(c3.begin(), c3.end(), std::copy(c2.begin(), c2.end(), std::copy(c1.begin(), c1.end(), result.begin())))); return result; } template C concat(const C& c1, const C& c2, const C& c3, const C& c4, const C& c5) { C result; result.resize(c1.size() + c2.size() + c3.size() + c4.size() + c5.size()); std::copy(c5.begin(), c5.end(), std::copy(c4.begin(), c4.end(), std::copy(c3.begin(), c3.end(), std::copy(c2.begin(), c2.end(), std::copy(c1.begin(), c1.end(), result.begin()))))); return result; } template C concat(const C& c1, const C& c2, const C& c3, const C& c4, const C& c5, const C& c6) { C result; result.resize(c1.size() + c2.size() + c3.size() + c4.size() + c5.size() + c6.size()); std::copy(c6.begin(), c6.end(), std::copy(c5.begin(), c5.end(), std::copy(c4.begin(), c4.end(), std::copy(c3.begin(), c3.end(), std::copy(c2.begin(), c2.end(), std::copy(c1.begin(), c1.end(), result.begin())))))); return result; } template C concat(const C& c1, const C& c2, const C& c3, const C& c4, const C& c5, const C& c6, const C& c7) { C result; result.resize(c1.size() + c2.size() + c3.size() + c4.size() + c5.size() + c6.size() + c7.size()); std::copy(c7.begin(), c7.end(), std::copy(c6.begin(), c6.end(), std::copy(c5.begin(), c5.end(), std::copy(c4.begin(), c4.end(), std::copy(c3.begin(), c3.end(), std::copy(c2.begin(), c2.end(), std::copy(c1.begin(), c1.end(), result.begin()))))))); return result; } template C concat(const C& c1, const C& c2, const C& c3, const C& c4, const C& c5, const C& c6, const C& c7, const C& c8) { C result; result.resize(c1.size() + c2.size() + c3.size() + c4.size() + c5.size() + c6.size() + c7.size() + c8.size()); std::copy(c8.begin(), c8.end(), std::copy(c7.begin(), c7.end(), std::copy(c6.begin(), c6.end(), std::copy(c5.begin(), c5.end(), std::copy(c4.begin(), c4.end(), std::copy(c3.begin(), c3.end(), std::copy(c2.begin(), c2.end(), std::copy(c1.begin(), c1.end(), result.begin())))))))); return result; } template C concat(const C& c1, const C& c2, const C& c3, const C& c4, const C& c5, const C& c6, const C& c7, const C& c8, const C& c9) { C result; result.resize(c1.size() + c2.size() + c3.size() + c4.size() + c5.size() + c6.size() + c7.size() + c8.size() + c9.size()); std::copy(c9.begin(), c9.end(), std::copy(c8.begin(), c8.end(), std::copy(c7.begin(), c7.end(), std::copy(c6.begin(), c6.end(), std::copy(c5.begin(), c5.end(), std::copy(c4.begin(), c4.end(), std::copy(c3.begin(), c3.end(), std::copy(c2.begin(), c2.end(), std::copy(c1.begin(), c1.end(), result.begin()))))))))); return result; } } swift-im-2.0+dev6/Swiften/Base/ByteArray.h0000644000175000017500000000212512227051774020227 0ustar kismithkismith/* * 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 #include #include namespace Swift { typedef std::vector ByteArray; SWIFTEN_API ByteArray createByteArray(const std::string& s); SWIFTEN_API ByteArray createByteArray(const char* c); inline ByteArray createByteArray(const unsigned char* c, size_t n) { return ByteArray(c, c + n); } inline ByteArray createByteArray(const char* c, size_t n) { return ByteArray(c, c + n); } inline ByteArray createByteArray(char c) { return std::vector(1, c); } template static const T* vecptr(const std::vector& v) { return v.empty() ? NULL : &v[0]; } template static T* vecptr(std::vector& v) { return v.empty() ? NULL : &v[0]; } SWIFTEN_API std::string byteArrayToString(const ByteArray& b); SWIFTEN_API void readByteArrayFromFile(ByteArray&, const std::string& file); } swift-im-2.0+dev6/Swiften/Base/Platform.h0000644000175000017500000000274012227051774020114 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once // Base platforms #if defined(linux) || defined(__linux) || defined(__linux__) #define SWIFTEN_PLATFORM_LINUX #elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) #define SWIFTEN_PLATFORM_BSD #elif defined(sun) || defined(__sun) #define SWIFTEN_PLATFORM_SOLARIS #elif defined(__sgi) #define SWIFTEN_PLATFORM_SGI #elif defined(__hpux) #define SWIFTEN_PLATFORM_HPUX #elif defined(__CYGWIN__) #define SWIFTEN_PLATFORM_CYGWIN #elif defined(_WIN32) || defined(__WIN32__) || defined(WIN32) #define SWIFTEN_PLATFORM_WIN32 #elif defined(__BEOS__) #define SWIFTEN_PLATFORM_BEOS #elif defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__) #define SWIFTEN_PLATFORM_MACOSX #include # if TARGET_OS_IPHONE == 1 # define SWIFTEN_PLATFORM_IPHONE # endif #elif defined(__IBMCPP__) || defined(_AIX) #define SWIFTEN_PLATFORM_AIX #elif defined(__amigaos__) #define SWIFTEN_PLATFORM_AMIGAOS #elif defined(__QNXNTO__) #define SWIFTEN_PLATFORM_QNX #endif // Derived platforms #if defined(SWIFTEN_PLATFORM_CYGWIN) || defined(SWIFTEN_PLATFORM_WIN32) #define SWIFTEN_PLATFORM_WINDOWS #endif // Endianness #include #if defined(BOOST_LITTLE_ENDIAN) #define SWIFTEN_LITTLE_ENDIAN #elif defined(BOOST_BIG_ENDIAN) #define SWIFTEN_BIG_ENDIAN #endif swift-im-2.0+dev6/Swiften/Base/String.cpp0000644000175000017500000000467112227051774020136 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include namespace Swift { static inline size_t sequenceLength(char firstByte) { if ((firstByte & 0x80) == 0) { return 1; } if ((firstByte & 0xE0) == 0xC0) { return 2; } if ((firstByte & 0xF0) == 0xE0) { return 3; } if ((firstByte & 0xF8) == 0xF0) { return 4; } if ((firstByte & 0xFC) == 0xF8) { return 5; } if ((firstByte & 0xFE) == 0xFC) { return 6; } assert(false); return 1; } std::vector String::getUnicodeCodePoints(const std::string& s) { std::vector result; for (size_t i = 0; i < s.size();) { unsigned int codePoint = 0; char firstChar = s[i]; size_t length = sequenceLength(firstChar); // First character is special size_t firstCharBitSize = 7 - length; if (length == 1) { firstCharBitSize = 7; } codePoint = firstChar & ((1<<(firstCharBitSize+1)) - 1); for (size_t j = 1; j < length; ++j) { codePoint = (codePoint<<6) | (s[i+j] & 0x3F); } result.push_back(codePoint); i += length; } return result; } std::pair String::getSplittedAtFirst(const std::string& s, char c) { assert((c & 0x80) == 0); size_t firstMatch = s.find(c); if (firstMatch != s.npos) { return std::make_pair(s.substr(0,firstMatch),s.substr(firstMatch+1,s.npos)); } else { return std::make_pair(s, ""); } } void String::replaceAll(std::string& src, char c, const std::string& s) { size_t lastPos = 0; size_t matchingIndex = 0; while ((matchingIndex = src.find(c, lastPos)) != src.npos) { src.replace(matchingIndex, 1, s); lastPos = matchingIndex + s.size(); } } std::vector String::split(const std::string& s, char c) { assert((c & 0x80) == 0); std::vector result; std::string accumulator; for (size_t i = 0; i < s.size(); ++i) { if (s[i] == c) { result.push_back(accumulator); accumulator = ""; } else { accumulator += s[i]; } } result.push_back(accumulator); return result; } std::string String::convertIntToHexString(int h) { std::stringstream ss; ss << std::setbase(16); ss << h; return ss.str(); } int String::convertHexStringToInt(const std::string& s) { std::stringstream ss; int h; ss << std::setbase(16); ss << s; ss >> h; return h; } } swift-im-2.0+dev6/Swiften/Base/SafeAllocator.h0000644000175000017500000000141012227051774021040 0ustar kismithkismith/* * 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 #include namespace Swift { template class SafeAllocator : public std::allocator { public: template struct rebind { typedef SafeAllocator other; }; SafeAllocator() throw() {} SafeAllocator(const SafeAllocator&) throw() : std::allocator() {} template SafeAllocator(const SafeAllocator&) throw() {} ~SafeAllocator() throw() {} void deallocate (T* p, size_t num) { std::fill(reinterpret_cast(p), reinterpret_cast(p + num), 0); std::allocator::deallocate(p, num); } }; }; swift-im-2.0+dev6/Swiften/Base/RandomGenerator.cpp0000644000175000017500000000041212227051774021744 0ustar kismithkismith/* * Copyright (c) 2012 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { RandomGenerator::~RandomGenerator() { } } swift-im-2.0+dev6/Swiften/Base/Log.cpp0000644000175000017500000000035312227051774017402 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { bool logging = false; } swift-im-2.0+dev6/Swiften/Base/Paths.h0000644000175000017500000000054712227051774017412 0ustar kismithkismith/* * 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 #include namespace Swift { class SWIFTEN_API Paths { public: static boost::filesystem::path getExecutablePath(); }; } swift-im-2.0+dev6/Swiften/Base/Algorithm.h0000644000175000017500000000631512227051774020260 0ustar kismithkismith/* * 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 #include #include #include namespace Swift { /* * Generic erase() */ namespace Detail { struct VectorCategory {}; struct ListCategory {}; struct MapCategory {}; template struct ContainerTraits; template struct ContainerTraits< std::vector > { typedef VectorCategory Category; }; template<> struct ContainerTraits< std::string > { typedef VectorCategory Category; }; template struct ContainerTraits< std::list > { typedef ListCategory Category; }; template struct ContainerTraits< std::map > { typedef MapCategory Category; }; template void eraseImpl(C& c, const V& v, VectorCategory) { c.erase(std::remove(c.begin(), c.end(), v), c.end()); } template void eraseImpl(C& c, const V& v, ListCategory) { c.remove(v); } template void eraseImpl(C& c, const V& v, MapCategory) { for (typename C::iterator it = c.begin(); it != c.end(); ) { if (it->second == v) { c.erase(it++); } else { ++it; } } } template void eraseIfImpl(C& c, const P& p, MapCategory) { for (typename C::iterator it = c.begin(); it != c.end(); ) { if (p(*it)) { c.erase(it++); } else { ++it; } } } template void eraseIfImpl(C& c, const P& p, VectorCategory) { c.erase(std::remove_if(c.begin(), c.end(), p), c.end()); } } template void erase(C& container, const V& value) { Detail::eraseImpl(container, value, typename Detail::ContainerTraits::Category()); } template void eraseIf(C& container, const P& predicate) { Detail::eraseIfImpl(container, predicate, typename Detail::ContainerTraits::Category()); } template void append(Target& target, const Source& source) { target.insert(target.end(), source.begin(), source.end()); } template B get(const std::map& map, const A& key, const B& defaultValue) { typename std::map::const_iterator i = map.find(key); if (i != map.end()) { return i->second; } else { return defaultValue; } } template void safeClear(Container& c) { std::fill(c.begin(), c.end(), 0); c.clear(); } /* * Functors */ template class PairFirstEquals { public: PairFirstEquals(const K& value) : value(value) { } bool operator()(const std::pair& pair) const { return pair.first == value; } private: K value; }; template class PairSecondEquals { public: PairSecondEquals(const V& value) : value(value) { } bool operator()(const std::pair& pair) const { return pair.second == value; } private: V value; }; } swift-im-2.0+dev6/Swiften/Base/SafeByteArray.cpp0000644000175000017500000000063112227051774021361 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include using namespace Swift; namespace Swift { SafeByteArray createSafeByteArray(const char* c) { SafeByteArray data; while (*c) { data.push_back(static_cast(*c)); ++c; } return data; } } swift-im-2.0+dev6/Swiften/Base/RandomGenerator.h0000644000175000017500000000057112227051774021417 0ustar kismithkismith/* * Copyright (c) 2012 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include namespace Swift { class SWIFTEN_API RandomGenerator { public: virtual ~RandomGenerator(); virtual int generateRandomInteger(int max) = 0; }; } swift-im-2.0+dev6/Swiften/Base/DateTime.cpp0000644000175000017500000000203112227051774020350 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include namespace Swift { boost::posix_time::ptime stringToDateTime(const std::string& string) { static std::locale locale(std::locale::classic(), new boost::local_time::local_time_input_facet("%Y-%m-%d %H:%M:%S%F%ZP")); std::istringstream stream(string); stream.imbue(locale); boost::local_time::local_date_time result(boost::date_time::not_a_date_time); stream >> result; return result.utc_time(); } std::string dateTimeToString(const boost::posix_time::ptime& time) { std::string stampString = std::string(boost::posix_time::to_iso_extended_string(time)); String::replaceAll(stampString, ',', "."); stampString += "Z"; return stampString; } } swift-im-2.0+dev6/Swiften/SConscript0000644000175000017500000005561612227051774017331 0ustar kismithkismithimport os, re, Version, os.path, time Import("env") ################################################################################ # Flags ################################################################################ swiften_dep_modules = ["BOOST", "GCONF", "ICU", "LIBIDN", "ZLIB", "OPENSSL", "LIBXML", "EXPAT", "AVAHI", "LIBMINIUPNPC", "LIBNATPMP", "SQLITE", "SQLITE_ASYNC"] external_swiften_dep_modules = ["BOOST"] if env["SCONS_STAGE"] == "flags" : env["SWIFTEN_DLL"] = env["swiften_dll"] env["SWIFTEN_VERSION"] = Version.getBuildVersion(env.Dir("#").abspath, "swift") version_match = re.match("(\d+)\.(\d+).*", env["SWIFTEN_VERSION"]) if version_match : env["SWIFTEN_VERSION_MAJOR"] = int(version_match.group(1)) env["SWIFTEN_VERSION_MINOR"] = int(version_match.group(2)) else : env["SWIFTEN_VERSION_MAJOR"] = 0 env["SWIFTEN_VERSION_MINOR"] = 0 env["SWIFTEN_VERSION_PATCH"] = 0 env["SWIFTEN_LIBRARY"] = "Swiften" env["SWIFTEN_LIBRARY_FILE"] = "Swiften" env["SWIFTEN_LIBRARY_ALIASES"] = [] if env["SWIFTEN_DLL"] : if env["PLATFORM"] == "win32" : env["SWIFTEN_LIBRARY"] = env.subst("Swiften${SWIFTEN_VERSION_MAJOR}") env["SWIFTEN_LIBRARY_FILE"] = env.subst("${SWIFTEN_LIBRARY}.dll") elif env["PLATFORM"] == "darwin" : env["SWIFTEN_LIBRARY_FILE"] = env.subst("Swiften.${SWIFTEN_VERSION_MAJOR}.${SWIFTEN_VERSION_MINOR}") env["SWIFTEN_LIBRARY_ALIASES"] = ["libSwiften.dylib", env.subst("libSwiften.${SWIFTEN_VERSION_MAJOR}.dylib")] else : env["SWIFTEN_LIBRARY_FILE"] = env.subst("libSwiften.so.${SWIFTEN_VERSION_MAJOR}.${SWIFTEN_VERSION_MINOR}") env["SWIFTEN_LIBRARY_ALIASES"] = ["libSwiften.so", env.subst("libSwiften.so.${SWIFTEN_VERSION_MAJOR}")] if env["SWIFTEN_DLL"] : env.AddMethod(lambda e,s : e.SharedObject(s), "SwiftenObject") else : env.AddMethod(lambda e,s : e.StaticObject(s), "SwiftenObject") swiften_env = env.Clone() swiften_env["LIBPATH"] = [Dir(".")] swiften_env["LIBRUNPATH"] = [Dir(".")] swiften_env["LIBS"] = [swiften_env["SWIFTEN_LIBRARY"]] if not env["SWIFTEN_DLL"] : swiften_env.Append(CPPDEFINES = ["SWIFTEN_STATIC"]) dep_env = env.Clone() for module in swiften_dep_modules : module_flags = env.get(module + "_FLAGS", {}) if env.get(module + "_BUNDLED", False) : if module in external_swiften_dep_modules : swiften_env.UseFlags(module_flags) else : if module in external_swiften_dep_modules : dep_env.UseFlags(module_flags) else : # Expose only libraries dep_env.Append(LIBPATH = module_flags.get("LIBPATH", [])) dep_env.Append(LIBS = module_flags.get("LIBS", [])) dep_env.Append(FRAMEWORKS = module_flags.get("FRAMEWORKS", [])) dep_env.UseFlags(dep_env["PLATFORM_FLAGS"]) if env.get("HAVE_SCHANNEL", 0) : dep_env.Append(LIBS = ["Winscard"]) for var, e in [("SWIFTEN_FLAGS", swiften_env), ("SWIFTEN_DEP_FLAGS", dep_env)] : env[var] = { "CPPDEFINES": e.get("CPPDEFINES", []), "CPPPATH": e.get("CPPPATH", []), "CPPFLAGS": e.get("CPPFLAGS", []), "LIBPATH": e.get("LIBPATH", []), "LIBRUNPATH": e.get("LIBRUNPATH", []), "LIBS": e.get("LIBS", []), "FRAMEWORKS": e.get("FRAMEWORKS", []), } ################################################################################ # Build ################################################################################ if env["SCONS_STAGE"] == "build" : swiften_env = env.Clone() swiften_env.Append(CPPDEFINES = ["SWIFTEN_BUILDING"]) for module in swiften_dep_modules : swiften_env.UseFlags(swiften_env.get(module + "_FLAGS", {})) if env.get(module + "_BUNDLED", False) : swiften_env.Append(SWIFTEN_OBJECTS = env.get(module + "_OBJECTS", [])) swiften_env.UseFlags(swiften_env["PLATFORM_FLAGS"]) if swiften_env["SWIFTEN_DLL"] : swiften_env.AddMethod(lambda e,l,o : e.SharedLibrary(l,o), "SwiftenLibrary") else : swiften_env.Append(CPPDEFINES = ["SWIFTEN_STATIC"]) swiften_env.AddMethod(lambda e,l,o : e.StaticLibrary(l,o), "SwiftenLibrary") Export("swiften_env") # TODO: Move all this to a submodule SConscript sources = [ "Chat/ChatStateTracker.cpp", "Chat/ChatStateNotifier.cpp", "Client/ClientSessionStanzaChannel.cpp", "Client/CoreClient.cpp", "Client/Client.cpp", "Client/ClientXMLTracer.cpp", "Client/ClientSession.cpp", "Client/BlockList.cpp", "Client/BlockListImpl.cpp", "Client/ClientBlockListManager.cpp", "Client/MemoryStorages.cpp", "Client/NickResolver.cpp", "Client/NickManager.cpp", "Client/NickManagerImpl.cpp", "Client/Storages.cpp", "Client/XMLBeautifier.cpp", "Compress/ZLibCodecompressor.cpp", "Compress/ZLibDecompressor.cpp", "Compress/ZLibCompressor.cpp", "Elements/DiscoInfo.cpp", "Elements/Presence.cpp", "Elements/Form.cpp", "Elements/StreamFeatures.cpp", "Elements/Element.cpp", "Elements/IQ.cpp", "Elements/Payload.cpp", "Elements/RosterItemExchangePayload.cpp", "Elements/RosterPayload.cpp", "Elements/Stanza.cpp", "Elements/StatusShow.cpp", "Elements/StreamManagementEnabled.cpp", "Elements/StreamResume.cpp", "Elements/StreamResumed.cpp", "Elements/VCard.cpp", "Elements/MUCOccupant.cpp", "Entity/Entity.cpp", "Entity/PayloadPersister.cpp", "MUC/MUC.cpp", "MUC/MUCManager.cpp", "MUC/MUCRegistry.cpp", "MUC/MUCBookmarkManager.cpp", "Queries/IQChannel.cpp", "Queries/IQHandler.cpp", "Queries/IQRouter.cpp", "Queries/Request.cpp", "Queries/Requests/GetInBandRegistrationFormRequest.cpp", "Queries/Requests/SubmitInBandRegistrationFormRequest.cpp", "Queries/Responders/SoftwareVersionResponder.cpp", "Roster/RosterStorage.cpp", "Roster/RosterMemoryStorage.cpp", "Roster/XMPPRoster.cpp", "Roster/XMPPRosterImpl.cpp", "Roster/XMPPRosterController.cpp", "Serializer/AuthRequestSerializer.cpp", "Serializer/AuthSuccessSerializer.cpp", "Serializer/AuthChallengeSerializer.cpp", "Serializer/AuthResponseSerializer.cpp", "Serializer/CompressRequestSerializer.cpp", "Serializer/ElementSerializer.cpp", "Serializer/MessageSerializer.cpp", "Serializer/StreamManagementEnabledSerializer.cpp", "Serializer/StreamResumeSerializer.cpp", "Serializer/StreamResumedSerializer.cpp", "Serializer/ComponentHandshakeSerializer.cpp", "Serializer/PayloadSerializer.cpp", "Serializer/PayloadSerializerCollection.cpp", "Serializer/PayloadSerializers/IBBSerializer.cpp", "Serializer/PayloadSerializers/CapsInfoSerializer.cpp", "Serializer/PayloadSerializers/ChatStateSerializer.cpp", "Serializer/PayloadSerializers/DiscoInfoSerializer.cpp", "Serializer/PayloadSerializers/DiscoItemsSerializer.cpp", "Serializer/PayloadSerializers/ErrorSerializer.cpp", "Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp", "Serializer/PayloadSerializers/MUCPayloadSerializer.cpp", "Serializer/PayloadSerializers/MUCUserPayloadSerializer.cpp", "Serializer/PayloadSerializers/MUCAdminPayloadSerializer.cpp", "Serializer/PayloadSerializers/MUCOwnerPayloadSerializer.cpp", "Serializer/PayloadSerializers/MUCDestroyPayloadSerializer.cpp", "Serializer/PayloadSerializers/MUCInvitationPayloadSerializer.cpp", "Serializer/PayloadSerializers/ResourceBindSerializer.cpp", "Serializer/PayloadSerializers/RosterItemExchangeSerializer.cpp", "Serializer/PayloadSerializers/RosterSerializer.cpp", "Serializer/PayloadSerializers/SecurityLabelSerializer.cpp", "Serializer/PayloadSerializers/SecurityLabelsCatalogSerializer.cpp", "Serializer/PayloadSerializers/SoftwareVersionSerializer.cpp", "Serializer/PayloadSerializers/StreamInitiationSerializer.cpp", "Serializer/PayloadSerializers/BytestreamsSerializer.cpp", "Serializer/PayloadSerializers/VCardSerializer.cpp", "Serializer/PayloadSerializers/VCardUpdateSerializer.cpp", "Serializer/PayloadSerializers/StorageSerializer.cpp", "Serializer/PayloadSerializers/PrivateStorageSerializer.cpp", "Serializer/PayloadSerializers/DelaySerializer.cpp", "Serializer/PayloadSerializers/CommandSerializer.cpp", "Serializer/PayloadSerializers/InBandRegistrationPayloadSerializer.cpp", "Serializer/PayloadSerializers/SearchPayloadSerializer.cpp", "Serializer/PayloadSerializers/FormSerializer.cpp", "Serializer/PayloadSerializers/NicknameSerializer.cpp", "Serializer/PayloadSerializers/JingleFileTransferDescriptionSerializer.cpp", "Serializer/PayloadSerializers/JinglePayloadSerializer.cpp", "Serializer/PayloadSerializers/JingleContentPayloadSerializer.cpp", "Serializer/PayloadSerializers/JingleFileTransferHashSerializer.cpp", "Serializer/PayloadSerializers/JingleFileTransferReceivedSerializer.cpp", "Serializer/PayloadSerializers/JingleIBBTransportPayloadSerializer.cpp", "Serializer/PayloadSerializers/JingleS5BTransportPayloadSerializer.cpp", "Serializer/PayloadSerializers/StreamInitiationFileInfoSerializer.cpp", "Serializer/PayloadSerializers/DeliveryReceiptSerializer.cpp", "Serializer/PayloadSerializers/DeliveryReceiptRequestSerializer.cpp", "Serializer/PayloadSerializers/WhiteboardSerializer.cpp", "Serializer/PresenceSerializer.cpp", "Serializer/StanzaSerializer.cpp", "Serializer/StreamErrorSerializer.cpp", "Serializer/StreamFeaturesSerializer.cpp", "Serializer/XML/XMLElement.cpp", "Serializer/XML/XMLNode.cpp", "Serializer/XMPPSerializer.cpp", "Session/Session.cpp", "Session/SessionTracer.cpp", "Session/SessionStream.cpp", "Session/BasicSessionStream.cpp", "Session/BOSHSessionStream.cpp", "StringCodecs/Base64.cpp", "StringCodecs/SHA256.cpp", "StringCodecs/MD5.cpp", "StringCodecs/Hexify.cpp", "Whiteboard/WhiteboardResponder.cpp", "Whiteboard/WhiteboardSession.cpp", "Whiteboard/IncomingWhiteboardSession.cpp", "Whiteboard/OutgoingWhiteboardSession.cpp", "Whiteboard/WhiteboardSessionManager.cpp", "Whiteboard/WhiteboardServer.cpp", "Whiteboard/WhiteboardClient.cpp", "Elements/Whiteboard/WhiteboardColor.cpp", "Whiteboard/WhiteboardTransformer.cpp", ] SConscript(dirs = [ "Avatars", "Base", "IDN", "SASL", "TLS", "EventLoop", "Parser", "JID", "Jingle", "Disco", "VCards", "Network", "Presence", "FileTransfer", "History", "StreamStack", "LinkLocal", "StreamManagement", "Component", "AdHoc" ]) if env["build_examples"] : SConscript(dirs = [ "Config", "Examples" ]) env.SConscript(test_only = True, dirs = [ "QA", ]) myenv = swiften_env.Clone() if myenv["PLATFORM"] == "win32": sources.append("StringCodecs/SHA1_Windows.cpp") else: sources.append("StringCodecs/SHA1.cpp") if myenv["PLATFORM"] != "darwin" and myenv["PLATFORM"] != "win32" and myenv.get("HAVE_GCONF", 0) : env.MergeFlags(env["GCONF_FLAGS"]) if myenv["SWIFTEN_DLL"] : if myenv["PLATFORM"] == "posix" : myenv.Append(LINKFLAGS = ["-Wl,-soname,libSwiften.so.$SWIFTEN_VERSION_MAJOR"]) myenv["SHLIBSUFFIX"] = "" elif myenv["PLATFORM"] == "darwin" : myenv.Append(LINKFLAGS = ["-Wl,-install_name,libSwiften.so.$SWIFTEN_VERSION_MAJOR", "-Wl,-compatibility_version,${SWIFTEN_VERSION_MAJOR}.${SWIFTEN_VERSION_MINOR}", "-Wl,-current_version,${SWIFTEN_VERSION_MAJOR}.${SWIFTEN_VERSION_MINOR}"]) elif myenv["PLATFORM"] == "win32" : res_env = myenv.Clone() res_env.Append(CPPDEFINES = [ ("SWIFTEN_LIBRARY_FILE", "\"\\\"${SWIFTEN_LIBRARY_FILE}\\\"\""), ("SWIFTEN_COPYRIGHT_YEAR", "\"\\\"2010-%s\\\"\"" % str(time.localtime()[0])), ("SWIFTEN_VERSION_MAJOR", "${SWIFTEN_VERSION_MAJOR}"), ("SWIFTEN_VERSION_MINOR", "${SWIFTEN_VERSION_MINOR}"), ("SWIFTEN_VERSION_PATCH", "${SWIFTEN_VERSION_PATCH}"), ]) res = res_env.RES("Swiften.rc") # For some reason, SCons isn't picking up the dependency correctly # Adding it explicitly until i figure out why res_env.Depends(res, "Version.h") sources += res swiften_lib = myenv.SwiftenLibrary(swiften_env["SWIFTEN_LIBRARY_FILE"], sources + swiften_env["SWIFTEN_OBJECTS"]) def symlink(env, target, source) : if os.path.exists(str(target[0])) : os.unlink(str(target[0])) os.symlink(source[0].get_contents(), str(target[0])) for alias in myenv["SWIFTEN_LIBRARY_ALIASES"] : myenv.Command(myenv.File(alias), [myenv.Value(swiften_lib[0].name), swiften_lib[0]], symlink) env.Append(UNITTEST_SOURCES = [ File("Avatars/UnitTest/VCardUpdateAvatarManagerTest.cpp"), File("Avatars/UnitTest/VCardAvatarManagerTest.cpp"), File("Avatars/UnitTest/CombinedAvatarProviderTest.cpp"), File("Base/UnitTest/IDGeneratorTest.cpp"), File("Base/UnitTest/SimpleIDGeneratorTest.cpp"), File("Base/UnitTest/StringTest.cpp"), File("Base/UnitTest/DateTimeTest.cpp"), File("Base/UnitTest/ByteArrayTest.cpp"), File("Base/UnitTest/URLTest.cpp"), File("Chat/UnitTest/ChatStateNotifierTest.cpp"), # File("Chat/UnitTest/ChatStateTrackerTest.cpp"), File("Client/UnitTest/ClientSessionTest.cpp"), File("Client/UnitTest/NickResolverTest.cpp"), File("Compress/UnitTest/ZLibCompressorTest.cpp"), File("Compress/UnitTest/ZLibDecompressorTest.cpp"), File("Component/UnitTest/ComponentHandshakeGeneratorTest.cpp"), File("Component/UnitTest/ComponentConnectorTest.cpp"), File("Component/UnitTest/ComponentSessionTest.cpp"), File("Disco/UnitTest/CapsInfoGeneratorTest.cpp"), File("Disco/UnitTest/CapsManagerTest.cpp"), File("Disco/UnitTest/EntityCapsManagerTest.cpp"), File("Disco/UnitTest/JIDDiscoInfoResponderTest.cpp"), File("Disco/UnitTest/DiscoInfoResponderTest.cpp"), File("Elements/UnitTest/IQTest.cpp"), File("Elements/UnitTest/StanzaTest.cpp"), File("Elements/UnitTest/FormTest.cpp"), File("EventLoop/UnitTest/EventLoopTest.cpp"), File("EventLoop/UnitTest/SimpleEventLoopTest.cpp"), # File("History/UnitTest/SQLiteHistoryManagerTest.cpp"), File("JID/UnitTest/JIDTest.cpp"), File("LinkLocal/UnitTest/LinkLocalConnectorTest.cpp"), File("LinkLocal/UnitTest/LinkLocalServiceBrowserTest.cpp"), File("LinkLocal/UnitTest/LinkLocalServiceInfoTest.cpp"), File("LinkLocal/UnitTest/LinkLocalServiceTest.cpp"), File("MUC/UnitTest/MUCTest.cpp"), File("Network/UnitTest/HostAddressTest.cpp"), File("Network/UnitTest/ConnectorTest.cpp"), File("Network/UnitTest/ChainedConnectorTest.cpp"), File("Network/UnitTest/DomainNameServiceQueryTest.cpp"), File("Network/UnitTest/HTTPConnectProxiedConnectionTest.cpp"), File("Network/UnitTest/BOSHConnectionTest.cpp"), File("Network/UnitTest/BOSHConnectionPoolTest.cpp"), File("Parser/PayloadParsers/UnitTest/BodyParserTest.cpp"), File("Parser/PayloadParsers/UnitTest/DiscoInfoParserTest.cpp"), File("Parser/PayloadParsers/UnitTest/DiscoItemsParserTest.cpp"), File("Parser/PayloadParsers/UnitTest/ErrorParserTest.cpp"), File("Parser/PayloadParsers/UnitTest/FormParserTest.cpp"), File("Parser/PayloadParsers/UnitTest/CommandParserTest.cpp"), File("Parser/PayloadParsers/UnitTest/PriorityParserTest.cpp"), File("Parser/PayloadParsers/UnitTest/RawXMLPayloadParserTest.cpp"), File("Parser/PayloadParsers/UnitTest/ResourceBindParserTest.cpp"), File("Parser/PayloadParsers/UnitTest/RosterItemExchangeParserTest.cpp"), File("Parser/PayloadParsers/UnitTest/RosterParserTest.cpp"), File("Parser/PayloadParsers/UnitTest/IBBParserTest.cpp"), File("Parser/PayloadParsers/UnitTest/JingleParserTest.cpp"), File("Parser/PayloadParsers/UnitTest/SearchPayloadParserTest.cpp"), File("Parser/PayloadParsers/UnitTest/SecurityLabelParserTest.cpp"), File("Parser/PayloadParsers/UnitTest/SecurityLabelsCatalogParserTest.cpp"), File("Parser/PayloadParsers/UnitTest/SoftwareVersionParserTest.cpp"), File("Parser/PayloadParsers/UnitTest/StatusParserTest.cpp"), File("Parser/PayloadParsers/UnitTest/StatusShowParserTest.cpp"), File("Parser/PayloadParsers/UnitTest/StreamInitiationParserTest.cpp"), File("Parser/PayloadParsers/UnitTest/VCardParserTest.cpp"), File("Parser/PayloadParsers/UnitTest/StorageParserTest.cpp"), File("Parser/PayloadParsers/UnitTest/PrivateStorageParserTest.cpp"), File("Parser/PayloadParsers/UnitTest/VCardUpdateParserTest.cpp"), File("Parser/PayloadParsers/UnitTest/ReplaceTest.cpp"), File("Parser/PayloadParsers/UnitTest/MUCAdminPayloadParserTest.cpp"), File("Parser/PayloadParsers/UnitTest/MUCUserPayloadParserTest.cpp"), File("Parser/PayloadParsers/UnitTest/DeliveryReceiptParserTest.cpp"), File("Parser/UnitTest/BOSHBodyExtractorTest.cpp"), File("Parser/UnitTest/AttributeMapTest.cpp"), File("Parser/UnitTest/IQParserTest.cpp"), File("Parser/UnitTest/GenericPayloadTreeParserTest.cpp"), File("Parser/UnitTest/MessageParserTest.cpp"), File("Parser/UnitTest/PayloadParserFactoryCollectionTest.cpp"), File("Parser/UnitTest/PresenceParserTest.cpp"), File("Parser/UnitTest/StanzaAckParserTest.cpp"), File("Parser/UnitTest/SerializingParserTest.cpp"), File("Parser/UnitTest/StanzaParserTest.cpp"), File("Parser/UnitTest/StreamFeaturesParserTest.cpp"), File("Parser/UnitTest/StreamManagementEnabledParserTest.cpp"), File("Parser/UnitTest/XMLParserTest.cpp"), File("Parser/UnitTest/XMPPParserTest.cpp"), File("Presence/UnitTest/PresenceOracleTest.cpp"), File("Presence/UnitTest/DirectedPresenceSenderTest.cpp"), File("Presence/UnitTest/PayloadAddingPresenceSenderTest.cpp"), File("Queries/Requests/UnitTest/GetPrivateStorageRequestTest.cpp"), File("Queries/UnitTest/IQRouterTest.cpp"), File("Queries/UnitTest/RequestTest.cpp"), File("Queries/UnitTest/ResponderTest.cpp"), File("Roster/UnitTest/XMPPRosterImplTest.cpp"), File("Roster/UnitTest/XMPPRosterControllerTest.cpp"), File("Roster/UnitTest/XMPPRosterSignalHandler.cpp"), File("Serializer/PayloadSerializers/UnitTest/PayloadsSerializer.cpp"), File("Serializer/PayloadSerializers/UnitTest/CapsInfoSerializerTest.cpp"), File("Serializer/PayloadSerializers/UnitTest/FormSerializerTest.cpp"), File("Serializer/PayloadSerializers/UnitTest/DiscoInfoSerializerTest.cpp"), File("Serializer/PayloadSerializers/UnitTest/ErrorSerializerTest.cpp"), File("Serializer/PayloadSerializers/UnitTest/PrioritySerializerTest.cpp"), File("Serializer/PayloadSerializers/UnitTest/ResourceBindSerializerTest.cpp"), File("Serializer/PayloadSerializers/UnitTest/RosterItemExchangeSerializerTest.cpp"), File("Serializer/PayloadSerializers/UnitTest/RosterSerializerTest.cpp"), File("Serializer/PayloadSerializers/UnitTest/SearchPayloadSerializerTest.cpp"), File("Serializer/PayloadSerializers/UnitTest/SecurityLabelSerializerTest.cpp"), File("Serializer/PayloadSerializers/UnitTest/SecurityLabelsCatalogSerializerTest.cpp"), File("Serializer/PayloadSerializers/UnitTest/SoftwareVersionSerializerTest.cpp"), File("Serializer/PayloadSerializers/UnitTest/StatusSerializerTest.cpp"), File("Serializer/PayloadSerializers/UnitTest/StatusShowSerializerTest.cpp"), File("Serializer/PayloadSerializers/UnitTest/StreamInitiationSerializerTest.cpp"), File("Serializer/PayloadSerializers/UnitTest/InBandRegistrationPayloadSerializerTest.cpp"), File("Serializer/PayloadSerializers/UnitTest/VCardUpdateSerializerTest.cpp"), File("Serializer/PayloadSerializers/UnitTest/VCardSerializerTest.cpp"), File("Serializer/PayloadSerializers/UnitTest/StorageSerializerTest.cpp"), File("Serializer/PayloadSerializers/UnitTest/PrivateStorageSerializerTest.cpp"), File("Serializer/PayloadSerializers/UnitTest/ReplaceSerializerTest.cpp"), File("Serializer/PayloadSerializers/UnitTest/MUCAdminPayloadSerializerTest.cpp"), File("Serializer/PayloadSerializers/UnitTest/JingleSerializersTest.cpp"), File("Serializer/PayloadSerializers/UnitTest/DeliveryReceiptSerializerTest.cpp"), File("Serializer/UnitTest/StreamFeaturesSerializerTest.cpp"), File("Serializer/UnitTest/AuthSuccessSerializerTest.cpp"), File("Serializer/UnitTest/AuthChallengeSerializerTest.cpp"), File("Serializer/UnitTest/AuthRequestSerializerTest.cpp"), File("Serializer/UnitTest/AuthResponseSerializerTest.cpp"), File("Serializer/UnitTest/XMPPSerializerTest.cpp"), File("Serializer/XML/UnitTest/XMLElementTest.cpp"), File("StreamManagement/UnitTest/StanzaAckRequesterTest.cpp"), File("StreamManagement/UnitTest/StanzaAckResponderTest.cpp"), File("StreamStack/UnitTest/StreamStackTest.cpp"), File("StreamStack/UnitTest/XMPPLayerTest.cpp"), File("StringCodecs/UnitTest/Base64Test.cpp"), File("StringCodecs/UnitTest/SHA1Test.cpp"), File("StringCodecs/UnitTest/SHA256Test.cpp"), File("StringCodecs/UnitTest/MD5Test.cpp"), File("StringCodecs/UnitTest/HexifyTest.cpp"), File("StringCodecs/UnitTest/HMACTest.cpp"), File("StringCodecs/UnitTest/PBKDF2Test.cpp"), File("TLS/UnitTest/ServerIdentityVerifierTest.cpp"), File("TLS/UnitTest/CertificateTest.cpp"), File("VCards/UnitTest/VCardManagerTest.cpp"), File("Whiteboard/UnitTest/WhiteboardServerTest.cpp"), File("Whiteboard/UnitTest/WhiteboardClientTest.cpp"), ]) # Generate the Swiften header def relpath(path, start) : i = len(os.path.commonprefix([path, start])) return path[i+1:] swiften_header = "#pragma once\n" swiften_includes = [] top_path = env.Dir("..").abspath for root, dirs, files in os.walk(env.Dir(".").abspath) : if root.endswith("UnitTest") : continue for file in files : if not file.endswith(".h") : continue include = relpath(os.path.join(root, file), top_path) if swiften_env["PLATFORM"] == "win32" : include = include.replace("\\", "/") swiften_includes.append(include) # Private modules if root.endswith("Config") : continue # Library-specfifc private modules if root.endswith("OpenSSL") or root.endswith("Cocoa") or root.endswith("Qt") or root.endswith("Avahi") or root.endswith("Bonjour") : continue # Library-specific files if file.endswith("_Private.h") or file.startswith("Schannel") or file.startswith("CAPI") or file.startswith("MacOSX") or file.startswith("Windows") or file.endswith("_Windows.h") or file.startswith("SQLite") or file == "ICUConverter.h" : continue # Specific headers we don't want to globally include if file == "Swiften.h" or file == "foreach.h" or file == "Log.h" or file == "format.h" : continue swiften_header += "#include <" + include + ">\n" swiften_includes.append(include) swiften_env.WriteVal("Swiften.h", swiften_env.Value(swiften_header)) swiften_includes.append("Swiften/Swiften.h") version_header = "#pragma once\n\n" version_header += "#define SWIFTEN_VERSION 0x%02X%02X%02X\n" % (swiften_env["SWIFTEN_VERSION_MAJOR"], swiften_env["SWIFTEN_VERSION_MINOR"], swiften_env["SWIFTEN_VERSION_PATCH"]) version_header += "#define SWIFTEN_VERSION_STRING \"%s\"\n" % swiften_env["SWIFTEN_VERSION"] swiften_env.WriteVal("Version.h", swiften_env.Value(version_header)) swiften_includes.append("Swiften/Version.h") # Install swiften if swiften_env.get("SWIFTEN_INSTALLDIR", "") : swiften_env.Install(os.path.join(swiften_env["SWIFTEN_INSTALLDIR"], "lib"), swiften_lib) for alias in myenv["SWIFTEN_LIBRARY_ALIASES"] : myenv.Command(myenv.File(os.path.join(swiften_env["SWIFTEN_INSTALLDIR"], "lib", alias)), [env.Value(swiften_lib[0].name), swiften_lib[0]], symlink) for include in swiften_includes : swiften_env.Install(os.path.join(swiften_env["SWIFTEN_INSTALLDIR"], "include", os.path.dirname(include)), "#/" + include) swift-im-2.0+dev6/Swiften/Roster/0000755000175000017500000000000012227051774016560 5ustar kismithkismithswift-im-2.0+dev6/Swiften/Roster/GetRosterRequest.h0000644000175000017500000000154512227051774022225 0ustar kismithkismith/* * 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 #include namespace Swift { class GetRosterRequest : public GenericRequest { public: typedef boost::shared_ptr ref; static ref create(IQRouter* router) { return ref(new GetRosterRequest(router)); } static ref create(IQRouter* router, const std::string& version) { ref result(new GetRosterRequest(router)); result->getPayloadGeneric()->setVersion(version); return result; } private: GetRosterRequest(IQRouter* router) : GenericRequest(IQ::Get, JID(), boost::shared_ptr(new RosterPayload()), router) { } }; } swift-im-2.0+dev6/Swiften/Roster/RosterMemoryStorage.h0000644000175000017500000000110212227051774022717 0ustar kismithkismith/* * 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 #include namespace Swift { class SWIFTEN_API RosterMemoryStorage : public RosterStorage { public: RosterMemoryStorage(); virtual boost::shared_ptr getRoster() const { return roster; } virtual void setRoster(boost::shared_ptr); private: boost::shared_ptr roster; }; } swift-im-2.0+dev6/Swiften/Roster/RosterStorage.h0000644000175000017500000000100112227051774021524 0ustar kismithkismith/* * 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 #include #include namespace Swift { class SWIFTEN_API RosterStorage { public: virtual ~RosterStorage(); virtual boost::shared_ptr getRoster() const = 0; virtual void setRoster(boost::shared_ptr) = 0; }; } swift-im-2.0+dev6/Swiften/Roster/RosterStorage.cpp0000644000175000017500000000040512227051774022066 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { RosterStorage::~RosterStorage() { } } swift-im-2.0+dev6/Swiften/Roster/XMPPRosterImpl.cpp0000644000175000017500000000532312227051774022074 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include namespace Swift { XMPPRosterImpl::XMPPRosterImpl() { } void XMPPRosterImpl::addContact(const JID& jid, const std::string& name, const std::vector& groups, RosterItemPayload::Subscription subscription) { JID bareJID(jid.toBare()); std::map::iterator i = entries_.find(bareJID); if (i != entries_.end()) { std::string oldName = i->second.getName(); std::vector oldGroups = i->second.getGroups(); i->second = XMPPRosterItem(jid, name, groups, subscription); onJIDUpdated(bareJID, oldName, oldGroups); } else { entries_.insert(std::make_pair(bareJID, XMPPRosterItem(jid, name, groups, subscription))); onJIDAdded(bareJID); } } void XMPPRosterImpl::removeContact(const JID& jid) { entries_.erase(JID(jid.toBare())); onJIDRemoved(jid); } void XMPPRosterImpl::clear() { entries_.clear(); onRosterCleared(); } bool XMPPRosterImpl::containsJID(const JID& jid) { return entries_.find(JID(jid.toBare())) != entries_.end(); } std::string XMPPRosterImpl::getNameForJID(const JID& jid) const { std::map::const_iterator i = entries_.find(jid.toBare()); if (i != entries_.end()) { return i->second.getName(); } else { return ""; } } std::vector XMPPRosterImpl::getGroupsForJID(const JID& jid) { std::map::iterator i = entries_.find(jid.toBare()); if (i != entries_.end()) { return i->second.getGroups(); } else { return std::vector(); } } RosterItemPayload::Subscription XMPPRosterImpl::getSubscriptionStateForJID(const JID& jid) { std::map::iterator i = entries_.find(jid.toBare()); if (i != entries_.end()) { return i->second.getSubscription(); } else { return RosterItemPayload::None; } } std::vector XMPPRosterImpl::getItems() const { std::vector result; foreach(const RosterMap::value_type& entry, entries_) { result.push_back(entry.second); } return result; } boost::optional XMPPRosterImpl::getItem(const JID& jid) const { std::map::const_iterator i = entries_.find(jid.toBare()); if (i != entries_.end()) { return i->second; } else { return boost::optional(); } } std::set XMPPRosterImpl::getGroups() const { std::set result; foreach(const RosterMap::value_type& entry, entries_) { std::vector groups = entry.second.getGroups(); result.insert(groups.begin(), groups.end()); } return result; } } swift-im-2.0+dev6/Swiften/Roster/XMPPRosterItem.h0000644000175000017500000000217712227051774021542 0ustar kismithkismith/* * 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 #include #include #include namespace Swift { class XMPPRosterItem { public: XMPPRosterItem(const JID& jid, const std::string& name, const std::vector& groups, RosterItemPayload::Subscription subscription) : jid(jid), name(name), groups(groups), subscription(subscription) { } const JID& getJID() const { return jid; } const std::string& getName() const { return name; } void setName(const std::string& name) { this->name = name; } const std::vector& getGroups() const { return groups; } void setGroups(const std::vector& groups) { this->groups = groups; } RosterItemPayload::Subscription getSubscription() const { return subscription; } private: JID jid; std::string name; std::vector groups; RosterItemPayload::Subscription subscription; }; } swift-im-2.0+dev6/Swiften/Roster/SetRosterRequest.h0000644000175000017500000000214412227051774022235 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include namespace Swift { class SetRosterRequest : public Request { public: typedef boost::shared_ptr ref; static ref create(RosterPayload::ref payload, IQRouter* router) { return ref(new SetRosterRequest(JID(), payload, router)); } static ref create(RosterPayload::ref payload, const JID& to, IQRouter* router) { return ref(new SetRosterRequest(to, payload, router)); } private: SetRosterRequest(const JID& to, boost::shared_ptr payload, IQRouter* router) : Request(IQ::Set, to, boost::shared_ptr(payload), router) { } virtual void handleResponse(boost::shared_ptr /*payload*/, ErrorPayload::ref error) { onResponse(error); } public: boost::signal onResponse; }; } swift-im-2.0+dev6/Swiften/Roster/RosterPushResponder.h0000644000175000017500000000172112227051774022732 0ustar kismithkismith/* * 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 #include #include namespace Swift { class RosterPushResponder : public SetResponder { public: RosterPushResponder(IQRouter* router) : SetResponder(router) {} public: boost::signal)> onRosterReceived; private: virtual bool handleSetRequest(const JID& from, const JID&, const std::string& id, boost::shared_ptr payload) { if (getIQRouter()->isAccountJID(from)) { onRosterReceived(payload); sendResponse(from, id, boost::shared_ptr()); } else { sendError(from, id, ErrorPayload::NotAuthorized, ErrorPayload::Cancel); } return true; } }; } swift-im-2.0+dev6/Swiften/Roster/XMPPRoster.cpp0000644000175000017500000000043412227051774021250 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { XMPPRoster::XMPPRoster() { } XMPPRoster::~XMPPRoster() { } } swift-im-2.0+dev6/Swiften/Roster/UnitTest/0000755000175000017500000000000012227051774020337 5ustar kismithkismithswift-im-2.0+dev6/Swiften/Roster/UnitTest/XMPPRosterSignalHandler.cpp0000644000175000017500000000174212227051774025466 0ustar kismithkismith/* * Copyright (c) 2010-2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include using namespace Swift; XMPPRosterSignalHandler::XMPPRosterSignalHandler(Swift::XMPPRoster* roster) : eventCount(0) { lastEvent_ = None; roster->onJIDAdded.connect(boost::bind(&XMPPRosterSignalHandler::handleJIDAdded, this, _1)); roster->onJIDRemoved.connect(boost::bind(&XMPPRosterSignalHandler::handleJIDRemoved, this, _1)); roster->onJIDUpdated.connect(boost::bind(&XMPPRosterSignalHandler::handleJIDUpdated, this, _1, _2, _3)); } void XMPPRosterSignalHandler::handleJIDUpdated(const Swift::JID& jid, const std::string& oldName, const std::vector& oldGroups) { assert(lastEvent_ == None); lastJID_ = jid; lastOldName_ = oldName; lastOldGroups_ = oldGroups; lastEvent_ = Update; eventCount++; } swift-im-2.0+dev6/Swiften/Roster/UnitTest/XMPPRosterSignalHandler.h0000644000175000017500000000233212227051774025127 0ustar kismithkismith/* * Copyright (c) 2010-2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include enum XMPPRosterEvents {None, Add, Remove, Update}; class XMPPRosterSignalHandler { public: XMPPRosterSignalHandler(Swift::XMPPRoster* roster); XMPPRosterEvents getLastEvent() { return lastEvent_; } Swift::JID getLastJID() { return lastJID_; } std::string getLastOldName() { return lastOldName_; } std::vector getLastOldGroups() { return lastOldGroups_; } void reset() { lastEvent_ = None; } int getEventCount() const { return eventCount; } private: void handleJIDAdded(const Swift::JID& jid) { lastJID_ = jid; lastEvent_ = Add; eventCount++; } void handleJIDRemoved(const Swift::JID& jid) { lastJID_ = jid; lastEvent_ = Remove; eventCount++; } void handleJIDUpdated(const Swift::JID& jid, const std::string& oldName, const std::vector& oldGroups); XMPPRosterEvents lastEvent_; Swift::JID lastJID_; std::string lastOldName_; std::vector lastOldGroups_; int eventCount; }; swift-im-2.0+dev6/Swiften/Roster/UnitTest/XMPPRosterControllerTest.cpp0000644000175000017500000003607312227051774025743 0ustar kismithkismith/* * Copyright (c) 2010-2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include #include #include using namespace Swift; class XMPPRosterControllerTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(XMPPRosterControllerTest); CPPUNIT_TEST(testGet_Response); CPPUNIT_TEST(testGet_EmptyResponse); CPPUNIT_TEST(testGet_NoRosterInStorage); CPPUNIT_TEST(testGet_NoVersionInStorage); CPPUNIT_TEST(testGet_VersionInStorage); CPPUNIT_TEST(testGet_ServerDoesNotSupportVersion); CPPUNIT_TEST(testGet_ResponseWithoutNewVersion); CPPUNIT_TEST(testGet_ResponseWithNewVersion); CPPUNIT_TEST(testAdd); CPPUNIT_TEST(testAddFromNonAccount); CPPUNIT_TEST(testModify); CPPUNIT_TEST(testRemove); CPPUNIT_TEST(testRemove_RosterStorageUpdated); CPPUNIT_TEST(testMany); CPPUNIT_TEST_SUITE_END(); public: void setUp() { channel_ = new DummyStanzaChannel(); router_ = new IQRouter(channel_); router_->setJID("me@bla.com"); xmppRoster_ = new XMPPRosterImpl(); handler_ = new XMPPRosterSignalHandler(xmppRoster_); rosterStorage_ = new RosterMemoryStorage(); jid1_ = JID("foo@bar.com"); jid2_ = JID("alice@wonderland.lit"); jid3_ = JID("jane@austen.lit"); } void tearDown() { delete rosterStorage_; delete handler_; delete xmppRoster_; delete router_; delete channel_; } void testGet_Response() { boost::shared_ptr testling(createController()); testling->requestRoster(); boost::shared_ptr payload = boost::make_shared(); payload->addItem(RosterItemPayload(jid1_, "Bob", RosterItemPayload::Both)); payload->addItem(RosterItemPayload(jid2_, "Alice", RosterItemPayload::Both)); channel_->onIQReceived(IQ::createResult("foo@bar.com", channel_->sentStanzas[0]->getID(), payload)); CPPUNIT_ASSERT_EQUAL(2, handler_->getEventCount()); CPPUNIT_ASSERT(xmppRoster_->getItem(jid1_)); CPPUNIT_ASSERT(xmppRoster_->getItem(jid2_)); } void testGet_EmptyResponse() { XMPPRosterController controller(router_, xmppRoster_, rosterStorage_); controller.requestRoster(); channel_->onIQReceived(IQ::createResult(JID("baz@fum.com/dum"), channel_->sentStanzas[0]->getID(), boost::shared_ptr())); } void testAdd() { XMPPRosterController controller(router_, xmppRoster_, rosterStorage_); boost::shared_ptr payload(new RosterPayload()); payload->addItem(RosterItemPayload(jid1_, "Bob", RosterItemPayload::Both)); channel_->onIQReceived(IQ::createRequest(IQ::Set, JID(), "eou", payload)); CPPUNIT_ASSERT_EQUAL(Add, handler_->getLastEvent()); CPPUNIT_ASSERT_EQUAL(jid1_, handler_->getLastJID()); CPPUNIT_ASSERT_EQUAL(static_cast(0), xmppRoster_->getGroupsForJID(jid1_).size()); CPPUNIT_ASSERT(xmppRoster_->containsJID(jid1_)); CPPUNIT_ASSERT_EQUAL(std::string("Bob"), xmppRoster_->getNameForJID(jid1_)); } void testGet_NoRosterInStorage() { boost::shared_ptr testling(createController()); testling->setUseVersioning(true); testling->requestRoster(); boost::shared_ptr roster = channel_->sentStanzas[0]->getPayload(); CPPUNIT_ASSERT(roster->getVersion()); CPPUNIT_ASSERT_EQUAL(std::string(""), *roster->getVersion()); } void testGet_NoVersionInStorage() { boost::shared_ptr testling(createController()); testling->setUseVersioning(true); rosterStorage_->setRoster(boost::make_shared()); testling->requestRoster(); boost::shared_ptr roster = channel_->sentStanzas[0]->getPayload(); CPPUNIT_ASSERT(roster->getVersion()); CPPUNIT_ASSERT_EQUAL(std::string(""), *roster->getVersion()); } void testGet_VersionInStorage() { boost::shared_ptr testling(createController()); testling->setUseVersioning(true); boost::shared_ptr payload(new RosterPayload()); payload->setVersion("foover"); rosterStorage_->setRoster(payload); testling->requestRoster(); boost::shared_ptr roster = channel_->sentStanzas[0]->getPayload(); CPPUNIT_ASSERT(roster->getVersion()); CPPUNIT_ASSERT_EQUAL(std::string("foover"), *roster->getVersion()); } void testGet_ServerDoesNotSupportVersion() { boost::shared_ptr testling(createController()); boost::shared_ptr payload(new RosterPayload()); payload->setVersion("foover"); rosterStorage_->setRoster(payload); testling->requestRoster(); boost::shared_ptr roster = channel_->sentStanzas[0]->getPayload(); CPPUNIT_ASSERT(!roster->getVersion()); } void testGet_ResponseWithoutNewVersion() { boost::shared_ptr testling(createController()); testling->setUseVersioning(true); boost::shared_ptr storedRoster(new RosterPayload()); storedRoster->setVersion("version10"); storedRoster->addItem(RosterItemPayload(jid1_, "Bob", RosterItemPayload::Both)); storedRoster->addItem(RosterItemPayload(jid2_, "Alice", RosterItemPayload::Both)); rosterStorage_->setRoster(storedRoster); testling->requestRoster(); channel_->onIQReceived(IQ::createResult("foo@bar.com", channel_->sentStanzas[0]->getID(), boost::shared_ptr())); CPPUNIT_ASSERT_EQUAL(2, handler_->getEventCount()); CPPUNIT_ASSERT(xmppRoster_->getItem(jid1_)); CPPUNIT_ASSERT(xmppRoster_->getItem(jid2_)); CPPUNIT_ASSERT_EQUAL(Add, handler_->getLastEvent()); CPPUNIT_ASSERT_EQUAL(jid2_, handler_->getLastJID()); CPPUNIT_ASSERT(rosterStorage_->getRoster()); CPPUNIT_ASSERT(rosterStorage_->getRoster()->getVersion()); CPPUNIT_ASSERT_EQUAL(std::string("version10"), *rosterStorage_->getRoster()->getVersion()); CPPUNIT_ASSERT(rosterStorage_->getRoster()->getItem(jid1_)); CPPUNIT_ASSERT(rosterStorage_->getRoster()->getItem(jid2_)); } void testGet_ResponseWithNewVersion() { boost::shared_ptr testling(createController()); testling->setUseVersioning(true); boost::shared_ptr storedRoster(new RosterPayload()); storedRoster->setVersion("version10"); storedRoster->addItem(RosterItemPayload(jid1_, "Bob", RosterItemPayload::Both)); rosterStorage_->setRoster(storedRoster); testling->requestRoster(); boost::shared_ptr serverRoster(new RosterPayload()); serverRoster->setVersion("version12"); serverRoster->addItem(RosterItemPayload(jid2_, "Alice", RosterItemPayload::Both)); std::vector groups; groups.push_back("foo"); groups.push_back("bar"); serverRoster->addItem(RosterItemPayload(jid3_, "Rabbit", RosterItemPayload::Both, groups)); channel_->onIQReceived(IQ::createResult("foo@bar.com", channel_->sentStanzas[0]->getID(), serverRoster)); CPPUNIT_ASSERT_EQUAL(2, handler_->getEventCount()); CPPUNIT_ASSERT(!xmppRoster_->getItem(jid1_)); CPPUNIT_ASSERT(xmppRoster_->getItem(jid2_)); CPPUNIT_ASSERT(xmppRoster_->getItem(jid3_)); CPPUNIT_ASSERT_EQUAL(jid3_, handler_->getLastJID()); CPPUNIT_ASSERT_EQUAL(Add, handler_->getLastEvent()); CPPUNIT_ASSERT(rosterStorage_->getRoster()); CPPUNIT_ASSERT(rosterStorage_->getRoster()->getVersion()); CPPUNIT_ASSERT_EQUAL(std::string("version12"), *rosterStorage_->getRoster()->getVersion()); CPPUNIT_ASSERT(!rosterStorage_->getRoster()->getItem(jid1_)); CPPUNIT_ASSERT(rosterStorage_->getRoster()->getItem(jid2_)); CPPUNIT_ASSERT(rosterStorage_->getRoster()->getItem(jid3_)); CPPUNIT_ASSERT_EQUAL(2, static_cast(rosterStorage_->getRoster()->getItem(jid3_)->getGroups().size())); } void testAddFromNonAccount() { boost::shared_ptr testling(createController()); boost::shared_ptr payload(new RosterPayload()); payload->addItem(RosterItemPayload(jid1_, "Bob", RosterItemPayload::Both)); IQ::ref request = IQ::createRequest(IQ::Set, JID(), "eou", payload); request->setFrom(jid2_); channel_->onIQReceived(request); CPPUNIT_ASSERT_EQUAL(None, handler_->getLastEvent()); } void testModify() { XMPPRosterController controller(router_, xmppRoster_, rosterStorage_); boost::shared_ptr payload1(new RosterPayload()); payload1->addItem(RosterItemPayload(jid1_, "Bob", RosterItemPayload::Both)); channel_->onIQReceived(IQ::createRequest(IQ::Set, JID(), "id1", payload1)); CPPUNIT_ASSERT_EQUAL(Add, handler_->getLastEvent()); CPPUNIT_ASSERT_EQUAL(jid1_, handler_->getLastJID()); handler_->reset(); boost::shared_ptr payload2(new RosterPayload()); payload2->addItem(RosterItemPayload(jid1_, "Bob2", RosterItemPayload::Both)); channel_->onIQReceived(IQ::createRequest(IQ::Set, JID(), "id2", payload2)); CPPUNIT_ASSERT_EQUAL(Update, handler_->getLastEvent()); CPPUNIT_ASSERT_EQUAL(jid1_, handler_->getLastJID()); CPPUNIT_ASSERT_EQUAL(std::string("Bob2"), xmppRoster_->getNameForJID(jid1_)); } void testRemove() { XMPPRosterController controller(router_, xmppRoster_, rosterStorage_); boost::shared_ptr payload1(new RosterPayload()); payload1->addItem(RosterItemPayload(jid1_, "Bob", RosterItemPayload::Both)); channel_->onIQReceived(IQ::createRequest(IQ::Set, JID(), "id1", payload1)); CPPUNIT_ASSERT_EQUAL(Add, handler_->getLastEvent()); CPPUNIT_ASSERT_EQUAL(jid1_, handler_->getLastJID()); handler_->reset(); boost::shared_ptr payload2(new RosterPayload()); payload2->addItem(RosterItemPayload(jid1_, "Bob", RosterItemPayload::Remove)); channel_->onIQReceived(IQ::createRequest(IQ::Set, JID(), "id2", payload2)); CPPUNIT_ASSERT(!xmppRoster_->containsJID(jid1_)); CPPUNIT_ASSERT_EQUAL(Remove, handler_->getLastEvent()); CPPUNIT_ASSERT_EQUAL(jid1_, handler_->getLastJID()); } void testRemove_RosterStorageUpdated() { boost::shared_ptr testling(createController()); testling->setUseVersioning(true); boost::shared_ptr storedRoster(new RosterPayload()); storedRoster->setVersion("version10"); storedRoster->addItem(RosterItemPayload(jid1_, "Bob", RosterItemPayload::Both)); storedRoster->addItem(RosterItemPayload(jid2_, "Alice", RosterItemPayload::Both)); rosterStorage_->setRoster(storedRoster); testling->requestRoster(); channel_->onIQReceived(IQ::createResult("foo@bar.com", channel_->sentStanzas[0]->getID(), boost::shared_ptr())); boost::shared_ptr payload2(new RosterPayload()); payload2->setVersion("version15"); payload2->addItem(RosterItemPayload(jid1_, "Bob", RosterItemPayload::Remove)); channel_->onIQReceived(IQ::createRequest(IQ::Set, JID(), "id2", payload2)); CPPUNIT_ASSERT(rosterStorage_->getRoster()); CPPUNIT_ASSERT(rosterStorage_->getRoster()->getVersion()); CPPUNIT_ASSERT_EQUAL(std::string("version15"), *rosterStorage_->getRoster()->getVersion()); CPPUNIT_ASSERT(!rosterStorage_->getRoster()->getItem(jid1_)); CPPUNIT_ASSERT(rosterStorage_->getRoster()->getItem(jid2_)); } void testMany() { XMPPRosterController controller(router_, xmppRoster_, rosterStorage_); boost::shared_ptr payload1(new RosterPayload()); payload1->addItem(RosterItemPayload(jid1_, "Bob", RosterItemPayload::Both)); channel_->onIQReceived(IQ::createRequest(IQ::Set, JID(), "id1", payload1)); CPPUNIT_ASSERT_EQUAL(Add, handler_->getLastEvent()); CPPUNIT_ASSERT_EQUAL(jid1_, handler_->getLastJID()); handler_->reset(); boost::shared_ptr payload2(new RosterPayload()); payload2->addItem(RosterItemPayload(jid2_, "Alice", RosterItemPayload::Both)); channel_->onIQReceived(IQ::createRequest(IQ::Set, JID(), "id2", payload2)); CPPUNIT_ASSERT_EQUAL(Add, handler_->getLastEvent()); CPPUNIT_ASSERT_EQUAL(jid2_, handler_->getLastJID()); handler_->reset(); boost::shared_ptr payload3(new RosterPayload()); payload3->addItem(RosterItemPayload(jid1_, "Ernie", RosterItemPayload::Both)); channel_->onIQReceived(IQ::createRequest(IQ::Set, JID(), "id3", payload3)); CPPUNIT_ASSERT_EQUAL(Update, handler_->getLastEvent()); CPPUNIT_ASSERT_EQUAL(jid1_, handler_->getLastJID()); handler_->reset(); boost::shared_ptr payload4(new RosterPayload()); RosterItemPayload item(jid3_, "Jane", RosterItemPayload::Both); std::string janesGroup("Jane's Group"); item.addGroup(janesGroup); payload4->addItem(item); channel_->onIQReceived(IQ::createRequest(IQ::Set, JID(), "id4", payload4)); CPPUNIT_ASSERT_EQUAL(Add, handler_->getLastEvent()); CPPUNIT_ASSERT_EQUAL(jid3_, handler_->getLastJID()); CPPUNIT_ASSERT_EQUAL(static_cast(1), xmppRoster_->getGroupsForJID(jid3_).size()); CPPUNIT_ASSERT_EQUAL(janesGroup, xmppRoster_->getGroupsForJID(jid3_)[0]); handler_->reset(); boost::shared_ptr payload5(new RosterPayload()); payload5->addItem(RosterItemPayload(jid1_, "Bob", RosterItemPayload::Remove)); channel_->onIQReceived(IQ::createRequest(IQ::Set, JID(), "id5", payload5)); CPPUNIT_ASSERT(!xmppRoster_->containsJID(jid1_)); CPPUNIT_ASSERT_EQUAL(Remove, handler_->getLastEvent()); CPPUNIT_ASSERT_EQUAL(jid1_, handler_->getLastJID()); handler_->reset(); boost::shared_ptr payload6(new RosterPayload()); RosterItemPayload item2(jid2_, "Little Alice", RosterItemPayload::Both); std::string alicesGroup("Alice's Group"); item2.addGroup(alicesGroup); payload6->addItem(item2); channel_->onIQReceived(IQ::createRequest(IQ::Set, JID(), "id6", payload6)); CPPUNIT_ASSERT_EQUAL(Update, handler_->getLastEvent()); CPPUNIT_ASSERT_EQUAL(jid2_, handler_->getLastJID()); CPPUNIT_ASSERT_EQUAL(std::string("Little Alice"), xmppRoster_->getNameForJID(jid2_)); CPPUNIT_ASSERT_EQUAL(std::string("Jane"), xmppRoster_->getNameForJID(jid3_)); CPPUNIT_ASSERT_EQUAL(static_cast(1), xmppRoster_->getGroupsForJID(jid2_).size()); CPPUNIT_ASSERT_EQUAL(alicesGroup, xmppRoster_->getGroupsForJID(jid2_)[0]); CPPUNIT_ASSERT_EQUAL(static_cast(1), xmppRoster_->getGroupsForJID(jid3_).size()); CPPUNIT_ASSERT_EQUAL(janesGroup, xmppRoster_->getGroupsForJID(jid3_)[0]); handler_->reset(); } private: XMPPRosterController* createController() { return new XMPPRosterController(router_, xmppRoster_, rosterStorage_); } private: DummyStanzaChannel* channel_; IQRouter* router_; XMPPRosterImpl* xmppRoster_; XMPPRosterSignalHandler* handler_; RosterMemoryStorage* rosterStorage_; JID jid1_; JID jid2_; JID jid3_; }; CPPUNIT_TEST_SUITE_REGISTRATION(XMPPRosterControllerTest); swift-im-2.0+dev6/Swiften/Roster/UnitTest/XMPPRosterImplTest.cpp0000644000175000017500000001015212227051774024507 0ustar kismithkismith/* * Copyright (c) 2010-2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include using namespace Swift; class XMPPRosterImplTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(XMPPRosterImplTest); CPPUNIT_TEST(testJIDAdded); CPPUNIT_TEST(testJIDRemoved); CPPUNIT_TEST(testJIDUpdated); CPPUNIT_TEST_SUITE_END(); public: void setUp() { jid1_ = JID("a@b.c"); jid2_ = JID("b@c.d"); jid3_ = JID("c@d.e"); roster_ = new XMPPRosterImpl(); handler_ = new XMPPRosterSignalHandler(roster_); groups1_.push_back("bobs"); groups1_.push_back("berts"); groups2_.push_back("ernies"); } void tearDown() { delete handler_; delete roster_; } void testJIDAdded() { roster_->addContact(jid1_, "NewName", groups1_, RosterItemPayload::Both); CPPUNIT_ASSERT_EQUAL(Add, handler_->getLastEvent()); CPPUNIT_ASSERT_EQUAL(jid1_, handler_->getLastJID()); CPPUNIT_ASSERT_EQUAL(std::string("NewName"), roster_->getNameForJID(jid1_)); CPPUNIT_ASSERT(groups1_ == roster_->getGroupsForJID(jid1_)); handler_->reset(); roster_->addContact(jid2_, "NameTwo", groups1_, RosterItemPayload::Both); CPPUNIT_ASSERT_EQUAL(Add, handler_->getLastEvent()); CPPUNIT_ASSERT_EQUAL(jid2_, handler_->getLastJID()); CPPUNIT_ASSERT_EQUAL(std::string("NameTwo"), roster_->getNameForJID(jid2_)); CPPUNIT_ASSERT_EQUAL(std::string("NewName"), roster_->getNameForJID(jid1_)); CPPUNIT_ASSERT(groups1_ == roster_->getGroupsForJID(jid2_)); CPPUNIT_ASSERT(groups1_ == roster_->getGroupsForJID(jid1_)); handler_->reset(); roster_->addContact(jid3_, "NewName", groups2_, RosterItemPayload::Both); CPPUNIT_ASSERT_EQUAL(Add, handler_->getLastEvent()); CPPUNIT_ASSERT_EQUAL(jid3_, handler_->getLastJID()); CPPUNIT_ASSERT_EQUAL(std::string("NewName"), roster_->getNameForJID(jid3_)); CPPUNIT_ASSERT(groups2_ == roster_->getGroupsForJID(jid3_)); } void testJIDRemoved() { roster_->addContact(jid1_, "NewName", groups1_, RosterItemPayload::Both); handler_->reset(); roster_->removeContact(jid1_); CPPUNIT_ASSERT_EQUAL(Remove, handler_->getLastEvent()); CPPUNIT_ASSERT_EQUAL(jid1_, handler_->getLastJID()); handler_->reset(); roster_->addContact(jid1_, "NewName2", groups1_, RosterItemPayload::Both); CPPUNIT_ASSERT_EQUAL(Add, handler_->getLastEvent()); CPPUNIT_ASSERT_EQUAL(jid1_, handler_->getLastJID()); CPPUNIT_ASSERT_EQUAL(std::string("NewName2"), roster_->getNameForJID(jid1_)); roster_->addContact(jid2_, "NewName3", groups1_, RosterItemPayload::Both); handler_->reset(); roster_->removeContact(jid2_); CPPUNIT_ASSERT_EQUAL(Remove, handler_->getLastEvent()); CPPUNIT_ASSERT_EQUAL(jid2_, handler_->getLastJID()); handler_->reset(); roster_->removeContact(jid1_); CPPUNIT_ASSERT_EQUAL(Remove, handler_->getLastEvent()); CPPUNIT_ASSERT_EQUAL(jid1_, handler_->getLastJID()); } void testJIDUpdated() { roster_->addContact(jid1_, "NewName", groups1_, RosterItemPayload::Both); CPPUNIT_ASSERT_EQUAL(Add, handler_->getLastEvent()); CPPUNIT_ASSERT_EQUAL(jid1_, handler_->getLastJID()); CPPUNIT_ASSERT_EQUAL(std::string("NewName"), roster_->getNameForJID(jid1_)); CPPUNIT_ASSERT(groups1_ == roster_->getGroupsForJID(jid1_)); handler_->reset(); roster_->addContact(jid1_, "NameTwo", groups2_, RosterItemPayload::Both); CPPUNIT_ASSERT_EQUAL(Update, handler_->getLastEvent()); CPPUNIT_ASSERT_EQUAL(jid1_, handler_->getLastJID()); CPPUNIT_ASSERT_EQUAL(std::string("NameTwo"), roster_->getNameForJID(jid1_)); CPPUNIT_ASSERT(groups2_ == roster_->getGroupsForJID(jid1_)); } private: XMPPRosterImpl* roster_; XMPPRosterSignalHandler* handler_; JID jid1_; JID jid2_; JID jid3_; std::vector groups1_; std::vector groups2_; }; CPPUNIT_TEST_SUITE_REGISTRATION(XMPPRosterImplTest); swift-im-2.0+dev6/Swiften/Roster/RosterMemoryStorage.cpp0000644000175000017500000000073712227051774023267 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include namespace Swift { RosterMemoryStorage::RosterMemoryStorage() { } void RosterMemoryStorage::setRoster(boost::shared_ptr r) { roster.reset(); if (r) { roster = boost::make_shared(*r); } } } swift-im-2.0+dev6/Swiften/Roster/XMPPRosterController.h0000644000175000017500000000222612227051774022762 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include #include #include #include #include namespace Swift { class IQRouter; class XMPPRosterImpl; class RosterStorage; class SWIFTEN_API XMPPRosterController { public: XMPPRosterController(IQRouter *iqRouter, XMPPRosterImpl* xmppRoster, RosterStorage* storage); ~XMPPRosterController(); void requestRoster(); void setUseVersioning(bool b) { useVersioning = b; } private: void handleRosterReceived(boost::shared_ptr rosterPayload, bool initial, boost::shared_ptr previousRoster); void saveRoster(const std::string& version); private: IQRouter* iqRouter_; RosterPushResponder rosterPushResponder_; XMPPRosterImpl* xmppRoster_; RosterStorage* rosterStorage_; bool useVersioning; }; } swift-im-2.0+dev6/Swiften/Roster/XMPPRosterController.cpp0000644000175000017500000000637012227051774023321 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include namespace Swift { /** * The controller does not gain ownership of these parameters. */ XMPPRosterController::XMPPRosterController(IQRouter* iqRouter, XMPPRosterImpl* xmppRoster, RosterStorage* rosterStorage) : iqRouter_(iqRouter), rosterPushResponder_(iqRouter), xmppRoster_(xmppRoster), rosterStorage_(rosterStorage), useVersioning(false) { rosterPushResponder_.onRosterReceived.connect(boost::bind(&XMPPRosterController::handleRosterReceived, this, _1, false, boost::shared_ptr())); rosterPushResponder_.start(); } XMPPRosterController::~XMPPRosterController() { rosterPushResponder_.stop(); } void XMPPRosterController::requestRoster() { xmppRoster_->clear(); boost::shared_ptr storedRoster = rosterStorage_->getRoster(); GetRosterRequest::ref rosterRequest; if (useVersioning) { std::string version = ""; if (storedRoster && storedRoster->getVersion()) { version = *storedRoster->getVersion(); } rosterRequest = GetRosterRequest::create(iqRouter_, version); } else { rosterRequest = GetRosterRequest::create(iqRouter_); } rosterRequest->onResponse.connect(boost::bind(&XMPPRosterController::handleRosterReceived, this, _1, true, storedRoster)); rosterRequest->send(); } void XMPPRosterController::handleRosterReceived(boost::shared_ptr rosterPayload, bool initial, boost::shared_ptr previousRoster) { if (rosterPayload) { foreach(const RosterItemPayload& item, rosterPayload->getItems()) { //Don't worry about the updated case, the XMPPRoster sorts that out. if (item.getSubscription() == RosterItemPayload::Remove) { xmppRoster_->removeContact(item.getJID()); } else { xmppRoster_->addContact(item.getJID(), item.getName(), item.getGroups(), item.getSubscription()); } } } else if (previousRoster) { // The cached version hasn't changed; emit all items foreach(const RosterItemPayload& item, previousRoster->getItems()) { if (item.getSubscription() != RosterItemPayload::Remove) { xmppRoster_->addContact(item.getJID(), item.getName(), item.getGroups(), item.getSubscription()); } else { std::cerr << "ERROR: Stored invalid roster item" << std::endl; } } } if (initial) { xmppRoster_->onInitialRosterPopulated(); } if (rosterPayload && rosterPayload->getVersion() && useVersioning) { saveRoster(*rosterPayload->getVersion()); } } void XMPPRosterController::saveRoster(const std::string& version) { std::vector items = xmppRoster_->getItems(); boost::shared_ptr roster(new RosterPayload()); roster->setVersion(version); foreach(const XMPPRosterItem& item, items) { roster->addItem(RosterItemPayload(item.getJID(), item.getName(), item.getSubscription(), item.getGroups())); } rosterStorage_->setRoster(roster); } } swift-im-2.0+dev6/Swiften/Roster/XMPPRosterImpl.h0000644000175000017500000000210512227051774021534 0ustar kismithkismith/* * 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 #include #include #include namespace Swift { class SWIFTEN_API XMPPRosterImpl : public XMPPRoster { public: XMPPRosterImpl(); void addContact(const JID& jid, const std::string& name, const std::vector& groups, const RosterItemPayload::Subscription subscription); void removeContact(const JID& jid); void clear(); bool containsJID(const JID& jid); RosterItemPayload::Subscription getSubscriptionStateForJID(const JID& jid); std::string getNameForJID(const JID& jid) const; std::vector getGroupsForJID(const JID& jid); virtual std::vector getItems() const; virtual boost::optional getItem(const JID&) const; virtual std::set getGroups() const; private: typedef std::map RosterMap; RosterMap entries_; }; } swift-im-2.0+dev6/Swiften/Roster/XMPPRoster.h0000644000175000017500000000501112227051774020711 0ustar kismithkismith/* * 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 #include #include #include #include #include #include #include namespace Swift { /** * This class represents the roster of an account, as stored on the XMPP server. * * Changes to the roster (either due to subscription requests or by going online/offline) are * emitted through signals. */ class XMPPRoster { public: XMPPRoster(); virtual ~XMPPRoster(); /** * Checks whether the bare jid of the given jid is in the roster. */ virtual bool containsJID(const JID& jid) = 0; /** * Retrieves the subscription state for the given jid. */ virtual RosterItemPayload::Subscription getSubscriptionStateForJID(const JID& jid) = 0; /** * Retrieves the stored roster name for the given jid. */ virtual std::string getNameForJID(const JID& jid) const = 0; /** * Returns the list of groups for the given JID. */ virtual std::vector getGroupsForJID(const JID& jid) = 0; /** * Retrieve the items in the roster. */ virtual std::vector getItems() const = 0; /** * Retrieve the item with the given JID. */ virtual boost::optional getItem(const JID&) const = 0; /** * Retrieve the list of (existing) groups. */ virtual std::set getGroups() const = 0; public: /** * Emitted when the given JID is added to the roster. */ boost::signal onJIDAdded; /** * Emitted when the given JID is removed from the roster. */ boost::signal onJIDRemoved; /** * Emitted when the name or the groups of the roster item with the * given JID changes. */ boost::signal&)> onJIDUpdated; /** * Emitted when the roster is reset (e.g. due to logging in/logging out). * After this signal is emitted, the roster is empty. It will be repopulated through * onJIDAdded and onJIDRemoved events. */ boost::signal onRosterCleared; /** * Emitted after the last contact of the initial roster request response * was added. */ boost::signal onInitialRosterPopulated; }; } swift-im-2.0+dev6/Swiften/Jingle/0000755000175000017500000000000012227051774016512 5ustar kismithkismithswift-im-2.0+dev6/Swiften/Jingle/SConscript0000644000175000017500000000043412227051774020525 0ustar kismithkismithImport("swiften_env") sources = [ "JingleSession.cpp", "JingleSessionImpl.cpp", "IncomingJingleSessionHandler.cpp", "JingleSessionManager.cpp", "JingleResponder.cpp", "FakeJingleSession.cpp", ] swiften_env.Append(SWIFTEN_OBJECTS = swiften_env.SwiftenObject(sources)) swift-im-2.0+dev6/Swiften/Jingle/JingleContentID.h0000644000175000017500000000122412227051774021642 0ustar kismithkismith/* * 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 #include namespace Swift { class JingleContentID { public: JingleContentID(const std::string& name, JingleContentPayload::Creator creator) : name(name), creator(creator) { } const std::string getName() const { return this->name; } JingleContentPayload::Creator getCreator() const { return this->creator; } private: std::string name; JingleContentPayload::Creator creator; }; } swift-im-2.0+dev6/Swiften/Jingle/JingleSessionImpl.cpp0000644000175000017500000001500312227051774022613 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include "Swiften/Serializer/PayloadSerializers/JinglePayloadSerializer.h" #include "Swiften/FileTransfer/JingleTransport.h" namespace Swift { JingleSessionImpl::JingleSessionImpl(const JID& initiator, const JID& peerJID, const std::string& id, IQRouter* router) : JingleSession(initiator, id), iqRouter(router), peerJID(peerJID) { SWIFT_LOG(debug) << "initiator: " << initiator << ", peerJID: " << peerJID << std::endl; } void JingleSessionImpl::handleIncomingAction(JinglePayload::ref action) { if (action->getAction() == JinglePayload::SessionTerminate) { onSessionTerminateReceived(action->getReason()); return; } if (action->getAction() == JinglePayload::SessionInfo) { onSessionInfoReceived(action); return; } JingleContentPayload::ref content = action->getPayload(); if (!content) { SWIFT_LOG(debug) << "no content payload!" << std::endl; return; } JingleContentID contentID(content->getName(), content->getCreator()); JingleDescription::ref description = content->getDescriptions().empty() ? JingleDescription::ref() : content->getDescriptions()[0]; JingleTransportPayload::ref transport = content->getTransports().empty() ? JingleTransportPayload::ref() : content->getTransports()[0]; switch(action->getAction()) { case JinglePayload::SessionAccept: onSessionAcceptReceived(contentID, description, transport); return; case JinglePayload::TransportAccept: onTransportAcceptReceived(contentID, transport); return; case JinglePayload::TransportInfo: onTransportInfoReceived(contentID, transport); return; case JinglePayload::TransportReject: onTransportRejectReceived(contentID, transport); return; case JinglePayload::TransportReplace: onTransportReplaceReceived(contentID, transport); return; // following unused Jingle actions case JinglePayload::ContentAccept: case JinglePayload::ContentAdd: case JinglePayload::ContentModify: case JinglePayload::ContentReject: case JinglePayload::ContentRemove: case JinglePayload::DescriptionInfo: case JinglePayload::SecurityInfo: // handled elsewhere case JinglePayload::SessionInitiate: case JinglePayload::SessionInfo: case JinglePayload::SessionTerminate: case JinglePayload::UnknownAction: return; } std::cerr << "Unhandled Jingle action!!! ACTION: " << action->getAction() << std::endl; } void JingleSessionImpl::sendInitiate(const JingleContentID& id, JingleDescription::ref description, JingleTransportPayload::ref transport) { JinglePayload::ref payload = boost::make_shared(JinglePayload::SessionInitiate, getID()); payload->setInitiator(getInitiator()); JingleContentPayload::ref content = boost::make_shared(); content->setCreator(id.getCreator()); content->setName(id.getName()); content->addDescription(description); content->addTransport(transport); payload->addPayload(content); sendSetRequest(payload); } void JingleSessionImpl::sendTerminate(JinglePayload::Reason::Type reason) { JinglePayload::ref payload = boost::make_shared(JinglePayload::SessionTerminate, getID()); payload->setReason(JinglePayload::Reason(reason)); payload->setInitiator(getInitiator()); sendSetRequest(payload); } void JingleSessionImpl::sendInfo(boost::shared_ptr info) { JinglePayload::ref payload = boost::make_shared(JinglePayload::SessionInfo, getID()); payload->addPayload(info); sendSetRequest(payload); } void JingleSessionImpl::sendAccept(const JingleContentID& id, JingleDescription::ref description, JingleTransportPayload::ref transPayload) { JinglePayload::ref payload = createPayload(); JingleContentPayload::ref content = boost::make_shared(); content->setCreator(id.getCreator()); content->setName(id.getName()); content->addTransport(transPayload); content->addDescription(description); payload->setAction(JinglePayload::SessionAccept); payload->addPayload(content); // put into IQ:set and send it away sendSetRequest(payload); } void JingleSessionImpl::sendTransportAccept(const JingleContentID& id, JingleTransportPayload::ref transPayload) { JinglePayload::ref payload = createPayload(); JingleContentPayload::ref content = boost::make_shared(); content->setCreator(id.getCreator()); content->setName(id.getName()); content->addTransport(transPayload); payload->setAction(JinglePayload::TransportAccept); payload->addPayload(content); // put into IQ:set and send it away sendSetRequest(payload); } void JingleSessionImpl::sendTransportInfo(const JingleContentID& id, JingleTransportPayload::ref transPayload) { JinglePayload::ref payload = createPayload(); JingleContentPayload::ref content = boost::make_shared(); content->setCreator(id.getCreator()); content->setName(id.getName()); content->addTransport(transPayload); payload->setAction(JinglePayload::TransportInfo); payload->addPayload(content); sendSetRequest(payload); } void JingleSessionImpl::sendTransportReject(const JingleContentID& /* id */, JingleTransportPayload::ref /* transPayload */) { SWIFT_LOG(debug) << "NOT IMPLEMENTED YET!!!!" << std::endl; } void JingleSessionImpl::sendTransportReplace(const JingleContentID& id, JingleTransportPayload::ref transPayload) { JinglePayload::ref payload = createPayload(); JingleContentPayload::ref content = boost::make_shared(); content->setCreator(id.getCreator()); content->setName(id.getName()); content->addTransport(transPayload); payload->setAction(JinglePayload::TransportReplace); payload->addContent(content); sendSetRequest(payload); } void JingleSessionImpl::sendSetRequest(JinglePayload::ref payload) { boost::shared_ptr > request = boost::make_shared >(IQ::Set, peerJID, payload, iqRouter); request->send(); } JinglePayload::ref JingleSessionImpl::createPayload() const { JinglePayload::ref payload = boost::make_shared(); payload->setSessionID(getID()); payload->setInitiator(getInitiator()); return payload; } } swift-im-2.0+dev6/Swiften/Jingle/FakeJingleSession.cpp0000644000175000017500000000335012227051774022562 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include #include namespace Swift { FakeJingleSession::FakeJingleSession(const JID& initiator, const std::string& id) : JingleSession(initiator, id) { } FakeJingleSession::~FakeJingleSession() { } void FakeJingleSession::sendInitiate(const JingleContentID& id, JingleDescription::ref desc, JingleTransportPayload::ref payload) { calledCommands.push_back(InitiateCall(id, desc, payload)); } void FakeJingleSession::sendTerminate(JinglePayload::Reason::Type reason) { calledCommands.push_back(TerminateCall(reason)); } void FakeJingleSession::sendInfo(boost::shared_ptr payload) { calledCommands.push_back(InfoCall(payload)); } void FakeJingleSession::sendAccept(const JingleContentID& id, JingleDescription::ref desc, JingleTransportPayload::ref payload) { calledCommands.push_back(AcceptCall(id, desc, payload)); } void FakeJingleSession::sendTransportInfo(const JingleContentID& id, JingleTransportPayload::ref payload) { calledCommands.push_back(InfoTransportCall(id, payload)); } void FakeJingleSession::sendTransportAccept(const JingleContentID& id, JingleTransportPayload::ref payload) { calledCommands.push_back(AcceptTransportCall(id, payload)); } void FakeJingleSession::sendTransportReject(const JingleContentID& id, JingleTransportPayload::ref payload) { calledCommands.push_back(RejectTransportCall(id, payload)); } void FakeJingleSession::sendTransportReplace(const JingleContentID& id, JingleTransportPayload::ref payload) { calledCommands.push_back(ReplaceTransportCall(id, payload)); } } swift-im-2.0+dev6/Swiften/Jingle/JingleSession.cpp0000644000175000017500000000113612227051774021773 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include namespace Swift { JingleSession::JingleSession(const JID& initiator, const std::string& id) : initiator(initiator), id(id) { // initiator must always be a full JID; session lookup based on it wouldn't work otherwise // this is checked on an upper level so that the assert never fails assert(!initiator.isBare()); } JingleSession::~JingleSession() { } } swift-im-2.0+dev6/Swiften/Jingle/JingleSessionManager.h0000644000175000017500000000314012227051774022730 0ustar kismithkismith/* * 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 #include #include #include #include namespace Swift { class IQRouter; class JingleResponder; class IncomingJingleSessionHandler; class SWIFTEN_API JingleSessionManager { friend class JingleResponder; public: JingleSessionManager(IQRouter* router); ~JingleSessionManager(); JingleSessionImpl::ref getSession(const JID& jid, const std::string& id) const; void addIncomingSessionHandler(IncomingJingleSessionHandler* handler); void removeIncomingSessionHandler(IncomingJingleSessionHandler* handler); void registerOutgoingSession(const JID& initiator, JingleSessionImpl::ref); protected: void handleIncomingSession(const JID& initiator, const JID& recipient, JingleSessionImpl::ref, const std::vector& contents); private: IQRouter* router; JingleResponder* responder; std::vector incomingSessionHandlers; struct JIDSession { JIDSession(const JID& initiator, const std::string& session) : initiator(initiator), session(session) {} bool operator<(const JIDSession& o) const { return initiator == o.initiator ? session < o.session : initiator < o.initiator; } JID initiator; std::string session; }; typedef std::map SessionMap; SessionMap sessions; }; } swift-im-2.0+dev6/Swiften/Jingle/IncomingJingleSessionHandler.h0000644000175000017500000000074112227051774024423 0ustar kismithkismith/* * 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 namespace Swift { class IncomingJingleSessionHandler { public: virtual ~IncomingJingleSessionHandler(); virtual bool handleIncomingJingleSession(JingleSession::ref, const std::vector& contents, const JID& recipient) = 0; }; } swift-im-2.0+dev6/Swiften/Jingle/JingleSession.h0000644000175000017500000000427312227051774021445 0ustar kismithkismith/* * 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 #include #include #include #include #include namespace Swift { class JingleContentID; class JingleSession { public: typedef boost::shared_ptr ref; JingleSession(const JID& initiator, const std::string& id); virtual ~JingleSession(); const JID& getInitiator() const { return initiator; } const std::string& getID() const { return id; } virtual void sendInitiate(const JingleContentID&, JingleDescription::ref, JingleTransportPayload::ref) = 0; virtual void sendTerminate(JinglePayload::Reason::Type reason) = 0; virtual void sendInfo(boost::shared_ptr) = 0; virtual void sendAccept(const JingleContentID&, JingleDescription::ref, JingleTransportPayload::ref = JingleTransportPayload::ref()) = 0; virtual void sendTransportInfo(const JingleContentID&, JingleTransportPayload::ref) = 0; virtual void sendTransportAccept(const JingleContentID&, JingleTransportPayload::ref) = 0; virtual void sendTransportReject(const JingleContentID&, JingleTransportPayload::ref) = 0; virtual void sendTransportReplace(const JingleContentID&, JingleTransportPayload::ref) = 0; public: boost::signal onSessionAcceptReceived; boost::signal onSessionInfoReceived; boost::signal)> onSessionTerminateReceived; boost::signal onTransportAcceptReceived; boost::signal onTransportInfoReceived; boost::signal onTransportRejectReceived; boost::signal onTransportReplaceReceived; private: JID initiator; std::string id; }; } swift-im-2.0+dev6/Swiften/Jingle/JingleSessionManager.cpp0000644000175000017500000000363312227051774023272 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include namespace Swift { JingleSessionManager::JingleSessionManager(IQRouter* router) : router(router) { responder = new JingleResponder(this, router); responder->start(); } JingleSessionManager::~JingleSessionManager() { responder->stop(); delete responder; } JingleSessionImpl::ref JingleSessionManager::getSession(const JID& jid, const std::string& id) const { SessionMap::const_iterator i = sessions.find(JIDSession(jid, id)); return i != sessions.end() ? i->second : JingleSessionImpl::ref(); } void JingleSessionManager::addIncomingSessionHandler(IncomingJingleSessionHandler* handler) { incomingSessionHandlers.push_back(handler); } void JingleSessionManager::removeIncomingSessionHandler(IncomingJingleSessionHandler* handler) { erase(incomingSessionHandlers, handler); } void JingleSessionManager::registerOutgoingSession(const JID& initiator, JingleSessionImpl::ref session) { sessions.insert(std::make_pair(JIDSession(initiator, session->getID()), session)); SWIFT_LOG(debug) << "Added session " << session->getID() << " for initiator " << initiator.toString() << std::endl; } void JingleSessionManager::handleIncomingSession(const JID& initiator, const JID& recipient, JingleSessionImpl::ref session, const std::vector& contents) { sessions.insert(std::make_pair(JIDSession(initiator, session->getID()), session)); foreach (IncomingJingleSessionHandler* handler, incomingSessionHandlers) { if (handler->handleIncomingJingleSession(session, contents, recipient)) { return; } } // TODO: Finish session } } swift-im-2.0+dev6/Swiften/Jingle/JingleResponder.cpp0000644000175000017500000000424312227051774022313 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include namespace Swift { JingleResponder::JingleResponder(JingleSessionManager* sessionManager, IQRouter* router) : SetResponder(router), sessionManager(sessionManager), router(router) { } JingleResponder::~JingleResponder() { } bool JingleResponder::handleSetRequest(const JID& from, const JID& to, const std::string& id, boost::shared_ptr payload) { if (payload->getAction() == JinglePayload::SessionInitiate) { if (sessionManager->getSession(from, payload->getSessionID())) { // TODO: Add tie-break error sendError(from, id, ErrorPayload::Conflict, ErrorPayload::Cancel); } else { sendResponse(from, id, boost::shared_ptr()); if (!payload->getInitiator().isBare()) { JingleSessionImpl::ref session = boost::make_shared(payload->getInitiator(), from, payload->getSessionID(), router); sessionManager->handleIncomingSession(from, to, session, payload->getContents()); } else { SWIFT_LOG(debug) << "Unable to create Jingle session due to initiator not being a full JID." << std::endl; } } } else { JingleSessionImpl::ref session; if (payload->getInitiator().isValid()) { SWIFT_LOG(debug) << "Lookup session by initiator." << std::endl; session = sessionManager->getSession(payload->getInitiator(), payload->getSessionID()); } else { SWIFT_LOG(debug) << "Lookup session by from attribute." << std::endl; session = sessionManager->getSession(from, payload->getSessionID()); } if (session) { session->handleIncomingAction(payload); sendResponse(from, id, boost::shared_ptr()); } else { std::cerr << "WARN: Didn't find jingle session!" << std::endl; // TODO: Add jingle-specific error sendError(from, id, ErrorPayload::ItemNotFound, ErrorPayload::Cancel); } } return true; } } swift-im-2.0+dev6/Swiften/Jingle/IncomingJingleSessionHandler.cpp0000644000175000017500000000046212227051774024756 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { IncomingJingleSessionHandler::~IncomingJingleSessionHandler() { } } swift-im-2.0+dev6/Swiften/Jingle/JingleResponder.h0000644000175000017500000000132712227051774021760 0ustar kismithkismith/* * 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 #include namespace Swift { class IQRouter; class JingleSessionManager; class JingleResponder : public SetResponder { public: JingleResponder(JingleSessionManager* sessionManager, IQRouter* router); virtual ~JingleResponder(); private: virtual bool handleSetRequest(const JID& from, const JID& to, const std::string& id, boost::shared_ptr payload); private: JingleSessionManager* sessionManager; IQRouter* router; }; } swift-im-2.0+dev6/Swiften/Jingle/FakeJingleSession.h0000644000175000017500000000657112227051774022237 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include #include #include #include #include #include #include #include #include namespace Swift { class JingleContentID; class SWIFTEN_API FakeJingleSession : public JingleSession { public: struct InitiateCall { InitiateCall(JingleContentID contentId, JingleDescription::ref desc, JingleTransportPayload::ref payL) : id(contentId), description(desc), payload(payL) {} JingleContentID id; JingleDescription::ref description; JingleTransportPayload::ref payload; }; struct TerminateCall { TerminateCall(JinglePayload::Reason::Type r) : reason(r) {} JinglePayload::Reason::Type reason; }; struct InfoCall { InfoCall(boost::shared_ptr info) : payload(info) {} boost::shared_ptr payload; }; struct AcceptCall { AcceptCall(JingleContentID contentId, JingleDescription::ref desc, JingleTransportPayload::ref payL) : id(contentId), description(desc), payload(payL) {} JingleContentID id; JingleDescription::ref description; JingleTransportPayload::ref payload; }; struct InfoTransportCall { InfoTransportCall(JingleContentID contentId, JingleTransportPayload::ref payL) : id(contentId), payload(payL) {} JingleContentID id; JingleTransportPayload::ref payload; }; struct AcceptTransportCall { AcceptTransportCall(JingleContentID contentId, JingleTransportPayload::ref payL) : id(contentId), payload(payL) {} JingleContentID id; JingleTransportPayload::ref payload; }; struct RejectTransportCall { RejectTransportCall(JingleContentID contentId, JingleTransportPayload::ref payL) : id(contentId), payload(payL) {} JingleContentID id; JingleTransportPayload::ref payload; }; struct ReplaceTransportCall { ReplaceTransportCall(JingleContentID contentId, JingleTransportPayload::ref payL) : id(contentId), payload(payL) {} JingleContentID id; JingleTransportPayload::ref payload; }; typedef boost::variant Command; public: typedef boost::shared_ptr ref; FakeJingleSession(const JID& initiator, const std::string& id); virtual ~FakeJingleSession(); virtual void sendInitiate(const JingleContentID&, JingleDescription::ref, JingleTransportPayload::ref); virtual void sendTerminate(JinglePayload::Reason::Type reason); virtual void sendInfo(boost::shared_ptr); virtual void sendAccept(const JingleContentID&, JingleDescription::ref, JingleTransportPayload::ref = JingleTransportPayload::ref()); virtual void sendTransportInfo(const JingleContentID&, JingleTransportPayload::ref); virtual void sendTransportAccept(const JingleContentID&, JingleTransportPayload::ref); virtual void sendTransportReject(const JingleContentID&, JingleTransportPayload::ref); virtual void sendTransportReplace(const JingleContentID&, JingleTransportPayload::ref); public: std::vector calledCommands; }; } swift-im-2.0+dev6/Swiften/Jingle/JingleSessionImpl.h0000644000175000017500000000264612227051774022271 0ustar kismithkismith/* * 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 #include namespace Swift { class IQRouter; class JingleSessionImpl : public JingleSession { friend class JingleResponder; public: typedef boost::shared_ptr ref; JingleSessionImpl(const JID& initiator, const JID&, const std::string& id, IQRouter* router); virtual void sendInitiate(const JingleContentID&, JingleDescription::ref, JingleTransportPayload::ref); virtual void sendTerminate(JinglePayload::Reason::Type reason); virtual void sendInfo(boost::shared_ptr); virtual void sendAccept(const JingleContentID&, JingleDescription::ref, JingleTransportPayload::ref); virtual void sendTransportInfo(const JingleContentID&, JingleTransportPayload::ref); virtual void sendTransportAccept(const JingleContentID&, JingleTransportPayload::ref); virtual void sendTransportReject(const JingleContentID&, JingleTransportPayload::ref); virtual void sendTransportReplace(const JingleContentID&, JingleTransportPayload::ref); private: void handleIncomingAction(JinglePayload::ref); void sendSetRequest(JinglePayload::ref payload); JinglePayload::ref createPayload() const; private: IQRouter *iqRouter; JID peerJID; }; } swift-im-2.0+dev6/Swiften/Jingle/Jingle.h0000644000175000017500000000112412227051774020071 0ustar kismithkismith/* * 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 #include namespace Swift { namespace Jingle { template JingleContentPayload::ref getContentWithDescription(const std::vector& contents) { for (size_t i = 0; i < contents.size(); ++i) { if (contents[i]->getDescription()) { return contents[i]; } } return JingleContentPayload::ref(); } } } swift-im-2.0+dev6/Swiften/IDN/0000755000175000017500000000000012227051774015714 5ustar kismithkismithswift-im-2.0+dev6/Swiften/IDN/SConscript0000644000175000017500000000101612227051774017724 0ustar kismithkismithImport("swiften_env", "env") myenv = swiften_env.Clone() if myenv.get("HAVE_ICU") : myenv.MergeFlags(swiften_env["ICU_FLAGS"]) myenv.Append(CPPDEFINES = ["HAVE_ICU"]) elif myenv.get("HAVE_LIBIDN") : myenv.MergeFlags(swiften_env["LIBIDN_FLAGS"]) myenv.Append(CPPDEFINES = ["HAVE_LIBIDN"]) objects = myenv.SwiftenObject([ "StringPrep.cpp", "IDNA.cpp", ]) swiften_env.Append(SWIFTEN_OBJECTS = [objects]) env.Append(UNITTEST_SOURCES = [ File("UnitTest/StringPrepTest.cpp"), File("UnitTest/IDNATest.cpp"), ]) swift-im-2.0+dev6/Swiften/IDN/IDNA.h0000644000175000017500000000052412227051774016601 0ustar kismithkismith/* * 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 #include namespace Swift { class SWIFTEN_API IDNA { public: static std::string getEncoded(const std::string& s); }; } swift-im-2.0+dev6/Swiften/IDN/StringPrep.cpp0000644000175000017500000001105712227051774020521 0ustar kismithkismith/* * Copyright (c) 2010-2012 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #if defined(HAVE_ICU) #pragma GCC diagnostic ignored "-Wold-style-cast" #include #include #include #elif defined(HAVE_LIBIDN) extern "C" { #include }; #endif #include #include #include #include using namespace Swift; #if defined(HAVE_ICU) namespace { static UStringPrepProfileType getICUProfileType(StringPrep::Profile profile) { switch(profile) { case StringPrep::NamePrep: return USPREP_RFC3491_NAMEPREP; break; case StringPrep::XMPPNodePrep: return USPREP_RFC3920_NODEPREP; break; case StringPrep::XMPPResourcePrep: return USPREP_RFC3920_RESOURCEPREP; break; case StringPrep::SASLPrep: return USPREP_RFC4013_SASLPREP; break; } assert(false); return USPREP_RFC3491_NAMEPREP; } template std::vector > getStringPrepared(const StringType& s, StringPrep::Profile profile) { UErrorCode status = U_ZERO_ERROR; ICUConverter converter; boost::shared_ptr icuProfile(usprep_openByType(getICUProfileType(profile), &status), usprep_close); assert(U_SUCCESS(status)); ICUConverter::ICUString icuInput = converter.convertToICUString(s); ICUConverter::ICUString icuResult; UParseError parseError; icuResult.resize(icuInput.size()); int icuResultLength = usprep_prepare(icuProfile.get(), vecptr(icuInput), icuInput.size(), vecptr(icuResult), icuResult.size(), USPREP_ALLOW_UNASSIGNED, &parseError, &status); icuResult.resize(icuResultLength); if (status == U_BUFFER_OVERFLOW_ERROR) { status = U_ZERO_ERROR; icuResult.resize(icuResultLength); icuResultLength = usprep_prepare(icuProfile.get(), vecptr(icuInput), icuInput.size(), vecptr(icuResult), icuResult.size(), USPREP_ALLOW_UNASSIGNED, &parseError, &status); icuResult.resize(icuResultLength); } if (U_FAILURE(status)) { return std::vector >(); } icuResult.resize(icuResultLength); return converter.convertToArray(icuResult); } } namespace Swift { std::string StringPrep::getPrepared(const std::string& s, Profile profile) { if (s.empty()) { return ""; } std::vector > preparedData = getStringPrepared(s, profile); if (preparedData.empty()) { throw std::exception(); } return std::string(vecptr(preparedData)); } SafeByteArray StringPrep::getPrepared(const SafeByteArray& s, Profile profile) { if (s.empty()) { return SafeByteArray(); } std::vector > preparedData = getStringPrepared(s, profile); if (preparedData.empty()) { throw std::exception(); } return createSafeByteArray(reinterpret_cast(vecptr(preparedData))); } } //////////////////////////////////////////////////////////////////////////////// #elif defined(HAVE_LIBIDN) namespace { static const int MAX_STRINGPREP_SIZE = 1024; const Stringprep_profile* getLibIDNProfile(StringPrep::Profile profile) { switch(profile) { case StringPrep::NamePrep: return stringprep_nameprep; break; case StringPrep::XMPPNodePrep: return stringprep_xmpp_nodeprep; break; case StringPrep::XMPPResourcePrep: return stringprep_xmpp_resourceprep; break; case StringPrep::SASLPrep: return stringprep_saslprep; break; } assert(false); return 0; } template ContainerType getStringPrepared(const StringType& s, StringPrep::Profile profile) { ContainerType input(s.begin(), s.end()); input.resize(MAX_STRINGPREP_SIZE); if (stringprep(&input[0], MAX_STRINGPREP_SIZE, static_cast(0), getLibIDNProfile(profile)) == 0) { return input; } else { return ContainerType(); } } } namespace Swift { std::string StringPrep::getPrepared(const std::string& s, Profile profile) { std::vector preparedData = getStringPrepared< std::string, std::vector >(s, profile); if (preparedData.empty()) { throw std::exception(); } return std::string(vecptr(preparedData)); } SafeByteArray StringPrep::getPrepared(const SafeByteArray& s, Profile profile) { std::vector > preparedData = getStringPrepared > >(s, profile); if (preparedData.empty()) { throw std::exception(); } return createSafeByteArray(reinterpret_cast(vecptr(preparedData))); } } #endif swift-im-2.0+dev6/Swiften/IDN/StringPrep.h0000644000175000017500000000107612227051774020166 0ustar kismithkismith/* * 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 #include #include namespace Swift { class SWIFTEN_API StringPrep { public: enum Profile { NamePrep, XMPPNodePrep, XMPPResourcePrep, SASLPrep, }; static std::string getPrepared(const std::string& s, Profile profile); static SafeByteArray getPrepared(const SafeByteArray& s, Profile profile); }; } swift-im-2.0+dev6/Swiften/IDN/ICUConverter.h0000644000175000017500000000442712227051774020404 0ustar kismithkismith/* * Copyright (c) 2012 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #pragma GCC diagnostic ignored "-Wold-style-cast" #include #include #include #include #include #include namespace Swift { class ICUConverter { public: typedef std::vector > ICUString; template ICUString convertToICUString(const T& s) { ICUString result; result.resize(s.size()); UErrorCode status = U_ZERO_ERROR; int icuResultLength = result.size(); u_strFromUTF8Lenient(vecptr(result), result.size(), &icuResultLength, toConstCharArray(s), s.size(), &status); if (status == U_BUFFER_OVERFLOW_ERROR) { status = U_ZERO_ERROR; result.resize(icuResultLength); u_strFromUTF8Lenient(vecptr(result), result.size(), &icuResultLength, toConstCharArray(s), s.size(), &status); } if (U_FAILURE(status)) { return ICUString(); } result.resize(icuResultLength); return result; } std::string convertToString(const ICUString& input) { return std::string(vecptr(convertToArray(input))); } std::vector > convertToArray(const ICUString& input) { std::vector > result; result.resize(input.size()); UErrorCode status = U_ZERO_ERROR; int inputLength = result.size(); u_strToUTF8(vecptr(result), result.size(), &inputLength, vecptr(input), input.size(), &status); if (status == U_BUFFER_OVERFLOW_ERROR) { status = U_ZERO_ERROR; result.resize(inputLength); u_strToUTF8(vecptr(result), result.size(), &inputLength, vecptr(input), input.size(), &status); } if (U_FAILURE(status)) { return std::vector >(); } result.resize(inputLength + 1); result[result.size() - 1] = '\0'; return result; } private: static const char* toConstCharArray(const std::string& input) { return input.c_str(); } static const char* toConstCharArray(const std::vector >& input) { return reinterpret_cast(vecptr(input)); } }; } swift-im-2.0+dev6/Swiften/IDN/UnitTest/0000755000175000017500000000000012227051774017473 5ustar kismithkismithswift-im-2.0+dev6/Swiften/IDN/UnitTest/StringPrepTest.cpp0000644000175000017500000000207312227051774023136 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include "Swiften/IDN/StringPrep.h" using namespace Swift; class StringPrepTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(StringPrepTest); CPPUNIT_TEST(testStringPrep); CPPUNIT_TEST(testStringPrep_Empty); CPPUNIT_TEST_SUITE_END(); public: void testStringPrep() { std::string result = StringPrep::getPrepared("tron\xc3\x87on", StringPrep::NamePrep); CPPUNIT_ASSERT_EQUAL(std::string("tron\xc3\xa7on"), result); } void testStringPrep_Empty() { CPPUNIT_ASSERT_EQUAL(std::string(""), StringPrep::getPrepared("", StringPrep::NamePrep)); CPPUNIT_ASSERT_EQUAL(std::string(""), StringPrep::getPrepared("", StringPrep::XMPPNodePrep)); CPPUNIT_ASSERT_EQUAL(std::string(""), StringPrep::getPrepared("", StringPrep::XMPPResourcePrep)); } }; CPPUNIT_TEST_SUITE_REGISTRATION(StringPrepTest); swift-im-2.0+dev6/Swiften/IDN/UnitTest/IDNATest.cpp0000644000175000017500000000157512227051774021562 0ustar kismithkismith/* * Copyright (c) 2012 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include using namespace Swift; class IDNATest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(IDNATest); CPPUNIT_TEST(testGetEncoded); CPPUNIT_TEST(testGetEncoded_International); CPPUNIT_TEST_SUITE_END(); public: void testGetEncoded() { std::string result = IDNA::getEncoded("www.swift.im"); CPPUNIT_ASSERT_EQUAL(std::string("www.swift.im"), result); } void testGetEncoded_International() { std::string result = IDNA::getEncoded("www.tron\xc3\x87on.com"); CPPUNIT_ASSERT_EQUAL(std::string("www.xn--tronon-zua.com"), result); } }; CPPUNIT_TEST_SUITE_REGISTRATION(IDNATest); swift-im-2.0+dev6/Swiften/IDN/IDNA.cpp0000644000175000017500000000307712227051774017142 0ustar kismithkismith/* * Copyright (c) 2010-2012 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #if defined(HAVE_ICU) #pragma GCC diagnostic ignored "-Wold-style-cast" #include #include #elif defined(HAVE_LIBIDN) #include #include #endif #include #include namespace Swift { std::string IDNA::getEncoded(const std::string& domain) { #if defined(HAVE_ICU) UErrorCode status = U_ZERO_ERROR; ICUConverter converter; ICUConverter::ICUString icuInput = converter.convertToICUString(domain); ICUConverter::ICUString icuResult; icuResult.resize(icuInput.size()); UParseError parseError; int icuResultLength = uidna_IDNToASCII(vecptr(icuInput), icuInput.size(), vecptr(icuResult), icuResult.size(), UIDNA_DEFAULT, &parseError, &status); if (status == U_BUFFER_OVERFLOW_ERROR) { status = U_ZERO_ERROR; icuResult.resize(icuResultLength); icuResultLength = uidna_IDNToASCII(vecptr(icuInput), icuInput.size(), vecptr(icuResult), icuResult.size(), UIDNA_DEFAULT, &parseError, &status); } if (U_FAILURE(status)) { return domain; } icuResult.resize(icuResultLength); return converter.convertToString(icuResult); #elif defined(HAVE_LIBIDN) char* output; if (idna_to_ascii_8z(domain.c_str(), &output, 0) == IDNA_SUCCESS) { std::string result(output); free(output); return result; } else { return domain; } #endif } } swift-im-2.0+dev6/Swiften/StreamStack/0000755000175000017500000000000012227051774017523 5ustar kismithkismithswift-im-2.0+dev6/Swiften/StreamStack/StreamStack.cpp0000644000175000017500000000173412227051774022455 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include namespace Swift { StreamStack::StreamStack(XMPPLayer* xmppLayer, LowLayer* physicalLayer) : xmppLayer_(xmppLayer), physicalLayer_(physicalLayer) { physicalLayer_->setParentLayer(xmppLayer_); xmppLayer_->setChildLayer(physicalLayer_); } StreamStack::~StreamStack() { } void StreamStack::addLayer(StreamLayer* newLayer) { LowLayer* lowLayer = layers_.empty() ? physicalLayer_ : *layers_.rbegin(); xmppLayer_->setChildLayer(newLayer); newLayer->setParentLayer(xmppLayer_); lowLayer->setParentLayer(newLayer); newLayer->setChildLayer(lowLayer); layers_.push_back(newLayer); } } swift-im-2.0+dev6/Swiften/StreamStack/SConscript0000644000175000017500000000046012227051774021535 0ustar kismithkismithImport("swiften_env") myenv = swiften_env.Clone() sources = [ "HighLayer.cpp", "LowLayer.cpp", "StreamStack.cpp", "ConnectionLayer.cpp", "TLSLayer.cpp", "WhitespacePingLayer.cpp", "XMPPLayer.cpp", ] objects = myenv.SwiftenObject(sources) swiften_env.Append(SWIFTEN_OBJECTS = [objects]) swift-im-2.0+dev6/Swiften/StreamStack/XMPPLayer.h0000644000175000017500000000413012227051774021453 0ustar kismithkismith/* * 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 #include #include #include #include #include #include #include #include namespace Swift { class ProtocolHeader; class XMPPParser; class PayloadParserFactoryCollection; class XMPPSerializer; class PayloadSerializerCollection; class XMLParserFactory; class BOSHSessionStream; class SWIFTEN_API XMPPLayer : public XMPPParserClient, public HighLayer, boost::noncopyable { friend class BOSHSessionStream; public: XMPPLayer( PayloadParserFactoryCollection* payloadParserFactories, PayloadSerializerCollection* payloadSerializers, XMLParserFactory* xmlParserFactory, StreamType streamType); ~XMPPLayer(); void writeHeader(const ProtocolHeader& header); void writeFooter(); void writeElement(boost::shared_ptr); void writeData(const std::string& data); void resetParser(); protected: void handleDataRead(const SafeByteArray& data); void writeDataInternal(const SafeByteArray& data); public: boost::signal onStreamStart; boost::signal)> onElement; boost::signal onWriteData; boost::signal onDataRead; boost::signal onError; private: void handleStreamStart(const ProtocolHeader&); void handleElement(boost::shared_ptr); void handleStreamEnd(); void doResetParser(); private: PayloadParserFactoryCollection* payloadParserFactories_; XMPPParser* xmppParser_; PayloadSerializerCollection* payloadSerializers_; XMLParserFactory* xmlParserFactory_; XMPPSerializer* xmppSerializer_; bool resetParserAfterParse_; bool inParser_; }; } swift-im-2.0+dev6/Swiften/StreamStack/XMPPLayer.cpp0000644000175000017500000000505012227051774022010 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include namespace Swift { XMPPLayer::XMPPLayer( PayloadParserFactoryCollection* payloadParserFactories, PayloadSerializerCollection* payloadSerializers, XMLParserFactory* xmlParserFactory, StreamType streamType) : payloadParserFactories_(payloadParserFactories), payloadSerializers_(payloadSerializers), xmlParserFactory_(xmlParserFactory), resetParserAfterParse_(false), inParser_(false) { xmppParser_ = new XMPPParser(this, payloadParserFactories_, xmlParserFactory); xmppSerializer_ = new XMPPSerializer(payloadSerializers_, streamType); } XMPPLayer::~XMPPLayer() { delete xmppSerializer_; delete xmppParser_; } void XMPPLayer::writeHeader(const ProtocolHeader& header) { writeDataInternal(createSafeByteArray(xmppSerializer_->serializeHeader(header))); } void XMPPLayer::writeFooter() { writeDataInternal(createSafeByteArray(xmppSerializer_->serializeFooter())); } void XMPPLayer::writeElement(boost::shared_ptr element) { writeDataInternal(xmppSerializer_->serializeElement(element)); } void XMPPLayer::writeData(const std::string& data) { writeDataInternal(createSafeByteArray(data)); } void XMPPLayer::writeDataInternal(const SafeByteArray& data) { onWriteData(data); writeDataToChildLayer(data); } void XMPPLayer::handleDataRead(const SafeByteArray& data) { onDataRead(data); inParser_ = true; // FIXME: Converting to unsafe string. Should be ok, since we don't take passwords // from the stream in clients. If servers start using this, and require safe storage, // we need to fix this. if (!xmppParser_->parse(byteArrayToString(ByteArray(data.begin(), data.end())))) { inParser_ = false; onError(); return; } inParser_ = false; if (resetParserAfterParse_) { doResetParser(); } } void XMPPLayer::doResetParser() { delete xmppParser_; xmppParser_ = new XMPPParser(this, payloadParserFactories_, xmlParserFactory_); resetParserAfterParse_ = false; } void XMPPLayer::handleStreamStart(const ProtocolHeader& header) { onStreamStart(header); } void XMPPLayer::handleElement(boost::shared_ptr stanza) { onElement(stanza); } void XMPPLayer::handleStreamEnd() { } void XMPPLayer::resetParser() { if (inParser_) { resetParserAfterParse_ = true; } else { doResetParser(); } } } swift-im-2.0+dev6/Swiften/StreamStack/TLSLayer.cpp0000644000175000017500000000273612227051774021676 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include namespace Swift { TLSLayer::TLSLayer(TLSContextFactory* factory) { context = factory->createTLSContext(); context->onDataForNetwork.connect(boost::bind(&TLSLayer::writeDataToChildLayer, this, _1)); context->onDataForApplication.connect(boost::bind(&TLSLayer::writeDataToParentLayer, this, _1)); context->onConnected.connect(onConnected); context->onError.connect(onError); } TLSLayer::~TLSLayer() { delete context; } void TLSLayer::connect() { context->connect(); } void TLSLayer::writeData(const SafeByteArray& data) { context->handleDataFromApplication(data); } void TLSLayer::handleDataRead(const SafeByteArray& data) { context->handleDataFromNetwork(data); } bool TLSLayer::setClientCertificate(CertificateWithKey::ref certificate) { return context->setClientCertificate(certificate); } Certificate::ref TLSLayer::getPeerCertificate() const { return context->getPeerCertificate(); } std::vector TLSLayer::getPeerCertificateChain() const { return context->getPeerCertificateChain(); } boost::shared_ptr TLSLayer::getPeerCertificateVerificationError() const { return context->getPeerCertificateVerificationError(); } } swift-im-2.0+dev6/Swiften/StreamStack/HighLayer.h0000644000175000017500000000132012227051774021544 0ustar kismithkismith/* * 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 #include namespace Swift { class LowLayer; class SWIFTEN_API HighLayer { friend class StreamStack; public: HighLayer(); virtual ~HighLayer(); virtual void handleDataRead(const SafeByteArray& data) = 0; protected: LowLayer* getChildLayer() { return childLayer; } void setChildLayer(LowLayer* childLayer) { this->childLayer = childLayer; } void writeDataToChildLayer(const SafeByteArray& data); private: LowLayer* childLayer; }; } swift-im-2.0+dev6/Swiften/StreamStack/WhitespacePingLayer.h0000644000175000017500000000141512227051774023604 0ustar kismithkismith/* * 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 #include #include namespace Swift { class Timer; class TimerFactory; class WhitespacePingLayer : public StreamLayer, boost::noncopyable { public: WhitespacePingLayer(TimerFactory* timerFactory); void setActive(); void setInactive(); void writeData(const SafeByteArray& data); void handleDataRead(const SafeByteArray& data); bool getIsActive() const { return isActive; } private: void handleTimerTick(); private: bool isActive; boost::shared_ptr timer; }; } swift-im-2.0+dev6/Swiften/StreamStack/TLSLayer.h0000644000175000017500000000231012227051774021327 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include namespace Swift { class TLSContext; class TLSContextFactory; class TLSLayer : public StreamLayer { public: TLSLayer(TLSContextFactory*); ~TLSLayer(); void connect(); bool setClientCertificate(CertificateWithKey::ref cert); Certificate::ref getPeerCertificate() const; std::vector getPeerCertificateChain() const; boost::shared_ptr getPeerCertificateVerificationError() const; void writeData(const SafeByteArray& data); void handleDataRead(const SafeByteArray& data); TLSContext* getContext() const { return context; } public: boost::signal)> onError; boost::signal onConnected; private: TLSContext* context; }; } swift-im-2.0+dev6/Swiften/StreamStack/HighLayer.cpp0000644000175000017500000000077612227051774022115 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include namespace Swift { HighLayer::HighLayer() : childLayer(NULL) { } HighLayer::~HighLayer() { } void HighLayer::writeDataToChildLayer(const SafeByteArray& data) { //assert(childLayer); if (childLayer) { childLayer->writeData(data); } } } swift-im-2.0+dev6/Swiften/StreamStack/LowLayer.cpp0000644000175000017500000000075112227051774021770 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include namespace Swift { LowLayer::LowLayer() : parentLayer(NULL) { } LowLayer::~LowLayer() { } void LowLayer::writeDataToParentLayer(const SafeByteArray& data) { assert(parentLayer); parentLayer->handleDataRead(data); } } swift-im-2.0+dev6/Swiften/StreamStack/LowLayer.h0000644000175000017500000000132412227051774021432 0ustar kismithkismith/* * 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 #include namespace Swift { class HighLayer; class SWIFTEN_API LowLayer { friend class StreamStack; public: LowLayer(); virtual ~LowLayer(); virtual void writeData(const SafeByteArray& data) = 0; protected: HighLayer* getParentLayer() { return parentLayer; } void setParentLayer(HighLayer* parentLayer) { this->parentLayer = parentLayer; } void writeDataToParentLayer(const SafeByteArray& data); private: HighLayer* parentLayer; }; } swift-im-2.0+dev6/Swiften/StreamStack/ConnectionLayer.cpp0000644000175000017500000000130412227051774023321 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include namespace Swift { ConnectionLayer::ConnectionLayer(boost::shared_ptr connection) : connection(connection) { connection->onDataRead.connect(boost::bind(&ConnectionLayer::handleDataRead, this, _1)); } ConnectionLayer::~ConnectionLayer() { connection->onDataRead.disconnect(boost::bind(&ConnectionLayer::writeDataToParentLayer, this, _1)); } void ConnectionLayer::handleDataRead(boost::shared_ptr data) { writeDataToParentLayer(*data); } } swift-im-2.0+dev6/Swiften/StreamStack/UnitTest/0000755000175000017500000000000012227051774021302 5ustar kismithkismithswift-im-2.0+dev6/Swiften/StreamStack/UnitTest/StreamStackTest.cpp0000644000175000017500000001362412227051774025075 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Swift; class StreamStackTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(StreamStackTest); CPPUNIT_TEST(testWriteData_NoIntermediateStreamStack); CPPUNIT_TEST(testWriteData_OneIntermediateStream); CPPUNIT_TEST(testWriteData_TwoIntermediateStreamStack); CPPUNIT_TEST(testReadData_NoIntermediateStreamStack); CPPUNIT_TEST(testReadData_OneIntermediateStream); CPPUNIT_TEST(testReadData_TwoIntermediateStreamStack); CPPUNIT_TEST(testAddLayer_ExistingOnWriteDataSlot); CPPUNIT_TEST_SUITE_END(); public: void setUp() { physicalStream_ = new TestLowLayer(); xmppStream_ = new XMPPLayer(&parserFactories_, &serializers_, &xmlParserFactory_, ClientStreamType); elementsReceived_ = 0; dataWriteReceived_ = 0; } void tearDown() { delete physicalStream_; delete xmppStream_; } void testWriteData_NoIntermediateStreamStack() { StreamStack testling(xmppStream_, physicalStream_); xmppStream_->writeData("foo"); CPPUNIT_ASSERT_EQUAL(static_cast(1), physicalStream_->data_.size()); CPPUNIT_ASSERT_EQUAL(createSafeByteArray("foo"), physicalStream_->data_[0]); } void testWriteData_OneIntermediateStream() { StreamStack testling(xmppStream_, physicalStream_); boost::shared_ptr xStream(new MyStreamLayer("X")); testling.addLayer(xStream.get()); xmppStream_->writeData("foo"); CPPUNIT_ASSERT_EQUAL(static_cast(1), physicalStream_->data_.size()); CPPUNIT_ASSERT_EQUAL(createSafeByteArray("Xfoo"), physicalStream_->data_[0]); } void testWriteData_TwoIntermediateStreamStack() { StreamStack testling(xmppStream_, physicalStream_); boost::shared_ptr xStream(new MyStreamLayer("X")); boost::shared_ptr yStream(new MyStreamLayer("Y")); testling.addLayer(xStream.get()); testling.addLayer(yStream.get()); xmppStream_->writeData("foo"); CPPUNIT_ASSERT_EQUAL(static_cast(1), physicalStream_->data_.size()); CPPUNIT_ASSERT_EQUAL(createSafeByteArray("XYfoo"), physicalStream_->data_[0]); } void testReadData_NoIntermediateStreamStack() { StreamStack testling(xmppStream_, physicalStream_); xmppStream_->onElement.connect(boost::bind(&StreamStackTest::handleElement, this, _1)); physicalStream_->onDataRead(createSafeByteArray("")); CPPUNIT_ASSERT_EQUAL(1, elementsReceived_); } void testReadData_OneIntermediateStream() { StreamStack testling(xmppStream_, physicalStream_); xmppStream_->onElement.connect(boost::bind(&StreamStackTest::handleElement, this, _1)); boost::shared_ptr xStream(new MyStreamLayer("<")); testling.addLayer(xStream.get()); physicalStream_->onDataRead(createSafeByteArray("stream:stream xmlns:stream='http://etherx.jabber.org/streams'>")); CPPUNIT_ASSERT_EQUAL(1, elementsReceived_); } void testReadData_TwoIntermediateStreamStack() { StreamStack testling(xmppStream_, physicalStream_); xmppStream_->onElement.connect(boost::bind(&StreamStackTest::handleElement, this, _1)); boost::shared_ptr xStream(new MyStreamLayer("s")); boost::shared_ptr yStream(new MyStreamLayer("<")); testling.addLayer(xStream.get()); testling.addLayer(yStream.get()); physicalStream_->onDataRead(createSafeByteArray("tream:stream xmlns:stream='http://etherx.jabber.org/streams'>")); CPPUNIT_ASSERT_EQUAL(1, elementsReceived_); } void testAddLayer_ExistingOnWriteDataSlot() { StreamStack testling(xmppStream_, physicalStream_); xmppStream_->onWriteData.connect(boost::bind(&StreamStackTest::handleWriteData, this, _1)); boost::shared_ptr xStream(new MyStreamLayer("X")); testling.addLayer(xStream.get()); xmppStream_->writeData("foo"); CPPUNIT_ASSERT_EQUAL(1, dataWriteReceived_); } void handleElement(boost::shared_ptr) { ++elementsReceived_; } void handleWriteData(const SafeByteArray&) { ++dataWriteReceived_; } private: class MyStreamLayer : public StreamLayer { public: MyStreamLayer(const std::string& prepend) : prepend_(prepend) { } virtual void writeData(const SafeByteArray& data) { writeDataToChildLayer(concat(createSafeByteArray(prepend_), data)); } virtual void handleDataRead(const SafeByteArray& data) { writeDataToParentLayer(concat(createSafeByteArray(prepend_), data)); } private: std::string prepend_; }; class TestLowLayer : public LowLayer { public: TestLowLayer() { } virtual void writeData(const SafeByteArray& data) { data_.push_back(data); } void onDataRead(const SafeByteArray& data) { writeDataToParentLayer(data); } std::vector data_; }; private: FullPayloadParserFactoryCollection parserFactories_; FullPayloadSerializerCollection serializers_; TestLowLayer* physicalStream_; PlatformXMLParserFactory xmlParserFactory_; XMPPLayer* xmppStream_; int elementsReceived_; int dataWriteReceived_; }; CPPUNIT_TEST_SUITE_REGISTRATION(StreamStackTest); swift-im-2.0+dev6/Swiften/StreamStack/UnitTest/XMPPLayerTest.cpp0000644000175000017500000001131712227051774024432 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include #include #include using namespace Swift; class XMPPLayerTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(XMPPLayerTest); CPPUNIT_TEST(testParseData_Error); CPPUNIT_TEST(testResetParser); CPPUNIT_TEST(testResetParser_FromSlot); CPPUNIT_TEST(testWriteHeader); CPPUNIT_TEST(testWriteElement); CPPUNIT_TEST(testWriteFooter); CPPUNIT_TEST_SUITE_END(); public: void setUp() { lowLayer_ = new DummyLowLayer(); testling_ = new XMPPLayerExposed(&parserFactories_, &serializers_, &xmlParserFactory_, ClientStreamType); testling_->setChildLayer(lowLayer_); elementsReceived_ = 0; errorReceived_ = 0; } void tearDown() { delete testling_; delete lowLayer_; } void testParseData_Error() { testling_->onError.connect(boost::bind(&XMPPLayerTest::handleError, this)); testling_->handleDataRead(createSafeByteArray("")); CPPUNIT_ASSERT_EQUAL(1, errorReceived_); } void testResetParser() { testling_->onElement.connect(boost::bind(&XMPPLayerTest::handleElement, this, _1)); testling_->onError.connect(boost::bind(&XMPPLayerTest::handleError, this)); testling_->handleDataRead(createSafeByteArray("")); testling_->resetParser(); testling_->handleDataRead(createSafeByteArray("")); testling_->handleDataRead(createSafeByteArray("")); CPPUNIT_ASSERT_EQUAL(1, elementsReceived_); CPPUNIT_ASSERT_EQUAL(0, errorReceived_); } void testResetParser_FromSlot() { testling_->onElement.connect(boost::bind(&XMPPLayerTest::handleElementAndReset, this, _1)); testling_->handleDataRead(createSafeByteArray("")); testling_->handleDataRead(createSafeByteArray("")); CPPUNIT_ASSERT_EQUAL(2, elementsReceived_); CPPUNIT_ASSERT_EQUAL(0, errorReceived_); } void testWriteHeader() { ProtocolHeader header; header.setTo("example.com"); testling_->writeHeader(header); CPPUNIT_ASSERT_EQUAL(std::string(""), lowLayer_->writtenData); } void testWriteElement() { testling_->writeElement(boost::make_shared()); CPPUNIT_ASSERT_EQUAL(std::string(""), lowLayer_->writtenData); } void testWriteFooter() { testling_->writeFooter(); CPPUNIT_ASSERT_EQUAL(std::string(""), lowLayer_->writtenData); } void handleElement(boost::shared_ptr) { ++elementsReceived_; } void handleElementAndReset(boost::shared_ptr) { ++elementsReceived_; testling_->resetParser(); } void handleError() { ++errorReceived_; } private: class XMPPLayerExposed : public XMPPLayer { public: XMPPLayerExposed( PayloadParserFactoryCollection* payloadParserFactories, PayloadSerializerCollection* payloadSerializers, XMLParserFactory* xmlParserFactory, StreamType streamType) : XMPPLayer(payloadParserFactories, payloadSerializers, xmlParserFactory, streamType) {} using XMPPLayer::handleDataRead; using HighLayer::setChildLayer; }; class DummyLowLayer : public LowLayer { public: virtual void writeData(const SafeByteArray& data) { writtenData += byteArrayToString(ByteArray(data.begin(), data.end())); } std::string writtenData; }; FullPayloadParserFactoryCollection parserFactories_; FullPayloadSerializerCollection serializers_; DummyLowLayer* lowLayer_; XMPPLayerExposed* testling_; PlatformXMLParserFactory xmlParserFactory_; int elementsReceived_; int errorReceived_; }; CPPUNIT_TEST_SUITE_REGISTRATION(XMPPLayerTest); swift-im-2.0+dev6/Swiften/StreamStack/StreamStack.h0000644000175000017500000000167112227051774022122 0ustar kismithkismith/* * 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 #include #include #include #include namespace Swift { class XMPPLayer; class LowLayer; class StreamLayer; class SWIFTEN_API StreamStack { public: StreamStack(XMPPLayer* xmppLayer, LowLayer* physicalLayer); ~StreamStack(); void addLayer(StreamLayer*); XMPPLayer* getXMPPLayer() const { return xmppLayer_; } template T* getLayer() { for (size_t i = 0; i < layers_.size(); ++i) { T* layer = dynamic_cast(layers_[i]); if (layer) { return layer; } } return NULL; } private: XMPPLayer* xmppLayer_; LowLayer* physicalLayer_; std::vector layers_; }; } swift-im-2.0+dev6/Swiften/StreamStack/WhitespacePingLayer.cpp0000644000175000017500000000213012227051774024132 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include namespace Swift { static const int TIMEOUT_MILLISECONDS = 60000; WhitespacePingLayer::WhitespacePingLayer(TimerFactory* timerFactory) : isActive(false) { timer = timerFactory->createTimer(TIMEOUT_MILLISECONDS); timer->onTick.connect(boost::bind(&WhitespacePingLayer::handleTimerTick, this)); } void WhitespacePingLayer::writeData(const SafeByteArray& data) { writeDataToChildLayer(data); } void WhitespacePingLayer::handleDataRead(const SafeByteArray& data) { writeDataToParentLayer(data); } void WhitespacePingLayer::handleTimerTick() { timer->stop(); writeDataToChildLayer(createSafeByteArray(" ")); timer->start(); } void WhitespacePingLayer::setActive() { isActive = true; timer->start(); } void WhitespacePingLayer::setInactive() { timer->stop(); isActive = false; } } swift-im-2.0+dev6/Swiften/StreamStack/CompressionLayer.h0000644000175000017500000000220412227051774023170 0ustar kismithkismith/* * 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 #include #include #include #include #include #include namespace Swift { class ZLibCompressor; class ZLibDecompressor; class CompressionLayer : public StreamLayer, boost::noncopyable { public: CompressionLayer() {} virtual void writeData(const SafeByteArray& data) { try { writeDataToChildLayer(compressor_.process(data)); } catch (const ZLibException&) { onError(); } } virtual void handleDataRead(const SafeByteArray& data) { try { writeDataToParentLayer(decompressor_.process(data)); } catch (const ZLibException&) { onError(); } } public: boost::signal onError; private: ZLibCompressor compressor_; ZLibDecompressor decompressor_; }; } swift-im-2.0+dev6/Swiften/StreamStack/ConnectionLayer.h0000644000175000017500000000122512227051774022770 0ustar kismithkismith/* * 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 #include #include namespace Swift { class ConnectionLayer : public LowLayer { public: ConnectionLayer(boost::shared_ptr connection); ~ConnectionLayer(); void writeData(const SafeByteArray& data) { connection->write(data); } private: void handleDataRead(boost::shared_ptr); private: boost::shared_ptr connection; }; } swift-im-2.0+dev6/Swiften/StreamStack/StreamLayer.h0000644000175000017500000000056312227051774022130 0ustar kismithkismith/* * 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 #include namespace Swift { class StreamLayer : public LowLayer, public HighLayer { public: StreamLayer() {} }; } swift-im-2.0+dev6/Swiften/Disco/0000755000175000017500000000000012227051774016343 5ustar kismithkismithswift-im-2.0+dev6/Swiften/Disco/SConscript0000644000175000017500000000061412227051774020356 0ustar kismithkismithImport("swiften_env") objects = swiften_env.SwiftenObject([ "CapsInfoGenerator.cpp", "CapsManager.cpp", "EntityCapsManager.cpp", "EntityCapsProvider.cpp", "DummyEntityCapsProvider.cpp", "CapsStorage.cpp", "ClientDiscoManager.cpp", "DiscoInfoResponder.cpp", "JIDDiscoInfoResponder.cpp", "DiscoServiceWalker.cpp", ]) swiften_env.Append(SWIFTEN_OBJECTS = [objects]) swift-im-2.0+dev6/Swiften/Disco/DummyEntityCapsProvider.cpp0000644000175000017500000000072512227051774023665 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include namespace Swift { DiscoInfo::ref DummyEntityCapsProvider::getCaps(const JID& jid) const { std::map::const_iterator i = caps.find(jid); if (i != caps.end()) { return i->second; } return DiscoInfo::ref(); } } swift-im-2.0+dev6/Swiften/Disco/CapsStorage.h0000644000175000017500000000100112227051774020717 0ustar kismithkismith/* * 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 #include #include namespace Swift { class SWIFTEN_API CapsStorage { public: virtual ~CapsStorage(); virtual DiscoInfo::ref getDiscoInfo(const std::string&) const = 0; virtual void setDiscoInfo(const std::string&, DiscoInfo::ref) = 0; }; } swift-im-2.0+dev6/Swiften/Disco/GetDiscoItemsRequest.h0000644000175000017500000000212712227051774022572 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include namespace Swift { class GetDiscoItemsRequest : public GenericRequest { public: typedef boost::shared_ptr ref; static ref create(const JID& jid, IQRouter* router) { return ref(new GetDiscoItemsRequest(jid, router)); } static ref create(const JID& jid, const std::string& node, IQRouter* router) { return ref(new GetDiscoItemsRequest(jid, node, router)); } private: GetDiscoItemsRequest(const JID& jid, IQRouter* router) : GenericRequest(IQ::Get, jid, boost::make_shared(), router) { } GetDiscoItemsRequest(const JID& jid, const std::string& node, IQRouter* router) : GenericRequest(IQ::Get, jid, boost::make_shared(), router) { getPayloadGeneric()->setNode(node); } }; } swift-im-2.0+dev6/Swiften/Disco/ClientDiscoManager.h0000644000175000017500000000417212227051774022213 0ustar kismithkismith/* * 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 #include #include #include namespace Swift { class IQRouter; class DiscoInfoResponder; class PayloadAddingPresenceSender; class PresenceSender; /** * Class responsible for managing outgoing disco information for a client. * * The manager will respond to disco#info requests, and add entity capabilities information * to outgoing presence. * * To use this class, call setCapsNode() once with the caps URI of the client. After this, * call setDiscoInfo() with the capabilities for the client. This can be * called whenever the capabilities change. */ class SWIFTEN_API ClientDiscoManager { public: /** * Constructs the manager * * \param iqRouter the router on which requests will be answered * \param presenceSender the presence sender to which all outgoing presence * (with caps information) will be sent. */ ClientDiscoManager(IQRouter* iqRouter, PresenceSender* presenceSender); ~ClientDiscoManager(); /** * Needs to be called before calling setDiscoInfo(). */ void setCapsNode(const std::string& node); /** * Sets the capabilities of the client. */ void setDiscoInfo(const DiscoInfo& info); /** * Returns the presence sender through which all outgoing presence * should be sent. * The manager will add the necessary caps information, and forward it to * the presence sender passed at construction time. */ PresenceSender* getPresenceSender() const { return presenceSender; } /** * Called when the client is connected. * This resets the presence sender, such that it assumes initial presence * hasn't been sent yet. */ void handleConnected(); private: PayloadAddingPresenceSender* presenceSender; DiscoInfoResponder* discoInfoResponder; std::string capsNode; CapsInfo::ref capsInfo; }; } swift-im-2.0+dev6/Swiften/Disco/JIDDiscoInfoResponder.cpp0000644000175000017500000000345412227051774023143 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include namespace Swift { JIDDiscoInfoResponder::JIDDiscoInfoResponder(IQRouter* router) : GetResponder(router) { } void JIDDiscoInfoResponder::clearDiscoInfo(const JID& jid) { info.erase(jid); } void JIDDiscoInfoResponder::setDiscoInfo(const JID& jid, const DiscoInfo& discoInfo) { JIDDiscoInfoMap::iterator i = info.insert(std::make_pair(jid, JIDDiscoInfo())).first; i->second.discoInfo = discoInfo; } void JIDDiscoInfoResponder::setDiscoInfo(const JID& jid, const std::string& node, const DiscoInfo& discoInfo) { JIDDiscoInfoMap::iterator i = info.insert(std::make_pair(jid, JIDDiscoInfo())).first; DiscoInfo newInfo(discoInfo); newInfo.setNode(node); i->second.nodeDiscoInfo[node] = newInfo; } bool JIDDiscoInfoResponder::handleGetRequest(const JID& from, const JID& to, const std::string& id, boost::shared_ptr discoInfo) { JIDDiscoInfoMap::const_iterator i = info.find(to); if (i != info.end()) { if (discoInfo->getNode().empty()) { sendResponse(from, to, id, boost::make_shared(i->second.discoInfo)); } else { std::map::const_iterator j = i->second.nodeDiscoInfo.find(discoInfo->getNode()); if (j != i->second.nodeDiscoInfo.end()) { sendResponse(from, to, id, boost::make_shared(j->second)); } else { sendError(from, to, id, ErrorPayload::ItemNotFound, ErrorPayload::Cancel); } } } else { sendError(from, to, id, ErrorPayload::ItemNotFound, ErrorPayload::Cancel); } return true; } } swift-im-2.0+dev6/Swiften/Disco/CapsInfoGenerator.h0000644000175000017500000000076412227051774022074 0ustar kismithkismith/* * 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 #include #include namespace Swift { class DiscoInfo; class SWIFTEN_API CapsInfoGenerator { public: CapsInfoGenerator(const std::string& node); CapsInfo generateCapsInfo(const DiscoInfo& discoInfo) const; private: std::string node_; }; } swift-im-2.0+dev6/Swiften/Disco/CapsManager.cpp0000644000175000017500000000611612227051774021234 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include namespace Swift { CapsManager::CapsManager(CapsStorage* capsStorage, StanzaChannel* stanzaChannel, IQRouter* iqRouter) : iqRouter(iqRouter), capsStorage(capsStorage), warnOnInvalidHash(true) { stanzaChannel->onPresenceReceived.connect(boost::bind(&CapsManager::handlePresenceReceived, this, _1)); stanzaChannel->onAvailableChanged.connect(boost::bind(&CapsManager::handleStanzaChannelAvailableChanged, this, _1)); } void CapsManager::handlePresenceReceived(boost::shared_ptr presence) { boost::shared_ptr capsInfo = presence->getPayload(); if (!capsInfo || capsInfo->getHash() != "sha-1" || presence->getPayload()) { return; } std::string hash = capsInfo->getVersion(); if (capsStorage->getDiscoInfo(hash)) { return; } if (failingCaps.find(std::make_pair(presence->getFrom(), hash)) != failingCaps.end()) { return; } if (requestedDiscoInfos.find(hash) != requestedDiscoInfos.end()) { fallbacks[hash].insert(std::make_pair(presence->getFrom(), capsInfo->getNode())); return; } requestDiscoInfo(presence->getFrom(), capsInfo->getNode(), hash); } void CapsManager::handleStanzaChannelAvailableChanged(bool available) { if (available) { failingCaps.clear(); fallbacks.clear(); requestedDiscoInfos.clear(); } } void CapsManager::handleDiscoInfoReceived(const JID& from, const std::string& hash, DiscoInfo::ref discoInfo, ErrorPayload::ref error) { requestedDiscoInfos.erase(hash); if (error || !discoInfo || CapsInfoGenerator("").generateCapsInfo(*discoInfo.get()).getVersion() != hash) { if (warnOnInvalidHash && !error && discoInfo) { std::cerr << "Warning: Caps from " << from.toString() << " do not verify" << std::endl; } failingCaps.insert(std::make_pair(from, hash)); std::map > >::iterator i = fallbacks.find(hash); if (i != fallbacks.end() && !i->second.empty()) { std::pair fallbackAndNode = *i->second.begin(); i->second.erase(i->second.begin()); requestDiscoInfo(fallbackAndNode.first, fallbackAndNode.second, hash); } return; } fallbacks.erase(hash); capsStorage->setDiscoInfo(hash, discoInfo); onCapsAvailable(hash); } void CapsManager::requestDiscoInfo(const JID& jid, const std::string& node, const std::string& hash) { GetDiscoInfoRequest::ref request = GetDiscoInfoRequest::create(jid, node + "#" + hash, iqRouter); request->onResponse.connect(boost::bind(&CapsManager::handleDiscoInfoReceived, this, jid, hash, _1, _2)); requestedDiscoInfos.insert(hash); request->send(); } DiscoInfo::ref CapsManager::getCaps(const std::string& hash) const { return capsStorage->getDiscoInfo(hash); } } swift-im-2.0+dev6/Swiften/Disco/CapsProvider.h0000644000175000017500000000100112227051774021105 0ustar kismithkismith/* * 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 #include #include namespace Swift { class CapsProvider { public: virtual ~CapsProvider() {} virtual DiscoInfo::ref getCaps(const std::string&) const = 0; boost::signal onCapsAvailable; }; } swift-im-2.0+dev6/Swiften/Disco/EntityCapsProvider.h0000644000175000017500000000153112227051774022312 0ustar kismithkismith/* * 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 #include #include #include namespace Swift { /** * This class provides information about capabilities of entities on the network. * This information is provided in the form of service discovery * information. */ class SWIFTEN_API EntityCapsProvider { public: virtual ~EntityCapsProvider(); /** * Returns the service discovery information of the given JID. */ virtual DiscoInfo::ref getCaps(const JID&) const = 0; /** * Emitted when the capabilities of a JID changes. */ boost::signal onCapsChanged; }; } swift-im-2.0+dev6/Swiften/Disco/ClientDiscoManager.cpp0000644000175000017500000000243712227051774022550 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include namespace Swift { ClientDiscoManager::ClientDiscoManager(IQRouter* iqRouter, PresenceSender* presenceSender) { discoInfoResponder = new DiscoInfoResponder(iqRouter); discoInfoResponder->start(); this->presenceSender = new PayloadAddingPresenceSender(presenceSender); } ClientDiscoManager::~ClientDiscoManager() { delete presenceSender; discoInfoResponder->stop(); delete discoInfoResponder; } void ClientDiscoManager::setCapsNode(const std::string& node) { capsNode = node; } void ClientDiscoManager::setDiscoInfo(const DiscoInfo& discoInfo) { capsInfo = CapsInfo::ref(new CapsInfo(CapsInfoGenerator(capsNode).generateCapsInfo(discoInfo))); discoInfoResponder->clearDiscoInfo(); discoInfoResponder->setDiscoInfo(discoInfo); discoInfoResponder->setDiscoInfo(capsInfo->getNode() + "#" + capsInfo->getVersion(), discoInfo); presenceSender->setPayload(capsInfo); } void ClientDiscoManager::handleConnected() { presenceSender->reset(); } } swift-im-2.0+dev6/Swiften/Disco/DiscoServiceWalker.cpp0000644000175000017500000001055012227051774022600 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include namespace Swift { DiscoServiceWalker::DiscoServiceWalker(const JID& service, IQRouter* iqRouter, size_t maxSteps) : service_(service), iqRouter_(iqRouter), maxSteps_(maxSteps), active_(false) { } void DiscoServiceWalker::beginWalk() { SWIFT_LOG(debug) << "Starting walk to " << service_ << std::endl; assert(!active_); assert(servicesBeingSearched_.empty()); active_ = true; walkNode(service_); } void DiscoServiceWalker::endWalk() { if (active_) { SWIFT_LOG(debug) << "Ending walk to " << service_ << std::endl; foreach (GetDiscoInfoRequest::ref request, pendingDiscoInfoRequests_) { request->onResponse.disconnect(boost::bind(&DiscoServiceWalker::handleDiscoInfoResponse, this, _1, _2, request)); } foreach (GetDiscoItemsRequest::ref request, pendingDiscoItemsRequests_) { request->onResponse.disconnect(boost::bind(&DiscoServiceWalker::handleDiscoItemsResponse, this, _1, _2, request)); } active_ = false; } } void DiscoServiceWalker::walkNode(const JID& jid) { SWIFT_LOG(debug) << "Walking node " << jid << std::endl; servicesBeingSearched_.insert(jid); searchedServices_.insert(jid); GetDiscoInfoRequest::ref discoInfoRequest = GetDiscoInfoRequest::create(jid, iqRouter_); discoInfoRequest->onResponse.connect(boost::bind(&DiscoServiceWalker::handleDiscoInfoResponse, this, _1, _2, discoInfoRequest)); pendingDiscoInfoRequests_.insert(discoInfoRequest); discoInfoRequest->send(); } void DiscoServiceWalker::handleDiscoInfoResponse(boost::shared_ptr info, ErrorPayload::ref error, GetDiscoInfoRequest::ref request) { /* If we got canceled, don't do anything */ if (!active_) { return; } SWIFT_LOG(debug) << "Disco info response from " << request->getReceiver() << std::endl; pendingDiscoInfoRequests_.erase(request); if (error) { handleDiscoError(request->getReceiver(), error); return; } bool couldContainServices = false; foreach (DiscoInfo::Identity identity, info->getIdentities()) { if (identity.getCategory() == "server") { couldContainServices = true; } } bool completed = false; if (couldContainServices) { GetDiscoItemsRequest::ref discoItemsRequest = GetDiscoItemsRequest::create(request->getReceiver(), iqRouter_); discoItemsRequest->onResponse.connect(boost::bind(&DiscoServiceWalker::handleDiscoItemsResponse, this, _1, _2, discoItemsRequest)); pendingDiscoItemsRequests_.insert(discoItemsRequest); discoItemsRequest->send(); } else { completed = true; } onServiceFound(request->getReceiver(), info); if (completed) { markNodeCompleted(request->getReceiver()); } } void DiscoServiceWalker::handleDiscoItemsResponse(boost::shared_ptr items, ErrorPayload::ref error, GetDiscoItemsRequest::ref request) { /* If we got canceled, don't do anything */ if (!active_) { return; } SWIFT_LOG(debug) << "Received disco items from " << request->getReceiver() << std::endl; pendingDiscoItemsRequests_.erase(request); if (error) { handleDiscoError(request->getReceiver(), error); return; } foreach (DiscoItems::Item item, items->getItems()) { if (item.getNode().empty()) { /* Don't look at noded items. It's possible that this will exclude some services, * but I've never seen one in the wild, and it's an easy fix for not looping. */ if (std::find(searchedServices_.begin(), searchedServices_.end(), item.getJID()) == searchedServices_.end()) { /* Don't recurse infinitely */ SWIFT_LOG(debug) << "Received disco item " << item.getJID() << std::endl; walkNode(item.getJID()); } } } markNodeCompleted(request->getReceiver()); } void DiscoServiceWalker::handleDiscoError(const JID& jid, ErrorPayload::ref /*error*/) { SWIFT_LOG(debug) << "Disco error from " << jid << std::endl; markNodeCompleted(jid); } void DiscoServiceWalker::markNodeCompleted(const JID& jid) { SWIFT_LOG(debug) << "Node completed " << jid << std::endl; servicesBeingSearched_.erase(jid); /* All results are in */ if (servicesBeingSearched_.empty()) { active_ = false; onWalkComplete(); } /* Check if we're on a rampage */ else if (searchedServices_.size() >= maxSteps_) { active_ = false; onWalkComplete(); } } } swift-im-2.0+dev6/Swiften/Disco/DummyEntityCapsProvider.h0000644000175000017500000000076412227051774023335 0ustar kismithkismith/* * 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 #include #include namespace Swift { class SWIFTEN_API DummyEntityCapsProvider : public EntityCapsProvider { public: DummyEntityCapsProvider() { } DiscoInfo::ref getCaps(const JID& jid) const; std::map caps; }; } swift-im-2.0+dev6/Swiften/Disco/EntityCapsProvider.cpp0000644000175000017500000000042312227051774022644 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { EntityCapsProvider::~EntityCapsProvider() { } } swift-im-2.0+dev6/Swiften/Disco/DiscoInfoResponder.cpp0000644000175000017500000000245212227051774022611 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include namespace Swift { DiscoInfoResponder::DiscoInfoResponder(IQRouter* router) : GetResponder(router) { } void DiscoInfoResponder::clearDiscoInfo() { info_ = DiscoInfo(); nodeInfo_.clear(); } void DiscoInfoResponder::setDiscoInfo(const DiscoInfo& info) { info_ = info; } void DiscoInfoResponder::setDiscoInfo(const std::string& node, const DiscoInfo& info) { DiscoInfo newInfo(info); newInfo.setNode(node); nodeInfo_[node] = newInfo; } bool DiscoInfoResponder::handleGetRequest(const JID& from, const JID&, const std::string& id, boost::shared_ptr info) { if (info->getNode().empty()) { sendResponse(from, id, boost::make_shared(info_)); } else { std::map::const_iterator i = nodeInfo_.find(info->getNode()); if (i != nodeInfo_.end()) { sendResponse(from, id, boost::make_shared((*i).second)); } else { sendError(from, id, ErrorPayload::ItemNotFound, ErrorPayload::Cancel); } } return true; } } swift-im-2.0+dev6/Swiften/Disco/CapsMemoryStorage.h0000644000175000017500000000146512227051774022126 0ustar kismithkismith/* * 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 #include #include #include namespace Swift { class CapsMemoryStorage : public CapsStorage { public: CapsMemoryStorage() {} virtual DiscoInfo::ref getDiscoInfo(const std::string& hash) const { CapsMap::const_iterator i = caps.find(hash); if (i != caps.end()) { return i->second; } else { return DiscoInfo::ref(); } } virtual void setDiscoInfo(const std::string& hash, DiscoInfo::ref discoInfo) { caps[hash] = discoInfo; } private: typedef std::map CapsMap; CapsMap caps; }; } swift-im-2.0+dev6/Swiften/Disco/CapsManager.h0000644000175000017500000000271412227051774020701 0ustar kismithkismith/* * 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 #include #include #include #include #include #include #include #include namespace Swift { class StanzaChannel; class IQRouter; class JID; class CapsStorage; class SWIFTEN_API CapsManager : public CapsProvider, public boost::bsignals::trackable { public: CapsManager(CapsStorage*, StanzaChannel*, IQRouter*); DiscoInfo::ref getCaps(const std::string&) const; // Mainly for testing purposes void setWarnOnInvalidHash(bool b) { warnOnInvalidHash = b; } private: void handlePresenceReceived(boost::shared_ptr); void handleStanzaChannelAvailableChanged(bool); void handleDiscoInfoReceived(const JID&, const std::string& hash, DiscoInfo::ref, ErrorPayload::ref); void requestDiscoInfo(const JID& jid, const std::string& node, const std::string& hash); private: IQRouter* iqRouter; CapsStorage* capsStorage; bool warnOnInvalidHash; std::set requestedDiscoInfos; std::set< std::pair > failingCaps; std::map > > fallbacks; }; } swift-im-2.0+dev6/Swiften/Disco/UnitTest/0000755000175000017500000000000012227051774020122 5ustar kismithkismithswift-im-2.0+dev6/Swiften/Disco/UnitTest/JIDDiscoInfoResponderTest.cpp0000644000175000017500000000741312227051774025561 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include using namespace Swift; class JIDDiscoInfoResponderTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(JIDDiscoInfoResponderTest); CPPUNIT_TEST(testHandleRequest_GetToplevelInfo); CPPUNIT_TEST(testHandleRequest_GetNodeInfo); CPPUNIT_TEST(testHandleRequest_GetInvalidNodeInfo); CPPUNIT_TEST(testHandleRequest_GetUnknownJID); CPPUNIT_TEST_SUITE_END(); public: void setUp() { channel_ = new DummyIQChannel(); router_ = new IQRouter(channel_); } void tearDown() { delete router_; delete channel_; } void testHandleRequest_GetToplevelInfo() { JIDDiscoInfoResponder testling(router_); testling.start(); DiscoInfo discoInfo; discoInfo.addFeature("foo"); testling.setDiscoInfo(JID("foo@bar.com/baz"), discoInfo); boost::shared_ptr query(new DiscoInfo()); channel_->onIQReceived(IQ::createRequest(IQ::Get, JID("foo@bar.com/baz"), "id-1", query)); CPPUNIT_ASSERT_EQUAL(1, static_cast(channel_->iqs_.size())); boost::shared_ptr payload(channel_->iqs_[0]->getPayload()); CPPUNIT_ASSERT(payload); CPPUNIT_ASSERT_EQUAL(std::string(""), payload->getNode()); CPPUNIT_ASSERT(payload->hasFeature("foo")); testling.stop(); } void testHandleRequest_GetNodeInfo() { JIDDiscoInfoResponder testling(router_); testling.start(); DiscoInfo discoInfo; discoInfo.addFeature("foo"); testling.setDiscoInfo(JID("foo@bar.com/baz"), discoInfo); DiscoInfo discoInfoBar; discoInfoBar.addFeature("bar"); testling.setDiscoInfo(JID("foo@bar.com/baz"), "bar-node", discoInfoBar); boost::shared_ptr query(new DiscoInfo()); query->setNode("bar-node"); channel_->onIQReceived(IQ::createRequest(IQ::Get, JID("foo@bar.com/baz"), "id-1", query)); CPPUNIT_ASSERT_EQUAL(1, static_cast(channel_->iqs_.size())); boost::shared_ptr payload(channel_->iqs_[0]->getPayload()); CPPUNIT_ASSERT(payload); CPPUNIT_ASSERT_EQUAL(std::string("bar-node"), payload->getNode()); CPPUNIT_ASSERT(payload->hasFeature("bar")); testling.stop(); } void testHandleRequest_GetInvalidNodeInfo() { JIDDiscoInfoResponder testling(router_); DiscoInfo discoInfo; discoInfo.addFeature("foo"); testling.setDiscoInfo(JID("foo@bar.com/baz"), discoInfo); testling.start(); boost::shared_ptr query(new DiscoInfo()); query->setNode("bar-node"); channel_->onIQReceived(IQ::createRequest(IQ::Get, JID("foo@bar.com/baz"), "id-1", query)); CPPUNIT_ASSERT_EQUAL(1, static_cast(channel_->iqs_.size())); boost::shared_ptr payload(channel_->iqs_[0]->getPayload()); CPPUNIT_ASSERT(payload); testling.stop(); } void testHandleRequest_GetUnknownJID() { JIDDiscoInfoResponder testling(router_); DiscoInfo discoInfo; discoInfo.addFeature("foo"); testling.setDiscoInfo(JID("foo@bar.com/baz"), discoInfo); testling.start(); boost::shared_ptr query(new DiscoInfo()); channel_->onIQReceived(IQ::createRequest(IQ::Get, JID("foo@bar.com/fum"), "id-1", query)); CPPUNIT_ASSERT_EQUAL(1, static_cast(channel_->iqs_.size())); boost::shared_ptr payload(channel_->iqs_[0]->getPayload()); CPPUNIT_ASSERT(payload); testling.stop(); } private: IQRouter* router_; DummyIQChannel* channel_; }; CPPUNIT_TEST_SUITE_REGISTRATION(JIDDiscoInfoResponderTest); swift-im-2.0+dev6/Swiften/Disco/UnitTest/EntityCapsManagerTest.cpp0000644000175000017500000001512312227051774025046 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include using namespace Swift; class EntityCapsManagerTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(EntityCapsManagerTest); CPPUNIT_TEST(testReceiveKnownHash); CPPUNIT_TEST(testReceiveKnownHashTwiceDoesNotTriggerChange); CPPUNIT_TEST(testReceiveUnknownHashDoesNotTriggerChange); CPPUNIT_TEST(testReceiveUnknownHashAfterKnownHashTriggersChangeAndClearsCaps); CPPUNIT_TEST(testReceiveUnavailablePresenceAfterKnownHashTriggersChangeAndClearsCaps); CPPUNIT_TEST(testReconnectTriggersChangeAndClearsCaps); CPPUNIT_TEST(testHashAvailable); CPPUNIT_TEST_SUITE_END(); public: void setUp() { stanzaChannel = new DummyStanzaChannel(); capsProvider = new DummyCapsProvider(); user1 = JID("user1@bar.com/bla"); discoInfo1 = boost::make_shared(); discoInfo1->addFeature("http://swift.im/feature1"); capsInfo1 = boost::make_shared(CapsInfoGenerator("http://node1.im").generateCapsInfo(*discoInfo1.get())); capsInfo1alt = boost::make_shared(CapsInfoGenerator("http://node2.im").generateCapsInfo(*discoInfo1.get())); user2 = JID("user2@foo.com/baz"); discoInfo2 = boost::make_shared(); discoInfo2->addFeature("http://swift.im/feature2"); capsInfo2 = boost::make_shared(CapsInfoGenerator("http://node2.im").generateCapsInfo(*discoInfo2.get())); user3 = JID("user3@foo.com/baz"); legacyCapsInfo = boost::make_shared("http://swift.im", "ver1", ""); } void tearDown() { delete capsProvider; delete stanzaChannel; } void testReceiveKnownHash() { boost::shared_ptr testling = createManager(); capsProvider->caps[capsInfo1->getVersion()] = discoInfo1; sendPresenceWithCaps(user1, capsInfo1); CPPUNIT_ASSERT_EQUAL(1, static_cast(changes.size())); CPPUNIT_ASSERT_EQUAL(user1, changes[0]); CPPUNIT_ASSERT_EQUAL(discoInfo1, testling->getCaps(user1)); } void testReceiveKnownHashTwiceDoesNotTriggerChange() { boost::shared_ptr testling = createManager(); capsProvider->caps[capsInfo1->getVersion()] = discoInfo1; sendPresenceWithCaps(user1, capsInfo1); changes.clear(); sendPresenceWithCaps(user1, capsInfo1); CPPUNIT_ASSERT_EQUAL(0, static_cast(changes.size())); } void testReceiveUnknownHashDoesNotTriggerChange() { boost::shared_ptr testling = createManager(); sendPresenceWithCaps(user1, capsInfo1); CPPUNIT_ASSERT_EQUAL(0, static_cast(changes.size())); } void testHashAvailable() { boost::shared_ptr testling = createManager(); sendPresenceWithCaps(user1, capsInfo1); capsProvider->caps[capsInfo1->getVersion()] = discoInfo1; capsProvider->onCapsAvailable(capsInfo1->getVersion()); CPPUNIT_ASSERT_EQUAL(1, static_cast(changes.size())); CPPUNIT_ASSERT_EQUAL(user1, changes[0]); CPPUNIT_ASSERT_EQUAL(discoInfo1, testling->getCaps(user1)); } void testReceiveUnknownHashAfterKnownHashTriggersChangeAndClearsCaps() { boost::shared_ptr testling = createManager(); capsProvider->caps[capsInfo1->getVersion()] = discoInfo1; sendPresenceWithCaps(user1, capsInfo1); changes.clear(); sendPresenceWithCaps(user1, capsInfo2); CPPUNIT_ASSERT_EQUAL(1, static_cast(changes.size())); CPPUNIT_ASSERT_EQUAL(user1, changes[0]); CPPUNIT_ASSERT(!testling->getCaps(user1)); } void testReceiveUnavailablePresenceAfterKnownHashTriggersChangeAndClearsCaps() { boost::shared_ptr testling = createManager(); capsProvider->caps[capsInfo1->getVersion()] = discoInfo1; sendPresenceWithCaps(user1, capsInfo1); changes.clear(); sendUnavailablePresence(user1); CPPUNIT_ASSERT_EQUAL(1, static_cast(changes.size())); CPPUNIT_ASSERT_EQUAL(user1, changes[0]); CPPUNIT_ASSERT(!testling->getCaps(user1)); } void testReconnectTriggersChangeAndClearsCaps() { boost::shared_ptr testling = createManager(); capsProvider->caps[capsInfo1->getVersion()] = discoInfo1; capsProvider->caps[capsInfo2->getVersion()] = discoInfo2; sendPresenceWithCaps(user1, capsInfo1); sendPresenceWithCaps(user2, capsInfo2); changes.clear(); stanzaChannel->setAvailable(false); stanzaChannel->setAvailable(true); CPPUNIT_ASSERT_EQUAL(2, static_cast(changes.size())); CPPUNIT_ASSERT_EQUAL(user1, changes[0]); CPPUNIT_ASSERT(!testling->getCaps(user1)); CPPUNIT_ASSERT_EQUAL(user2, changes[1]); CPPUNIT_ASSERT(!testling->getCaps(user2)); } private: boost::shared_ptr createManager() { boost::shared_ptr manager(new EntityCapsManager(capsProvider, stanzaChannel)); manager->onCapsChanged.connect(boost::bind(&EntityCapsManagerTest::handleCapsChanged, this, _1)); return manager; } void handleCapsChanged(const JID& jid) { changes.push_back(jid); } void sendPresenceWithCaps(const JID& jid, boost::shared_ptr caps) { boost::shared_ptr presence(new Presence()); presence->setFrom(jid); presence->addPayload(caps); stanzaChannel->onPresenceReceived(presence); } void sendUnavailablePresence(const JID& jid) { boost::shared_ptr presence(new Presence()); presence->setFrom(jid); presence->setType(Presence::Unavailable); stanzaChannel->onPresenceReceived(presence); } private: struct DummyCapsProvider : public CapsProvider { virtual DiscoInfo::ref getCaps(const std::string& hash) const { std::map::const_iterator i = caps.find(hash); if (i != caps.end()) { return i->second; } return DiscoInfo::ref(); } std::map caps; }; private: DummyStanzaChannel* stanzaChannel; DummyCapsProvider* capsProvider; JID user1; boost::shared_ptr discoInfo1; boost::shared_ptr capsInfo1; boost::shared_ptr capsInfo1alt; JID user2; boost::shared_ptr discoInfo2; boost::shared_ptr capsInfo2; boost::shared_ptr legacyCapsInfo; JID user3; std::vector changes; }; CPPUNIT_TEST_SUITE_REGISTRATION(EntityCapsManagerTest); swift-im-2.0+dev6/Swiften/Disco/UnitTest/CapsInfoGeneratorTest.cpp0000644000175000017500000000625112227051774025043 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include using namespace Swift; class CapsInfoGeneratorTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(CapsInfoGeneratorTest); CPPUNIT_TEST(testGenerate_XEP0115SimpleExample); CPPUNIT_TEST(testGenerate_XEP0115ComplexExample); CPPUNIT_TEST_SUITE_END(); public: void testGenerate_XEP0115SimpleExample() { DiscoInfo discoInfo; discoInfo.addIdentity(DiscoInfo::Identity("Exodus 0.9.1", "client", "pc")); discoInfo.addFeature("http://jabber.org/protocol/disco#items"); discoInfo.addFeature("http://jabber.org/protocol/caps"); discoInfo.addFeature("http://jabber.org/protocol/disco#info"); discoInfo.addFeature("http://jabber.org/protocol/muc"); CapsInfoGenerator testling("http://code.google.com/p/exodus"); CapsInfo result = testling.generateCapsInfo(discoInfo); CPPUNIT_ASSERT_EQUAL(std::string("http://code.google.com/p/exodus"), result.getNode()); CPPUNIT_ASSERT_EQUAL(std::string("sha-1"), result.getHash()); CPPUNIT_ASSERT_EQUAL(std::string("QgayPKawpkPSDYmwT/WM94uAlu0="), result.getVersion()); } void testGenerate_XEP0115ComplexExample() { DiscoInfo discoInfo; discoInfo.addIdentity(DiscoInfo::Identity("Psi 0.11", "client", "pc", "en")); discoInfo.addIdentity(DiscoInfo::Identity("\xce\xa8 0.11", "client", "pc", "el")); discoInfo.addFeature("http://jabber.org/protocol/disco#items"); discoInfo.addFeature("http://jabber.org/protocol/caps"); discoInfo.addFeature("http://jabber.org/protocol/disco#info"); discoInfo.addFeature("http://jabber.org/protocol/muc"); Form::ref extension(new Form(Form::ResultType)); FormField::ref field = HiddenFormField::create("urn:xmpp:dataforms:softwareinfo"); field->setName("FORM_TYPE"); extension->addField(field); std::vector ipVersions; ipVersions.push_back("ipv6"); ipVersions.push_back("ipv4"); field = ListMultiFormField::create(ipVersions); field->addRawValue("ipv6"); field->addRawValue("ipv4"); field->setName("ip_version"); extension->addField(field); field = TextSingleFormField::create("Psi"); field->addRawValue("Psi"); field->setName("software"); extension->addField(field); field = TextSingleFormField::create("0.11"); field->addRawValue("0.11"); field->setName("software_version"); extension->addField(field); field = TextSingleFormField::create("Mac"); field->setName("os"); field->addRawValue("Mac"); extension->addField(field); field = TextSingleFormField::create("10.5.1"); field->setName("os_version"); field->addRawValue("10.5.1"); extension->addField(field); discoInfo.addExtension(extension); CapsInfoGenerator testling("http://psi-im.org"); CapsInfo result = testling.generateCapsInfo(discoInfo); CPPUNIT_ASSERT_EQUAL(std::string("q07IKJEyjvHSyhy//CH0CxmKi8w="), result.getVersion()); } }; CPPUNIT_TEST_SUITE_REGISTRATION(CapsInfoGeneratorTest); swift-im-2.0+dev6/Swiften/Disco/UnitTest/DiscoInfoResponderTest.cpp0000644000175000017500000000565612227051774025241 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include using namespace Swift; class DiscoInfoResponderTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(DiscoInfoResponderTest); CPPUNIT_TEST(testHandleRequest_GetToplevelInfo); CPPUNIT_TEST(testHandleRequest_GetNodeInfo); CPPUNIT_TEST(testHandleRequest_GetInvalidNodeInfo); CPPUNIT_TEST_SUITE_END(); public: void setUp() { channel_ = new DummyIQChannel(); router_ = new IQRouter(channel_); } void tearDown() { delete router_; delete channel_; } void testHandleRequest_GetToplevelInfo() { DiscoInfoResponder testling(router_); testling.start(); DiscoInfo discoInfo; discoInfo.addFeature("foo"); testling.setDiscoInfo(discoInfo); boost::shared_ptr query(new DiscoInfo()); channel_->onIQReceived(IQ::createRequest(IQ::Get, JID("foo@bar.com"), "id-1", query)); CPPUNIT_ASSERT_EQUAL(1, static_cast(channel_->iqs_.size())); boost::shared_ptr payload(channel_->iqs_[0]->getPayload()); CPPUNIT_ASSERT(payload); CPPUNIT_ASSERT_EQUAL(std::string(""), payload->getNode()); CPPUNIT_ASSERT(payload->hasFeature("foo")); testling.stop(); } void testHandleRequest_GetNodeInfo() { DiscoInfoResponder testling(router_); testling.start(); DiscoInfo discoInfo; discoInfo.addFeature("foo"); testling.setDiscoInfo(discoInfo); DiscoInfo discoInfoBar; discoInfoBar.addFeature("bar"); testling.setDiscoInfo("bar-node", discoInfoBar); boost::shared_ptr query(new DiscoInfo()); query->setNode("bar-node"); channel_->onIQReceived(IQ::createRequest(IQ::Get, JID("foo@bar.com"), "id-1", query)); CPPUNIT_ASSERT_EQUAL(1, static_cast(channel_->iqs_.size())); boost::shared_ptr payload(channel_->iqs_[0]->getPayload()); CPPUNIT_ASSERT(payload); CPPUNIT_ASSERT_EQUAL(std::string("bar-node"), payload->getNode()); CPPUNIT_ASSERT(payload->hasFeature("bar")); testling.stop(); } void testHandleRequest_GetInvalidNodeInfo() { DiscoInfoResponder testling(router_); boost::shared_ptr query(new DiscoInfo()); query->setNode("bar-node"); channel_->onIQReceived(IQ::createRequest(IQ::Get, JID("foo@bar.com"), "id-1", query)); testling.start(); CPPUNIT_ASSERT_EQUAL(1, static_cast(channel_->iqs_.size())); boost::shared_ptr payload(channel_->iqs_[0]->getPayload()); CPPUNIT_ASSERT(payload); testling.stop(); } private: IQRouter* router_; DummyIQChannel* channel_; }; CPPUNIT_TEST_SUITE_REGISTRATION(DiscoInfoResponderTest); swift-im-2.0+dev6/Swiften/Disco/UnitTest/CapsManagerTest.cpp0000644000175000017500000002742312227051774023657 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include #include using namespace Swift; class CapsManagerTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(CapsManagerTest); CPPUNIT_TEST(testReceiveNewHashRequestsDisco); CPPUNIT_TEST(testReceiveSameHashDoesNotRequestDisco); CPPUNIT_TEST(testReceiveLegacyCapsDoesNotRequestDisco); CPPUNIT_TEST(testReceiveSameHashFromSameUserAfterFailedDiscoDoesNotRequestDisco); CPPUNIT_TEST(testReceiveSameHashFromDifferentUserAfterFailedDiscoRequestsDisco); CPPUNIT_TEST(testReceiveSameHashFromDifferentUserAfterIncorrectVerificationRequestsDisco); CPPUNIT_TEST(testReceiveDifferentHashFromSameUserAfterFailedDiscoDoesNotRequestDisco); CPPUNIT_TEST(testReceiveSameHashAfterSuccesfulDiscoDoesNotRequestDisco); CPPUNIT_TEST(testReceiveSuccesfulDiscoStoresCaps); CPPUNIT_TEST(testReceiveIncorrectVerificationDiscoDoesNotStoreCaps); CPPUNIT_TEST(testReceiveFailingDiscoFallsBack); CPPUNIT_TEST(testReceiveNoDiscoFallsBack); CPPUNIT_TEST(testReceiveFailingFallbackDiscoFallsBack); CPPUNIT_TEST(testReceiveSameHashFromFailingUserAfterReconnectRequestsDisco); CPPUNIT_TEST(testReconnectResetsFallback); CPPUNIT_TEST(testReconnectResetsRequests); CPPUNIT_TEST_SUITE_END(); public: void setUp() { stanzaChannel = new DummyStanzaChannel(); iqRouter = new IQRouter(stanzaChannel); storage = new CapsMemoryStorage(); user1 = JID("user1@bar.com/bla"); discoInfo1 = boost::make_shared(); discoInfo1->addFeature("http://swift.im/feature1"); capsInfo1 = boost::make_shared(CapsInfoGenerator("http://node1.im").generateCapsInfo(*discoInfo1.get())); capsInfo1alt = boost::make_shared(CapsInfoGenerator("http://node2.im").generateCapsInfo(*discoInfo1.get())); user2 = JID("user2@foo.com/baz"); discoInfo2 = boost::make_shared(); discoInfo2->addFeature("http://swift.im/feature2"); capsInfo2 = boost::make_shared(CapsInfoGenerator("http://node2.im").generateCapsInfo(*discoInfo2.get())); user3 = JID("user3@foo.com/baz"); legacyCapsInfo = boost::make_shared("http://swift.im", "ver1", ""); } void tearDown() { delete storage; delete iqRouter; delete stanzaChannel; } void testReceiveNewHashRequestsDisco() { boost::shared_ptr testling = createManager(); sendPresenceWithCaps(user1, capsInfo1); CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex(0, user1, IQ::Get)); boost::shared_ptr discoInfo(stanzaChannel->sentStanzas[0]->getPayload()); CPPUNIT_ASSERT(discoInfo); CPPUNIT_ASSERT_EQUAL("http://node1.im#" + capsInfo1->getVersion(), discoInfo->getNode()); } void testReceiveSameHashDoesNotRequestDisco() { boost::shared_ptr testling = createManager(); sendPresenceWithCaps(user1, capsInfo1); stanzaChannel->sentStanzas.clear(); sendPresenceWithCaps(user1, capsInfo1); CPPUNIT_ASSERT_EQUAL(0, static_cast(stanzaChannel->sentStanzas.size())); } void testReceiveLegacyCapsDoesNotRequestDisco() { boost::shared_ptr testling = createManager(); sendPresenceWithCaps(user1, legacyCapsInfo); CPPUNIT_ASSERT_EQUAL(0, static_cast(stanzaChannel->sentStanzas.size())); } void testReceiveSameHashAfterSuccesfulDiscoDoesNotRequestDisco() { boost::shared_ptr testling = createManager(); sendPresenceWithCaps(user1, capsInfo1); sendDiscoInfoResult(discoInfo1); stanzaChannel->sentStanzas.clear(); sendPresenceWithCaps(user1, capsInfo1); CPPUNIT_ASSERT_EQUAL(0, static_cast(stanzaChannel->sentStanzas.size())); } void testReceiveSameHashFromSameUserAfterFailedDiscoDoesNotRequestDisco() { boost::shared_ptr testling = createManager(); sendPresenceWithCaps(user1, capsInfo1); stanzaChannel->onIQReceived(IQ::createError(JID("baz@fum.com/foo"), stanzaChannel->sentStanzas[0]->getID())); stanzaChannel->sentStanzas.clear(); sendPresenceWithCaps(user1, capsInfo1); CPPUNIT_ASSERT_EQUAL(0, static_cast(stanzaChannel->sentStanzas.size())); } void testReceiveSameHashFromSameUserAfterIncorrectVerificationDoesNotRequestDisco() { boost::shared_ptr testling = createManager(); sendPresenceWithCaps(user1, capsInfo1); sendDiscoInfoResult(discoInfo2); stanzaChannel->sentStanzas.clear(); sendPresenceWithCaps(user1, capsInfo1); CPPUNIT_ASSERT_EQUAL(0, static_cast(stanzaChannel->sentStanzas.size())); } void testReceiveSameHashFromDifferentUserAfterFailedDiscoRequestsDisco() { boost::shared_ptr testling = createManager(); sendPresenceWithCaps(user1, capsInfo1); stanzaChannel->onIQReceived(IQ::createError(JID("baz@fum.com/foo"), stanzaChannel->sentStanzas[0]->getTo(), stanzaChannel->sentStanzas[0]->getID())); stanzaChannel->sentStanzas.clear(); sendPresenceWithCaps(user2, capsInfo1); CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex(0, user2, IQ::Get)); } void testReceiveSameHashFromDifferentUserAfterIncorrectVerificationRequestsDisco() { boost::shared_ptr testling = createManager(); sendPresenceWithCaps(user1, capsInfo1); sendDiscoInfoResult(discoInfo2); stanzaChannel->sentStanzas.clear(); sendPresenceWithCaps(user2, capsInfo1); CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex(0, user2, IQ::Get)); } void testReceiveDifferentHashFromSameUserAfterFailedDiscoDoesNotRequestDisco() { boost::shared_ptr testling = createManager(); sendPresenceWithCaps(user1, capsInfo1); stanzaChannel->onIQReceived(IQ::createError(JID("baz@fum.com/foo"), stanzaChannel->sentStanzas[0]->getID())); stanzaChannel->sentStanzas.clear(); sendPresenceWithCaps(user1, capsInfo2); CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex(0, user1, IQ::Get)); } void testReceiveSuccesfulDiscoStoresCaps() { boost::shared_ptr testling = createManager(); sendPresenceWithCaps(user1, capsInfo1); sendDiscoInfoResult(discoInfo1); boost::shared_ptr discoInfo(storage->getDiscoInfo(capsInfo1->getVersion())); CPPUNIT_ASSERT(discoInfo); CPPUNIT_ASSERT(discoInfo->hasFeature("http://swift.im/feature1")); } void testReceiveIncorrectVerificationDiscoDoesNotStoreCaps() { boost::shared_ptr testling = createManager(); sendPresenceWithCaps(user1, capsInfo1); sendDiscoInfoResult(discoInfo2); boost::shared_ptr discoInfo(storage->getDiscoInfo(capsInfo1->getVersion())); CPPUNIT_ASSERT(!discoInfo); } void testReceiveFailingDiscoFallsBack() { boost::shared_ptr testling = createManager(); sendPresenceWithCaps(user1, capsInfo1); sendPresenceWithCaps(user2, capsInfo1alt); stanzaChannel->onIQReceived(IQ::createError(JID("baz@fum.com/foo"), stanzaChannel->sentStanzas[0]->getTo(), stanzaChannel->sentStanzas[0]->getID())); CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex(1, user2, IQ::Get)); boost::shared_ptr discoInfo(stanzaChannel->sentStanzas[1]->getPayload()); CPPUNIT_ASSERT(discoInfo); CPPUNIT_ASSERT_EQUAL("http://node2.im#" + capsInfo1alt->getVersion(), discoInfo->getNode()); } void testReceiveNoDiscoFallsBack() { boost::shared_ptr testling = createManager(); sendPresenceWithCaps(user1, capsInfo1); sendPresenceWithCaps(user2, capsInfo1alt); stanzaChannel->onIQReceived(IQ::createResult(JID("baz@fum.com/dum"), stanzaChannel->sentStanzas[0]->getTo(), stanzaChannel->sentStanzas[0]->getID(), boost::shared_ptr())); CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex(1, user2, IQ::Get)); boost::shared_ptr discoInfo(stanzaChannel->sentStanzas[1]->getPayload()); CPPUNIT_ASSERT(discoInfo); CPPUNIT_ASSERT_EQUAL("http://node2.im#" + capsInfo1alt->getVersion(), discoInfo->getNode()); } void testReceiveFailingFallbackDiscoFallsBack() { boost::shared_ptr testling = createManager(); sendPresenceWithCaps(user1, capsInfo1); sendPresenceWithCaps(user2, capsInfo1alt); sendPresenceWithCaps(user3, capsInfo1); stanzaChannel->onIQReceived(IQ::createError(JID("baz@fum.com/foo"), stanzaChannel->sentStanzas[0]->getTo(), stanzaChannel->sentStanzas[0]->getID())); stanzaChannel->onIQReceived(IQ::createError(JID("baz@fum.com/foo"), stanzaChannel->sentStanzas[1]->getTo(), stanzaChannel->sentStanzas[1]->getID())); CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex(2, user3, IQ::Get)); } void testReceiveSameHashFromFailingUserAfterReconnectRequestsDisco() { boost::shared_ptr testling = createManager(); sendPresenceWithCaps(user1, capsInfo1); stanzaChannel->onIQReceived(IQ::createError(JID("baz@fum.com/foo"), stanzaChannel->sentStanzas[0]->getTo(), stanzaChannel->sentStanzas[0]->getID())); stanzaChannel->setAvailable(false); stanzaChannel->setAvailable(true); stanzaChannel->sentStanzas.clear(); sendPresenceWithCaps(user1, capsInfo1); CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex(0, user1, IQ::Get)); } void testReconnectResetsFallback() { boost::shared_ptr testling = createManager(); sendPresenceWithCaps(user1, capsInfo1); sendPresenceWithCaps(user2, capsInfo1alt); stanzaChannel->setAvailable(false); stanzaChannel->setAvailable(true); stanzaChannel->sentStanzas.clear(); sendPresenceWithCaps(user1, capsInfo1); stanzaChannel->onIQReceived(IQ::createError(JID("baz@fum.com/foo"), stanzaChannel->sentStanzas[0]->getTo(), stanzaChannel->sentStanzas[0]->getID())); CPPUNIT_ASSERT_EQUAL(1, static_cast(stanzaChannel->sentStanzas.size())); } void testReconnectResetsRequests() { boost::shared_ptr testling = createManager(); sendPresenceWithCaps(user1, capsInfo1); stanzaChannel->sentStanzas.clear(); stanzaChannel->setAvailable(false); stanzaChannel->setAvailable(true); sendPresenceWithCaps(user1, capsInfo1); CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex(0, user1, IQ::Get)); } private: boost::shared_ptr createManager() { boost::shared_ptr manager(new CapsManager(storage, stanzaChannel, iqRouter)); manager->setWarnOnInvalidHash(false); //manager->onCapsChanged.connect(boost::bind(&CapsManagerTest::handleCapsChanged, this, _1)); return manager; } void handleCapsChanged(const JID& jid) { changes.push_back(jid); } void sendPresenceWithCaps(const JID& jid, boost::shared_ptr caps) { boost::shared_ptr presence(new Presence()); presence->setFrom(jid); presence->addPayload(caps); stanzaChannel->onPresenceReceived(presence); } void sendDiscoInfoResult(boost::shared_ptr discoInfo) { stanzaChannel->onIQReceived(IQ::createResult(JID("baz@fum.com/dum"), stanzaChannel->sentStanzas[0]->getTo(), stanzaChannel->sentStanzas[0]->getID(), discoInfo)); } private: DummyStanzaChannel* stanzaChannel; IQRouter* iqRouter; CapsStorage* storage; std::vector changes; JID user1; boost::shared_ptr discoInfo1; boost::shared_ptr capsInfo1; boost::shared_ptr capsInfo1alt; JID user2; boost::shared_ptr discoInfo2; boost::shared_ptr capsInfo2; boost::shared_ptr legacyCapsInfo; JID user3; }; CPPUNIT_TEST_SUITE_REGISTRATION(CapsManagerTest); swift-im-2.0+dev6/Swiften/Disco/EntityCapsManager.cpp0000644000175000017500000000461612227051774022434 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include namespace Swift { EntityCapsManager::EntityCapsManager(CapsProvider* capsProvider, StanzaChannel* stanzaChannel) : capsProvider(capsProvider) { stanzaChannel->onPresenceReceived.connect(boost::bind(&EntityCapsManager::handlePresenceReceived, this, _1)); stanzaChannel->onAvailableChanged.connect(boost::bind(&EntityCapsManager::handleStanzaChannelAvailableChanged, this, _1)); capsProvider->onCapsAvailable.connect(boost::bind(&EntityCapsManager::handleCapsAvailable, this, _1)); } void EntityCapsManager::handlePresenceReceived(boost::shared_ptr presence) { JID from = presence->getFrom(); if (presence->isAvailable()) { boost::shared_ptr capsInfo = presence->getPayload(); if (!capsInfo || capsInfo->getHash() != "sha-1" || presence->getPayload()) { return; } std::string hash = capsInfo->getVersion(); std::map::iterator i = caps.find(from); if (i == caps.end() || i->second != hash) { caps.insert(std::make_pair(from, hash)); DiscoInfo::ref disco = capsProvider->getCaps(hash); if (disco) { onCapsChanged(from); } else if (i != caps.end()) { caps.erase(i); onCapsChanged(from); } } } else { std::map::iterator i = caps.find(from); if (i != caps.end()) { caps.erase(i); onCapsChanged(from); } } } void EntityCapsManager::handleStanzaChannelAvailableChanged(bool available) { if (available) { std::map capsCopy; capsCopy.swap(caps); for (std::map::const_iterator i = capsCopy.begin(); i != capsCopy.end(); ++i) { onCapsChanged(i->first); } } } void EntityCapsManager::handleCapsAvailable(const std::string& hash) { // TODO: Use Boost.Bimap ? for (std::map::const_iterator i = caps.begin(); i != caps.end(); ++i) { if (i->second == hash) { onCapsChanged(i->first); } } } DiscoInfo::ref EntityCapsManager::getCaps(const JID& jid) const { std::map::const_iterator i = caps.find(jid); if (i != caps.end()) { return capsProvider->getCaps(i->second); } return DiscoInfo::ref(); } } swift-im-2.0+dev6/Swiften/Disco/GetDiscoInfoRequest.h0000644000175000017500000000211712227051774022403 0ustar kismithkismith/* * 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 #include #include namespace Swift { class GetDiscoInfoRequest : public GenericRequest { public: typedef boost::shared_ptr ref; static ref create(const JID& jid, IQRouter* router) { return ref(new GetDiscoInfoRequest(jid, router)); } static ref create(const JID& jid, const std::string& node, IQRouter* router) { return ref(new GetDiscoInfoRequest(jid, node, router)); } private: GetDiscoInfoRequest(const JID& jid, IQRouter* router) : GenericRequest(IQ::Get, jid, boost::make_shared(), router) { } GetDiscoInfoRequest(const JID& jid, const std::string& node, IQRouter* router) : GenericRequest(IQ::Get, jid, boost::make_shared(), router) { getPayloadGeneric()->setNode(node); } }; } swift-im-2.0+dev6/Swiften/Disco/CapsStorage.cpp0000644000175000017500000000037612227051774021270 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { CapsStorage::~CapsStorage() { } } swift-im-2.0+dev6/Swiften/Disco/CapsInfoGenerator.cpp0000644000175000017500000000371512227051774022426 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include namespace { bool compareFields(Swift::FormField::ref f1, Swift::FormField::ref f2) { return f1->getName() < f2->getName(); } } namespace Swift { CapsInfoGenerator::CapsInfoGenerator(const std::string& node) : node_(node) { } CapsInfo CapsInfoGenerator::generateCapsInfo(const DiscoInfo& discoInfo) const { std::string serializedCaps; std::vector identities(discoInfo.getIdentities()); std::sort(identities.begin(), identities.end()); foreach (const DiscoInfo::Identity& identity, identities) { serializedCaps += identity.getCategory() + "/" + identity.getType() + "/" + identity.getLanguage() + "/" + identity.getName() + "<"; } std::vector features(discoInfo.getFeatures()); std::sort(features.begin(), features.end()); foreach (const std::string& feature, features) { serializedCaps += feature + "<"; } foreach(Form::ref extension, discoInfo.getExtensions()) { serializedCaps += extension->getFormType() + "<"; std::vector fields(extension->getFields()); std::sort(fields.begin(), fields.end(), &compareFields); foreach(FormField::ref field, fields) { if (field->getName() == "FORM_TYPE") { continue; } serializedCaps += field->getName() + "<"; std::vector values(field->getRawValues()); std::sort(values.begin(), values.end()); foreach(const std::string& value, values) { serializedCaps += value + "<"; } } } std::string version(Base64::encode(SHA1::getHash(createByteArray(serializedCaps)))); return CapsInfo(node_, version, "sha-1"); } } swift-im-2.0+dev6/Swiften/Disco/DiscoInfoResponder.h0000644000175000017500000000147512227051774022262 0ustar kismithkismith/* * 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 #include #include #include namespace Swift { class IQRouter; class SWIFTEN_API DiscoInfoResponder : public GetResponder { public: DiscoInfoResponder(IQRouter* router); void clearDiscoInfo(); void setDiscoInfo(const DiscoInfo& info); void setDiscoInfo(const std::string& node, const DiscoInfo& info); private: virtual bool handleGetRequest(const JID& from, const JID& to, const std::string& id, boost::shared_ptr payload); private: DiscoInfo info_; std::map nodeInfo_; }; } swift-im-2.0+dev6/Swiften/Disco/JIDDiscoInfoResponder.h0000644000175000017500000000201012227051774022573 0ustar kismithkismith/* * 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 #include #include #include #include namespace Swift { class IQRouter; class SWIFTEN_API JIDDiscoInfoResponder : public GetResponder { public: JIDDiscoInfoResponder(IQRouter* router); void clearDiscoInfo(const JID& jid); void setDiscoInfo(const JID& jid, const DiscoInfo& info); void setDiscoInfo(const JID& jid, const std::string& node, const DiscoInfo& info); private: virtual bool handleGetRequest(const JID& from, const JID& to, const std::string& id, boost::shared_ptr payload); private: struct JIDDiscoInfo { DiscoInfo discoInfo; std::map nodeDiscoInfo; }; typedef std::map JIDDiscoInfoMap; JIDDiscoInfoMap info; }; } swift-im-2.0+dev6/Swiften/Disco/DiscoServiceWalker.h0000644000175000017500000000376312227051774022255 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include #include #include #include #include #include #include #include #include namespace Swift { class IQRouter; /** * Recursively walk service discovery trees to find all services offered. * This stops on any disco item that's not reporting itself as a server. */ class SWIFTEN_API DiscoServiceWalker { public: DiscoServiceWalker(const JID& service, IQRouter* iqRouter, size_t maxSteps = 200); /** * Start the walk. * * Call this exactly once. */ void beginWalk(); /** * End the walk. */ void endWalk(); bool isActive() const { return active_; } /** Emitted for each service found. */ boost::signal)> onServiceFound; /** Emitted when walking is complete.*/ boost::signal onWalkComplete; private: void walkNode(const JID& jid); void markNodeCompleted(const JID& jid); void handleDiscoInfoResponse(boost::shared_ptr info, ErrorPayload::ref error, GetDiscoInfoRequest::ref request); void handleDiscoItemsResponse(boost::shared_ptr items, ErrorPayload::ref error, GetDiscoItemsRequest::ref request); void handleDiscoError(const JID& jid, ErrorPayload::ref error); private: JID service_; IQRouter* iqRouter_; size_t maxSteps_; bool active_; std::set servicesBeingSearched_; std::set searchedServices_; std::set pendingDiscoInfoRequests_; std::set pendingDiscoItemsRequests_; }; } swift-im-2.0+dev6/Swiften/Disco/EntityCapsManager.h0000644000175000017500000000234712227051774022100 0ustar kismithkismith/* * 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 #include #include #include #include #include #include namespace Swift { class StanzaChannel; class CapsProvider; /** * This class is responsible for gathering and providing * information about capabilities of entities on the network. * This information is provided in the form of service discovery * information. */ class SWIFTEN_API EntityCapsManager : public EntityCapsProvider, public boost::bsignals::trackable { public: EntityCapsManager(CapsProvider*, StanzaChannel*); /** * Returns the service discovery information of the given JID. */ DiscoInfo::ref getCaps(const JID&) const; private: void handlePresenceReceived(boost::shared_ptr); void handleStanzaChannelAvailableChanged(bool); void handleCapsAvailable(const std::string&); private: CapsProvider* capsProvider; std::map caps; }; } swift-im-2.0+dev6/Swiften/History/0000755000175000017500000000000012227051774016743 5ustar kismithkismithswift-im-2.0+dev6/Swiften/History/SConscript0000644000175000017500000000053512227051774020760 0ustar kismithkismithImport("swiften_env") myenv = swiften_env.Clone() if myenv["target"] == "native": myenv.MergeFlags(swiften_env.get("SQLITE_FLAGS", {})) myenv.MergeFlags(swiften_env.get("SQLITE_FLAGS_ASYNC", {})) if myenv["experimental"]: objects = myenv.SwiftenObject([ "SQLiteHistoryStorage.cpp", ]) swiften_env.Append(SWIFTEN_OBJECTS = [objects]) swift-im-2.0+dev6/Swiften/History/HistoryMessage.h0000644000175000017500000000257112227051774022067 0ustar kismithkismith/* * 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 #include namespace Swift { class HistoryMessage { public: enum Type { Chat = 0, Groupchat = 1, PrivateMessage = 2 }; HistoryMessage( const std::string& message, const JID& fromJID, const JID& toJID, Type type, const boost::posix_time::ptime& time, int utcOffset = 0) : message_(message), fromJID_(fromJID), toJID_(toJID), type_(type), time_(time), utcOffset_(utcOffset) { } const std::string& getMessage() const { return message_; } const JID& getFromJID() const { return fromJID_; } const JID& getToJID() const { return toJID_; } Type getType() const { return type_; } boost::posix_time::ptime getTime() const { return time_; } int getOffset() const { return utcOffset_; } bool operator==(const HistoryMessage& o) const { return message_ == o.message_ && fromJID_ == o.fromJID_ && toJID_ == o.toJID_ && type_ == o.type_ && time_ == o.time_; } private: std::string message_; JID fromJID_; JID toJID_; Type type_; boost::posix_time::ptime time_; int utcOffset_; }; } swift-im-2.0+dev6/Swiften/History/SQLiteHistoryStorage.cpp0000644000175000017500000003537712227051774023536 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include <3rdParty/SQLiteAsync/sqlite3async.h> #include #include inline std::string getEscapedString(const std::string& s) { std::string result(s); size_t pos = result.find('\''); while (pos != std::string::npos) { result.insert(pos, "'"); pos = result.find('\'', pos + 2); } return result; } namespace Swift { SQLiteHistoryStorage::SQLiteHistoryStorage(const std::string& file) : db_(0) { sqlite3async_initialize(NULL, false); thread_ = new boost::thread(boost::bind(&SQLiteHistoryStorage::run, this)); sqlite3_open_v2(file.c_str(), &db_, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, "sqlite3async"); if (!db_) { std::cerr << "Error opening database " << file << std::endl; } char* errorMessage; int result = sqlite3_exec(db_, "CREATE TABLE IF NOT EXISTS messages('message' STRING, 'fromBare' INTEGER, 'fromResource' STRING, 'toBare' INTEGER, 'toResource' STRING, 'type' INTEGER, 'time' INTEGER, 'offset' INTEGER)", 0, 0, &errorMessage); if (result != SQLITE_OK) { std::cerr << "SQL Error: " << errorMessage << std::endl; sqlite3_free(errorMessage); } result = sqlite3_exec(db_, "CREATE TABLE IF NOT EXISTS jids('id' INTEGER PRIMARY KEY ASC AUTOINCREMENT, 'jid' STRING UNIQUE NOT NULL)", 0, 0, &errorMessage); if (result != SQLITE_OK) { std::cerr << "SQL Error: " << errorMessage << std::endl; sqlite3_free(errorMessage); } } SQLiteHistoryStorage::~SQLiteHistoryStorage() { sqlite3async_shutdown(); sqlite3_close(db_); delete thread_; } void SQLiteHistoryStorage::addMessage(const HistoryMessage& message) { int secondsSinceEpoch = (message.getTime() - boost::posix_time::ptime(boost::gregorian::date(1970, 1, 1))).total_seconds(); std::string statement = std::string("INSERT INTO messages('message', 'fromBare', 'fromResource', 'toBare', 'toResource', 'type', 'time', 'offset') VALUES(") + "'" + getEscapedString(message.getMessage()) + "', " + boost::lexical_cast(getIDForJID(message.getFromJID().toBare())) + ", '" + getEscapedString(message.getFromJID().getResource()) + "', " + boost::lexical_cast(getIDForJID(message.getToJID().toBare())) + ", '" + getEscapedString(message.getToJID().getResource()) + "', " + boost::lexical_cast(message.getType()) + ", " + boost::lexical_cast(secondsSinceEpoch) + ", " + boost::lexical_cast(message.getOffset()) + ")"; char* errorMessage; int result = sqlite3_exec(db_, statement.c_str(), 0, 0, &errorMessage); if (result != SQLITE_OK) { std::cerr << "SQL Error: " << errorMessage << std::endl; sqlite3_free(errorMessage); } } std::vector SQLiteHistoryStorage::getMessagesFromDate(const JID& selfJID, const JID& contactJID, HistoryMessage::Type type, const boost::gregorian::date& date) const { sqlite3_stmt* selectStatement; boost::optional selfID = getIDFromJID(selfJID.toBare()); boost::optional contactID = getIDFromJID(contactJID.toBare()); if (!selfID || !contactID) { // JIDs missing from the database return std::vector(); } std::string selectQuery = "SELECT * FROM messages WHERE (type=" + boost::lexical_cast(type); if (contactJID.isBare()) { // match only bare jid selectQuery += " AND ((fromBare=" + boost::lexical_cast(*selfID) + " AND toBare=" + boost::lexical_cast(*contactID) + ") OR (fromBare=" + boost::lexical_cast(*contactID) + " AND toBare=" + boost::lexical_cast(*selfID) + ")))"; } else { // match resource too selectQuery += " AND ((fromBare=" + boost::lexical_cast(*selfID) + " AND (toBare=" + boost::lexical_cast(*contactID) +" AND toResource='" + getEscapedString(contactJID.getResource()) + "')) OR ((fromBare=" + boost::lexical_cast(*contactID) + " AND fromResource='" + getEscapedString(contactJID.getResource()) + "') AND toBare=" + boost::lexical_cast(*selfID) + ")))"; } if (!date.is_not_a_date()) { int lowerBound = (boost::posix_time::ptime(date) - boost::posix_time::ptime(boost::gregorian::date(1970, 1, 1))).total_seconds(); int upperBound = lowerBound + 86400; selectQuery += " AND (time>=" + boost::lexical_cast(lowerBound) + " AND time<" + boost::lexical_cast(upperBound) + ")"; } int r = sqlite3_prepare(db_, selectQuery.c_str(), selectQuery.size(), &selectStatement, NULL); if (r != SQLITE_OK) { std::cout << "Error: " << sqlite3_errmsg(db_) << std::endl; } r = sqlite3_step(selectStatement); // Retrieve result std::vector result; while (r == SQLITE_ROW) { std::string message(reinterpret_cast(sqlite3_column_text(selectStatement, 0))); // fromJID boost::optional fromJID(getJIDFromID(sqlite3_column_int(selectStatement, 1))); std::string fromResource(reinterpret_cast(sqlite3_column_text(selectStatement, 2))); if (fromJID) { fromJID = boost::optional(JID(fromJID->getNode(), fromJID->getDomain(), fromResource)); } // toJID boost::optional toJID(getJIDFromID(sqlite3_column_int(selectStatement, 3))); std::string toResource(reinterpret_cast(sqlite3_column_text(selectStatement, 4))); if (toJID) { toJID = boost::optional(JID(toJID->getNode(), toJID->getDomain(), toResource)); } // message type HistoryMessage::Type type = static_cast(sqlite3_column_int(selectStatement, 5)); // timestamp int secondsSinceEpoch(sqlite3_column_int(selectStatement, 6)); boost::posix_time::ptime time(boost::gregorian::date(1970, 1, 1), boost::posix_time::seconds(secondsSinceEpoch)); // offset from utc int offset = sqlite3_column_int(selectStatement, 7); result.push_back(HistoryMessage(message, (fromJID ? *fromJID : JID()), (toJID ? *toJID : JID()), type, time, offset)); r = sqlite3_step(selectStatement); } if (r != SQLITE_DONE) { std::cout << "Error: " << sqlite3_errmsg(db_) << std::endl; } sqlite3_finalize(selectStatement); return result; } int SQLiteHistoryStorage::getIDForJID(const JID& jid) { boost::optional id = getIDFromJID(jid); if (id) { return *id; } else { return addJID(jid); } } int SQLiteHistoryStorage::addJID(const JID& jid) { std::string statement = std::string("INSERT INTO jids('jid') VALUES('") + getEscapedString(jid.toString()) + "')"; char* errorMessage; int result = sqlite3_exec(db_, statement.c_str(), 0, 0, &errorMessage); if (result != SQLITE_OK) { std::cerr << "SQL Error: " << errorMessage << std::endl; sqlite3_free(errorMessage); } return sqlite3_last_insert_rowid(db_); } boost::optional SQLiteHistoryStorage::getJIDFromID(int id) const { boost::optional result; sqlite3_stmt* selectStatement; std::string selectQuery("SELECT jid FROM jids WHERE id=" + boost::lexical_cast(id)); int r = sqlite3_prepare(db_, selectQuery.c_str(), selectQuery.size(), &selectStatement, NULL); if (r != SQLITE_OK) { std::cout << "Error: " << sqlite3_errmsg(db_) << std::endl; } r = sqlite3_step(selectStatement); if (r == SQLITE_ROW) { result = boost::optional(reinterpret_cast(sqlite3_column_text(selectStatement, 0))); } sqlite3_finalize(selectStatement); return result; } boost::optional SQLiteHistoryStorage::getIDFromJID(const JID& jid) const { boost::optional result; sqlite3_stmt* selectStatement; std::string selectQuery("SELECT id FROM jids WHERE jid='" + jid.toString() + "'"); int r = sqlite3_prepare(db_, selectQuery.c_str(), selectQuery.size(), &selectStatement, NULL); if (r != SQLITE_OK) { std::cout << "Error: " << sqlite3_errmsg(db_) << std::endl; } r = sqlite3_step(selectStatement); if (r == SQLITE_ROW) { result = boost::optional(sqlite3_column_int(selectStatement, 0)); } sqlite3_finalize(selectStatement); return result; } ContactsMap SQLiteHistoryStorage::getContacts(const JID& selfJID, HistoryMessage::Type type, const std::string& keyword) const { ContactsMap result; sqlite3_stmt* selectStatement; // get id boost::optional id = getIDFromJID(selfJID); if (!id) { return result; } // get contacts std::string query = "SELECT DISTINCT messages.'fromBare', messages.'fromResource', messages.'toBare', messages.'toResource', messages.'time' " "FROM messages WHERE (type=" + boost::lexical_cast(type) + " AND (toBare=" + boost::lexical_cast(*id) + " OR fromBare=" + boost::lexical_cast(*id) + "))"; // match keyword if (getEscapedString(keyword).length()) { query += " AND message LIKE '%" + getEscapedString(keyword) + "%'"; } int r = sqlite3_prepare(db_, query.c_str(), query.size(), &selectStatement, NULL); if (r != SQLITE_OK) { std::cout << "Error: " << sqlite3_errmsg(db_) << std::endl; } r = sqlite3_step(selectStatement); while (r == SQLITE_ROW) { int fromBareID = sqlite3_column_int(selectStatement, 0); std::string fromResource(reinterpret_cast(sqlite3_column_text(selectStatement, 1))); int toBareID = sqlite3_column_int(selectStatement, 2); std::string toResource(reinterpret_cast(sqlite3_column_text(selectStatement, 3))); std::string resource; int secondsSinceEpoch(sqlite3_column_int(selectStatement, 4)); boost::posix_time::ptime time(boost::gregorian::date(1970, 1, 1), boost::posix_time::seconds(secondsSinceEpoch)); boost::optional contactJID; if (fromBareID == *id) { contactJID = getJIDFromID(toBareID); resource = toResource; } else { contactJID = getJIDFromID(fromBareID); resource = fromResource; } // check if it is a MUC contact (from a private conversation) if (type == HistoryMessage::PrivateMessage) { contactJID = boost::optional(JID(contactJID->getNode(), contactJID->getDomain(), resource)); } if (contactJID) { result[*contactJID].insert(time.date()); } r = sqlite3_step(selectStatement); } if (r != SQLITE_DONE) { std::cout << "Error: " << sqlite3_errmsg(db_) << std::endl; } sqlite3_finalize(selectStatement); return result; } boost::gregorian::date SQLiteHistoryStorage::getNextDateWithLogs(const JID& selfJID, const JID& contactJID, HistoryMessage::Type type, const boost::gregorian::date& date, bool reverseOrder) const { sqlite3_stmt* selectStatement; boost::optional selfID = getIDFromJID(selfJID.toBare()); boost::optional contactID = getIDFromJID(contactJID.toBare()); if (!selfID || !contactID) { // JIDs missing from the database return boost::gregorian::date(boost::gregorian::not_a_date_time); } std::string selectQuery = "SELECT time FROM messages WHERE (type=" + boost::lexical_cast(type); if (contactJID.isBare()) { // match only bare jid selectQuery += " AND ((fromBare=" + boost::lexical_cast(*selfID) + " AND toBare=" + boost::lexical_cast(*contactID) + ") OR (fromBare=" + boost::lexical_cast(*contactID) + " AND toBare=" + boost::lexical_cast(*selfID) + ")))"; } else { // match resource too selectQuery += " AND ((fromBare=" + boost::lexical_cast(*selfID) + " AND (toBare=" + boost::lexical_cast(*contactID) +" AND toResource='" + getEscapedString(contactJID.getResource()) + "')) OR ((fromBare=" + boost::lexical_cast(*contactID) + " AND fromResource='" + getEscapedString(contactJID.getResource()) + "') AND toBare=" + boost::lexical_cast(*selfID) + ")))"; } int timeStamp = (boost::posix_time::ptime(date) - boost::posix_time::ptime(boost::gregorian::date(1970, 1, 1))).total_seconds() + (reverseOrder ? 0 : 86400); selectQuery += " AND time" + (reverseOrder ? std::string("<") : std::string(">")) + boost::lexical_cast(timeStamp); selectQuery += " ORDER BY time " + (reverseOrder ? std::string("DESC") : std::string("ASC")) + " LIMIT 1"; int r = sqlite3_prepare(db_, selectQuery.c_str(), selectQuery.size(), &selectStatement, NULL); if (r != SQLITE_OK) { std::cout << "Error: " << sqlite3_errmsg(db_) << std::endl; } r = sqlite3_step(selectStatement); if (r == SQLITE_ROW) { int secondsSinceEpoch(sqlite3_column_int(selectStatement, 0)); boost::posix_time::ptime time(boost::gregorian::date(1970, 1, 1), boost::posix_time::seconds(secondsSinceEpoch)); std::cout << "next day is: " << time.date() << "\n"; return time.date(); } return boost::gregorian::date(boost::gregorian::not_a_date_time); } std::vector SQLiteHistoryStorage::getMessagesFromNextDate(const JID& selfJID, const JID& contactJID, HistoryMessage::Type type, const boost::gregorian::date& date) const { boost::gregorian::date nextDate = getNextDateWithLogs(selfJID, contactJID, type, date, false); if (nextDate.is_not_a_date()) { return std::vector(); } return getMessagesFromDate(selfJID, contactJID, type, nextDate); } std::vector SQLiteHistoryStorage::getMessagesFromPreviousDate(const JID& selfJID, const JID& contactJID, HistoryMessage::Type type, const boost::gregorian::date& date) const { boost::gregorian::date previousDate = getNextDateWithLogs(selfJID, contactJID, type, date, true); if (previousDate.is_not_a_date()) { return std::vector(); } return getMessagesFromDate(selfJID, contactJID, type, previousDate); } boost::posix_time::ptime SQLiteHistoryStorage::getLastTimeStampFromMUC(const JID& selfJID, const JID& mucJID) const { boost::optional selfID = getIDFromJID(selfJID.toBare()); boost::optional mucID = getIDFromJID(mucJID.toBare()); if (!selfID || !mucID) { // JIDs missing from the database return boost::posix_time::ptime(boost::posix_time::not_a_date_time); } sqlite3_stmt* selectStatement; std::string selectQuery = "SELECT messages.'time', messages.'offset' from messages WHERE type=1 AND (toBare=" + boost::lexical_cast(*selfID) + " AND fromBare=" + boost::lexical_cast(*mucID) + ") ORDER BY time DESC LIMIT 1"; int r = sqlite3_prepare(db_, selectQuery.c_str(), selectQuery.size(), &selectStatement, NULL); if (r != SQLITE_OK) { std::cout << "Error: " << sqlite3_errmsg(db_) << std::endl; } r = sqlite3_step(selectStatement); if (r == SQLITE_ROW) { int secondsSinceEpoch(sqlite3_column_int(selectStatement, 0)); boost::posix_time::ptime time(boost::gregorian::date(1970, 1, 1), boost::posix_time::seconds(secondsSinceEpoch)); int offset = sqlite3_column_int(selectStatement, 1); return time - boost::posix_time::hours(offset); } return boost::posix_time::ptime(boost::posix_time::not_a_date_time); } void SQLiteHistoryStorage::run() { sqlite3async_run(); } } swift-im-2.0+dev6/Swiften/History/UnitTest/0000755000175000017500000000000012227051774020522 5ustar kismithkismithswift-im-2.0+dev6/Swiften/History/UnitTest/SQLiteHistoryManagerTest.cpp0000644000175000017500000000740112227051774026106 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include using namespace Swift; class SQLiteHistoryManagerTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(SQLiteHistoryManagerTest); //CPPUNIT_TEST(testAddMessage); //CPPUNIT_TEST(testAddMessage_TwoMessages); //CPPUNIT_TEST(testGetIDForJID_SameJID); //CPPUNIT_TEST(testGetIDForJID_DifferentJIDs); //CPPUNIT_TEST(getJIDFromID); //CPPUNIT_TEST(getJIDFromID_UnexistingID); //CPPUNIT_TEST(getIDFromJID); //CPPUNIT_TEST(getIDFromJID_UnexistingJID); //CPPUNIT_TEST_SUITE_END(); public: SQLiteHistoryManagerTest() {} void setUp() { } void tearDown() { } void testAddMessage() { boost::shared_ptr testling(createHistoryManager()); HistoryMessage testMessage("Test", JID("foo@bar.com"), JID("fum@baz.org"), boost::posix_time::time_from_string("1980-01-21 22:03")); testling->addMessage(testMessage); std::vector messages = testling->getMessages(); CPPUNIT_ASSERT_EQUAL(1, static_cast(messages.size())); CPPUNIT_ASSERT(testMessage == messages[0]); } void testAddMessage_TwoMessages() { boost::shared_ptr testling(createHistoryManager()); HistoryMessage testMessage1("Test1", JID("foo@bar.com"), JID("fum@baz.org"), boost::posix_time::time_from_string("1980-01-21 22:03")); testling->addMessage(testMessage1); HistoryMessage testMessage2("Test2", JID("fum@baz.org"), JID("foo@bar.com"), boost::posix_time::time_from_string("1975-03-09 22:04")); testling->addMessage(testMessage2); std::vector messages = testling->getMessages(); CPPUNIT_ASSERT_EQUAL(2, static_cast(messages.size())); CPPUNIT_ASSERT(testMessage1 == messages[0]); CPPUNIT_ASSERT(testMessage2 == messages[1]); } void testGetIDForJID_SameJID() { boost::shared_ptr testling(createHistoryManager()); int id1 = testling->getIDForJID(JID("foo@bar.com")); int id2 = testling->getIDForJID(JID("foo@bar.com")); CPPUNIT_ASSERT_EQUAL(id1, id2); } void testGetIDForJID_DifferentJIDs() { boost::shared_ptr testling(createHistoryManager()); int id1 = testling->getIDForJID(JID("foo@bar.com")); int id2 = testling->getIDForJID(JID("foo@baz.com")); CPPUNIT_ASSERT(id1 != id2); } void getJIDFromID() { boost::shared_ptr testling(createHistoryManager()); int id = testling->addJID(JID("foo@bar.com")); boost::optional result(testling->getJIDFromID(id)); CPPUNIT_ASSERT(result); CPPUNIT_ASSERT_EQUAL(JID("foo@bar.com"), *result); } void getJIDFromID_UnexistingID() { boost::shared_ptr testling(createHistoryManager()); boost::optional result(testling->getJIDFromID(1)); CPPUNIT_ASSERT(!result); } void getIDFromJID() { boost::shared_ptr testling(createHistoryManager()); int id = testling->addJID(JID("foo@bar.com")); boost::optional result(testling->getIDFromJID(JID("foo@bar.com"))); CPPUNIT_ASSERT(result); CPPUNIT_ASSERT_EQUAL(id, *result); } void getIDFromJID_UnexistingJID() { boost::shared_ptr testling(createHistoryManager()); boost::optional result(testling->getIDFromJID(JID("foo@bar.com"))); CPPUNIT_ASSERT(!result); } private: SQLiteHistoryManager* createHistoryManager() { return new SQLiteHistoryManager(":memory:"); } }; CPPUNIT_TEST_SUITE_REGISTRATION(SQLiteHistoryManagerTest); swift-im-2.0+dev6/Swiften/History/HistoryStorage.h0000644000175000017500000000261012227051774022101 0ustar kismithkismith/* * 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 #include #include #include #include #include namespace Swift { typedef std::map > ContactsMap; class HistoryStorage { /** * Messages are stored using localtime timestamps. */ public: virtual ~HistoryStorage() {}; virtual void addMessage(const HistoryMessage& message) = 0; virtual std::vector getMessagesFromDate(const JID& selfJID, const JID& contactJID, HistoryMessage::Type type, const boost::gregorian::date& date) const = 0; virtual std::vector getMessagesFromNextDate(const JID& selfJID, const JID& contactJID, HistoryMessage::Type type, const boost::gregorian::date& date) const = 0; virtual std::vector getMessagesFromPreviousDate(const JID& selfJID, const JID& contactJID, HistoryMessage::Type type, const boost::gregorian::date& date) const = 0; virtual ContactsMap getContacts(const JID& selfJID, HistoryMessage::Type type, const std::string& keyword) const = 0; virtual boost::posix_time::ptime getLastTimeStampFromMUC(const JID& selfJID, const JID& mucJID) const = 0; }; } swift-im-2.0+dev6/Swiften/History/SQLiteHistoryStorage.h0000644000175000017500000000315612227051774023171 0ustar kismithkismith/* * 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 #include #include struct sqlite3; namespace Swift { class SQLiteHistoryStorage : public HistoryStorage { public: SQLiteHistoryStorage(const std::string& file); ~SQLiteHistoryStorage(); void addMessage(const HistoryMessage& message); ContactsMap getContacts(const JID& selfJID, HistoryMessage::Type type, const std::string& keyword) const; std::vector getMessagesFromDate(const JID& selfJID, const JID& contactJID, HistoryMessage::Type type, const boost::gregorian::date& date) const; std::vector getMessagesFromNextDate(const JID& selfJID, const JID& contactJID, HistoryMessage::Type type, const boost::gregorian::date& date) const; std::vector getMessagesFromPreviousDate(const JID& selfJID, const JID& contactJID, HistoryMessage::Type type, const boost::gregorian::date& date) const; boost::posix_time::ptime getLastTimeStampFromMUC(const JID& selfJID, const JID& mucJID) const; private: void run(); boost::gregorian::date getNextDateWithLogs(const JID& selfJID, const JID& contactJID, HistoryMessage::Type type, const boost::gregorian::date& date, bool reverseOrder) const; int getIDForJID(const JID&); int addJID(const JID&); boost::optional getJIDFromID(int id) const; boost::optional getIDFromJID(const JID& jid) const; sqlite3* db_; boost::thread* thread_; }; } swift-im-2.0+dev6/Swiften/Compress/0000755000175000017500000000000012227051774017075 5ustar kismithkismithswift-im-2.0+dev6/Swiften/Compress/ZLibCodecompressor_Private.h0000644000175000017500000000050412227051774024507 0ustar kismithkismith/* * Copyright (c) 2012 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include namespace Swift { struct ZLibCodecompressor::Private { z_stream stream; }; } swift-im-2.0+dev6/Swiften/Compress/ZLibDecompressor.cpp0000644000175000017500000000121512227051774023026 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #pragma GCC diagnostic ignored "-Wold-style-cast" namespace Swift { ZLibDecompressor::ZLibDecompressor() { int result = inflateInit(&p->stream); assert(result == Z_OK); (void) result; } ZLibDecompressor::~ZLibDecompressor() { inflateEnd(&p->stream); } int ZLibDecompressor::processZStream() { return inflate(&p->stream, Z_SYNC_FLUSH); } } swift-im-2.0+dev6/Swiften/Compress/ZLibCompressor.h0000644000175000017500000000074412227051774022170 0ustar kismithkismith/* * 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 #include namespace Swift { class SWIFTEN_API ZLibCompressor : public ZLibCodecompressor { public: ZLibCompressor(); ~ZLibCompressor(); virtual int processZStream(); private: static const int COMPRESSION_LEVEL = 9; }; } swift-im-2.0+dev6/Swiften/Compress/ZLibDecompressor.h0000644000175000017500000000066312227051774022501 0ustar kismithkismith/* * 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 #include namespace Swift { class SWIFTEN_API ZLibDecompressor : public ZLibCodecompressor { public: ZLibDecompressor(); ~ZLibDecompressor(); virtual int processZStream(); }; } swift-im-2.0+dev6/Swiften/Compress/UnitTest/0000755000175000017500000000000012227051774020654 5ustar kismithkismithswift-im-2.0+dev6/Swiften/Compress/UnitTest/ZLibDecompressorTest.cpp0000644000175000017500000000466012227051774025454 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include using namespace Swift; class ZLibDecompressorTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(ZLibDecompressorTest); CPPUNIT_TEST(testProcess); CPPUNIT_TEST(testProcess_Twice); CPPUNIT_TEST(testProcess_Invalid); CPPUNIT_TEST(testProcess_Huge); CPPUNIT_TEST(testProcess_ChunkSize); CPPUNIT_TEST_SUITE_END(); public: ZLibDecompressorTest() {} void testProcess() { ZLibDecompressor testling; SafeByteArray result = testling.process(createSafeByteArray("\x78\xda\x4a\xcb\xcf\x07\x00\x00\x00\xff\xff", 11)); CPPUNIT_ASSERT_EQUAL(createSafeByteArray("foo"), result); } void testProcess_Twice() { ZLibDecompressor testling; testling.process(createSafeByteArray("\x78\xda\x4a\xcb\xcf\x07\x00\x00\x00\xff\xff", 11)); SafeByteArray result = testling.process(createSafeByteArray("\x4a\x4a\x2c\x02\x00\x00\x00\xff\xff", 9)); CPPUNIT_ASSERT_EQUAL(createSafeByteArray("bar"), result); } void testProcess_Invalid() { ZLibDecompressor testling; CPPUNIT_ASSERT_THROW(testling.process(createSafeByteArray("invalid")), ZLibException); } void testProcess_Huge() { std::vector data; data.reserve(2048); for (unsigned int i = 0; i < 2048; ++i) { data.push_back(static_cast(i)); } SafeByteArray original(createSafeByteArray(&data[0], data.size())); SafeByteArray compressed = ZLibCompressor().process(original); SafeByteArray decompressed = ZLibDecompressor().process(compressed); CPPUNIT_ASSERT_EQUAL(original, decompressed); } void testProcess_ChunkSize() { std::vector data; data.reserve(1024); for (unsigned int i = 0; i < 1024; ++i) { data.push_back(static_cast(i)); } SafeByteArray original(createSafeByteArray(&data[0], data.size())); SafeByteArray compressed = ZLibCompressor().process(original); SafeByteArray decompressed = ZLibDecompressor().process(compressed); CPPUNIT_ASSERT_EQUAL(original, decompressed); } }; CPPUNIT_TEST_SUITE_REGISTRATION(ZLibDecompressorTest); swift-im-2.0+dev6/Swiften/Compress/UnitTest/ZLibCompressorTest.cpp0000644000175000017500000000225412227051774025140 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include using namespace Swift; class ZLibCompressorTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(ZLibCompressorTest); CPPUNIT_TEST(testProcess); CPPUNIT_TEST(testProcess_Twice); CPPUNIT_TEST_SUITE_END(); public: ZLibCompressorTest() {} void testProcess() { ZLibCompressor testling; SafeByteArray result = testling.process(createSafeByteArray("foo")); CPPUNIT_ASSERT_EQUAL(createSafeByteArray("\x78\xda\x4a\xcb\xcf\x07\x00\x00\x00\xff\xff", 11), result); } void testProcess_Twice() { ZLibCompressor testling; testling.process(createSafeByteArray("foo")); SafeByteArray result = testling.process(createSafeByteArray("bar")); CPPUNIT_ASSERT_EQUAL(createSafeByteArray("\x4a\x4a\x2c\x02\x00\x00\x00\xff\xff",9), result); } }; CPPUNIT_TEST_SUITE_REGISTRATION(ZLibCompressorTest); swift-im-2.0+dev6/Swiften/Compress/ZLibCodecompressor.h0000644000175000017500000000102712227051774023016 0ustar kismithkismith/* * 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 #include namespace Swift { class SWIFTEN_API ZLibCodecompressor { public: ZLibCodecompressor(); virtual ~ZLibCodecompressor(); SafeByteArray process(const SafeByteArray& data); virtual int processZStream() = 0; protected: struct Private; boost::shared_ptr p; }; } swift-im-2.0+dev6/Swiften/Compress/ZLibCompressor.cpp0000644000175000017500000000122412227051774022515 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #pragma GCC diagnostic ignored "-Wold-style-cast" namespace Swift { ZLibCompressor::ZLibCompressor() { int result = deflateInit(&p->stream, COMPRESSION_LEVEL); assert(result == Z_OK); (void) result; } ZLibCompressor::~ZLibCompressor() { deflateEnd(&p->stream); } int ZLibCompressor::processZStream() { return deflate(&p->stream, Z_SYNC_FLUSH); } } swift-im-2.0+dev6/Swiften/Compress/ZLibCodecompressor.cpp0000644000175000017500000000271012227051774023351 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include namespace Swift { static const int CHUNK_SIZE = 1024; // If you change this, also change the unittest ZLibCodecompressor::ZLibCodecompressor() : p(boost::make_shared()) { memset(&p->stream, 0, sizeof(z_stream)); p->stream.zalloc = Z_NULL; p->stream.zfree = Z_NULL; p->stream.opaque = Z_NULL; } ZLibCodecompressor::~ZLibCodecompressor() { } SafeByteArray ZLibCodecompressor::process(const SafeByteArray& input) { SafeByteArray output; p->stream.avail_in = input.size(); p->stream.next_in = reinterpret_cast(const_cast(vecptr(input))); int outputPosition = 0; do { output.resize(outputPosition + CHUNK_SIZE); p->stream.avail_out = CHUNK_SIZE; p->stream.next_out = reinterpret_cast(vecptr(output) + outputPosition); int result = processZStream(); if (result != Z_OK && result != Z_BUF_ERROR) { throw ZLibException(/* p->stream.msg */); } outputPosition += CHUNK_SIZE; } while (p->stream.avail_out == 0); if (p->stream.avail_in != 0) { throw ZLibException(); } output.resize(outputPosition - p->stream.avail_out); return output; } } swift-im-2.0+dev6/Swiften/Compress/ZLibException.h0000644000175000017500000000037612227051774021773 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once namespace Swift { class ZLibException { public: ZLibException() {} }; } swift-im-2.0+dev6/Swiften/SASL/0000755000175000017500000000000012227051774016044 5ustar kismithkismithswift-im-2.0+dev6/Swiften/SASL/SConscript0000644000175000017500000000123412227051774020056 0ustar kismithkismithImport("swiften_env", "env") myenv = swiften_env.Clone() objects = myenv.SwiftenObject([ "ClientAuthenticator.cpp", "EXTERNALClientAuthenticator.cpp", "PLAINClientAuthenticator.cpp", "PLAINMessage.cpp", "SCRAMSHA1ClientAuthenticator.cpp", "DIGESTMD5Properties.cpp", "DIGESTMD5ClientAuthenticator.cpp", ]) swiften_env.Append(SWIFTEN_OBJECTS = [objects]) env.Append(UNITTEST_SOURCES = [ File("UnitTest/PLAINMessageTest.cpp"), File("UnitTest/PLAINClientAuthenticatorTest.cpp"), File("UnitTest/SCRAMSHA1ClientAuthenticatorTest.cpp"), File("UnitTest/DIGESTMD5PropertiesTest.cpp"), File("UnitTest/DIGESTMD5ClientAuthenticatorTest.cpp"), ]) swift-im-2.0+dev6/Swiften/SASL/DIGESTMD5ClientAuthenticator.h0000644000175000017500000000162612227051774023401 0ustar kismithkismith/* * 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 #include #include #include #include #include #include namespace Swift { class SWIFTEN_API DIGESTMD5ClientAuthenticator : public ClientAuthenticator { public: DIGESTMD5ClientAuthenticator(const std::string& host, const std::string& nonce); virtual boost::optional getResponse() const; virtual bool setChallenge(const boost::optional >&); static bool canBeUsed(); private: enum Step { Initial, Response, Final, } step; std::string host; std::string cnonce; DIGESTMD5Properties challenge; }; } swift-im-2.0+dev6/Swiften/SASL/PLAINMessage.cpp0000644000175000017500000000201112227051774020712 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include namespace Swift { PLAINMessage::PLAINMessage(const std::string& authcid, const SafeByteArray& password, const std::string& authzid) : authcid(authcid), authzid(authzid), password(password) { } PLAINMessage::PLAINMessage(const SafeByteArray& value) { size_t i = 0; while (i < value.size() && value[i] != '\0') { authzid += value[i]; ++i; } if (i == value.size()) { return; } ++i; while (i < value.size() && value[i] != '\0') { authcid += value[i]; ++i; } if (i == value.size()) { authcid = ""; return; } ++i; while (i < value.size()) { password.push_back(value[i]); ++i; } } SafeByteArray PLAINMessage::getValue() const { return concat(createSafeByteArray(authzid), createSafeByteArray('\0'), createSafeByteArray(authcid), createSafeByteArray('\0'), password); } } swift-im-2.0+dev6/Swiften/SASL/DIGESTMD5ClientAuthenticator.cpp0000644000175000017500000000573212227051774023736 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include namespace Swift { DIGESTMD5ClientAuthenticator::DIGESTMD5ClientAuthenticator(const std::string& host, const std::string& nonce) : ClientAuthenticator("DIGEST-MD5"), step(Initial), host(host), cnonce(nonce) { } bool DIGESTMD5ClientAuthenticator::canBeUsed() { return MD5::isAllowedForCrypto(); } boost::optional DIGESTMD5ClientAuthenticator::getResponse() const { if (step == Initial) { return boost::optional(); } else if (step == Response) { std::string realm; if (challenge.getValue("realm")) { realm = *challenge.getValue("realm"); } std::string qop = "auth"; std::string digestURI = "xmpp/" + host; std::string nc = "00000001"; // Compute the response value ByteArray A1 = concat( MD5::getHash( concat(createSafeByteArray(getAuthenticationID().c_str()), createSafeByteArray(":"), createSafeByteArray(realm.c_str()), createSafeByteArray(":"), getPassword())), createByteArray(":"), createByteArray(*challenge.getValue("nonce")), createByteArray(":"), createByteArray(cnonce)); if (!getAuthorizationID().empty()) { append(A1, createByteArray(":" + getAuthenticationID())); } ByteArray A2 = createByteArray("AUTHENTICATE:" + digestURI); std::string responseValue = Hexify::hexify(MD5::getHash(createByteArray( Hexify::hexify(MD5::getHash(A1)) + ":" + *challenge.getValue("nonce") + ":" + nc + ":" + cnonce + ":" + qop + ":" + Hexify::hexify(MD5::getHash(A2))))); DIGESTMD5Properties response; response.setValue("username", getAuthenticationID()); if (!realm.empty()) { response.setValue("realm", realm); } response.setValue("nonce", *challenge.getValue("nonce")); response.setValue("cnonce", cnonce); response.setValue("nc", "00000001"); response.setValue("qop", qop); response.setValue("digest-uri", digestURI); response.setValue("charset", "utf-8"); response.setValue("response", responseValue); if (!getAuthorizationID().empty()) { response.setValue("authzid", getAuthorizationID()); } return createSafeByteArray(response.serialize()); } else { return boost::optional(); } } bool DIGESTMD5ClientAuthenticator::setChallenge(const boost::optional& challengeData) { if (step == Initial) { if (!challengeData) { return false; } challenge = DIGESTMD5Properties::parse(*challengeData); // Sanity checks if (!challenge.getValue("nonce")) { return false; } if (!challenge.getValue("charset") || *challenge.getValue("charset") != "utf-8") { return false; } step = Response; return true; } else { step = Final; // TODO: Check RSPAuth return true; } } } swift-im-2.0+dev6/Swiften/SASL/EXTERNALClientAuthenticator.cpp0000644000175000017500000000117112227051774023664 0ustar kismithkismith/* * Copyright (c) 2012 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { EXTERNALClientAuthenticator::EXTERNALClientAuthenticator() : ClientAuthenticator("EXTERNAL"), finished(false) { } boost::optional EXTERNALClientAuthenticator::getResponse() const { return boost::optional(); } bool EXTERNALClientAuthenticator::setChallenge(const boost::optional&) { if (finished) { return false; } finished = true; return true; } } swift-im-2.0+dev6/Swiften/SASL/PLAINClientAuthenticator.h0000644000175000017500000000105412227051774022752 0ustar kismithkismith/* * 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 #include #include namespace Swift { class SWIFTEN_API PLAINClientAuthenticator : public ClientAuthenticator { public: PLAINClientAuthenticator(); virtual boost::optional getResponse() const; virtual bool setChallenge(const boost::optional&); }; } swift-im-2.0+dev6/Swiften/SASL/ClientAuthenticator.cpp0000644000175000017500000000055612227051774022527 0ustar kismithkismith/* * Copyright (c) 2010-2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { ClientAuthenticator::ClientAuthenticator(const std::string& name) : name(name) { } ClientAuthenticator::~ClientAuthenticator() { } } swift-im-2.0+dev6/Swiften/SASL/UnitTest/0000755000175000017500000000000012227051774017623 5ustar kismithkismithswift-im-2.0+dev6/Swiften/SASL/UnitTest/PLAINClientAuthenticatorTest.cpp0000644000175000017500000000222612227051774025726 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include using namespace Swift; class PLAINClientAuthenticatorTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(PLAINClientAuthenticatorTest); CPPUNIT_TEST(testGetResponse_WithoutAuthzID); CPPUNIT_TEST(testGetResponse_WithAuthzID); CPPUNIT_TEST_SUITE_END(); public: void testGetResponse_WithoutAuthzID() { PLAINClientAuthenticator testling; testling.setCredentials("user", createSafeByteArray("pass")); CPPUNIT_ASSERT_EQUAL(*testling.getResponse(), createSafeByteArray("\0user\0pass", 10)); } void testGetResponse_WithAuthzID() { PLAINClientAuthenticator testling; testling.setCredentials("user", createSafeByteArray("pass"), "authz"); CPPUNIT_ASSERT_EQUAL(*testling.getResponse(), createSafeByteArray("authz\0user\0pass", 15)); } }; CPPUNIT_TEST_SUITE_REGISTRATION(PLAINClientAuthenticatorTest); swift-im-2.0+dev6/Swiften/SASL/UnitTest/DIGESTMD5ClientAuthenticatorTest.cpp0000644000175000017500000000453612227051774026356 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include using namespace Swift; class DIGESTMD5ClientAuthenticatorTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(DIGESTMD5ClientAuthenticatorTest); CPPUNIT_TEST(testGetInitialResponse); CPPUNIT_TEST(testGetResponse); CPPUNIT_TEST(testGetResponse_WithAuthorizationID); //CPPUNIT_TEST(testSetChallenge); CPPUNIT_TEST_SUITE_END(); public: void testGetInitialResponse() { DIGESTMD5ClientAuthenticator testling("xmpp.example.com", "abcdefgh"); CPPUNIT_ASSERT(!testling.getResponse()); } void testGetResponse() { DIGESTMD5ClientAuthenticator testling("xmpp.example.com", "abcdefgh"); testling.setCredentials("user", createSafeByteArray("pass"), ""); testling.setChallenge(createByteArray( "realm=\"example.com\"," "nonce=\"O6skKPuaCZEny3hteI19qXMBXSadoWs840MchORo\"," "qop=auth,charset=utf-8,algorithm=md5-sess")); SafeByteArray response = *testling.getResponse(); CPPUNIT_ASSERT_EQUAL(createSafeByteArray("charset=utf-8,cnonce=\"abcdefgh\",digest-uri=\"xmpp/xmpp.example.com\",nc=00000001,nonce=\"O6skKPuaCZEny3hteI19qXMBXSadoWs840MchORo\",qop=auth,realm=\"example.com\",response=088891c800ecff1b842159ad6459104a,username=\"user\""), response); } void testGetResponse_WithAuthorizationID() { DIGESTMD5ClientAuthenticator testling("xmpp.example.com", "abcdefgh"); testling.setCredentials("user", createSafeByteArray("pass"), "myauthzid"); testling.setChallenge(createByteArray( "realm=\"example.com\"," "nonce=\"O6skKPuaCZEny3hteI19qXMBXSadoWs840MchORo\"," "qop=auth,charset=utf-8,algorithm=md5-sess")); SafeByteArray response = *testling.getResponse(); CPPUNIT_ASSERT_EQUAL(createSafeByteArray("authzid=\"myauthzid\",charset=utf-8,cnonce=\"abcdefgh\",digest-uri=\"xmpp/xmpp.example.com\",nc=00000001,nonce=\"O6skKPuaCZEny3hteI19qXMBXSadoWs840MchORo\",qop=auth,realm=\"example.com\",response=4293834432b6e7889a2dee7e8fe7dd06,username=\"user\""), response); } }; CPPUNIT_TEST_SUITE_REGISTRATION(DIGESTMD5ClientAuthenticatorTest); swift-im-2.0+dev6/Swiften/SASL/UnitTest/PLAINMessageTest.cpp0000644000175000017500000000440612227051774023343 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include using namespace Swift; class PLAINMessageTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(PLAINMessageTest); CPPUNIT_TEST(testGetValue_WithoutAuthzID); CPPUNIT_TEST(testGetValue_WithAuthzID); CPPUNIT_TEST(testConstructor_WithoutAuthzID); CPPUNIT_TEST(testConstructor_WithAuthzID); CPPUNIT_TEST(testConstructor_NoAuthcid); CPPUNIT_TEST(testConstructor_NoPassword); CPPUNIT_TEST_SUITE_END(); public: PLAINMessageTest() {} void testGetValue_WithoutAuthzID() { PLAINMessage message("user", createSafeByteArray("pass")); CPPUNIT_ASSERT_EQUAL(message.getValue(), createSafeByteArray("\0user\0pass", 10)); } void testGetValue_WithAuthzID() { PLAINMessage message("user", createSafeByteArray("pass"), "authz"); CPPUNIT_ASSERT_EQUAL(message.getValue(), createSafeByteArray("authz\0user\0pass", 15)); } void testConstructor_WithoutAuthzID() { PLAINMessage message(createSafeByteArray("\0user\0pass", 10)); CPPUNIT_ASSERT_EQUAL(std::string(""), message.getAuthorizationID()); CPPUNIT_ASSERT_EQUAL(std::string("user"), message.getAuthenticationID()); CPPUNIT_ASSERT_EQUAL(createSafeByteArray("pass"), message.getPassword()); } void testConstructor_WithAuthzID() { PLAINMessage message(createSafeByteArray("authz\0user\0pass", 15)); CPPUNIT_ASSERT_EQUAL(std::string("authz"), message.getAuthorizationID()); CPPUNIT_ASSERT_EQUAL(std::string("user"), message.getAuthenticationID()); CPPUNIT_ASSERT_EQUAL(createSafeByteArray("pass"), message.getPassword()); } void testConstructor_NoAuthcid() { PLAINMessage message(createSafeByteArray("authzid", 7)); CPPUNIT_ASSERT_EQUAL(std::string(""), message.getAuthenticationID()); } void testConstructor_NoPassword() { PLAINMessage message(createSafeByteArray("authzid\0authcid", 15)); CPPUNIT_ASSERT_EQUAL(std::string(""), message.getAuthenticationID()); } }; CPPUNIT_TEST_SUITE_REGISTRATION(PLAINMessageTest); swift-im-2.0+dev6/Swiften/SASL/UnitTest/DIGESTMD5PropertiesTest.cpp0000644000175000017500000000422312227051774024532 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include using namespace Swift; class DIGESTMD5PropertiesTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(DIGESTMD5PropertiesTest); CPPUNIT_TEST(testParse); CPPUNIT_TEST(testSerialize); CPPUNIT_TEST_SUITE_END(); public: void testParse() { DIGESTMD5Properties properties = DIGESTMD5Properties::parse(createByteArray( "realm=\"myrealm1\",realm=\"myrealm2\",nonce=\"mynonce\"," "algorithm=md5-sess,charset=utf-8")); CPPUNIT_ASSERT(properties.getValue("realm")); CPPUNIT_ASSERT_EQUAL(std::string("myrealm1"), *properties.getValue("realm")); CPPUNIT_ASSERT(properties.getValue("nonce")); CPPUNIT_ASSERT_EQUAL(std::string("mynonce"), *properties.getValue("nonce")); CPPUNIT_ASSERT(properties.getValue("algorithm")); CPPUNIT_ASSERT_EQUAL(std::string("md5-sess"), *properties.getValue("algorithm")); CPPUNIT_ASSERT(properties.getValue("charset")); CPPUNIT_ASSERT_EQUAL(std::string("utf-8"), *properties.getValue("charset")); } void testSerialize() { DIGESTMD5Properties properties; properties.setValue("authzid", "myauthzid"); properties.setValue("charset", "utf-8"); properties.setValue("cnonce", "mycnonce"); properties.setValue("digest-uri", "mydigesturi"); properties.setValue("nc", "1"); properties.setValue("nonce", "mynonce"); properties.setValue("qop", "auth"); properties.setValue("realm", "myrealm"); properties.setValue("response", "myresponse"); properties.setValue("username", "myuser"); ByteArray result = properties.serialize(); ByteArray expected(createByteArray("authzid=\"myauthzid\",charset=utf-8,cnonce=\"mycnonce\",digest-uri=\"mydigesturi\",nc=1,nonce=\"mynonce\",qop=auth,realm=\"myrealm\",response=myresponse,username=\"myuser\"")); CPPUNIT_ASSERT_EQUAL(byteArrayToString(expected), byteArrayToString(result)); } }; CPPUNIT_TEST_SUITE_REGISTRATION(DIGESTMD5PropertiesTest); swift-im-2.0+dev6/Swiften/SASL/UnitTest/SCRAMSHA1ClientAuthenticatorTest.cpp0000644000175000017500000002104412227051774026344 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include using namespace Swift; class SCRAMSHA1ClientAuthenticatorTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(SCRAMSHA1ClientAuthenticatorTest); CPPUNIT_TEST(testGetInitialResponse); CPPUNIT_TEST(testGetInitialResponse_UsernameHasSpecialChars); CPPUNIT_TEST(testGetInitialResponse_WithAuthorizationID); CPPUNIT_TEST(testGetInitialResponse_WithAuthorizationIDWithSpecialChars); CPPUNIT_TEST(testGetInitialResponse_WithoutChannelBindingWithTLSChannelBindingData); CPPUNIT_TEST(testGetInitialResponse_WithChannelBindingWithTLSChannelBindingData); CPPUNIT_TEST(testGetFinalResponse); CPPUNIT_TEST(testGetFinalResponse_WithoutChannelBindingWithTLSChannelBindingData); CPPUNIT_TEST(testGetFinalResponse_WithChannelBindingWithTLSChannelBindingData); CPPUNIT_TEST(testSetChallenge); CPPUNIT_TEST(testSetChallenge_InvalidClientNonce); CPPUNIT_TEST(testSetChallenge_OnlyClientNonce); CPPUNIT_TEST(testSetChallenge_InvalidIterations); CPPUNIT_TEST(testSetChallenge_ZeroIterations); CPPUNIT_TEST(testSetChallenge_NegativeIterations); CPPUNIT_TEST(testSetChallenge_MissingIterations); CPPUNIT_TEST(testSetFinalChallenge); CPPUNIT_TEST(testSetFinalChallenge_InvalidChallenge); CPPUNIT_TEST(testGetResponseAfterFinalChallenge); CPPUNIT_TEST_SUITE_END(); public: void setUp() { } void testGetInitialResponse() { SCRAMSHA1ClientAuthenticator testling("abcdefghABCDEFGH"); testling.setCredentials("user", createSafeByteArray("pass"), ""); SafeByteArray response = *testling.getResponse(); CPPUNIT_ASSERT_EQUAL(createSafeByteArray("n,,n=user,r=abcdefghABCDEFGH"), response); } void testGetInitialResponse_UsernameHasSpecialChars() { SCRAMSHA1ClientAuthenticator testling("abcdefghABCDEFGH"); testling.setCredentials(",us=,er=", createSafeByteArray("pass"), ""); SafeByteArray response = *testling.getResponse(); CPPUNIT_ASSERT_EQUAL(createSafeByteArray("n,,n==2Cus=3D=2Cer=3D,r=abcdefghABCDEFGH"), response); } void testGetInitialResponse_WithAuthorizationID() { SCRAMSHA1ClientAuthenticator testling("abcdefghABCDEFGH"); testling.setCredentials("user", createSafeByteArray("pass"), "auth"); SafeByteArray response = *testling.getResponse(); CPPUNIT_ASSERT_EQUAL(createSafeByteArray("n,a=auth,n=user,r=abcdefghABCDEFGH"), response); } void testGetInitialResponse_WithAuthorizationIDWithSpecialChars() { SCRAMSHA1ClientAuthenticator testling("abcdefghABCDEFGH"); testling.setCredentials("user", createSafeByteArray("pass"), "a=u,th"); SafeByteArray response = *testling.getResponse(); CPPUNIT_ASSERT_EQUAL(createSafeByteArray("n,a=a=3Du=2Cth,n=user,r=abcdefghABCDEFGH"), response); } void testGetInitialResponse_WithoutChannelBindingWithTLSChannelBindingData() { SCRAMSHA1ClientAuthenticator testling("abcdefghABCDEFGH", false); testling.setTLSChannelBindingData(createByteArray("xyza")); testling.setCredentials("user", createSafeByteArray("pass"), ""); SafeByteArray response = *testling.getResponse(); CPPUNIT_ASSERT_EQUAL(createSafeByteArray("y,,n=user,r=abcdefghABCDEFGH"), response); } void testGetInitialResponse_WithChannelBindingWithTLSChannelBindingData() { SCRAMSHA1ClientAuthenticator testling("abcdefghABCDEFGH", true); testling.setTLSChannelBindingData(createByteArray("xyza")); testling.setCredentials("user", createSafeByteArray("pass"), ""); SafeByteArray response = *testling.getResponse(); CPPUNIT_ASSERT_EQUAL(createSafeByteArray("p=tls-unique,,n=user,r=abcdefghABCDEFGH"), response); } void testGetFinalResponse() { SCRAMSHA1ClientAuthenticator testling("abcdefgh"); testling.setCredentials("user", createSafeByteArray("pass"), ""); testling.setChallenge(createByteArray("r=abcdefghABCDEFGH,s=MTIzNDU2NzgK,i=4096")); SafeByteArray response = *testling.getResponse(); CPPUNIT_ASSERT_EQUAL(createSafeByteArray("c=biws,r=abcdefghABCDEFGH,p=CZbjGDpIteIJwQNBgO0P8pKkMGY="), response); } void testGetFinalResponse_WithoutChannelBindingWithTLSChannelBindingData() { SCRAMSHA1ClientAuthenticator testling("abcdefgh", false); testling.setCredentials("user", createSafeByteArray("pass"), ""); testling.setTLSChannelBindingData(createByteArray("xyza")); testling.setChallenge(createByteArray("r=abcdefghABCDEFGH,s=MTIzNDU2NzgK,i=4096")); SafeByteArray response = *testling.getResponse(); CPPUNIT_ASSERT_EQUAL(createSafeByteArray("c=eSws,r=abcdefghABCDEFGH,p=JNpsiFEcxZvNZ1+FFBBqrYvYxMk="), response); } void testGetFinalResponse_WithChannelBindingWithTLSChannelBindingData() { SCRAMSHA1ClientAuthenticator testling("abcdefgh", true); testling.setCredentials("user", createSafeByteArray("pass"), ""); testling.setTLSChannelBindingData(createByteArray("xyza")); testling.setChallenge(createByteArray("r=abcdefghABCDEFGH,s=MTIzNDU2NzgK,i=4096")); SafeByteArray response = *testling.getResponse(); CPPUNIT_ASSERT_EQUAL(createSafeByteArray("c=cD10bHMtdW5pcXVlLCx4eXph,r=abcdefghABCDEFGH,p=i6Rghite81P1ype8XxaVAa5l7v0="), response); } void testSetFinalChallenge() { SCRAMSHA1ClientAuthenticator testling("abcdefgh"); testling.setCredentials("user", createSafeByteArray("pass"), ""); testling.setChallenge(createByteArray("r=abcdefghABCDEFGH,s=MTIzNDU2NzgK,i=4096")); bool result = testling.setChallenge(createByteArray("v=Dd+Q20knZs9jeeK0pi1Mx1Se+yo=")); CPPUNIT_ASSERT(result); } void testSetChallenge() { SCRAMSHA1ClientAuthenticator testling("abcdefgh"); testling.setCredentials("user", createSafeByteArray("pass"), ""); bool result = testling.setChallenge(createByteArray("r=abcdefghABCDEFGH,s=MTIzNDU2NzgK,i=4096")); CPPUNIT_ASSERT(result); } void testSetChallenge_InvalidClientNonce() { SCRAMSHA1ClientAuthenticator testling("abcdefgh"); testling.setCredentials("user", createSafeByteArray("pass"), ""); bool result = testling.setChallenge(createByteArray("r=abcdefgiABCDEFGH,s=MTIzNDU2NzgK,i=4096")); CPPUNIT_ASSERT(!result); } void testSetChallenge_OnlyClientNonce() { SCRAMSHA1ClientAuthenticator testling("abcdefgh"); testling.setCredentials("user", createSafeByteArray("pass"), ""); bool result = testling.setChallenge(createByteArray("r=abcdefgh,s=MTIzNDU2NzgK,i=4096")); CPPUNIT_ASSERT(!result); } void testSetChallenge_InvalidIterations() { SCRAMSHA1ClientAuthenticator testling("abcdefgh"); testling.setCredentials("user", createSafeByteArray("pass"), ""); bool result = testling.setChallenge(createByteArray("r=abcdefghABCDEFGH,s=MTIzNDU2NzgK,i=bla")); CPPUNIT_ASSERT(!result); } void testSetChallenge_MissingIterations() { SCRAMSHA1ClientAuthenticator testling("abcdefgh"); testling.setCredentials("user", createSafeByteArray("pass"), ""); bool result = testling.setChallenge(createByteArray("r=abcdefghABCDEFGH,s=MTIzNDU2NzgK")); CPPUNIT_ASSERT(!result); } void testSetChallenge_ZeroIterations() { SCRAMSHA1ClientAuthenticator testling("abcdefgh"); testling.setCredentials("user", createSafeByteArray("pass"), ""); bool result = testling.setChallenge(createByteArray("r=abcdefghABCDEFGH,s=MTIzNDU2NzgK,i=0")); CPPUNIT_ASSERT(!result); } void testSetChallenge_NegativeIterations() { SCRAMSHA1ClientAuthenticator testling("abcdefgh"); testling.setCredentials("user", createSafeByteArray("pass"), ""); bool result = testling.setChallenge(createByteArray("r=abcdefghABCDEFGH,s=MTIzNDU2NzgK,i=-1")); CPPUNIT_ASSERT(!result); } void testSetFinalChallenge_InvalidChallenge() { SCRAMSHA1ClientAuthenticator testling("abcdefgh"); testling.setCredentials("user", createSafeByteArray("pass"), ""); testling.setChallenge(createByteArray("r=abcdefghABCDEFGH,s=MTIzNDU2NzgK,i=4096")); bool result = testling.setChallenge(createByteArray("v=e26kI69ICb6zosapLLxrER/631A=")); CPPUNIT_ASSERT(!result); } void testGetResponseAfterFinalChallenge() { SCRAMSHA1ClientAuthenticator testling("abcdefgh"); testling.setCredentials("user", createSafeByteArray("pass"), ""); testling.setChallenge(createByteArray("r=abcdefghABCDEFGH,s=MTIzNDU2NzgK,i=4096")); testling.setChallenge(createByteArray("v=Dd+Q20knZs9jeeK0pi1Mx1Se+yo=")); CPPUNIT_ASSERT(!testling.getResponse()); } }; CPPUNIT_TEST_SUITE_REGISTRATION(SCRAMSHA1ClientAuthenticatorTest); swift-im-2.0+dev6/Swiften/SASL/EXTERNALClientAuthenticator.h0000644000175000017500000000104612227051774023332 0ustar kismithkismith/* * Copyright (c) 2012 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include namespace Swift { class EXTERNALClientAuthenticator : public ClientAuthenticator { public: EXTERNALClientAuthenticator(); virtual boost::optional getResponse() const; virtual bool setChallenge(const boost::optional&); private: bool finished; }; } swift-im-2.0+dev6/Swiften/SASL/ClientAuthenticator.h0000644000175000017500000000236412227051774022173 0ustar kismithkismith/* * 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 #include #include #include #include #include namespace Swift { class SWIFTEN_API ClientAuthenticator { public: ClientAuthenticator(const std::string& name); virtual ~ClientAuthenticator(); const std::string& getName() const { return name; } void setCredentials(const std::string& authcid, const SafeByteArray& password, const std::string& authzid = std::string()) { this->authcid = authcid; this->password = password; this->authzid = authzid; } virtual boost::optional getResponse() const = 0; virtual bool setChallenge(const boost::optional&) = 0; const std::string& getAuthenticationID() const { return authcid; } const std::string& getAuthorizationID() const { return authzid; } const SafeByteArray& getPassword() const { return password; } private: std::string name; std::string authcid; SafeByteArray password; std::string authzid; }; } swift-im-2.0+dev6/Swiften/SASL/SCRAMSHA1ClientAuthenticator.cpp0000644000175000017500000001261612227051774023732 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include namespace Swift { static std::string escape(const std::string& s) { std::string result; for (size_t i = 0; i < s.size(); ++i) { if (s[i] == ',') { result += "=2C"; } else if (s[i] == '=') { result += "=3D"; } else { result += s[i]; } } return result; } SCRAMSHA1ClientAuthenticator::SCRAMSHA1ClientAuthenticator(const std::string& nonce, bool useChannelBinding) : ClientAuthenticator(useChannelBinding ? "SCRAM-SHA-1-PLUS" : "SCRAM-SHA-1"), step(Initial), clientnonce(nonce), useChannelBinding(useChannelBinding) { } boost::optional SCRAMSHA1ClientAuthenticator::getResponse() const { if (step == Initial) { return createSafeByteArray(concat(getGS2Header(), getInitialBareClientMessage())); } else if (step == Proof) { ByteArray clientKey = HMAC_SHA1()(saltedPassword, createByteArray("Client Key")); ByteArray storedKey = SHA1::getHash(clientKey); ByteArray clientSignature = HMAC_SHA1()(createSafeByteArray(storedKey), authMessage); ByteArray clientProof = clientKey; for (unsigned int i = 0; i < clientProof.size(); ++i) { clientProof[i] ^= clientSignature[i]; } ByteArray result = concat(getFinalMessageWithoutProof(), createByteArray(",p="), createByteArray(Base64::encode(clientProof))); return createSafeByteArray(result); } else { return boost::optional(); } } bool SCRAMSHA1ClientAuthenticator::setChallenge(const boost::optional& challenge) { if (step == Initial) { if (!challenge) { return false; } initialServerMessage = *challenge; std::map keys = parseMap(byteArrayToString(initialServerMessage)); // Extract the salt ByteArray salt = Base64::decode(keys['s']); // Extract the server nonce std::string clientServerNonce = keys['r']; if (clientServerNonce.size() <= clientnonce.size()) { return false; } std::string receivedClientNonce = clientServerNonce.substr(0, clientnonce.size()); if (receivedClientNonce != clientnonce) { return false; } serverNonce = createByteArray(clientServerNonce.substr(clientnonce.size(), clientServerNonce.npos)); // Extract the number of iterations int iterations = 0; try { iterations = boost::lexical_cast(keys['i']); } catch (const boost::bad_lexical_cast&) { return false; } if (iterations <= 0) { return false; } // Compute all the values needed for the server signature try { saltedPassword = PBKDF2::encode(StringPrep::getPrepared(getPassword(), StringPrep::SASLPrep), salt, iterations); } catch (const std::exception&) { } authMessage = concat(getInitialBareClientMessage(), createByteArray(","), initialServerMessage, createByteArray(","), getFinalMessageWithoutProof()); ByteArray serverKey = HMAC_SHA1()(saltedPassword, createByteArray("Server Key")); serverSignature = HMAC_SHA1()(serverKey, authMessage); step = Proof; return true; } else if (step == Proof) { ByteArray result = concat(createByteArray("v="), createByteArray(Base64::encode(serverSignature))); step = Final; return challenge && challenge == result; } else { return true; } } std::map SCRAMSHA1ClientAuthenticator::parseMap(const std::string& s) { std::map result; if (s.size() > 0) { char key = 0; std::string value; size_t i = 0; bool expectKey = true; while (i < s.size()) { if (expectKey) { key = s[i]; expectKey = false; i++; } else if (s[i] == ',') { result[static_cast(key)] = value; value = ""; expectKey = true; } else { value += s[i]; } i++; } result[key] = value; } return result; } ByteArray SCRAMSHA1ClientAuthenticator::getInitialBareClientMessage() const { std::string authenticationID; try { authenticationID = StringPrep::getPrepared(getAuthenticationID(), StringPrep::SASLPrep); } catch (const std::exception&) { } return createByteArray(std::string("n=" + escape(authenticationID) + ",r=" + clientnonce)); } ByteArray SCRAMSHA1ClientAuthenticator::getGS2Header() const { ByteArray channelBindingHeader(createByteArray("n")); if (tlsChannelBindingData) { if (useChannelBinding) { channelBindingHeader = createByteArray("p=tls-unique"); } else { channelBindingHeader = createByteArray("y"); } } return concat(channelBindingHeader, createByteArray(","), (getAuthorizationID().empty() ? ByteArray() : createByteArray("a=" + escape(getAuthorizationID()))), createByteArray(",")); } void SCRAMSHA1ClientAuthenticator::setTLSChannelBindingData(const ByteArray& channelBindingData) { this->tlsChannelBindingData = channelBindingData; } ByteArray SCRAMSHA1ClientAuthenticator::getFinalMessageWithoutProof() const { ByteArray channelBindData; if (useChannelBinding && tlsChannelBindingData) { channelBindData = *tlsChannelBindingData; } return concat(createByteArray("c=" + Base64::encode(concat(getGS2Header(), channelBindData)) + ",r=" + clientnonce), serverNonce); } } swift-im-2.0+dev6/Swiften/SASL/PLAINClientAuthenticator.cpp0000644000175000017500000000130312227051774023302 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include namespace Swift { PLAINClientAuthenticator::PLAINClientAuthenticator() : ClientAuthenticator("PLAIN") { } boost::optional PLAINClientAuthenticator::getResponse() const { return concat(createSafeByteArray(getAuthorizationID()), createSafeByteArray('\0'), createSafeByteArray(getAuthenticationID()), createSafeByteArray('\0'), getPassword()); } bool PLAINClientAuthenticator::setChallenge(const boost::optional&) { return true; } } swift-im-2.0+dev6/Swiften/SASL/DIGESTMD5Properties.cpp0000644000175000017500000000571212227051774022117 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include namespace Swift { namespace { bool insideQuotes(const ByteArray& v) { if (v.empty()) { return false; } else if (v.size() == 1) { return v[0] == '"'; } else if (v[0] == '"') { return v[v.size() - 1] != '"'; } else { return false; } } ByteArray stripQuotes(const ByteArray& v) { const char* data = reinterpret_cast(vecptr(v)); size_t size = v.size(); if (v[0] == '"') { data++; size--; } if (v[v.size() - 1] == '"') { size--; } return createByteArray(data, size); } } DIGESTMD5Properties::DIGESTMD5Properties() { } DIGESTMD5Properties DIGESTMD5Properties::parse(const ByteArray& data) { DIGESTMD5Properties result; bool inKey = true; ByteArray currentKey; ByteArray currentValue; for (size_t i = 0; i < data.size(); ++i) { char c = data[i]; if (inKey) { if (c == '=') { inKey = false; } else { currentKey.push_back(c); } } else { if (c == ',' && !insideQuotes(currentValue)) { std::string key = byteArrayToString(currentKey); if (isQuoted(key)) { result.setValue(key, byteArrayToString(stripQuotes(currentValue))); } else { result.setValue(key, byteArrayToString(currentValue)); } inKey = true; currentKey = ByteArray(); currentValue = ByteArray(); } else { currentValue.push_back(c); } } } if (!currentKey.empty()) { std::string key = byteArrayToString(currentKey); if (isQuoted(key)) { result.setValue(key, byteArrayToString(stripQuotes(currentValue))); } else { result.setValue(key, byteArrayToString(currentValue)); } } return result; } ByteArray DIGESTMD5Properties::serialize() const { ByteArray result; for(DIGESTMD5PropertiesMap::const_iterator i = properties.begin(); i != properties.end(); ++i) { if (i != properties.begin()) { result.push_back(','); } append(result, createByteArray(i->first)); result.push_back('='); if (isQuoted(i->first)) { append(result, createByteArray("\"")); append(result, i->second); append(result, createByteArray("\"")); } else { append(result, i->second); } } return result; } boost::optional DIGESTMD5Properties::getValue(const std::string& key) const { DIGESTMD5PropertiesMap::const_iterator i = properties.find(key); if (i != properties.end()) { return byteArrayToString(i->second); } else { return boost::optional(); } } void DIGESTMD5Properties::setValue(const std::string& key, const std::string& value) { properties.insert(DIGESTMD5PropertiesMap::value_type(key, createByteArray(value))); } bool DIGESTMD5Properties::isQuoted(const std::string& p) { return p == "authzid" || p == "cnonce" || p == "digest-uri" || p == "nonce" || p == "realm" || p == "username"; } } swift-im-2.0+dev6/Swiften/SASL/SCRAMSHA1ClientAuthenticator.h0000644000175000017500000000242512227051774023374 0ustar kismithkismith/* * 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 #include #include #include #include #include namespace Swift { class SWIFTEN_API SCRAMSHA1ClientAuthenticator : public ClientAuthenticator { public: SCRAMSHA1ClientAuthenticator(const std::string& nonce, bool useChannelBinding = false); void setTLSChannelBindingData(const ByteArray& channelBindingData); virtual boost::optional getResponse() const; virtual bool setChallenge(const boost::optional&); private: ByteArray getInitialBareClientMessage() const; ByteArray getGS2Header() const; ByteArray getFinalMessageWithoutProof() const; static std::map parseMap(const std::string&); private: enum Step { Initial, Proof, Final } step; std::string clientnonce; ByteArray initialServerMessage; ByteArray serverNonce; ByteArray authMessage; ByteArray saltedPassword; ByteArray serverSignature; bool useChannelBinding; boost::optional tlsChannelBindingData; }; } swift-im-2.0+dev6/Swiften/SASL/PLAINMessage.h0000644000175000017500000000154112227051774020366 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ // TODO: Get rid of this // #pragma once #include #include #include namespace Swift { class SWIFTEN_API PLAINMessage { public: PLAINMessage(const std::string& authcid, const SafeByteArray& password, const std::string& authzid = ""); PLAINMessage(const SafeByteArray& value); SafeByteArray getValue() const; const std::string& getAuthenticationID() const { return authcid; } const SafeByteArray& getPassword() const { return password; } const std::string& getAuthorizationID() const { return authzid; } private: std::string authcid; std::string authzid; SafeByteArray password; }; } swift-im-2.0+dev6/Swiften/SASL/DIGESTMD5Properties.h0000644000175000017500000000147512227051774021566 0ustar kismithkismith/* * 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 #include #include #include #include namespace Swift { class SWIFTEN_API DIGESTMD5Properties { public: DIGESTMD5Properties(); boost::optional getValue(const std::string& key) const; void setValue(const std::string& key, const std::string& value); ByteArray serialize() const; static DIGESTMD5Properties parse(const ByteArray&); private: static bool isQuoted(const std::string& property); private: typedef std::multimap DIGESTMD5PropertiesMap; DIGESTMD5PropertiesMap properties; }; } swift-im-2.0+dev6/Swiften/MUC/0000755000175000017500000000000012227051774015726 5ustar kismithkismithswift-im-2.0+dev6/Swiften/MUC/MUCBookmarkManager.h0000644000175000017500000000266512227051774021515 0ustar kismithkismith/* * 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 #include #include #include #include #include #include #include namespace Swift { class IQRouter; class SWIFTEN_API MUCBookmarkManager { public: MUCBookmarkManager(IQRouter* iqRouter); void addBookmark(const MUCBookmark& bookmark); void removeBookmark(const MUCBookmark& bookmark); void replaceBookmark(const MUCBookmark& oldBookmark, const MUCBookmark& newBookmark); const std::vector& getBookmarks() const; public: boost::signal onBookmarkAdded; boost::signal onBookmarkRemoved; /** * When server bookmarks are ready to be used (request response has been received). */ boost::signal onBookmarksReady; private: bool containsEquivalent(const std::vector& list, const MUCBookmark& bookmark); void handleBookmarksReceived(boost::shared_ptr payload, ErrorPayload::ref error); void flush(); private: bool ready_; std::vector bookmarks_; IQRouter* iqRouter_; boost::shared_ptr storage; }; } swift-im-2.0+dev6/Swiften/MUC/MUCManager.h0000644000175000017500000000127212227051774020020 0ustar kismithkismith/* * 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 #include namespace Swift { class IQRouter; class StanzaChannel; class DirectedPresenceSender; class MUCRegistry; class SWIFTEN_API MUCManager { public: MUCManager(StanzaChannel* stanzaChannel, IQRouter* iqRouter, DirectedPresenceSender* presenceSender, MUCRegistry* mucRegistry); MUC::ref createMUC(const JID&); private: StanzaChannel* stanzaChannel; IQRouter* iqRouter; DirectedPresenceSender* presenceSender; MUCRegistry* mucRegistry; }; } swift-im-2.0+dev6/Swiften/MUC/MUCBookmarkManager.cpp0000644000175000017500000000641612227051774022046 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "MUCBookmarkManager.h" #include #include #include #include #include #include #include namespace Swift { MUCBookmarkManager::MUCBookmarkManager(IQRouter* iqRouter) { iqRouter_ = iqRouter; ready_ = false; GetPrivateStorageRequest::ref request = GetPrivateStorageRequest::create(iqRouter_); request->onResponse.connect(boost::bind(&MUCBookmarkManager::handleBookmarksReceived, this, _1, _2)); request->send(); } void MUCBookmarkManager::handleBookmarksReceived(boost::shared_ptr payload, ErrorPayload::ref error) { if (error) { return; } ready_ = true; onBookmarksReady(); storage = payload; std::vector receivedBookmarks; foreach (Storage::Room room, payload->getRooms()) { receivedBookmarks.push_back(MUCBookmark(room)); } std::vector newBookmarks; foreach (const MUCBookmark& oldBookmark, bookmarks_) { if (containsEquivalent(receivedBookmarks, oldBookmark)) { newBookmarks.push_back(oldBookmark); } else { onBookmarkRemoved(oldBookmark); } } foreach (const MUCBookmark& newBookmark, receivedBookmarks) { if (!containsEquivalent(bookmarks_, newBookmark)) { newBookmarks.push_back(newBookmark); onBookmarkAdded(newBookmark); } } bookmarks_ = newBookmarks; } bool MUCBookmarkManager::containsEquivalent(const std::vector& list, const MUCBookmark& bookmark) { return std::find(list.begin(), list.end(), bookmark) != list.end(); } void MUCBookmarkManager::replaceBookmark(const MUCBookmark& oldBookmark, const MUCBookmark& newBookmark) { if (!ready_) return; for (size_t i = 0; i < bookmarks_.size(); i++) { if (bookmarks_[i] == oldBookmark) { bookmarks_[i] = newBookmark; flush(); onBookmarkRemoved(oldBookmark); onBookmarkAdded(newBookmark); return; } } } void MUCBookmarkManager::addBookmark(const MUCBookmark& bookmark) { if (!ready_) return; bookmarks_.push_back(bookmark); onBookmarkAdded(bookmark); flush(); } void MUCBookmarkManager::removeBookmark(const MUCBookmark& bookmark) { if (!ready_) return; std::vector::iterator it; for (it = bookmarks_.begin(); it != bookmarks_.end(); ++it) { if ((*it) == bookmark) { bookmarks_.erase(it); onBookmarkRemoved(bookmark); break; } } flush(); } void MUCBookmarkManager::flush() { if (!storage) { storage = boost::make_shared(); } // Update the storage element storage->clearRooms(); foreach(const MUCBookmark& bookmark, bookmarks_) { storage->addRoom(bookmark.toStorage()); } // Send an iq to save the storage element SetPrivateStorageRequest::ref request = SetPrivateStorageRequest::create(storage, iqRouter_); // FIXME: We should care about the result //request->onResponse.connect(boost::bind(&MUCBookmarkManager::handleBookmarksSet, this, _1, _2)); request->send(); } const std::vector& MUCBookmarkManager::getBookmarks() const { return bookmarks_; } } swift-im-2.0+dev6/Swiften/MUC/MUCRegistry.cpp0000644000175000017500000000102112227051774020601 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include namespace Swift { MUCRegistry::~MUCRegistry() { } bool MUCRegistry::isMUC(const JID& j) const { return std::find(mucs.begin(), mucs.end(), j) != mucs.end(); } void MUCRegistry::addMUC(const JID& j) { mucs.push_back(j); } void MUCRegistry::removeMUC(const JID& j) { erase(mucs, j); } } swift-im-2.0+dev6/Swiften/MUC/MUCManager.cpp0000644000175000017500000000114112227051774020346 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { MUCManager::MUCManager(StanzaChannel* stanzaChannel, IQRouter* iqRouter, DirectedPresenceSender* presenceSender, MUCRegistry* mucRegistry) : stanzaChannel(stanzaChannel), iqRouter(iqRouter), presenceSender(presenceSender), mucRegistry(mucRegistry) { } MUC::ref MUCManager::createMUC(const JID& jid) { return MUC::ref(new MUC(stanzaChannel, iqRouter, presenceSender, jid, mucRegistry)); } } swift-im-2.0+dev6/Swiften/MUC/MUCBookmark.h0000644000175000017500000000364012227051774020214 0ustar kismithkismith/* * 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 #include #include #include namespace Swift { class MUCBookmark { public: MUCBookmark(const Storage::Room& room) { name_ = room.name; room_ = room.jid; nick_ = room.nick; password_ = room.password; autojoin_ = room.autoJoin; } MUCBookmark(const JID& room, const std::string& bookmarkName) : room_(room), name_(bookmarkName), autojoin_(false) { } void setAutojoin(bool enabled) { autojoin_ = enabled; } bool getAutojoin() const { return autojoin_; } void setNick(const boost::optional& nick) { nick_ = nick; } void setPassword(const boost::optional& password) { password_ = password; } const boost::optional& getNick() const { return nick_; } const boost::optional& getPassword() const { return password_; } const std::string& getName() const { return name_; } const JID& getRoom() const { return room_; } bool operator==(const MUCBookmark& rhs) const { /* FIXME: not checking passwords for equality - which might make sense, perhaps */ return rhs.room_ == room_ && rhs.name_ == name_ && rhs.nick_ == nick_ /*&& rhs.password_ == password_*/ && rhs.autojoin_ == autojoin_; } Storage::Room toStorage() const { Storage::Room room; room.name = name_; room.jid = room_; if (nick_) { room.nick = *nick_; } if (password_) { room.password = *password_; } room.autoJoin = autojoin_; return room; } private: JID room_; std::string name_; boost::optional nick_; boost::optional password_; bool autojoin_; }; } swift-im-2.0+dev6/Swiften/MUC/UnitTest/0000755000175000017500000000000012227051774017505 5ustar kismithkismithswift-im-2.0+dev6/Swiften/MUC/UnitTest/MUCTest.cpp0000644000175000017500000001572112227051774021503 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Swift; class MUCTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(MUCTest); CPPUNIT_TEST(testJoin); CPPUNIT_TEST(testJoin_ChangePresenceDuringJoinDoesNotSendPresenceBeforeJoinSuccess); CPPUNIT_TEST(testJoin_ChangePresenceDuringJoinResendsPresenceAfterJoinSuccess); CPPUNIT_TEST(testCreateInstant); CPPUNIT_TEST(testReplicateBug); /*CPPUNIT_TEST(testJoin_Success); CPPUNIT_TEST(testJoin_Fail);*/ CPPUNIT_TEST_SUITE_END(); public: void setUp() { channel = new DummyStanzaChannel(); router = new IQRouter(channel); mucRegistry = new MUCRegistry(); stanzaChannelPresenceSender = new StanzaChannelPresenceSender(channel); presenceSender = new DirectedPresenceSender(stanzaChannelPresenceSender); } void tearDown() { delete presenceSender; delete stanzaChannelPresenceSender; delete mucRegistry; delete router; delete channel; } void testJoin() { MUC::ref testling = createMUC(JID("foo@bar.com")); testling->joinAs("Alice"); CPPUNIT_ASSERT(mucRegistry->isMUC(JID("foo@bar.com"))); Presence::ref p = channel->getStanzaAtIndex(0); CPPUNIT_ASSERT(p); CPPUNIT_ASSERT_EQUAL(JID("foo@bar.com/Alice"), p->getTo()); } void testJoin_ChangePresenceDuringJoinDoesNotSendPresenceBeforeJoinSuccess() { MUC::ref testling = createMUC(JID("foo@bar.com")); testling->joinAs("Alice"); presenceSender->sendPresence(Presence::create("Test")); CPPUNIT_ASSERT_EQUAL(2, static_cast(channel->sentStanzas.size())); } void testJoin_ChangePresenceDuringJoinResendsPresenceAfterJoinSuccess() { MUC::ref testling = createMUC(JID("foo@bar.com")); testling->joinAs("Alice"); presenceSender->sendPresence(Presence::create("Test")); receivePresence(JID("foo@bar.com/Rabbit"), "Here"); CPPUNIT_ASSERT_EQUAL(3, static_cast(channel->sentStanzas.size())); Presence::ref p = channel->getStanzaAtIndex(2); CPPUNIT_ASSERT(p); CPPUNIT_ASSERT_EQUAL(JID("foo@bar.com/Alice"), p->getTo()); CPPUNIT_ASSERT_EQUAL(std::string("Test"), p->getStatus()); } void testCreateInstant() { MUC::ref testling = createMUC(JID("rabbithole@wonderland.lit")); testling->joinAs("Alice"); Presence::ref serverRespondsLocked = boost::make_shared(); serverRespondsLocked->setFrom(JID("rabbithole@wonderland.lit/Alice")); MUCUserPayload::ref mucPayload(new MUCUserPayload()); MUCItem myItem; myItem.affiliation = MUCOccupant::Owner; myItem.role = MUCOccupant::Moderator; mucPayload->addItem(myItem); mucPayload->addStatusCode(MUCUserPayload::StatusCode(110)); mucPayload->addStatusCode(MUCUserPayload::StatusCode(201)); serverRespondsLocked->addPayload(mucPayload); channel->onPresenceReceived(serverRespondsLocked); CPPUNIT_ASSERT_EQUAL(2, static_cast(channel->sentStanzas.size())); IQ::ref iq = channel->getStanzaAtIndex(1); CPPUNIT_ASSERT(iq); CPPUNIT_ASSERT(iq->getPayload()); CPPUNIT_ASSERT(iq->getPayload()->getForm()); CPPUNIT_ASSERT_EQUAL(Form::SubmitType, iq->getPayload()->getForm()->getType()); } void testReplicateBug() { Presence::ref initialPresence = boost::make_shared(); initialPresence->setStatus(""); VCard::ref vcard = boost::make_shared(); vcard->setPhoto(createByteArray("15c30080ae98ec48be94bf0e191d43edd06e500a")); initialPresence->addPayload(vcard); CapsInfo::ref caps = boost::make_shared(); caps->setNode("http://swift.im"); caps->setVersion("p2UP0DrcVgKM6jJqYN/B92DKK0o="); initialPresence->addPayload(caps); channel->sendPresence(initialPresence); MUC::ref testling = createMUC(JID("test@rooms.swift.im")); testling->joinAs("Test"); Presence::ref serverRespondsLocked = boost::make_shared(); serverRespondsLocked->setFrom(JID("test@rooms.swift.im/Test")); serverRespondsLocked->setTo(JID("test@swift.im/6913d576d55f0b67")); serverRespondsLocked->addPayload(vcard); serverRespondsLocked->addPayload(caps); serverRespondsLocked->setStatus(""); MUCUserPayload::ref mucPayload(new MUCUserPayload()); MUCItem myItem; myItem.affiliation = MUCOccupant::Owner; myItem.role = MUCOccupant::Moderator; mucPayload->addItem(myItem); mucPayload->addStatusCode(MUCUserPayload::StatusCode(201)); serverRespondsLocked->addPayload(mucPayload); channel->onPresenceReceived(serverRespondsLocked); CPPUNIT_ASSERT_EQUAL(3, static_cast(channel->sentStanzas.size())); IQ::ref iq = channel->getStanzaAtIndex(2); CPPUNIT_ASSERT(iq); CPPUNIT_ASSERT(iq->getPayload()); CPPUNIT_ASSERT(iq->getPayload()->getForm()); CPPUNIT_ASSERT_EQUAL(Form::SubmitType, iq->getPayload()->getForm()->getType()); } /*void testJoin_Success() { MUC::ref testling = createMUC(JID("foo@bar.com")); testling->onJoinFinished.connect(boost::bind(&MUCTest::handleJoinFinished, this, _1, _2)); testling->joinAs("Alice"); receivePresence(JID("foo@bar.com/Rabbit"), "Here"); CPPUNIT_ASSERT_EQUAL(1, static_cast(joinResults.size())); CPPUNIT_ASSERT_EQUAL(std::string("Alice"), joinResults[0].nick); CPPUNIT_ASSERT(joinResults[0].error); } void testJoin_Fail() { //CPPUNIT_ASSERT(!mucRegistry->isMUC(JID("foo@bar.com"))); }*/ private: MUC::ref createMUC(const JID& jid) { return boost::make_shared(channel, router, presenceSender, jid, mucRegistry); } void handleJoinFinished(const std::string& nick, ErrorPayload::ref error) { JoinResult r; r.nick = nick; r.error = error; joinResults.push_back(r); } void receivePresence(const JID& jid, const std::string& status) { Presence::ref p = Presence::create(status); p->setFrom(jid); //MUCUserPayload::ref mucUserPayload = boost::make_shared(); //mucUserPayload->addItem(item); //p->addPayload(mucUserPayload); channel->onPresenceReceived(p); } private: DummyStanzaChannel* channel; IQRouter* router; MUCRegistry* mucRegistry; StanzaChannelPresenceSender* stanzaChannelPresenceSender; DirectedPresenceSender* presenceSender; struct JoinResult { std::string nick; ErrorPayload::ref error; }; std::vector joinResults; }; CPPUNIT_TEST_SUITE_REGISTRATION(MUCTest); swift-im-2.0+dev6/Swiften/MUC/MUCRegistry.h0000644000175000017500000000075112227051774020257 0ustar kismithkismith/* * 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 #include #include namespace Swift { class JID; class SWIFTEN_API MUCRegistry { public: ~MUCRegistry(); bool isMUC(const JID& j) const; void addMUC(const JID& j); void removeMUC(const JID& j); private: std::vector mucs; }; } swift-im-2.0+dev6/Swiften/MUC/MUC.cpp0000644000175000017500000003335112227051774017063 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Swift { typedef std::pair StringMUCOccupantPair; MUC::MUC(StanzaChannel* stanzaChannel, IQRouter* iqRouter, DirectedPresenceSender* presenceSender, const JID &muc, MUCRegistry* mucRegistry) : ownMUCJID(muc), stanzaChannel(stanzaChannel), iqRouter_(iqRouter), presenceSender(presenceSender), mucRegistry(mucRegistry), createAsReservedIfNew(false), unlocking(false) { scopedConnection_ = stanzaChannel->onPresenceReceived.connect(boost::bind(&MUC::handleIncomingPresence, this, _1)); } //FIXME: discover reserved nickname /** * Join the MUC with default context. */ void MUC::joinAs(const std::string &nick) { joinSince_ = boost::posix_time::not_a_date_time; internalJoin(nick); } /** * Set the password used for entering the room. */ void MUC::setPassword(const boost::optional& newPassword) { password = newPassword; } /** * Join the MUC with context since date. */ void MUC::joinWithContextSince(const std::string &nick, const boost::posix_time::ptime& since) { joinSince_ = since; internalJoin(nick); } void MUC::internalJoin(const std::string &nick) { //TODO: history request joinComplete_ = false; joinSucceeded_ = false; mucRegistry->addMUC(getJID()); ownMUCJID = JID(ownMUCJID.getNode(), ownMUCJID.getDomain(), nick); Presence::ref joinPresence = boost::make_shared(*presenceSender->getLastSentUndirectedPresence()); assert(joinPresence->getType() == Presence::Available); joinPresence->setTo(ownMUCJID); MUCPayload::ref mucPayload = boost::make_shared(); if (joinSince_ != boost::posix_time::not_a_date_time) { mucPayload->setSince(joinSince_); } if (password) { mucPayload->setPassword(*password); } joinPresence->addPayload(mucPayload); presenceSender->sendPresence(joinPresence); } void MUC::part() { presenceSender->removeDirectedPresenceReceiver(ownMUCJID, DirectedPresenceSender::AndSendPresence); mucRegistry->removeMUC(getJID()); } void MUC::handleUserLeft(LeavingType type) { std::map::iterator i = occupants.find(ownMUCJID.getResource()); if (i != occupants.end()) { MUCOccupant me = i->second; occupants.erase(i); onOccupantLeft(me, type, ""); } occupants.clear(); joinComplete_ = false; joinSucceeded_ = false; presenceSender->removeDirectedPresenceReceiver(ownMUCJID, DirectedPresenceSender::DontSendPresence); } void MUC::handleIncomingPresence(Presence::ref presence) { if (!isFromMUC(presence->getFrom())) { return; } MUCUserPayload::ref mucPayload; foreach (MUCUserPayload::ref payload, presence->getPayloads()) { if (!payload->getItems().empty() || !payload->getStatusCodes().empty()) { mucPayload = payload; } } // On the first incoming presence, check if our join has succeeded // (i.e. we start getting non-error presence from the MUC) or not if (!joinSucceeded_) { if (presence->getType() == Presence::Error) { std::string reason; onJoinFailed(presence->getPayload()); return; } else { joinSucceeded_ = true; presenceSender->addDirectedPresenceReceiver(ownMUCJID, DirectedPresenceSender::AndSendPresence); } } std::string nick = presence->getFrom().getResource(); if (nick.empty()) { return; } MUCOccupant::Role role(MUCOccupant::NoRole); MUCOccupant::Affiliation affiliation(MUCOccupant::NoAffiliation); boost::optional realJID; if (mucPayload && mucPayload->getItems().size() > 0) { role = mucPayload->getItems()[0].role ? mucPayload->getItems()[0].role.get() : MUCOccupant::NoRole; affiliation = mucPayload->getItems()[0].affiliation ? mucPayload->getItems()[0].affiliation.get() : MUCOccupant::NoAffiliation; realJID = mucPayload->getItems()[0].realJID; } //100 is non-anonymous //TODO: 100 may also be specified in a //170 is room logging to http //TODO: Nick changes if (presence->getType() == Presence::Unavailable) { LeavingType type = LeavePart; if (mucPayload) { if (boost::dynamic_pointer_cast(mucPayload->getPayload())) { type = LeaveDestroy; } else foreach (MUCUserPayload::StatusCode status, mucPayload->getStatusCodes()) { if (status.code == 307) { type = LeaveKick; } else if (status.code == 301) { type = LeaveBan; } else if (status.code == 321) { type = LeaveNotMember; } } } if (presence->getFrom() == ownMUCJID) { handleUserLeft(type); return; } else { std::map::iterator i = occupants.find(nick); if (i != occupants.end()) { //TODO: part type onOccupantLeft(i->second, type, ""); occupants.erase(i); } } } else if (presence->getType() == Presence::Available) { std::map::iterator it = occupants.find(nick); MUCOccupant occupant(nick, role, affiliation); bool isJoin = true; if (realJID) { occupant.setRealJID(realJID.get()); } if (it != occupants.end()) { isJoin = false; MUCOccupant oldOccupant = it->second; if (oldOccupant.getRole() != role) { onOccupantRoleChanged(nick, occupant, oldOccupant.getRole()); } if (oldOccupant.getAffiliation() != affiliation) { onOccupantAffiliationChanged(nick, affiliation, oldOccupant.getAffiliation()); } occupants.erase(it); } std::pair::iterator, bool> result = occupants.insert(std::make_pair(nick, occupant)); if (isJoin) { onOccupantJoined(result.first->second); } onOccupantPresenceChange(presence); } if (mucPayload && !joinComplete_) { foreach (MUCUserPayload::StatusCode status, mucPayload->getStatusCodes()) { if (status.code == 110) { /* Simply knowing this is your presence is enough, 210 doesn't seem to be necessary. */ joinComplete_ = true; if (ownMUCJID != presence->getFrom()) { presenceSender->removeDirectedPresenceReceiver(ownMUCJID, DirectedPresenceSender::DontSendPresence); ownMUCJID = presence->getFrom(); presenceSender->addDirectedPresenceReceiver(ownMUCJID, DirectedPresenceSender::AndSendPresence); } onJoinComplete(getOwnNick()); } if (status.code == 201) { /* Room is created and locked */ /* Currently deal with this by making an instant room */ if (ownMUCJID != presence->getFrom()) { presenceSender->removeDirectedPresenceReceiver(ownMUCJID, DirectedPresenceSender::DontSendPresence); ownMUCJID = presence->getFrom(); presenceSender->addDirectedPresenceReceiver(ownMUCJID, DirectedPresenceSender::AndSendPresence); } if (createAsReservedIfNew) { unlocking = true; requestConfigurationForm(); } else { MUCOwnerPayload::ref mucPayload(new MUCOwnerPayload()); presenceSender->addDirectedPresenceReceiver(ownMUCJID, DirectedPresenceSender::DontSendPresence); mucPayload->setPayload(boost::make_shared
(Form::SubmitType)); GenericRequest* request = new GenericRequest(IQ::Set, getJID(), mucPayload, iqRouter_); request->onResponse.connect(boost::bind(&MUC::handleCreationConfigResponse, this, _1, _2)); request->send(); } } } } } void MUC::handleCreationConfigResponse(MUCOwnerPayload::ref /*unused*/, ErrorPayload::ref error) { unlocking = false; if (error) { presenceSender->removeDirectedPresenceReceiver(ownMUCJID, DirectedPresenceSender::AndSendPresence); onJoinFailed(error); } else { onJoinComplete(getOwnNick()); /* Previously, this wasn't needed here, as the presence duplication bug caused an emit elsewhere. */ } } bool MUC::hasOccupant(const std::string& nick) { return occupants.find(nick) != occupants.end(); } const MUCOccupant& MUC::getOccupant(const std::string& nick) { return occupants.find(nick)->second; } void MUC::kickOccupant(const JID& jid) { changeOccupantRole(jid, MUCOccupant::NoRole); } /** * Call with the room JID, not the real JID. */ void MUC::changeOccupantRole(const JID& jid, MUCOccupant::Role role) { MUCAdminPayload::ref mucPayload = boost::make_shared(); MUCItem item; item.role = role; item.nick = jid.getResource(); mucPayload->addItem(item); GenericRequest* request = new GenericRequest(IQ::Set, getJID(), mucPayload, iqRouter_); request->onResponse.connect(boost::bind(&MUC::handleOccupantRoleChangeResponse, this, _1, _2, jid, role)); request->send(); } void MUC::handleOccupantRoleChangeResponse(MUCAdminPayload::ref /*unused*/, ErrorPayload::ref error, const JID& jid, MUCOccupant::Role role) { if (error) { onRoleChangeFailed(error, jid, role); } } void MUC::requestAffiliationList(MUCOccupant::Affiliation affiliation) { MUCAdminPayload::ref mucPayload = boost::make_shared(); MUCItem item; item.affiliation = affiliation; mucPayload->addItem(item); GenericRequest* request = new GenericRequest(IQ::Get, getJID(), mucPayload, iqRouter_); request->onResponse.connect(boost::bind(&MUC::handleAffiliationListResponse, this, _1, _2, affiliation)); request->send(); } /** * Must be called with the real JID, not the room JID. */ void MUC::changeAffiliation(const JID& jid, MUCOccupant::Affiliation affiliation) { MUCAdminPayload::ref mucPayload = boost::make_shared(); MUCItem item; item.affiliation = affiliation; item.realJID = jid.toBare(); mucPayload->addItem(item); GenericRequest* request = new GenericRequest(IQ::Set, getJID(), mucPayload, iqRouter_); request->onResponse.connect(boost::bind(&MUC::handleAffiliationChangeResponse, this, _1, _2, jid, affiliation)); request->send(); } void MUC::handleAffiliationListResponse(MUCAdminPayload::ref payload, ErrorPayload::ref error, MUCOccupant::Affiliation affiliation) { if (error) { onAffiliationListFailed(error); } else { std::vector jids; foreach (MUCItem item, payload->getItems()) { if (item.realJID) { jids.push_back(*item.realJID); } } onAffiliationListReceived(affiliation, jids); } } void MUC::handleAffiliationChangeResponse(MUCAdminPayload::ref /*unused*/, ErrorPayload::ref error, const JID& jid, MUCOccupant::Affiliation affiliation) { if (error) { onAffiliationChangeFailed(error, jid, affiliation); } } void MUC::changeSubject(const std::string& subject) { Message::ref message = boost::make_shared(); message->setSubject(subject); message->setType(Message::Groupchat); message->setTo(ownMUCJID.toBare()); stanzaChannel->sendMessage(message); } void MUC::requestConfigurationForm() { MUCOwnerPayload::ref mucPayload(new MUCOwnerPayload()); GenericRequest* request = new GenericRequest(IQ::Get, getJID(), mucPayload, iqRouter_); request->onResponse.connect(boost::bind(&MUC::handleConfigurationFormReceived, this, _1, _2)); request->send(); } void MUC::cancelConfigureRoom() { MUCOwnerPayload::ref mucPayload(new MUCOwnerPayload()); mucPayload->setPayload(boost::make_shared(Form::CancelType)); GenericRequest* request = new GenericRequest(IQ::Set, getJID(), mucPayload, iqRouter_); request->send(); } void MUC::handleConfigurationFormReceived(MUCOwnerPayload::ref payload, ErrorPayload::ref error) { Form::ref form; if (payload) { form = payload->getForm(); } if (error || !form) { onConfigurationFailed(error); } else { onConfigurationFormReceived(form); } } void MUC::handleConfigurationResultReceived(MUCOwnerPayload::ref /*payload*/, ErrorPayload::ref error) { if (error) { onConfigurationFailed(error); } } void MUC::configureRoom(Form::ref form) { MUCOwnerPayload::ref mucPayload(new MUCOwnerPayload()); mucPayload->setPayload(form); GenericRequest* request = new GenericRequest(IQ::Set, getJID(), mucPayload, iqRouter_); if (unlocking) { request->onResponse.connect(boost::bind(&MUC::handleCreationConfigResponse, this, _1, _2)); } else { request->onResponse.connect(boost::bind(&MUC::handleConfigurationResultReceived, this, _1, _2)); } request->send(); } void MUC::destroyRoom() { MUCOwnerPayload::ref mucPayload = boost::make_shared(); MUCDestroyPayload::ref mucDestroyPayload = boost::make_shared(); mucPayload->setPayload(mucDestroyPayload); GenericRequest* request = new GenericRequest(IQ::Set, getJID(), mucPayload, iqRouter_); request->onResponse.connect(boost::bind(&MUC::handleConfigurationResultReceived, this, _1, _2)); request->send(); } void MUC::invitePerson(const JID& person, const std::string& reason) { Message::ref message = boost::make_shared(); message->setTo(person); message->setType(Message::Normal); MUCInvitationPayload::ref invite = boost::make_shared(); invite->setReason(reason); invite->setJID(ownMUCJID.toBare()); message->addPayload(invite); stanzaChannel->sendMessage(message); } //TODO: Invites(direct/mediated) //TODO: requesting membership //TODO: get member list //TODO: request voice //TODO: moderator use cases //TODO: Admin use cases //TODO: Owner use cases } swift-im-2.0+dev6/Swiften/MUC/MUC.h0000644000175000017500000001175212227051774016531 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Swift { class StanzaChannel; class IQRouter; class DirectedPresenceSender; class SWIFTEN_API MUC { public: typedef boost::shared_ptr ref; enum JoinResult { JoinSucceeded, JoinFailed }; enum LeavingType { LeavePart, LeaveKick, LeaveBan, LeaveDestroy, LeaveNotMember, Disconnect }; public: MUC(StanzaChannel* stanzaChannel, IQRouter* iqRouter, DirectedPresenceSender* presenceSender, const JID &muc, MUCRegistry* mucRegistry); /** * Returns the (bare) JID of the MUC. */ JID getJID() const { return ownMUCJID.toBare(); } void joinAs(const std::string &nick); void joinWithContextSince(const std::string &nick, const boost::posix_time::ptime& since); /*void queryRoomInfo(); */ /*void queryRoomItems(); */ std::string getCurrentNick(); void part(); void handleIncomingMessage(Message::ref message); /** Expose public so it can be called when e.g. user goes offline */ void handleUserLeft(LeavingType); /** Get occupant information*/ const MUCOccupant& getOccupant(const std::string& nick); bool hasOccupant(const std::string& nick); void kickOccupant(const JID& jid); void changeOccupantRole(const JID& jid, MUCOccupant::Role role); void requestAffiliationList(MUCOccupant::Affiliation); void changeAffiliation(const JID& jid, MUCOccupant::Affiliation affiliation); void changeSubject(const std::string& subject); void requestConfigurationForm(); void configureRoom(Form::ref); void cancelConfigureRoom(); void destroyRoom(); /** Send an invite for the person to join the MUC */ void invitePerson(const JID& person, const std::string& reason = ""); void setCreateAsReservedIfNew() {createAsReservedIfNew = true;} void setPassword(const boost::optional& password); public: boost::signal onJoinComplete; boost::signal onJoinFailed; boost::signal onRoleChangeFailed; boost::signal onAffiliationChangeFailed; boost::signal onConfigurationFailed; boost::signal onAffiliationListFailed; boost::signal onOccupantPresenceChange; boost::signal onOccupantRoleChanged; boost::signal onOccupantAffiliationChanged; boost::signal onOccupantJoined; boost::signal onOccupantLeft; boost::signal onConfigurationFormReceived; boost::signal&)> onAffiliationListReceived; /* boost::signal onInfoResult; */ /* boost::signal onItemsResult; */ private: bool isFromMUC(const JID& j) const { return ownMUCJID.equals(j, JID::WithoutResource); } const std::string& getOwnNick() const { return ownMUCJID.getResource(); } private: void handleIncomingPresence(Presence::ref presence); void internalJoin(const std::string& nick); void handleCreationConfigResponse(MUCOwnerPayload::ref, ErrorPayload::ref); void handleOccupantRoleChangeResponse(MUCAdminPayload::ref, ErrorPayload::ref, const JID&, MUCOccupant::Role); void handleAffiliationChangeResponse(MUCAdminPayload::ref, ErrorPayload::ref, const JID&, MUCOccupant::Affiliation); void handleAffiliationListResponse(MUCAdminPayload::ref, ErrorPayload::ref, MUCOccupant::Affiliation); void handleConfigurationFormReceived(MUCOwnerPayload::ref, ErrorPayload::ref); void handleConfigurationResultReceived(MUCOwnerPayload::ref, ErrorPayload::ref); private: JID ownMUCJID; StanzaChannel* stanzaChannel; IQRouter* iqRouter_; DirectedPresenceSender* presenceSender; MUCRegistry* mucRegistry; std::map occupants; bool joinSucceeded_; bool joinComplete_; boost::bsignals::scoped_connection scopedConnection_; boost::posix_time::ptime joinSince_; bool createAsReservedIfNew; bool unlocking; boost::optional password; }; } swift-im-2.0+dev6/Swiften/Queries/0000755000175000017500000000000012227051774016717 5ustar kismithkismithswift-im-2.0+dev6/Swiften/Queries/IQChannel.h0000644000175000017500000000115412227051774020673 0ustar kismithkismith/* * 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 #include #include #include #include namespace Swift { class SWIFTEN_API IQChannel { public: virtual ~IQChannel(); virtual void sendIQ(boost::shared_ptr) = 0; virtual std::string getNewIQID() = 0; virtual bool isAvailable() const = 0; boost::signal)> onIQReceived; }; } swift-im-2.0+dev6/Swiften/Queries/RawRequest.h0000644000175000017500000000306112227051774021172 0ustar kismithkismith/* * 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 #include #include #include #include #include #include #include #include namespace Swift { class RawRequest : public Request { public: typedef boost::shared_ptr ref; static ref create(IQ::Type type, const JID& recipient, const std::string& data, IQRouter* router) { return ref(new RawRequest(type, recipient, data, router)); } boost::signal onResponse; private: RawRequest(IQ::Type type, const JID& receiver, const std::string& data, IQRouter* router) : Request(type, receiver, boost::make_shared(data), router) { } virtual void handleResponse(boost::shared_ptr payload, ErrorPayload::ref error) { if (error) { onResponse(ErrorSerializer(&serializers).serializePayload(error)); } else { assert(payload); PayloadSerializer* serializer = serializers.getPayloadSerializer(payload); assert(serializer); onResponse(serializer->serialize(payload)); } } private: FullPayloadSerializerCollection serializers; }; } swift-im-2.0+dev6/Swiften/Queries/Responder.h0000644000175000017500000000751112227051774021035 0ustar kismithkismith/* * 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 #include #include namespace Swift { /** * A class for handling incoming IQ Get and Set requests of a specific payload type. * * Concrete subclasses of this class need to implement handleGetRequest() and handleSetRequest() to * implement the behavior of the responder. * * \tparam PAYLOAD_TYPE The type of payload this Responder handles. Only IQ requests containing this * payload type will be passed to handleGetRequest() and handleSetRequest() */ template class Responder : public IQHandler { public: Responder(IQRouter* router) : router_(router) { } ~Responder() { } /** * Starts the responder. * * After the responder has started, it will start receiving and responding to requests. * * \see stop() */ void start() { router_->addHandler(this); } /** * Stops the responder. * * When the responder is stopped, it will no longer receive incoming requests. * * \see start() */ void stop() { router_->removeHandler(this); } protected: /** * Handle an incoming IQ-Get request containing a payload of class PAYLOAD_TYPE. * * This method is implemented in the concrete subclasses. */ virtual bool handleGetRequest(const JID& from, const JID& to, const std::string& id, boost::shared_ptr payload) = 0; /** * Handle an incoming IQ-Set request containing a payload of class PAYLOAD_TYPE. * * This method is implemented in the concrete subclasses. */ virtual bool handleSetRequest(const JID& from, const JID& to, const std::string& id, boost::shared_ptr payload) = 0; /** * Convenience function for sending an IQ response. */ void sendResponse(const JID& to, const std::string& id, boost::shared_ptr payload) { router_->sendIQ(IQ::createResult(to, id, payload)); } /** * Convenience function for sending an IQ response, with a specific from address. */ void sendResponse(const JID& to, const JID& from, const std::string& id, boost::shared_ptr payload) { router_->sendIQ(IQ::createResult(to, from, id, payload)); } /** * Convenience function for responding with an error. */ void sendError(const JID& to, const std::string& id, ErrorPayload::Condition condition, ErrorPayload::Type type, Payload::ref payload = Payload::ref()) { router_->sendIQ(IQ::createError(to, id, condition, type, payload)); } /** * Convenience function for responding with an error from a specific from address. */ void sendError(const JID& to, const JID& from, const std::string& id, ErrorPayload::Condition condition, ErrorPayload::Type type, Payload::ref payload = Payload::ref()) { router_->sendIQ(IQ::createError(to, from, id, condition, type, payload)); } IQRouter* getIQRouter() const { return router_; } private: virtual bool handleIQ(boost::shared_ptr iq) { if (iq->getType() == IQ::Set || iq->getType() == IQ::Get) { boost::shared_ptr payload(iq->getPayload()); if (payload) { bool result; if (iq->getType() == IQ::Set) { result = handleSetRequest(iq->getFrom(), iq->getTo(), iq->getID(), payload); } else { result = handleGetRequest(iq->getFrom(), iq->getTo(), iq->getID(), payload); } if (!result) { router_->sendIQ(IQ::createError(iq->getFrom(), iq->getID(), ErrorPayload::NotAllowed, ErrorPayload::Cancel)); } return true; } } return false; } private: IQRouter* router_; }; } swift-im-2.0+dev6/Swiften/Queries/GetResponder.h0000644000175000017500000000104112227051774021465 0ustar kismithkismith/* * 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 #include namespace Swift { template class SWIFTEN_API GetResponder : public Responder { public: GetResponder(IQRouter* router) : Responder(router) {} private: virtual bool handleSetRequest(const JID&, const JID&, const std::string&, boost::shared_ptr) { return false; } }; } swift-im-2.0+dev6/Swiften/Queries/IQRouter.h0000644000175000017500000000421412227051774020603 0ustar kismithkismith/* * 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 #include #include #include #include namespace Swift { class IQChannel; class IQHandler; class SWIFTEN_API IQRouter { public: IQRouter(IQChannel* channel); ~IQRouter(); /** * Sets the JID of this IQ router. * * This JID is used by requests to check whether incoming * results are addressed correctly. */ void setJID(const JID& jid) { jid_ = jid; } const JID& getJID() const { return jid_; } /** * Sets the 'from' JID for all outgoing IQ stanzas. * * By default, IQRouter does not add a from to IQ stanzas, since * this is automatically added by the server. This overrides this * default behavior, which is necessary for e.g. components. */ void setFrom(const JID& from) { from_ = from; } void addHandler(IQHandler* handler); void removeHandler(IQHandler* handler); void addHandler(boost::shared_ptr handler); void removeHandler(boost::shared_ptr handler); /** * Sends an IQ stanza. * * If a JID was specified using setFrom, the JID will * be set as the 'from' address on iq before sending * it. */ void sendIQ(boost::shared_ptr iq); std::string getNewIQID(); bool isAvailable(); /** * Checks whether the given jid is the account JID (i.e. it is either * the bare JID, or it is the empty JID). * Can be used to check whether a stanza is sent by the server on behalf * of the user's account. */ bool isAccountJID(const JID& jid) { return jid.isValid() ? jid_.toBare().equals(jid, JID::WithResource) : true; } private: void handleIQ(boost::shared_ptr iq); void processPendingRemoves(); private: IQChannel* channel_; JID jid_; JID from_; std::vector< boost::shared_ptr > handlers_; std::vector< boost::shared_ptr > queuedRemoves_; bool queueRemoves_; }; } swift-im-2.0+dev6/Swiften/Queries/DummyIQChannel.h0000644000175000017500000000111312227051774021702 0ustar kismithkismith/* * 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 #include namespace Swift { class DummyIQChannel : public IQChannel { public: DummyIQChannel() {} virtual void sendIQ(boost::shared_ptr iq) { iqs_.push_back(iq); } virtual std::string getNewIQID() { return "test-id"; } virtual bool isAvailable() const { return true; } std::vector > iqs_; }; } swift-im-2.0+dev6/Swiften/Queries/SetResponder.h0000644000175000017500000000076712227051774021517 0ustar kismithkismith/* * 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 namespace Swift { template class SetResponder : public Responder { public: SetResponder(IQRouter* router) : Responder(router) {} private: virtual bool handleGetRequest(const JID&, const JID&, const std::string&, boost::shared_ptr) { return false; } }; } swift-im-2.0+dev6/Swiften/Queries/Request.h0000644000175000017500000000426512227051774020527 0ustar kismithkismith/* * 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 #include #include #include #include #include #include #include #include #include namespace Swift { /** * An IQ get/set request query. */ class SWIFTEN_API Request : public IQHandler, public boost::enable_shared_from_this { public: void send(); const JID& getReceiver() const { return receiver_; } protected: /** * Constructs a request of a certain type to a specific receiver, and attaches the given * payload. */ Request( IQ::Type type, const JID& receiver, boost::shared_ptr payload, IQRouter* router); /** * Constructs a request of a certain type to a specific receiver from a specific sender, and attaches the given * payload. */ Request( IQ::Type type, const JID& sender, const JID& receiver, boost::shared_ptr payload, IQRouter* router); /** * Constructs a request of a certain type to a specific receiver. */ Request( IQ::Type type, const JID& receiver, IQRouter* router); /** * Constructs a request of a certain type to a specific receiver from a specific sender. */ Request( IQ::Type type, const JID& sender, const JID& receiver, IQRouter* router); virtual void setPayload(boost::shared_ptr payload) { payload_ = payload; } boost::shared_ptr getPayload() const { return payload_; } virtual void handleResponse(boost::shared_ptr, boost::shared_ptr) = 0; private: bool handleIQ(boost::shared_ptr); bool isCorrectSender(const JID& jid); private: IQRouter* router_; IQ::Type type_; JID sender_; JID receiver_; boost::shared_ptr payload_; std::string id_; bool sent_; }; } swift-im-2.0+dev6/Swiften/Queries/IQHandler.cpp0000644000175000017500000000044012227051774021230 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include namespace Swift { IQHandler::~IQHandler() { } } swift-im-2.0+dev6/Swiften/Queries/IQHandler.h0000644000175000017500000000066112227051774020702 0ustar kismithkismith/* * 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 #include #include namespace Swift { class IQRouter; class SWIFTEN_API IQHandler { public: virtual ~IQHandler(); virtual bool handleIQ(boost::shared_ptr) = 0; }; } swift-im-2.0+dev6/Swiften/Queries/GenericRequest.h0000644000175000017500000000531112227051774022015 0ustar kismithkismith/* * 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 #include namespace Swift { /** * GenericRequest is used for managing the sending of, and handling of replies to, iq stanzas that do not have their own Request types. * * To create an iq stanza, call a constructor with the type of the iq that needs to be sent (either Set or Get), addressing information (clients should use the constructor that doesn't specify a sender), the payload that should be sent in the iq, and the IQRouter for the connection, obtained through the Client or CoreClient object. * * Having created a GenericRequest, connect to the onResponse signal to be told when a response (either a result or an error) has been received by Swiften. * * To send the iq, then call send() - onResponse will be called when a reply is received. */ template class GenericRequest : public Request { public: /** * Create a request suitable for client use. * @param type Iq type - Get or Set. * @param receiver JID to send request to. * @param payload Payload to send in stanza. * @param router IQRouter instance for current connection. */ GenericRequest( IQ::Type type, const JID& receiver, boost::shared_ptr payload, IQRouter* router) : Request(type, receiver, payload, router) { } /** * Create a request suitable for component or server use. As a client, use the other constructor instead. * @param type Iq type - Get or Set. * @param sender JID to use in "from" of stanza. * @param receiver JID to send request to. * @param payload Payload to send in stanza. * @param router IQRouter instance for current connection. */ GenericRequest( IQ::Type type, const JID& sender, const JID& receiver, boost::shared_ptr payload, IQRouter* router) : Request(type, sender, receiver, payload, router) { } /** * Internal method, do not use. */ virtual void handleResponse(boost::shared_ptr payload, ErrorPayload::ref error) { onResponse(boost::dynamic_pointer_cast(payload), error); } protected: boost::shared_ptr getPayloadGeneric() const { return boost::dynamic_pointer_cast(getPayload()); } public: /** * Signal emitted when a reply to the iq has been received. Contains a payload if one was present, and an error if one was present. */ boost::signal, ErrorPayload::ref)> onResponse; }; } swift-im-2.0+dev6/Swiften/Queries/UnitTest/0000755000175000017500000000000012227051774020476 5ustar kismithkismithswift-im-2.0+dev6/Swiften/Queries/UnitTest/IQRouterTest.cpp0000644000175000017500000001136712227051774023564 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include using namespace Swift; class IQRouterTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(IQRouterTest); CPPUNIT_TEST(testRemoveHandler); CPPUNIT_TEST(testRemoveHandler_AfterHandleIQ); CPPUNIT_TEST(testHandleIQ_SuccesfulHandlerFirst); CPPUNIT_TEST(testHandleIQ_SuccesfulHandlerLast); CPPUNIT_TEST(testHandleIQ_NoSuccesfulHandler); CPPUNIT_TEST(testHandleIQ_HandlerRemovedDuringHandle); CPPUNIT_TEST(testSendIQ_WithFrom); CPPUNIT_TEST(testSendIQ_WithoutFrom); CPPUNIT_TEST(testHandleIQ_WithFrom); CPPUNIT_TEST_SUITE_END(); public: void setUp() { channel_ = new DummyIQChannel(); } void tearDown() { delete channel_; } void testRemoveHandler() { IQRouter testling(channel_); DummyIQHandler handler1(true, &testling); DummyIQHandler handler2(true, &testling); testling.removeHandler(&handler1); channel_->onIQReceived(boost::make_shared()); CPPUNIT_ASSERT_EQUAL(0, handler1.called); CPPUNIT_ASSERT_EQUAL(1, handler2.called); } void testRemoveHandler_AfterHandleIQ() { IQRouter testling(channel_); DummyIQHandler handler2(true, &testling); DummyIQHandler handler1(true, &testling); channel_->onIQReceived(boost::make_shared()); testling.removeHandler(&handler1); channel_->onIQReceived(boost::make_shared()); CPPUNIT_ASSERT_EQUAL(1, handler1.called); CPPUNIT_ASSERT_EQUAL(1, handler2.called); } void testHandleIQ_SuccesfulHandlerFirst() { IQRouter testling(channel_); DummyIQHandler handler2(false, &testling); DummyIQHandler handler1(true, &testling); channel_->onIQReceived(boost::make_shared()); CPPUNIT_ASSERT_EQUAL(1, handler1.called); CPPUNIT_ASSERT_EQUAL(0, handler2.called); CPPUNIT_ASSERT_EQUAL(0, static_cast(channel_->iqs_.size())); } void testHandleIQ_SuccesfulHandlerLast() { IQRouter testling(channel_); DummyIQHandler handler2(true, &testling); DummyIQHandler handler1(false, &testling); channel_->onIQReceived(boost::make_shared()); CPPUNIT_ASSERT_EQUAL(1, handler1.called); CPPUNIT_ASSERT_EQUAL(1, handler2.called); CPPUNIT_ASSERT_EQUAL(0, static_cast(channel_->iqs_.size())); } void testHandleIQ_NoSuccesfulHandler() { IQRouter testling(channel_); DummyIQHandler handler(false, &testling); channel_->onIQReceived(boost::make_shared()); CPPUNIT_ASSERT_EQUAL(1, static_cast(channel_->iqs_.size())); CPPUNIT_ASSERT(channel_->iqs_[0]->getPayload()); } void testHandleIQ_HandlerRemovedDuringHandle() { IQRouter testling(channel_); DummyIQHandler handler2(true, &testling); RemovingIQHandler handler1(&testling); channel_->onIQReceived(boost::make_shared()); channel_->onIQReceived(boost::make_shared()); CPPUNIT_ASSERT_EQUAL(1, handler1.called); CPPUNIT_ASSERT_EQUAL(2, handler2.called); } void testSendIQ_WithFrom() { IQRouter testling(channel_); testling.setFrom(JID("foo@bar.com/baz")); testling.sendIQ(boost::make_shared()); CPPUNIT_ASSERT_EQUAL(JID("foo@bar.com/baz"), channel_->iqs_[0]->getFrom()); } void testSendIQ_WithoutFrom() { IQRouter testling(channel_); testling.sendIQ(boost::make_shared()); CPPUNIT_ASSERT_EQUAL(JID(), channel_->iqs_[0]->getFrom()); } void testHandleIQ_WithFrom() { IQRouter testling(channel_); testling.setFrom(JID("foo@bar.com/baz")); DummyIQHandler handler(false, &testling); channel_->onIQReceived(boost::make_shared()); CPPUNIT_ASSERT_EQUAL(JID("foo@bar.com/baz"), channel_->iqs_[0]->getFrom()); } private: struct DummyIQHandler : public IQHandler { DummyIQHandler(bool handle, IQRouter* router) : handle(handle), router(router), called(0) { router->addHandler(this); } ~DummyIQHandler() { router->removeHandler(this); } virtual bool handleIQ(boost::shared_ptr) { called++; return handle; } bool handle; IQRouter* router; int called; }; struct RemovingIQHandler : public IQHandler { RemovingIQHandler(IQRouter* router) : router(router), called(0) { router->addHandler(this); } virtual bool handleIQ(boost::shared_ptr) { called++; router->removeHandler(this); return false; } IQRouter* router; int called; }; DummyIQChannel* channel_; }; CPPUNIT_TEST_SUITE_REGISTRATION(IQRouterTest); swift-im-2.0+dev6/Swiften/Queries/UnitTest/ResponderTest.cpp0000644000175000017500000001146212227051774024007 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include using namespace Swift; class ResponderTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(ResponderTest); CPPUNIT_TEST(testConstructor); CPPUNIT_TEST(testStart); CPPUNIT_TEST(testStop); CPPUNIT_TEST(testHandleIQ_Set); CPPUNIT_TEST(testHandleIQ_Get); CPPUNIT_TEST(testHandleIQ_Error); CPPUNIT_TEST(testHandleIQ_Result); CPPUNIT_TEST(testHandleIQ_NoPayload); CPPUNIT_TEST_SUITE_END(); public: void setUp() { channel_ = new DummyIQChannel(); router_ = new IQRouter(channel_); payload_ = boost::make_shared("foo"); } void tearDown() { delete router_; delete channel_; } void testConstructor() { MyResponder testling(router_); channel_->onIQReceived(createRequest(IQ::Set)); CPPUNIT_ASSERT_EQUAL(0, static_cast(testling.setPayloads_.size())); } void testStart() { MyResponder testling(router_); testling.start(); channel_->onIQReceived(createRequest(IQ::Set)); CPPUNIT_ASSERT_EQUAL(1, static_cast(testling.setPayloads_.size())); } void testStop() { MyResponder testling(router_); testling.start(); testling.stop(); channel_->onIQReceived(createRequest(IQ::Set)); CPPUNIT_ASSERT_EQUAL(0, static_cast(testling.setPayloads_.size())); } void testHandleIQ_Set() { MyResponder testling(router_); CPPUNIT_ASSERT(dynamic_cast(&testling)->handleIQ(createRequest(IQ::Set))); CPPUNIT_ASSERT_EQUAL(1, static_cast(testling.setPayloads_.size())); CPPUNIT_ASSERT(payload_ == testling.setPayloads_[0]); CPPUNIT_ASSERT_EQUAL(0, static_cast(testling.getPayloads_.size())); } void testHandleIQ_Get() { MyResponder testling(router_); CPPUNIT_ASSERT(dynamic_cast(&testling)->handleIQ(createRequest(IQ::Get))); CPPUNIT_ASSERT_EQUAL(1, static_cast(testling.getPayloads_.size())); CPPUNIT_ASSERT_EQUAL(0, static_cast(testling.setPayloads_.size())); CPPUNIT_ASSERT(payload_ == testling.getPayloads_[0]); } void testHandleIQ_Error() { MyResponder testling(router_); CPPUNIT_ASSERT(!dynamic_cast(&testling)->handleIQ(createRequest(IQ::Error))); CPPUNIT_ASSERT_EQUAL(0, static_cast(testling.getPayloads_.size())); CPPUNIT_ASSERT_EQUAL(0, static_cast(testling.setPayloads_.size())); } void testHandleIQ_Result() { MyResponder testling(router_); CPPUNIT_ASSERT(!dynamic_cast(&testling)->handleIQ(createRequest(IQ::Result))); CPPUNIT_ASSERT_EQUAL(0, static_cast(testling.getPayloads_.size())); CPPUNIT_ASSERT_EQUAL(0, static_cast(testling.setPayloads_.size())); } void testHandleIQ_NoPayload() { MyResponder testling(router_); CPPUNIT_ASSERT(!dynamic_cast(&testling)->handleIQ(boost::make_shared(IQ::Get))); CPPUNIT_ASSERT_EQUAL(0, static_cast(testling.getPayloads_.size())); CPPUNIT_ASSERT_EQUAL(0, static_cast(testling.setPayloads_.size())); } private: boost::shared_ptr createRequest(IQ::Type type) { boost::shared_ptr iq(new IQ(type)); iq->addPayload(payload_); iq->setID("myid"); iq->setFrom(JID("foo@bar.com/baz")); return iq; } private: class MyResponder : public Responder { public: MyResponder(IQRouter* router) : Responder(router), getRequestResponse_(true), setRequestResponse_(true) {} virtual bool handleGetRequest(const JID& from, const JID&, const std::string& id, boost::shared_ptr payload) { CPPUNIT_ASSERT_EQUAL(JID("foo@bar.com/baz"), from); CPPUNIT_ASSERT_EQUAL(std::string("myid"), id); getPayloads_.push_back(payload); return getRequestResponse_; } virtual bool handleSetRequest(const JID& from, const JID&, const std::string& id, boost::shared_ptr payload) { CPPUNIT_ASSERT_EQUAL(JID("foo@bar.com/baz"), from); CPPUNIT_ASSERT_EQUAL(std::string("myid"), id); setPayloads_.push_back(payload); return setRequestResponse_; } bool getRequestResponse_; bool setRequestResponse_; std::vector > getPayloads_; std::vector > setPayloads_; }; private: IQRouter* router_; DummyIQChannel* channel_; boost::shared_ptr payload_; }; CPPUNIT_TEST_SUITE_REGISTRATION(ResponderTest); swift-im-2.0+dev6/Swiften/Queries/UnitTest/RequestTest.cpp0000644000175000017500000003256212227051774023502 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include using namespace Swift; class RequestTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(RequestTest); CPPUNIT_TEST(testSendGet); CPPUNIT_TEST(testSendSet); CPPUNIT_TEST(testHandleIQ); CPPUNIT_TEST(testHandleIQ_InvalidID); CPPUNIT_TEST(testHandleIQ_Error); CPPUNIT_TEST(testHandleIQ_ErrorWithoutPayload); CPPUNIT_TEST(testHandleIQ_BeforeSend); CPPUNIT_TEST(testHandleIQ_DifferentPayload); CPPUNIT_TEST(testHandleIQ_RawXMLPayload); CPPUNIT_TEST(testHandleIQ_GetWithSameID); CPPUNIT_TEST(testHandleIQ_SetWithSameID); CPPUNIT_TEST(testHandleIQ_IncorrectSender); CPPUNIT_TEST(testHandleIQ_IncorrectSenderForServerQuery); CPPUNIT_TEST(testHandleIQ_IncorrectOtherResourceSenderForServerQuery); CPPUNIT_TEST(testHandleIQ_ServerRespondsWithDomain); CPPUNIT_TEST(testHandleIQ_ServerRespondsWithBareJID); CPPUNIT_TEST(testHandleIQ_ServerRespondsWithoutFrom); CPPUNIT_TEST(testHandleIQ_ServerRespondsWithFullJID); CPPUNIT_TEST_SUITE_END(); public: class MyPayload : public Payload { public: MyPayload(const std::string& s = "") : text_(s) {} std::string text_; }; struct MyOtherPayload : public Payload { }; class MyRequest : public Request { public: MyRequest( IQ::Type type, const JID& receiver, boost::shared_ptr payload, IQRouter* router) : Request(type, receiver, payload, router) { } virtual void handleResponse(boost::shared_ptr payload, ErrorPayload::ref error) { onResponse(payload, error); } public: boost::signal, ErrorPayload::ref)> onResponse; }; public: void setUp() { channel_ = new DummyIQChannel(); router_ = new IQRouter(channel_); payload_ = boost::shared_ptr(new MyPayload("foo")); responsePayload_ = boost::shared_ptr(new MyPayload("bar")); responsesReceived_ = 0; } void tearDown() { delete router_; delete channel_; } void testSendSet() { MyRequest testling(IQ::Set, JID("foo@bar.com/baz"), payload_, router_); testling.send(); CPPUNIT_ASSERT_EQUAL(1, static_cast(channel_->iqs_.size())); CPPUNIT_ASSERT_EQUAL(JID("foo@bar.com/baz"), channel_->iqs_[0]->getTo()); CPPUNIT_ASSERT_EQUAL(IQ::Set, channel_->iqs_[0]->getType()); CPPUNIT_ASSERT_EQUAL(std::string("test-id"), channel_->iqs_[0]->getID()); } void testSendGet() { MyRequest testling(IQ::Get, JID("foo@bar.com/baz"), payload_, router_); testling.send(); CPPUNIT_ASSERT_EQUAL(1, static_cast(channel_->iqs_.size())); CPPUNIT_ASSERT_EQUAL(IQ::Get, channel_->iqs_[0]->getType()); } void testHandleIQ() { MyRequest testling(IQ::Get, JID("foo@bar.com/baz"), payload_, router_); testling.onResponse.connect(boost::bind(&RequestTest::handleResponse, this, _1, _2)); testling.send(); channel_->onIQReceived(createResponse(JID("foo@bar.com/baz"),"test-id")); CPPUNIT_ASSERT_EQUAL(1, responsesReceived_); CPPUNIT_ASSERT_EQUAL(0, static_cast(receivedErrors.size())); CPPUNIT_ASSERT_EQUAL(1, static_cast(channel_->iqs_.size())); } // FIXME: Doesn't test that it didn't handle the payload void testHandleIQ_InvalidID() { MyRequest testling(IQ::Get, JID("foo@bar.com/baz"), payload_, router_); testling.onResponse.connect(boost::bind(&RequestTest::handleResponse, this, _1, _2)); testling.send(); channel_->onIQReceived(createResponse(JID("foo@bar.com/baz"),"different-id")); CPPUNIT_ASSERT_EQUAL(0, responsesReceived_); CPPUNIT_ASSERT_EQUAL(0, static_cast(receivedErrors.size())); CPPUNIT_ASSERT_EQUAL(1, static_cast(channel_->iqs_.size())); } void testHandleIQ_Error() { MyRequest testling(IQ::Get, JID("foo@bar.com/baz"), payload_, router_); testling.onResponse.connect(boost::bind(&RequestTest::handleResponse, this, _1, _2)); testling.send(); boost::shared_ptr error = createError(JID("foo@bar.com/baz"),"test-id"); boost::shared_ptr errorPayload = boost::make_shared(ErrorPayload::InternalServerError); error->addPayload(errorPayload); channel_->onIQReceived(error); CPPUNIT_ASSERT_EQUAL(0, responsesReceived_); CPPUNIT_ASSERT_EQUAL(1, static_cast(receivedErrors.size())); CPPUNIT_ASSERT_EQUAL(1, static_cast(channel_->iqs_.size())); CPPUNIT_ASSERT_EQUAL(ErrorPayload::InternalServerError, receivedErrors[0].getCondition()); } void testHandleIQ_ErrorWithoutPayload() { MyRequest testling(IQ::Get, JID("foo@bar.com/baz"), payload_, router_); testling.onResponse.connect(boost::bind(&RequestTest::handleResponse, this, _1, _2)); testling.send(); channel_->onIQReceived(createError(JID("foo@bar.com/baz"),"test-id")); CPPUNIT_ASSERT_EQUAL(0, responsesReceived_); CPPUNIT_ASSERT_EQUAL(1, static_cast(receivedErrors.size())); CPPUNIT_ASSERT_EQUAL(1, static_cast(channel_->iqs_.size())); CPPUNIT_ASSERT_EQUAL(ErrorPayload::UndefinedCondition, receivedErrors[0].getCondition()); } void testHandleIQ_BeforeSend() { MyRequest testling(IQ::Get, JID("foo@bar.com/baz"), payload_, router_); testling.onResponse.connect(boost::bind(&RequestTest::handleResponse, this, _1, _2)); channel_->onIQReceived(createResponse(JID("foo@bar.com/baz"),"test-id")); CPPUNIT_ASSERT_EQUAL(0, responsesReceived_); CPPUNIT_ASSERT_EQUAL(0, static_cast(receivedErrors.size())); CPPUNIT_ASSERT_EQUAL(0, static_cast(channel_->iqs_.size())); } void testHandleIQ_DifferentPayload() { MyRequest testling(IQ::Get, JID("foo@bar.com/baz"), payload_, router_); testling.onResponse.connect(boost::bind(&RequestTest::handleDifferentResponse, this, _1, _2)); testling.send(); responsePayload_ = boost::make_shared(); channel_->onIQReceived(createResponse(JID("foo@bar.com/baz"),"test-id")); CPPUNIT_ASSERT_EQUAL(1, responsesReceived_); CPPUNIT_ASSERT_EQUAL(0, static_cast(receivedErrors.size())); CPPUNIT_ASSERT_EQUAL(1, static_cast(channel_->iqs_.size())); } void testHandleIQ_RawXMLPayload() { payload_ = boost::make_shared(""); MyRequest testling(IQ::Get, JID("foo@bar.com/baz"), payload_, router_); testling.onResponse.connect(boost::bind(&RequestTest::handleRawXMLResponse, this, _1, _2)); testling.send(); responsePayload_ = boost::make_shared(); channel_->onIQReceived(createResponse(JID("foo@bar.com/baz"),"test-id")); CPPUNIT_ASSERT_EQUAL(1, responsesReceived_); CPPUNIT_ASSERT_EQUAL(0, static_cast(receivedErrors.size())); CPPUNIT_ASSERT_EQUAL(1, static_cast(channel_->iqs_.size())); } void testHandleIQ_GetWithSameID() { MyRequest testling(IQ::Get, JID("foo@bar.com/baz"), payload_, router_); testling.onResponse.connect(boost::bind(&RequestTest::handleResponse, this, _1, _2)); testling.send(); boost::shared_ptr response = createResponse(JID("foo@bar.com/baz"),"test-id"); response->setType(IQ::Get); channel_->onIQReceived(response); CPPUNIT_ASSERT_EQUAL(0, responsesReceived_); CPPUNIT_ASSERT_EQUAL(0, static_cast(receivedErrors.size())); CPPUNIT_ASSERT_EQUAL(2, static_cast(channel_->iqs_.size())); } void testHandleIQ_SetWithSameID() { MyRequest testling(IQ::Get, JID("foo@bar.com/baz"), payload_, router_); testling.onResponse.connect(boost::bind(&RequestTest::handleResponse, this, _1, _2)); testling.send(); boost::shared_ptr response = createResponse(JID("foo@bar.com/baz"), "test-id"); response->setType(IQ::Set); channel_->onIQReceived(response); CPPUNIT_ASSERT_EQUAL(0, responsesReceived_); CPPUNIT_ASSERT_EQUAL(0, static_cast(receivedErrors.size())); CPPUNIT_ASSERT_EQUAL(2, static_cast(channel_->iqs_.size())); } void testHandleIQ_IncorrectSender() { MyRequest testling(IQ::Get, JID("foo@bar.com/baz"), payload_, router_); router_->setJID("alice@wonderland.lit/TeaParty"); testling.onResponse.connect(boost::bind(&RequestTest::handleResponse, this, _1, _2)); testling.send(); channel_->onIQReceived(createResponse(JID("anotherfoo@bar.com/baz"), "test-id")); CPPUNIT_ASSERT_EQUAL(0, responsesReceived_); CPPUNIT_ASSERT_EQUAL(0, static_cast(receivedErrors.size())); CPPUNIT_ASSERT_EQUAL(1, static_cast(channel_->iqs_.size())); } void testHandleIQ_IncorrectSenderForServerQuery() { MyRequest testling(IQ::Get, JID(), payload_, router_); router_->setJID("alice@wonderland.lit/TeaParty"); testling.onResponse.connect(boost::bind(&RequestTest::handleResponse, this, _1, _2)); testling.send(); channel_->onIQReceived(createResponse(JID("foo@bar.com/baz"), "test-id")); CPPUNIT_ASSERT_EQUAL(0, responsesReceived_); CPPUNIT_ASSERT_EQUAL(0, static_cast(receivedErrors.size())); CPPUNIT_ASSERT_EQUAL(1, static_cast(channel_->iqs_.size())); } void testHandleIQ_IncorrectOtherResourceSenderForServerQuery() { MyRequest testling(IQ::Get, JID(), payload_, router_); router_->setJID("alice@wonderland.lit/TeaParty"); testling.onResponse.connect(boost::bind(&RequestTest::handleResponse, this, _1, _2)); testling.send(); channel_->onIQReceived(createResponse(JID("alice@wonderland.lit/RabbitHole"), "test-id")); CPPUNIT_ASSERT_EQUAL(0, responsesReceived_); CPPUNIT_ASSERT_EQUAL(0, static_cast(receivedErrors.size())); CPPUNIT_ASSERT_EQUAL(1, static_cast(channel_->iqs_.size())); } void testHandleIQ_ServerRespondsWithDomain() { MyRequest testling(IQ::Get, JID(), payload_, router_); router_->setJID("alice@wonderland.lit/TeaParty"); testling.onResponse.connect(boost::bind(&RequestTest::handleResponse, this, _1, _2)); testling.send(); channel_->onIQReceived(createResponse(JID("wonderland.lit"),"test-id")); CPPUNIT_ASSERT_EQUAL(0, responsesReceived_); CPPUNIT_ASSERT_EQUAL(0, static_cast(receivedErrors.size())); CPPUNIT_ASSERT_EQUAL(1, static_cast(channel_->iqs_.size())); } void testHandleIQ_ServerRespondsWithBareJID() { MyRequest testling(IQ::Get, JID(), payload_, router_); router_->setJID("alice@wonderland.lit/TeaParty"); testling.onResponse.connect(boost::bind(&RequestTest::handleResponse, this, _1, _2)); testling.send(); channel_->onIQReceived(createResponse(JID("alice@wonderland.lit"),"test-id")); CPPUNIT_ASSERT_EQUAL(1, responsesReceived_); CPPUNIT_ASSERT_EQUAL(0, static_cast(receivedErrors.size())); CPPUNIT_ASSERT_EQUAL(1, static_cast(channel_->iqs_.size())); } // This tests a bug in ejabberd servers (2.0.5) void testHandleIQ_ServerRespondsWithFullJID() { MyRequest testling(IQ::Get, JID(), payload_, router_); router_->setJID("alice@wonderland.lit/TeaParty"); testling.onResponse.connect(boost::bind(&RequestTest::handleResponse, this, _1, _2)); testling.send(); channel_->onIQReceived(createResponse(JID("alice@wonderland.lit/TeaParty"),"test-id")); CPPUNIT_ASSERT_EQUAL(1, responsesReceived_); CPPUNIT_ASSERT_EQUAL(0, static_cast(receivedErrors.size())); CPPUNIT_ASSERT_EQUAL(1, static_cast(channel_->iqs_.size())); } void testHandleIQ_ServerRespondsWithoutFrom() { MyRequest testling(IQ::Get, JID(), payload_, router_); router_->setJID("alice@wonderland.lit/TeaParty"); testling.onResponse.connect(boost::bind(&RequestTest::handleResponse, this, _1, _2)); testling.send(); channel_->onIQReceived(createResponse(JID(),"test-id")); CPPUNIT_ASSERT_EQUAL(1, responsesReceived_); CPPUNIT_ASSERT_EQUAL(0, static_cast(receivedErrors.size())); CPPUNIT_ASSERT_EQUAL(1, static_cast(channel_->iqs_.size())); } private: void handleResponse(boost::shared_ptr p, ErrorPayload::ref e) { if (e) { receivedErrors.push_back(*e); } else { boost::shared_ptr payload(boost::dynamic_pointer_cast(p)); CPPUNIT_ASSERT(payload); CPPUNIT_ASSERT_EQUAL(std::string("bar"), payload->text_); ++responsesReceived_; } } void handleDifferentResponse(boost::shared_ptr p, ErrorPayload::ref e) { CPPUNIT_ASSERT(!e); CPPUNIT_ASSERT(!p); ++responsesReceived_; } void handleRawXMLResponse(boost::shared_ptr p, ErrorPayload::ref e) { CPPUNIT_ASSERT(!e); CPPUNIT_ASSERT(p); CPPUNIT_ASSERT(boost::dynamic_pointer_cast(p)); ++responsesReceived_; } boost::shared_ptr createResponse(const JID& from, const std::string& id) { boost::shared_ptr iq(new IQ(IQ::Result)); iq->setFrom(from); iq->addPayload(responsePayload_); iq->setID(id); return iq; } boost::shared_ptr createError(const JID& from, const std::string& id) { boost::shared_ptr iq(new IQ(IQ::Error)); iq->setFrom(from); iq->setID(id); return iq; } private: IQRouter* router_; DummyIQChannel* channel_; boost::shared_ptr payload_; boost::shared_ptr responsePayload_; int responsesReceived_; std::vector receivedErrors; }; CPPUNIT_TEST_SUITE_REGISTRATION(RequestTest); swift-im-2.0+dev6/Swiften/Queries/Responders/0000755000175000017500000000000012227051774021043 5ustar kismithkismithswift-im-2.0+dev6/Swiften/Queries/Responders/SoftwareVersionResponder.cpp0000644000175000017500000000155012227051774026572 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include namespace Swift { SoftwareVersionResponder::SoftwareVersionResponder(IQRouter* router) : GetResponder(router) { } void SoftwareVersionResponder::setVersion(const std::string& client, const std::string& version, const std::string& os) { this->client = client; this->version = version; this->os = os; } bool SoftwareVersionResponder::handleGetRequest(const JID& from, const JID&, const std::string& id, boost::shared_ptr) { sendResponse(from, id, boost::make_shared(client, version, os)); return true; } } swift-im-2.0+dev6/Swiften/Queries/Responders/SoftwareVersionResponder.h0000644000175000017500000000144412227051774026241 0ustar kismithkismith/* * 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 #include #include namespace Swift { class IQRouter; class SWIFTEN_API SoftwareVersionResponder : public GetResponder { public: SoftwareVersionResponder(IQRouter* router); void setVersion(const std::string& client, const std::string& version, const std::string& os = ""); private: virtual bool handleGetRequest(const JID& from, const JID& to, const std::string& id, boost::shared_ptr payload); private: std::string client; std::string version; std::string os; }; } swift-im-2.0+dev6/Swiften/Queries/Request.cpp0000644000175000017500000000555312227051774021063 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include namespace Swift { Request::Request(IQ::Type type, const JID& receiver, boost::shared_ptr payload, IQRouter* router) : router_(router), type_(type), receiver_(receiver), payload_(payload), sent_(false) { } Request::Request(IQ::Type type, const JID& receiver, IQRouter* router) : router_(router), type_(type), receiver_(receiver), sent_(false) { } Request::Request(IQ::Type type, const JID& sender, const JID& receiver, boost::shared_ptr payload, IQRouter* router) : router_(router), type_(type), sender_(sender), receiver_(receiver), payload_(payload), sent_(false) { } Request::Request(IQ::Type type, const JID& sender, const JID& receiver, IQRouter* router) : router_(router), type_(type), sender_(sender), receiver_(receiver), sent_(false) { } void Request::send() { assert(payload_); assert(!sent_); sent_ = true; boost::shared_ptr iq(new IQ(type_)); iq->setTo(receiver_); iq->setFrom(sender_); iq->addPayload(payload_); id_ = router_->getNewIQID(); iq->setID(id_); try { router_->addHandler(shared_from_this()); } catch (const std::exception&) { router_->addHandler(this); } router_->sendIQ(iq); } bool Request::handleIQ(boost::shared_ptr iq) { bool handled = false; if (iq->getType() == IQ::Result || iq->getType() == IQ::Error) { if (sent_ && iq->getID() == id_) { if (isCorrectSender(iq->getFrom())) { if (iq->getType() == IQ::Result) { boost::shared_ptr payload = iq->getPayloadOfSameType(payload_); if (!payload && boost::dynamic_pointer_cast(payload_) && !iq->getPayloads().empty()) { payload = iq->getPayloads().front(); } handleResponse(payload, ErrorPayload::ref()); } else { ErrorPayload::ref errorPayload = iq->getPayload(); if (errorPayload) { handleResponse(boost::shared_ptr(), errorPayload); } else { handleResponse(boost::shared_ptr(), ErrorPayload::ref(new ErrorPayload(ErrorPayload::UndefinedCondition))); } } router_->removeHandler(this); handled = true; } } } return handled; } bool Request::isCorrectSender(const JID& jid) { if (router_->isAccountJID(receiver_)) { if (jid.isValid() && jid.equals(router_->getJID(), JID::WithResource)) { // This unspecified behavior seems to happen in ejabberd versions (e.g. 2.0.5) SWIFT_LOG(warning) << "Server responded to an account request with a full JID, which is not allowed. Handling it anyway."; return true; } return router_->isAccountJID(jid); } else { return jid.equals(receiver_, JID::WithResource); } } } swift-im-2.0+dev6/Swiften/Queries/IQRouter.cpp0000644000175000017500000000455512227051774021146 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include namespace Swift { static void noop(IQHandler*) {} IQRouter::IQRouter(IQChannel* channel) : channel_(channel), queueRemoves_(false) { channel->onIQReceived.connect(boost::bind(&IQRouter::handleIQ, this, _1)); } IQRouter::~IQRouter() { channel_->onIQReceived.disconnect(boost::bind(&IQRouter::handleIQ, this, _1)); } bool IQRouter::isAvailable() { return channel_->isAvailable(); } void IQRouter::handleIQ(boost::shared_ptr iq) { queueRemoves_ = true; bool handled = false; // Go through the handlers in reverse order, to give precedence to the last added handler std::vector >::const_reverse_iterator i = handlers_.rbegin(); std::vector >::const_reverse_iterator rend = handlers_.rend(); for (; i != rend; ++i) { handled |= (*i)->handleIQ(iq); if (handled) { break; } } if (!handled && (iq->getType() == IQ::Get || iq->getType() == IQ::Set) ) { sendIQ(IQ::createError(iq->getFrom(), iq->getID(), ErrorPayload::FeatureNotImplemented, ErrorPayload::Cancel)); } processPendingRemoves(); queueRemoves_ = false; } void IQRouter::processPendingRemoves() { foreach(boost::shared_ptr handler, queuedRemoves_) { erase(handlers_, handler); } queuedRemoves_.clear(); } void IQRouter::addHandler(IQHandler* handler) { addHandler(boost::shared_ptr(handler, noop)); } void IQRouter::removeHandler(IQHandler* handler) { removeHandler(boost::shared_ptr(handler, noop)); } void IQRouter::addHandler(boost::shared_ptr handler) { handlers_.push_back(handler); } void IQRouter::removeHandler(boost::shared_ptr handler) { if (queueRemoves_) { queuedRemoves_.push_back(handler); } else { erase(handlers_, handler); } } void IQRouter::sendIQ(boost::shared_ptr iq) { if (from_.isValid() && !iq->getFrom().isValid()) { iq->setFrom(from_); } channel_->sendIQ(iq); } std::string IQRouter::getNewIQID() { return channel_->getNewIQID(); } } swift-im-2.0+dev6/Swiften/Queries/IQChannel.cpp0000644000175000017500000000037212227051774021227 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { IQChannel::~IQChannel() { } } swift-im-2.0+dev6/Swiften/Queries/Requests/0000755000175000017500000000000012227051774020532 5ustar kismithkismithswift-im-2.0+dev6/Swiften/Queries/Requests/GetPrivateStorageRequest.h0000644000175000017500000000255212227051774025657 0ustar kismithkismith/* * 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 #include #include #include #include #include namespace Swift { template class GetPrivateStorageRequest : public Request { public: typedef boost::shared_ptr > ref; static ref create(IQRouter* router) { return ref(new GetPrivateStorageRequest(router)); } private: GetPrivateStorageRequest(IQRouter* router) : Request(IQ::Get, JID(), boost::make_shared(boost::shared_ptr(new PAYLOAD_TYPE())), router) { } virtual void handleResponse(boost::shared_ptr payload, ErrorPayload::ref error) { boost::shared_ptr storage = boost::dynamic_pointer_cast(payload); if (storage) { onResponse(boost::dynamic_pointer_cast(storage->getPayload()), error); } else { onResponse(boost::shared_ptr(), error); } } public: boost::signal, ErrorPayload::ref)> onResponse; }; } swift-im-2.0+dev6/Swiften/Queries/Requests/SubmitInBandRegistrationFormRequest.h0000644000175000017500000000210012227051774030003 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include namespace Swift { class SetInBandRegistrationRequest : public Request { public: typedef boost::shared_ptr ref; static ref create(const JID& to, InBandRegistrationPayload::ref payload, IQRouter* router) { return ref(new SetInBandRegistrationRequest(to, payload, router)); } private: SetInBandRegistrationRequest(const JID& to, InBandRegistrationPayload::ref payload, IQRouter* router) : Request(IQ::Set, to, InBandRegistrationPayload::ref(payload), router) { } virtual void handleResponse(boost::shared_ptr payload, ErrorPayload::ref error) { onResponse(payload, error); } public: boost::signal, ErrorPayload::ref)> onResponse; }; } swift-im-2.0+dev6/Swiften/Queries/Requests/UnitTest/0000755000175000017500000000000012227051774022311 5ustar kismithkismithswift-im-2.0+dev6/Swiften/Queries/Requests/UnitTest/GetPrivateStorageRequestTest.cpp0000644000175000017500000000670112227051774030631 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include using namespace Swift; class GetPrivateStorageRequestTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(GetPrivateStorageRequestTest); CPPUNIT_TEST(testSend); CPPUNIT_TEST(testHandleResponse); CPPUNIT_TEST(testHandleResponse_Error); CPPUNIT_TEST_SUITE_END(); public: class MyPayload : public Payload { public: MyPayload(const std::string& text = "") : text(text) {} std::string text; }; public: void setUp() { channel = new DummyIQChannel(); router = new IQRouter(channel); } void tearDown() { delete router; delete channel; } void testSend() { GetPrivateStorageRequest::ref request = GetPrivateStorageRequest::create(router); request->send(); CPPUNIT_ASSERT_EQUAL(1, static_cast(channel->iqs_.size())); CPPUNIT_ASSERT_EQUAL(JID(), channel->iqs_[0]->getTo()); CPPUNIT_ASSERT_EQUAL(IQ::Get, channel->iqs_[0]->getType()); boost::shared_ptr storage = channel->iqs_[0]->getPayload(); CPPUNIT_ASSERT(storage); boost::shared_ptr payload = boost::dynamic_pointer_cast(storage->getPayload()); CPPUNIT_ASSERT(payload); } void testHandleResponse() { GetPrivateStorageRequest::ref testling = GetPrivateStorageRequest::create(router); testling->onResponse.connect(boost::bind(&GetPrivateStorageRequestTest::handleResponse, this, _1, _2)); testling->send(); channel->onIQReceived(createResponse("test-id", "foo")); CPPUNIT_ASSERT_EQUAL(1, static_cast(responses.size())); CPPUNIT_ASSERT_EQUAL(std::string("foo"), boost::dynamic_pointer_cast(responses[0])->text); } void testHandleResponse_Error() { GetPrivateStorageRequest::ref testling = GetPrivateStorageRequest::create(router); testling->onResponse.connect(boost::bind(&GetPrivateStorageRequestTest::handleResponse, this, _1, _2)); testling->send(); channel->onIQReceived(createError("test-id")); CPPUNIT_ASSERT_EQUAL(0, static_cast(responses.size())); CPPUNIT_ASSERT_EQUAL(1, static_cast(errors.size())); } private: void handleResponse(boost::shared_ptr p, ErrorPayload::ref e) { if (e) { errors.push_back(*e); } else { responses.push_back(p); } } boost::shared_ptr createResponse(const std::string& id, const std::string& text) { boost::shared_ptr iq(new IQ(IQ::Result)); boost::shared_ptr storage(new PrivateStorage()); storage->setPayload(boost::shared_ptr(new MyPayload(text))); iq->addPayload(storage); iq->setID(id); return iq; } boost::shared_ptr createError(const std::string& id) { boost::shared_ptr iq(new IQ(IQ::Error)); iq->setID(id); return iq; } private: IQRouter* router; DummyIQChannel* channel; std::vector< ErrorPayload > errors; std::vector< boost::shared_ptr > responses; }; CPPUNIT_TEST_SUITE_REGISTRATION(GetPrivateStorageRequestTest); swift-im-2.0+dev6/Swiften/Queries/Requests/SetPrivateStorageRequest.h0000644000175000017500000000216512227051774025673 0ustar kismithkismith/* * 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 #include #include #include #include #include namespace Swift { template class SetPrivateStorageRequest : public Request { public: typedef boost::shared_ptr > ref; static ref create(boost::shared_ptr payload, IQRouter* router) { return ref(new SetPrivateStorageRequest(payload, router)); } private: SetPrivateStorageRequest(boost::shared_ptr payload, IQRouter* router) : Request(IQ::Set, JID(), boost::make_shared(payload), router) { } virtual void handleResponse(boost::shared_ptr, ErrorPayload::ref error) { onResponse(error); } public: boost::signal onResponse; }; } swift-im-2.0+dev6/Swiften/Queries/Requests/SubmitInBandRegistrationFormRequest.cpp0000644000175000017500000000035212227051774030345 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include swift-im-2.0+dev6/Swiften/Queries/Requests/GetSoftwareVersionRequest.h0000644000175000017500000000147612227051774026064 0ustar kismithkismith/* * 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 #include #include namespace Swift { class GetSoftwareVersionRequest : public GenericRequest { public: typedef boost::shared_ptr ref; static ref create(const JID& recipient, IQRouter* router) { return ref(new GetSoftwareVersionRequest(recipient, router)); } private: GetSoftwareVersionRequest( const JID& recipient, IQRouter* router) : GenericRequest( IQ::Get, recipient, boost::make_shared(), router) { } }; } swift-im-2.0+dev6/Swiften/Queries/Requests/GetInBandRegistrationFormRequest.cpp0000644000175000017500000000034712227051774027625 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include swift-im-2.0+dev6/Swiften/Queries/Requests/GetSecurityLabelsCatalogRequest.h0000644000175000017500000000156412227051774027147 0ustar kismithkismith/* * 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 #include #include namespace Swift { class GetSecurityLabelsCatalogRequest : public GenericRequest { public: typedef boost::shared_ptr ref; static ref create(const JID& recipient, IQRouter* router) { return ref(new GetSecurityLabelsCatalogRequest(recipient, router)); } private: GetSecurityLabelsCatalogRequest( const JID& recipient, IQRouter* router) : GenericRequest( IQ::Get, JID(), boost::make_shared(recipient), router) { } }; } swift-im-2.0+dev6/Swiften/Queries/Requests/GetInBandRegistrationFormRequest.h0000644000175000017500000000146112227051774027270 0ustar kismithkismith/* * 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 #include namespace Swift { class GetInBandRegistrationFormRequest : public GenericRequest { public: typedef boost::shared_ptr ref; static ref create(const JID& to, IQRouter* router) { return ref(new GetInBandRegistrationFormRequest(to, router)); } private: GetInBandRegistrationFormRequest(const JID& to, IQRouter* router) : GenericRequest(IQ::Get, to, InBandRegistrationPayload::ref(new InBandRegistrationPayload()), router) { } }; } swift-im-2.0+dev6/Swiften/Examples/0000755000175000017500000000000012227051774017060 5ustar kismithkismithswift-im-2.0+dev6/Swiften/Examples/SConscript0000644000175000017500000000027612227051774021077 0ustar kismithkismithImport("swiften_env") myenv = swiften_env.Clone() SConscript(dirs = [ "SendMessage", "SendFile", "ConnectivityTest", "LinkLocalTool", "NetworkTool", "ParserTester", "BenchTool", ]) swift-im-2.0+dev6/Swiften/Examples/LinkLocalTool/0000755000175000017500000000000012227051774021566 5ustar kismithkismithswift-im-2.0+dev6/Swiften/Examples/LinkLocalTool/SConscript0000644000175000017500000000027412227051774023603 0ustar kismithkismithImport("env") myenv = env.Clone() myenv.MergeFlags(myenv["SWIFTEN_FLAGS"]) myenv.MergeFlags(myenv["SWIFTEN_DEP_FLAGS"]) linkLocalTool = myenv.Program("LinkLocalTool", [ "main.cpp" ]) swift-im-2.0+dev6/Swiften/Examples/LinkLocalTool/main.cpp0000644000175000017500000000263712227051774023226 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ // FIXME: This example is not complete yet #include #include #include #include #include #include using namespace Swift; int main(int argc, char* argv[]) { if (argc < 2) { std::cerr << "Invalid parameters" << std::endl; return -1; } SimpleEventLoop eventLoop; PlatformDNSSDQuerierFactory factory(&eventLoop); boost::shared_ptr querier = factory.createQuerier(); querier->start(); if (std::string(argv[1]) == "browse") { boost::shared_ptr browseQuery = querier->createBrowseQuery(); browseQuery->startBrowsing(); eventLoop.run(); browseQuery->stopBrowsing(); } else if (std::string(argv[1]) == "resolve-service") { if (argc < 5) { std::cerr << "Invalid parameters" << std::endl; return -1; } boost::shared_ptr resolveQuery = querier->createResolveServiceQuery(DNSSDServiceID(argv[2], argv[3], argv[4])); resolveQuery->start(); eventLoop.run(); std::cerr << "Done running" << std::endl; resolveQuery->stop(); } querier->stop(); } swift-im-2.0+dev6/Swiften/Examples/BenchTool/0000755000175000017500000000000012227051774020735 5ustar kismithkismithswift-im-2.0+dev6/Swiften/Examples/BenchTool/SConscript0000644000175000017500000000025712227051774022753 0ustar kismithkismithimport os Import("env") myenv = env.Clone() myenv.UseFlags(myenv["SWIFTEN_FLAGS"]) myenv.UseFlags(myenv["SWIFTEN_DEP_FLAGS"]) myenv.Program("BenchTool", ["BenchTool.cpp"]) swift-im-2.0+dev6/Swiften/Examples/BenchTool/BenchTool.cpp0000644000175000017500000000344312227051774023322 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include #include #include using namespace Swift; SimpleEventLoop eventLoop; BoostNetworkFactories networkFactories(&eventLoop); int numberOfConnectedClients = 0; int numberOfInstances = 100; void handleConnected() { numberOfConnectedClients++; std::cout << "Connected " << numberOfConnectedClients << std::endl; } int main(int, char**) { char* jid = getenv("SWIFT_BENCHTOOL_JID"); if (!jid) { std::cerr << "Please set the SWIFT_BENCHTOOL_JID environment variable" << std::endl; return -1; } char* pass = getenv("SWIFT_BENCHTOOL_PASS"); if (!pass) { std::cerr << "Please set the SWIFT_BENCHTOOL_PASS environment variable" << std::endl; return -1; } BlindCertificateTrustChecker trustChecker; std::vector clients; for (int i = 0; i < numberOfInstances; ++i) { CoreClient* client = new Swift::CoreClient(JID(jid), createSafeByteArray(std::string(pass)), &networkFactories); client->setCertificateTrustChecker(&trustChecker); client->onConnected.connect(&handleConnected); clients.push_back(client); } for (size_t i = 0; i < clients.size(); ++i) { clients[i]->connect(); } eventLoop.run(); for (size_t i = 0; i < clients.size(); ++i) { delete clients[i]; } return 0; } swift-im-2.0+dev6/Swiften/Examples/NetworkTool/0000755000175000017500000000000012227051774021347 5ustar kismithkismithswift-im-2.0+dev6/Swiften/Examples/NetworkTool/SConscript0000644000175000017500000000033112227051774023356 0ustar kismithkismithImport("env") if env["experimental"] : myenv = env.Clone() myenv.MergeFlags(myenv["SWIFTEN_FLAGS"]) myenv.MergeFlags(myenv["SWIFTEN_DEP_FLAGS"]) linkLocalTool = myenv.Program("NetworkTool", [ "main.cpp" ]) swift-im-2.0+dev6/Swiften/Examples/NetworkTool/main.cpp0000644000175000017500000000534312227051774023004 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include using namespace Swift; SimpleEventLoop eventLoop; void handleGetPublicIPRequestResponse(const boost::optional& result) { if (result) { std::cerr << "Result: " << result->toString() << std::endl;; } else { std::cerr << "No result" << std::endl; } eventLoop.stop(); } void handleGetForwardPortRequestResponse(const boost::optional& result) { if (result) { std::cerr << "Result: " << result->getPublicPort() << " -> " << result->getLocalPort() << std::endl;; } else { std::cerr << "No result" << std::endl; } eventLoop.stop(); } void handleRemovePortForwardingRequestResponse(bool result) { if (result) { std::cerr << "Result: OK" << std::endl; } else { std::cerr << "Result: ERROR" << std::endl; } eventLoop.stop(); } int main(int argc, char* argv[]) { if (argc < 2) { std::cerr << "Invalid parameters" << std::endl; return -1; } PlatformNATTraversalWorker natTraverser(&eventLoop); if (std::string(argv[1]) == "get-public-ip") { boost::shared_ptr query = natTraverser.createGetPublicIPRequest(); query->onResult.connect(boost::bind(&handleGetPublicIPRequestResponse, _1)); query->run(); eventLoop.run(); } else if (std::string(argv[1]) == "add-port-forward") { if (argc < 4) { std::cerr << "Invalid parameters" << std::endl; } boost::shared_ptr query = natTraverser.createForwardPortRequest(boost::lexical_cast(argv[2]), boost::lexical_cast(argv[3])); query->onResult.connect(boost::bind(&handleGetForwardPortRequestResponse, _1)); query->run(); eventLoop.run(); } else if (std::string(argv[1]) == "remove-port-forward") { if (argc < 4) { std::cerr << "Invalid parameters" << std::endl; } boost::shared_ptr query = natTraverser.createRemovePortForwardingRequest(boost::lexical_cast(argv[2]), boost::lexical_cast(argv[3])); query->onResult.connect(boost::bind(&handleRemovePortForwardingRequestResponse, _1)); query->run(); eventLoop.run(); } else if (std::string(argv[1]) == "get-local-ip") { std::cout << PlatformNetworkEnvironment().getLocalAddress().toString() << std::endl; } } swift-im-2.0+dev6/Swiften/Examples/ParserTester/0000755000175000017500000000000012227051774021503 5ustar kismithkismithswift-im-2.0+dev6/Swiften/Examples/ParserTester/SConscript0000644000175000017500000000025612227051774023520 0ustar kismithkismithImport("env") myenv = env.Clone() myenv.MergeFlags(myenv["SWIFTEN_FLAGS"]) myenv.MergeFlags(myenv["SWIFTEN_DEP_FLAGS"]) myenv.Program("ParserTester", ["ParserTester.cpp"]) swift-im-2.0+dev6/Swiften/Examples/ParserTester/ParserTester.cpp0000644000175000017500000000321212227051774024630 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include using namespace Swift; class MyXMPPParserClient : public XMPPParserClient { public: virtual void handleStreamStart(const ProtocolHeader&) { std::cout << "-> Stream start" << std::endl; } virtual void handleElement(boost::shared_ptr element) { std::cout << "-> Element " << typeid(*element.get()).name() << std::endl; } virtual void handleStreamEnd() { std::cout << "-> Stream end" << std::endl; } }; int main(int argc, char* argv[]) { if (argc != 2) { std::cerr << "Usage: " << argv[0] << " file" << std::endl; return 0; } FullPayloadParserFactoryCollection factories; MyXMPPParserClient parserClient; PlatformXMLParserFactory xmlParserFactory; XMPPParser parser(&parserClient, &factories, &xmlParserFactory); ParserTester tester(&parser); std::string line; std::ifstream myfile (argv[1]); if (myfile.is_open()) { while (!myfile.eof()) { getline (myfile,line); std::cout << "Parsing: " << line << std::endl; if (!tester.parse(line)) { std::cerr << "PARSE ERROR" << std::endl; return -1; } } myfile.close(); } else { std::cerr << "Unable to open file " << argv[1] << std::endl; } return 0; } swift-im-2.0+dev6/Swiften/Examples/SendMessage/0000755000175000017500000000000012227051774021256 5ustar kismithkismithswift-im-2.0+dev6/Swiften/Examples/SendMessage/SConscript0000644000175000017500000000026512227051774023273 0ustar kismithkismithImport("env") myenv = env.Clone() myenv.MergeFlags(myenv["SWIFTEN_FLAGS"]) myenv.MergeFlags(myenv["SWIFTEN_DEP_FLAGS"]) tester = myenv.Program("SendMessage", ["SendMessage.cpp"]) swift-im-2.0+dev6/Swiften/Examples/SendMessage/SendMessage.cpp0000644000175000017500000000433112227051774024161 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include using namespace Swift; SimpleEventLoop eventLoop; BoostNetworkFactories networkFactories(&eventLoop); Client* client = 0; JID recipient; std::string messageBody; int exitCode = 2; boost::bsignals::connection errorConnection; void handleConnected() { boost::shared_ptr message(new Message()); message->setBody(messageBody); message->setTo(recipient); client->sendMessage(message); exitCode = 0; errorConnection.disconnect(); client->disconnect(); eventLoop.stop(); } void handleDisconnected(const boost::optional&) { std::cerr << "Error!" << std::endl; exitCode = 1; eventLoop.stop(); } int main(int argc, char* argv[]) { if (argc < 5 || argc > 6) { std::cerr << "Usage: " << argv[0] << " [] " << std::endl; return -1; } int argi = 1; std::string jid = argv[argi++]; std::string connectHost = ""; if (argc == 6) { connectHost = argv[argi++]; } client = new Swift::Client(JID(jid), std::string(argv[argi++]), &networkFactories); client->setAlwaysTrustCertificates(); recipient = JID(argv[argi++]); messageBody = std::string(argv[argi++]); ClientXMLTracer* tracer = new ClientXMLTracer(client); client->onConnected.connect(&handleConnected); errorConnection = client->onDisconnected.connect(&handleDisconnected); if (!connectHost.empty()) { ClientOptions options; options.manualHostname = connectHost; client->connect(options); } else { client->connect(); } { Timer::ref timer = networkFactories.getTimerFactory()->createTimer(30000); timer->onTick.connect(boost::bind(&SimpleEventLoop::stop, &eventLoop)); timer->start(); eventLoop.run(); } delete tracer; delete client; return exitCode; } swift-im-2.0+dev6/Swiften/Examples/ConnectivityTest/0000755000175000017500000000000012227051774022376 5ustar kismithkismithswift-im-2.0+dev6/Swiften/Examples/ConnectivityTest/SConscript0000644000175000017500000000027712227051774024416 0ustar kismithkismithImport("env") myenv = env.Clone() myenv.MergeFlags(myenv["SWIFTEN_FLAGS"]) myenv.MergeFlags(myenv["SWIFTEN_DEP_FLAGS"]) tester = myenv.Program("ConnectivityTest", ["ConnectivityTest.cpp"]) swift-im-2.0+dev6/Swiften/Examples/ConnectivityTest/ConnectivityTest.cpp0000644000175000017500000000536312227051774026427 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include #include using namespace Swift; enum ExitCodes {OK = 0, CANNOT_CONNECT, CANNOT_AUTH, NO_RESPONSE, DISCO_ERROR}; SimpleEventLoop eventLoop; BoostNetworkFactories networkFactories(&eventLoop); Client* client = 0; JID recipient; int exitCode = CANNOT_CONNECT; boost::bsignals::connection errorConnection; void handleServerDiscoInfoResponse(boost::shared_ptr /*info*/, ErrorPayload::ref error) { if (!error) { errorConnection.disconnect(); client->disconnect(); eventLoop.stop(); exitCode = OK; } else { errorConnection.disconnect(); exitCode = DISCO_ERROR; } } void handleConnected() { exitCode = NO_RESPONSE; GetDiscoInfoRequest::ref discoInfoRequest = GetDiscoInfoRequest::create(JID(), client->getIQRouter()); discoInfoRequest->onResponse.connect(&handleServerDiscoInfoResponse); discoInfoRequest->send(); } void handleDisconnected(const boost::optional&) { exitCode = CANNOT_AUTH; eventLoop.stop(); } int main(int argc, char* argv[]) { if (argc < 4 || argc > 5) { std::cerr << "Usage: " << argv[0] << " [] " << std::endl; return -1; } int argi = 1; std::string jid = argv[argi++]; std::string connectHost = ""; if (argc == 5) { connectHost = argv[argi++]; } client = new Swift::Client(JID(jid), std::string(argv[argi++]), &networkFactories); char* timeoutChar = argv[argi++]; int timeout = atoi(timeoutChar); timeout = (timeout ? timeout : 30) * 1000; ClientXMLTracer* tracer = new ClientXMLTracer(client); client->onConnected.connect(&handleConnected); errorConnection = client->onDisconnected.connect(&handleDisconnected); std::cout << "Connecting to JID " << jid << " with timeout " << timeout << "ms on host: "; ; if (!connectHost.empty()) { std::cout << connectHost << std::endl; ClientOptions options; options.manualHostname = connectHost; client->connect(options); } else { std::cout << " Default" << std::endl; client->connect(); } { Timer::ref timer = networkFactories.getTimerFactory()->createTimer(timeout); timer->onTick.connect(boost::bind(&SimpleEventLoop::stop, &eventLoop)); timer->start(); eventLoop.run(); } delete tracer; delete client; return exitCode; } swift-im-2.0+dev6/Swiften/Examples/SendFile/0000755000175000017500000000000012227051774020551 5ustar kismithkismithswift-im-2.0+dev6/Swiften/Examples/SendFile/SConscript0000644000175000017500000000033012227051774022557 0ustar kismithkismithImport("env") myenv = env.Clone() myenv.MergeFlags(myenv["SWIFTEN_FLAGS"]) myenv.MergeFlags(myenv["SWIFTEN_DEP_FLAGS"]) myenv.Program("SendFile", ["SendFile.cpp"]) myenv.Program("ReceiveFile", ["ReceiveFile.cpp"]) swift-im-2.0+dev6/Swiften/Examples/SendFile/SendFile.cpp0000644000175000017500000001236012227051774022750 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Swift; SimpleEventLoop eventLoop; BoostNetworkFactories networkFactories(&eventLoop); int exitCode = 2; class FileSender { public: FileSender(const JID& jid, const std::string& password, const JID& recipient, const boost::filesystem::path& file) : jid(jid), password(password), recipient(recipient), file(file) { client = new Swift::Client(jid, password, &networkFactories); client->onConnected.connect(boost::bind(&FileSender::handleConnected, this)); client->onDisconnected.connect(boost::bind(&FileSender::handleDisconnected, this, _1)); tracer = new ClientXMLTracer(client); client->getEntityCapsProvider()->onCapsChanged.connect(boost::bind(&FileSender::handleCapsChanged, this, _1)); } ~FileSender() { delete tracer; client->onDisconnected.disconnect(boost::bind(&FileSender::handleDisconnected, this, _1)); client->onConnected.disconnect(boost::bind(&FileSender::handleConnected, this)); delete client; } void start() { client->connect(); } private: void handleConnected() { client->sendPresence(Presence::create()); client->getFileTransferManager()->startListeningOnPort(19999); //ByteArray fileData; //readByteArrayFromFile(fileData, file.string()); // gather file information /*StreamInitiationFileInfo fileInfo; fileInfo.setName(file.filename()); fileInfo.setSize(boost::filesystem::file_size(file)); fileInfo.setDescription("Some file!"); fileInfo.setDate(boost::posix_time::from_time_t(boost::filesystem::last_write_time(file)));*/ //fileInfo.setHash(Hexify::hexify(MD5::getHash(fileData))); /* transfer = new OutgoingSIFileTransfer("myid", client->getJID(), recipient, file.filename(), boost::filesystem::file_size(file), "A file", boost::make_shared(file)), client->getIQRouter(), socksBytestreamServer); transfer->onFinished.connect(boost::bind(&FileSender::handleFileTransferFinished, this, _1)); transfer->start(); */ } void handleCapsChanged(JID jid) { if (jid.toBare() == recipient) { // create ReadBytestream from file boost::shared_ptr fileStream = boost::make_shared(file); outgoingFileTransfer = client->getFileTransferManager()->createOutgoingFileTransfer(recipient, file, "Some File!", fileStream); if (outgoingFileTransfer) { std::cout << "started FT" << std::endl; outgoingFileTransfer->start(); // TODO: getting notified about FT status and end } else { std::cout << "[ ERROR ] " << recipient << " doesn't support any kind of file transfer!" << std::endl; //client->disconnect(); } } } void handleDisconnected(const boost::optional&) { std::cerr << "Error!" << std::endl; exit(-1); } void handleFileTransferFinished(const boost::optional& error) { std::cout << "File transfer finished" << std::endl; if (error) { exit(-1); } else { exit(0); } } void exit(int code) { exitCode = code; eventLoop.stop(); } private: BoostConnectionServer::ref connectionServer; OutgoingFileTransfer::ref outgoingFileTransfer; JID jid; std::string password; JID recipient; boost::filesystem::path file; Client* client; ClientXMLTracer* tracer; }; int main(int argc, char* argv[]) { if (argc != 5) { std::cerr << "Usage: " << argv[0] << " " << std::endl; return -1; } JID sender(argv[1]); JID recipient(argv[3]); Swift::logging = true; FileSender fileSender(sender, std::string(argv[2]), recipient, boost::filesystem::path(argv[4])); fileSender.start(); { /*BoostTimer::ref timer(BoostTimer::create(30000, &MainBoostIOServiceThread::getInstance().getIOService())); timer->onTick.connect(boost::bind(&SimpleEventLoop::stop, &eventLoop)); timer->start();*/ eventLoop.run(); } return exitCode; } swift-im-2.0+dev6/Swiften/Examples/SendFile/ReceiveFile.cpp0000644000175000017500000001043112227051774023436 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Swift; SimpleEventLoop eventLoop; BoostNetworkFactories networkFactories(&eventLoop); int exitCode = 2; static const std::string CLIENT_NAME = "Swiften FT Test"; static const std::string CLIENT_NODE = "http://swift.im"; class FileReceiver { public: FileReceiver(const JID& jid, const std::string& password) : jid(jid), password(password), jingleSessionManager(NULL), incomingFileTransferManager(NULL) { client = new Swift::Client(jid, password, &networkFactories); client->onConnected.connect(boost::bind(&FileReceiver::handleConnected, this)); client->onDisconnected.connect(boost::bind(&FileReceiver::handleDisconnected, this, _1)); tracer = new ClientXMLTracer(client); } ~FileReceiver() { delete tracer; client->onDisconnected.disconnect(boost::bind(&FileReceiver::handleDisconnected, this, _1)); client->onConnected.disconnect(boost::bind(&FileReceiver::handleConnected, this)); delete client; } void start() { client->connect(); } void stop() { foreach(const IncomingFileTransfer::ref transfer, incomingFileTransfers) { //transfer->stop(); } client->disconnect(); } private: void handleConnected() { Swift::logging = true; client->getFileTransferManager()->startListeningOnPort(9999); client->getFileTransferManager()->onIncomingFileTransfer.connect(boost::bind(&FileReceiver::handleIncomingFileTransfer, this, _1)); DiscoInfo discoInfo; discoInfo.addIdentity(DiscoInfo::Identity(CLIENT_NAME, "client", "pc")); discoInfo.addFeature(DiscoInfo::JingleFeature); discoInfo.addFeature(DiscoInfo::JingleFTFeature); discoInfo.addFeature(DiscoInfo::Bytestream); discoInfo.addFeature(DiscoInfo::JingleTransportsIBBFeature); discoInfo.addFeature(DiscoInfo::JingleTransportsS5BFeature); client->getDiscoManager()->setCapsNode(CLIENT_NODE); client->getDiscoManager()->setDiscoInfo(discoInfo); client->getPresenceSender()->sendPresence(Presence::create()); } void handleIncomingFileTransfer(IncomingFileTransfer::ref transfer) { SWIFT_LOG(debug) << "foo" << std::endl; incomingFileTransfers.push_back(transfer); transfer->accept(boost::make_shared("out")); //transfer->onFinished.connect(boost::bind(&FileReceiver::handleFileTransferFinished, this, _1)); //transfer->start(); } void handleDisconnected(const boost::optional&) { std::cerr << "Error!" << std::endl; exit(-1); } /* void handleFileTransferFinished(const boost::optional& error) { std::cout << "File transfer finished" << std::endl; if (error) { exit(-1); } else { exit(0); } }*/ void exit(int code) { exitCode = code; stop(); eventLoop.stop(); } private: JID jid; std::string password; Client* client; ClientXMLTracer* tracer; JingleSessionManager* jingleSessionManager; IncomingFileTransferManager* incomingFileTransferManager; std::vector incomingFileTransfers; }; int main(int argc, char* argv[]) { if (argc != 3) { std::cerr << "Usage: " << argv[0] << " " << std::endl; return -1; } JID jid(argv[1]); FileReceiver fileReceiver(jid, std::string(argv[2])); fileReceiver.start(); eventLoop.run(); return exitCode; } swift-im-2.0+dev6/Swiften/Component/0000755000175000017500000000000012227051774017244 5ustar kismithkismithswift-im-2.0+dev6/Swiften/Component/Component.h0000644000175000017500000000177512227051774021371 0ustar kismithkismith/* * 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 #include namespace Swift { class SoftwareVersionResponder; /** * Provides the core functionality for writing XMPP component software. * * Besides connecting to an XMPP server, this class also provides interfaces for * performing most component tasks on the XMPP network. */ class SWIFTEN_API Component : public CoreComponent { public: Component(EventLoop* eventLoop, NetworkFactories* networkFactories, const JID& jid, const std::string& secret); ~Component(); /** * Sets the software version of the client. * * This will be used to respond to version queries from other entities. */ void setSoftwareVersion(const std::string& name, const std::string& version); private: SoftwareVersionResponder* softwareVersionResponder; }; } swift-im-2.0+dev6/Swiften/Component/SConscript0000644000175000017500000000047012227051774021257 0ustar kismithkismithImport("swiften_env") sources = [ "ComponentHandshakeGenerator.cpp", "ComponentConnector.cpp", "ComponentSession.cpp", "ComponentSessionStanzaChannel.cpp", "CoreComponent.cpp", "Component.cpp", "ComponentXMLTracer.cpp", ] swiften_env.Append(SWIFTEN_OBJECTS = swiften_env.SwiftenObject(sources)) swift-im-2.0+dev6/Swiften/Component/ComponentSession.h0000644000175000017500000000415412227051774022727 0ustar kismithkismith/* * 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 #include #include #include #include #include #include #include #include #include namespace Swift { class ComponentAuthenticator; class SWIFTEN_API ComponentSession : public boost::enable_shared_from_this { public: enum State { Initial, WaitingForStreamStart, Authenticating, Initialized, Finishing, Finished }; struct Error : public Swift::Error { enum Type { AuthenticationFailedError, UnexpectedElementError, } type; Error(Type type) : type(type) {} }; ~ComponentSession(); static boost::shared_ptr create(const JID& jid, const std::string& secret, boost::shared_ptr stream) { return boost::shared_ptr(new ComponentSession(jid, secret, stream)); } State getState() const { return state; } void start(); void finish(); void sendStanza(boost::shared_ptr); public: boost::signal onInitialized; boost::signal)> onFinished; boost::signal)> onStanzaReceived; private: ComponentSession(const JID& jid, const std::string& secret, boost::shared_ptr); void finishSession(Error::Type error); void finishSession(boost::shared_ptr error); void sendStreamHeader(); void handleElement(boost::shared_ptr); void handleStreamStart(const ProtocolHeader&); void handleStreamClosed(boost::shared_ptr); bool checkState(State); private: JID jid; std::string secret; boost::shared_ptr stream; boost::shared_ptr error; State state; }; } swift-im-2.0+dev6/Swiften/Component/ComponentConnector.cpp0000644000175000017500000000673712227051774023602 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include namespace Swift { ComponentConnector::ComponentConnector(const std::string& hostname, int port, DomainNameResolver* resolver, ConnectionFactory* connectionFactory, TimerFactory* timerFactory) : hostname(hostname), port(port), resolver(resolver), connectionFactory(connectionFactory), timerFactory(timerFactory), timeoutMilliseconds(0) { } void ComponentConnector::setTimeoutMilliseconds(int milliseconds) { timeoutMilliseconds = milliseconds; } void ComponentConnector::start() { assert(!currentConnection); assert(!timer); assert(!addressQuery); addressQuery = resolver->createAddressQuery(hostname); addressQuery->onResult.connect(boost::bind(&ComponentConnector::handleAddressQueryResult, shared_from_this(), _1, _2)); if (timeoutMilliseconds > 0) { timer = timerFactory->createTimer(timeoutMilliseconds); timer->onTick.connect(boost::bind(&ComponentConnector::handleTimeout, shared_from_this())); timer->start(); } addressQuery->run(); } void ComponentConnector::stop() { finish(boost::shared_ptr()); } void ComponentConnector::handleAddressQueryResult(const std::vector& addresses, boost::optional error) { addressQuery.reset(); if (error || addresses.empty()) { finish(boost::shared_ptr()); } else { addressQueryResults = std::deque(addresses.begin(), addresses.end()); tryNextAddress(); } } void ComponentConnector::tryNextAddress() { assert(!addressQueryResults.empty()); HostAddress address = addressQueryResults.front(); addressQueryResults.pop_front(); tryConnect(HostAddressPort(address, port)); } void ComponentConnector::tryConnect(const HostAddressPort& target) { assert(!currentConnection); currentConnection = connectionFactory->createConnection(); currentConnection->onConnectFinished.connect(boost::bind(&ComponentConnector::handleConnectionConnectFinished, shared_from_this(), _1)); currentConnection->connect(target); } void ComponentConnector::handleConnectionConnectFinished(bool error) { currentConnection->onConnectFinished.disconnect(boost::bind(&ComponentConnector::handleConnectionConnectFinished, shared_from_this(), _1)); if (error) { currentConnection.reset(); if (!addressQueryResults.empty()) { tryNextAddress(); } else { finish(boost::shared_ptr()); } } else { finish(currentConnection); } } void ComponentConnector::finish(boost::shared_ptr connection) { if (timer) { timer->stop(); timer->onTick.disconnect(boost::bind(&ComponentConnector::handleTimeout, shared_from_this())); timer.reset(); } if (addressQuery) { addressQuery->onResult.disconnect(boost::bind(&ComponentConnector::handleAddressQueryResult, shared_from_this(), _1, _2)); addressQuery.reset(); } if (currentConnection) { currentConnection->onConnectFinished.disconnect(boost::bind(&ComponentConnector::handleConnectionConnectFinished, shared_from_this(), _1)); currentConnection.reset(); } onConnectFinished(connection); } void ComponentConnector::handleTimeout() { finish(boost::shared_ptr()); } }; swift-im-2.0+dev6/Swiften/Component/CoreComponent.h0000644000175000017500000000627412227051774022201 0ustar kismithkismith/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Swift { class IQRouter; class NetworkFactories; class ComponentSession; class BasicSessionStream; /** * The central class for communicating with an XMPP server as a component. * * This class is responsible for setting up the connection with the XMPP * server and authenticating the component. * * This class can be used directly in your application, although the Component * subclass provides more functionality and interfaces, and is better suited * for most needs. */ class SWIFTEN_API CoreComponent : public Entity { public: CoreComponent(EventLoop* eventLoop, NetworkFactories* networkFactories, const JID& jid, const std::string& secret); ~CoreComponent(); void connect(const std::string& host, int port); void disconnect(); void sendMessage(boost::shared_ptr); void sendPresence(boost::shared_ptr); IQRouter* getIQRouter() const { return iqRouter_; } StanzaChannel* getStanzaChannel() const { return stanzaChannel_; } bool isAvailable() const { return stanzaChannel_->isAvailable(); } /** * Returns the JID of the component */ const JID& getJID() const { return jid_; } public: boost::signal onError; boost::signal onConnected; boost::signal onDataRead; boost::signal onDataWritten; boost::signal)> onMessageReceived; boost::signal) > onPresenceReceived; private: void handleConnectorFinished(boost::shared_ptr); void handleStanzaChannelAvailableChanged(bool available); void handleSessionFinished(boost::shared_ptr); void handleDataRead(const SafeByteArray&); void handleDataWritten(const SafeByteArray&); private: EventLoop* eventLoop; NetworkFactories* networkFactories; PlatformDomainNameResolver resolver_; JID jid_; std::string secret_; ComponentSessionStanzaChannel* stanzaChannel_; IQRouter* iqRouter_; ComponentConnector::ref connector_; boost::shared_ptr connection_; boost::shared_ptr sessionStream_; boost::shared_ptr session_; bool disconnectRequested_; }; } swift-im-2.0+dev6/Swiften/Component/ComponentError.h0000644000175000017500000000104412227051774022370 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once namespace Swift { class ComponentError { public: enum Type { UnknownError, ConnectionError, ConnectionReadError, ConnectionWriteError, XMLError, AuthenticationFailedError, UnexpectedElementError, }; ComponentError(Type type = UnknownError) : type_(type) {} Type getType() const { return type_; } private: Type type_; }; } swift-im-2.0+dev6/Swiften/Component/ComponentConnector.h0000644000175000017500000000405512227051774023236 0ustar kismithkismith/* * 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 #include #include #include #include #include #include #include #include namespace Swift { class DomainNameAddressQuery; class DomainNameResolver; class ConnectionFactory; class TimerFactory; class SWIFTEN_API ComponentConnector : public boost::bsignals::trackable, public boost::enable_shared_from_this { public: typedef boost::shared_ptr ref; static ComponentConnector::ref create(const std::string& hostname, int port, DomainNameResolver* resolver, ConnectionFactory* connectionFactory, TimerFactory* timerFactory) { return ref(new ComponentConnector(hostname, port, resolver, connectionFactory, timerFactory)); } void setTimeoutMilliseconds(int milliseconds); void start(); void stop(); boost::signal)> onConnectFinished; private: ComponentConnector(const std::string& hostname, int port, DomainNameResolver*, ConnectionFactory*, TimerFactory*); void handleAddressQueryResult(const std::vector& address, boost::optional error); void tryNextAddress(); void tryConnect(const HostAddressPort& target); void handleConnectionConnectFinished(bool error); void finish(boost::shared_ptr); void handleTimeout(); private: std::string hostname; int port; DomainNameResolver* resolver; ConnectionFactory* connectionFactory; TimerFactory* timerFactory; int timeoutMilliseconds; boost::shared_ptr timer; boost::shared_ptr addressQuery; std::deque addressQueryResults; boost::shared_ptr currentConnection; }; }; swift-im-2.0+dev6/Swiften/Component/ComponentSessionStanzaChannel.h0000644000175000017500000000275012227051774025401 0ustar kismithkismith/* * 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 #include #include #include #include #include #include namespace Swift { /** * StanzaChannel implementation around a ComponentSession. */ class ComponentSessionStanzaChannel : public StanzaChannel { public: void setSession(boost::shared_ptr session); void sendIQ(boost::shared_ptr iq); void sendMessage(boost::shared_ptr message); void sendPresence(boost::shared_ptr presence); bool getStreamManagementEnabled() const { return false; } std::vector getPeerCertificateChain() const { // TODO: actually implement this method return std::vector(); } bool isAvailable() const { return session && session->getState() == ComponentSession::Initialized; } private: std::string getNewIQID(); void send(boost::shared_ptr stanza); void handleSessionFinished(boost::shared_ptr error); void handleStanza(boost::shared_ptr stanza); void handleSessionInitialized(); private: IDGenerator idGenerator; boost::shared_ptr session; }; } swift-im-2.0+dev6/Swiften/Component/CoreComponent.cpp0000644000175000017500000001415112227051774022525 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include #include namespace Swift { CoreComponent::CoreComponent(EventLoop* eventLoop, NetworkFactories* networkFactories, const JID& jid, const std::string& secret) : eventLoop(eventLoop), networkFactories(networkFactories), resolver_(eventLoop), jid_(jid), secret_(secret), disconnectRequested_(false) { stanzaChannel_ = new ComponentSessionStanzaChannel(); stanzaChannel_->onMessageReceived.connect(boost::ref(onMessageReceived)); stanzaChannel_->onPresenceReceived.connect(boost::ref(onPresenceReceived)); stanzaChannel_->onAvailableChanged.connect(boost::bind(&CoreComponent::handleStanzaChannelAvailableChanged, this, _1)); iqRouter_ = new IQRouter(stanzaChannel_); iqRouter_->setFrom(jid); } CoreComponent::~CoreComponent() { if (session_ || connection_) { std::cerr << "Warning: Component not disconnected properly" << std::endl; } delete iqRouter_; stanzaChannel_->onAvailableChanged.disconnect(boost::bind(&CoreComponent::handleStanzaChannelAvailableChanged, this, _1)); stanzaChannel_->onMessageReceived.disconnect(boost::ref(onMessageReceived)); stanzaChannel_->onPresenceReceived.disconnect(boost::ref(onPresenceReceived)); delete stanzaChannel_; } void CoreComponent::connect(const std::string& host, int port) { assert(!connector_); connector_ = ComponentConnector::create(host, port, &resolver_, networkFactories->getConnectionFactory(), networkFactories->getTimerFactory()); connector_->onConnectFinished.connect(boost::bind(&CoreComponent::handleConnectorFinished, this, _1)); connector_->setTimeoutMilliseconds(60*1000); connector_->start(); } void CoreComponent::handleConnectorFinished(boost::shared_ptr connection) { connector_->onConnectFinished.disconnect(boost::bind(&CoreComponent::handleConnectorFinished, this, _1)); connector_.reset(); if (!connection) { if (!disconnectRequested_) { onError(ComponentError::ConnectionError); } } else { assert(!connection_); connection_ = connection; assert(!sessionStream_); sessionStream_ = boost::shared_ptr(new BasicSessionStream(ComponentStreamType, connection_, getPayloadParserFactories(), getPayloadSerializers(), NULL, networkFactories->getTimerFactory(), networkFactories->getXMLParserFactory())); sessionStream_->onDataRead.connect(boost::bind(&CoreComponent::handleDataRead, this, _1)); sessionStream_->onDataWritten.connect(boost::bind(&CoreComponent::handleDataWritten, this, _1)); session_ = ComponentSession::create(jid_, secret_, sessionStream_); stanzaChannel_->setSession(session_); session_->onFinished.connect(boost::bind(&CoreComponent::handleSessionFinished, this, _1)); session_->start(); } } void CoreComponent::disconnect() { // FIXME: We should be able to do without this boolean. We just have to make sure we can tell the difference between // connector finishing without a connection due to an error or because of a disconnect. disconnectRequested_ = true; if (session_) { session_->finish(); } else if (connector_) { connector_->stop(); assert(!session_); } assert(!session_); assert(!sessionStream_); assert(!connector_); disconnectRequested_ = false; } void CoreComponent::handleSessionFinished(boost::shared_ptr error) { session_->onFinished.disconnect(boost::bind(&CoreComponent::handleSessionFinished, this, _1)); session_.reset(); sessionStream_->onDataRead.disconnect(boost::bind(&CoreComponent::handleDataRead, this, _1)); sessionStream_->onDataWritten.disconnect(boost::bind(&CoreComponent::handleDataWritten, this, _1)); sessionStream_.reset(); connection_->disconnect(); connection_.reset(); if (error) { ComponentError componentError; if (boost::shared_ptr actualError = boost::dynamic_pointer_cast(error)) { switch(actualError->type) { case ComponentSession::Error::AuthenticationFailedError: componentError = ComponentError(ComponentError::AuthenticationFailedError); break; case ComponentSession::Error::UnexpectedElementError: componentError = ComponentError(ComponentError::UnexpectedElementError); break; } } else if (boost::shared_ptr actualError = boost::dynamic_pointer_cast(error)) { switch(actualError->type) { case SessionStream::SessionStreamError::ParseError: componentError = ComponentError(ComponentError::XMLError); break; case SessionStream::SessionStreamError::TLSError: assert(false); componentError = ComponentError(ComponentError::UnknownError); break; case SessionStream::SessionStreamError::InvalidTLSCertificateError: assert(false); componentError = ComponentError(ComponentError::UnknownError); break; case SessionStream::SessionStreamError::ConnectionReadError: componentError = ComponentError(ComponentError::ConnectionReadError); break; case SessionStream::SessionStreamError::ConnectionWriteError: componentError = ComponentError(ComponentError::ConnectionWriteError); break; } } onError(componentError); } } void CoreComponent::handleDataRead(const SafeByteArray& data) { onDataRead(data); } void CoreComponent::handleDataWritten(const SafeByteArray& data) { onDataWritten(data); } void CoreComponent::handleStanzaChannelAvailableChanged(bool available) { if (available) { onConnected(); } } void CoreComponent::sendMessage(boost::shared_ptr message) { stanzaChannel_->sendMessage(message); } void CoreComponent::sendPresence(boost::shared_ptr presence) { stanzaChannel_->sendPresence(presence); } } swift-im-2.0+dev6/Swiften/Component/ComponentSessionStanzaChannel.cpp0000644000175000017500000000462612227051774025740 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include namespace Swift { void ComponentSessionStanzaChannel::setSession(boost::shared_ptr session) { assert(!this->session); this->session = session; session->onInitialized.connect(boost::bind(&ComponentSessionStanzaChannel::handleSessionInitialized, this)); session->onFinished.connect(boost::bind(&ComponentSessionStanzaChannel::handleSessionFinished, this, _1)); session->onStanzaReceived.connect(boost::bind(&ComponentSessionStanzaChannel::handleStanza, this, _1)); } void ComponentSessionStanzaChannel::sendIQ(boost::shared_ptr iq) { send(iq); } void ComponentSessionStanzaChannel::sendMessage(boost::shared_ptr message) { send(message); } void ComponentSessionStanzaChannel::sendPresence(boost::shared_ptr presence) { send(presence); } std::string ComponentSessionStanzaChannel::getNewIQID() { return idGenerator.generateID(); } void ComponentSessionStanzaChannel::send(boost::shared_ptr stanza) { if (!isAvailable()) { std::cerr << "Warning: Component: Trying to send a stanza while disconnected." << std::endl; return; } session->sendStanza(stanza); } void ComponentSessionStanzaChannel::handleSessionFinished(boost::shared_ptr) { session->onFinished.disconnect(boost::bind(&ComponentSessionStanzaChannel::handleSessionFinished, this, _1)); session->onStanzaReceived.disconnect(boost::bind(&ComponentSessionStanzaChannel::handleStanza, this, _1)); session->onInitialized.disconnect(boost::bind(&ComponentSessionStanzaChannel::handleSessionInitialized, this)); session.reset(); onAvailableChanged(false); } void ComponentSessionStanzaChannel::handleStanza(boost::shared_ptr stanza) { boost::shared_ptr message = boost::dynamic_pointer_cast(stanza); if (message) { onMessageReceived(message); return; } boost::shared_ptr presence = boost::dynamic_pointer_cast(stanza); if (presence) { onPresenceReceived(presence); return; } boost::shared_ptr iq = boost::dynamic_pointer_cast(stanza); if (iq) { onIQReceived(iq); return; } } void ComponentSessionStanzaChannel::handleSessionInitialized() { onAvailableChanged(true); } } swift-im-2.0+dev6/Swiften/Component/ComponentHandshakeGenerator.cpp0000644000175000017500000000155212227051774025373 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include namespace Swift { std::string ComponentHandshakeGenerator::getHandshake(const std::string& streamID, const std::string& secret) { std::string concatenatedString = streamID + secret; String::replaceAll(concatenatedString, '&', "&"); String::replaceAll(concatenatedString, '<', "<"); String::replaceAll(concatenatedString, '>', ">"); String::replaceAll(concatenatedString, '\'', "'"); String::replaceAll(concatenatedString, '"', """); return Hexify::hexify(SHA1::getHash(createByteArray(concatenatedString))); } } swift-im-2.0+dev6/Swiften/Component/UnitTest/0000755000175000017500000000000012227051774021023 5ustar kismithkismithswift-im-2.0+dev6/Swiften/Component/UnitTest/ComponentHandshakeGeneratorTest.cpp0000644000175000017500000000207312227051774030011 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include using namespace Swift; class ComponentHandshakeGeneratorTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(ComponentHandshakeGeneratorTest); CPPUNIT_TEST(testGetHandshake); CPPUNIT_TEST(testGetHandshake_SpecialChars); CPPUNIT_TEST_SUITE_END(); public: void testGetHandshake() { std::string result = ComponentHandshakeGenerator::getHandshake("myid", "mysecret"); CPPUNIT_ASSERT_EQUAL(std::string("4011cd31f9b99ac089a0cd7ce297da7323fa2525"), result); } void testGetHandshake_SpecialChars() { std::string result = ComponentHandshakeGenerator::getHandshake("&<", ">'\""); CPPUNIT_ASSERT_EQUAL(std::string("33631b3e0aaeb2a11c4994c917919324028873fe"), result); } }; CPPUNIT_TEST_SUITE_REGISTRATION(ComponentHandshakeGeneratorTest); swift-im-2.0+dev6/Swiften/Component/UnitTest/ComponentSessionTest.cpp0000644000175000017500000001350012227051774025674 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include using namespace Swift; class ComponentSessionTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(ComponentSessionTest); CPPUNIT_TEST(testStart); CPPUNIT_TEST(testStart_Error); CPPUNIT_TEST(testStart_Unauthorized); CPPUNIT_TEST_SUITE_END(); public: void setUp() { server = boost::make_shared(); sessionFinishedReceived = false; } void testStart() { boost::shared_ptr session(createSession()); session->start(); server->receiveStreamStart(); server->sendStreamStart(); server->receiveHandshake(); server->sendHandshakeResponse(); CPPUNIT_ASSERT(server->whitespacePingEnabled); session->finish(); CPPUNIT_ASSERT(!server->whitespacePingEnabled); } void testStart_Error() { boost::shared_ptr session(createSession()); session->start(); server->breakConnection(); CPPUNIT_ASSERT_EQUAL(ComponentSession::Finished, session->getState()); CPPUNIT_ASSERT(sessionFinishedReceived); CPPUNIT_ASSERT(sessionFinishedError); } void testStart_Unauthorized() { boost::shared_ptr session(createSession()); session->start(); server->receiveStreamStart(); server->sendStreamStart(); server->receiveHandshake(); server->sendHandshakeError(); CPPUNIT_ASSERT_EQUAL(ComponentSession::Finished, session->getState()); CPPUNIT_ASSERT(sessionFinishedReceived); CPPUNIT_ASSERT(sessionFinishedError); } private: boost::shared_ptr createSession() { boost::shared_ptr session = ComponentSession::create(JID("service.foo.com"), "servicesecret", server); session->onFinished.connect(boost::bind(&ComponentSessionTest::handleSessionFinished, this, _1)); return session; } void handleSessionFinished(boost::shared_ptr error) { sessionFinishedReceived = true; sessionFinishedError = error; } class MockSessionStream : public SessionStream { public: struct Event { Event(boost::shared_ptr element) : element(element), footer(false) {} Event(const ProtocolHeader& header) : header(header), footer(false) {} Event() : footer(true) {} boost::shared_ptr element; boost::optional header; bool footer; }; MockSessionStream() : available(true), whitespacePingEnabled(false), resetCount(0) { } virtual void close() { onClosed(boost::shared_ptr()); } virtual bool isOpen() { return available; } virtual void writeHeader(const ProtocolHeader& header) { receivedEvents.push_back(Event(header)); } virtual void writeFooter() { receivedEvents.push_back(Event()); } virtual void writeElement(boost::shared_ptr element) { receivedEvents.push_back(Event(element)); } virtual void writeData(const std::string&) { } virtual bool supportsTLSEncryption() { return false; } virtual void addTLSEncryption() { assert(false); } virtual bool isTLSEncrypted() { return false; } virtual ByteArray getTLSFinishMessage() const { return ByteArray(); } virtual Certificate::ref getPeerCertificate() const { return Certificate::ref(); } virtual std::vector getPeerCertificateChain() const { return std::vector(); } virtual boost::shared_ptr getPeerCertificateVerificationError() const { return boost::shared_ptr(); } virtual bool supportsZLibCompression() { return true; } virtual void addZLibCompression() { assert(false); } virtual void setWhitespacePingEnabled(bool enabled) { whitespacePingEnabled = enabled; } virtual void resetXMPPParser() { resetCount++; } void breakConnection() { onClosed(boost::make_shared(SessionStream::SessionStreamError::ConnectionReadError)); } void sendStreamStart() { ProtocolHeader header; header.setFrom("service.foo.com"); return onStreamStartReceived(header); } void sendHandshakeResponse() { onElementReceived(ComponentHandshake::ref(new ComponentHandshake())); } void sendHandshakeError() { // FIXME: This isn't the correct element onElementReceived(AuthFailure::ref(new AuthFailure())); } void receiveStreamStart() { Event event = popEvent(); CPPUNIT_ASSERT(event.header); } void receiveHandshake() { Event event = popEvent(); CPPUNIT_ASSERT(event.element); ComponentHandshake::ref handshake(boost::dynamic_pointer_cast(event.element)); CPPUNIT_ASSERT(handshake); CPPUNIT_ASSERT_EQUAL(std::string("4c4f8a41141722c8bbfbdd92d827f7b2fc0a542b"), handshake->getData()); } Event popEvent() { CPPUNIT_ASSERT(!receivedEvents.empty()); Event event = receivedEvents.front(); receivedEvents.pop_front(); return event; } bool available; bool whitespacePingEnabled; std::string bindID; int resetCount; std::deque receivedEvents; }; boost::shared_ptr server; bool sessionFinishedReceived; bool needCredentials; boost::shared_ptr sessionFinishedError; }; CPPUNIT_TEST_SUITE_REGISTRATION(ComponentSessionTest); swift-im-2.0+dev6/Swiften/Component/UnitTest/ComponentConnectorTest.cpp0000644000175000017500000001502012227051774026202 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include #include using namespace Swift; class ComponentConnectorTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(ComponentConnectorTest); CPPUNIT_TEST(testConnect); CPPUNIT_TEST(testConnect_FirstAddressHostFails); CPPUNIT_TEST(testConnect_NoHosts); CPPUNIT_TEST(testConnect_TimeoutDuringResolve); CPPUNIT_TEST(testConnect_TimeoutDuringConnect); CPPUNIT_TEST(testConnect_NoTimeout); CPPUNIT_TEST(testStop_Timeout); CPPUNIT_TEST_SUITE_END(); public: void setUp() { host1 = HostAddress("1.1.1.1"); host2 = HostAddress("2.2.2.2"); eventLoop = new DummyEventLoop(); resolver = new StaticDomainNameResolver(eventLoop); connectionFactory = new MockConnectionFactory(eventLoop); timerFactory = new DummyTimerFactory(); } void tearDown() { delete timerFactory; delete connectionFactory; delete resolver; delete eventLoop; } void testConnect() { ComponentConnector::ref testling(createConnector("foo.com", 1234)); resolver->addAddress("foo.com", host1); testling->start(); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(1, static_cast(connections.size())); CPPUNIT_ASSERT(connections[0]); CPPUNIT_ASSERT(HostAddressPort(host1, 1234) == *(connections[0]->hostAddressPort)); } void testConnect_FirstAddressHostFails() { ComponentConnector::ref testling(createConnector("foo.com", 1234)); resolver->addAddress("foo.com", host1); resolver->addAddress("foo.com", host2); connectionFactory->failingPorts.push_back(HostAddressPort(host1, 1234)); testling->start(); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(1, static_cast(connections.size())); CPPUNIT_ASSERT(connections[0]); CPPUNIT_ASSERT(HostAddressPort(host2, 1234) == *(connections[0]->hostAddressPort)); } void testConnect_NoHosts() { ComponentConnector::ref testling(createConnector("foo.com", 1234)); testling->start(); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(1, static_cast(connections.size())); CPPUNIT_ASSERT(!connections[0]); } void testConnect_TimeoutDuringResolve() { ComponentConnector::ref testling(createConnector("foo.com", 1234)); testling->setTimeoutMilliseconds(10); resolver->setIsResponsive(false); testling->start(); eventLoop->processEvents(); timerFactory->setTime(10); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(1, static_cast(connections.size())); CPPUNIT_ASSERT(!connections[0]); } void testConnect_TimeoutDuringConnect() { ComponentConnector::ref testling(createConnector("foo.com", 1234)); testling->setTimeoutMilliseconds(10); resolver->addAddress("foo.com", host1); connectionFactory->isResponsive = false; testling->start(); eventLoop->processEvents(); timerFactory->setTime(10); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(1, static_cast(connections.size())); CPPUNIT_ASSERT(!connections[0]); } void testConnect_NoTimeout() { ComponentConnector::ref testling(createConnector("foo.com", 1234)); testling->setTimeoutMilliseconds(10); resolver->addAddress("foo.com", host1); testling->start(); eventLoop->processEvents(); timerFactory->setTime(10); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(1, static_cast(connections.size())); CPPUNIT_ASSERT(connections[0]); } void testStop_Timeout() { ComponentConnector::ref testling(createConnector("foo.com", 1234)); testling->setTimeoutMilliseconds(10); resolver->addAddress("foo.com", host1); testling->start(); testling->stop(); eventLoop->processEvents(); timerFactory->setTime(10); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(1, static_cast(connections.size())); CPPUNIT_ASSERT(!connections[0]); } private: ComponentConnector::ref createConnector(const std::string& hostname, int port) { ComponentConnector::ref connector = ComponentConnector::create(hostname, port, resolver, connectionFactory, timerFactory); connector->onConnectFinished.connect(boost::bind(&ComponentConnectorTest::handleConnectorFinished, this, _1)); return connector; } void handleConnectorFinished(boost::shared_ptr connection) { boost::shared_ptr c(boost::dynamic_pointer_cast(connection)); if (connection) { assert(c); } connections.push_back(c); } struct MockConnection : public Connection { public: MockConnection(const std::vector& failingPorts, bool isResponsive, EventLoop* eventLoop) : eventLoop(eventLoop), failingPorts(failingPorts), isResponsive(isResponsive) {} void listen() { assert(false); } void connect(const HostAddressPort& address) { hostAddressPort = address; if (isResponsive) { bool fail = std::find(failingPorts.begin(), failingPorts.end(), address) != failingPorts.end(); eventLoop->postEvent(boost::bind(boost::ref(onConnectFinished), fail)); } } void disconnect() { assert(false); } void write(const SafeByteArray&) { assert(false); } HostAddressPort getLocalAddress() const { return HostAddressPort(); } EventLoop* eventLoop; boost::optional hostAddressPort; std::vector failingPorts; bool isResponsive; }; struct MockConnectionFactory : public ConnectionFactory { MockConnectionFactory(EventLoop* eventLoop) : eventLoop(eventLoop), isResponsive(true) { } boost::shared_ptr createConnection() { return boost::shared_ptr(new MockConnection(failingPorts, isResponsive, eventLoop)); } EventLoop* eventLoop; bool isResponsive; std::vector failingPorts; }; private: HostAddress host1; HostAddress host2; DummyEventLoop* eventLoop; StaticDomainNameResolver* resolver; MockConnectionFactory* connectionFactory; DummyTimerFactory* timerFactory; std::vector< boost::shared_ptr > connections; }; CPPUNIT_TEST_SUITE_REGISTRATION(ComponentConnectorTest); swift-im-2.0+dev6/Swiften/Component/ComponentHandshakeGenerator.h0000644000175000017500000000062012227051774025033 0ustar kismithkismith/* * 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 #include namespace Swift { class SWIFTEN_API ComponentHandshakeGenerator { public: static std::string getHandshake(const std::string& streamID, const std::string& secret); }; } swift-im-2.0+dev6/Swiften/Component/ComponentSession.cpp0000644000175000017500000000724312227051774023264 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include namespace Swift { ComponentSession::ComponentSession(const JID& jid, const std::string& secret, boost::shared_ptr stream) : jid(jid), secret(secret), stream(stream), state(Initial) { } ComponentSession::~ComponentSession() { } void ComponentSession::start() { stream->onStreamStartReceived.connect(boost::bind(&ComponentSession::handleStreamStart, shared_from_this(), _1)); stream->onElementReceived.connect(boost::bind(&ComponentSession::handleElement, shared_from_this(), _1)); stream->onClosed.connect(boost::bind(&ComponentSession::handleStreamClosed, shared_from_this(), _1)); assert(state == Initial); state = WaitingForStreamStart; sendStreamHeader(); } void ComponentSession::sendStreamHeader() { ProtocolHeader header; header.setTo(jid); stream->writeHeader(header); } void ComponentSession::sendStanza(boost::shared_ptr stanza) { stream->writeElement(stanza); } void ComponentSession::handleStreamStart(const ProtocolHeader& header) { checkState(WaitingForStreamStart); state = Authenticating; stream->writeElement(ComponentHandshake::ref(new ComponentHandshake(ComponentHandshakeGenerator::getHandshake(header.getID(), secret)))); } void ComponentSession::handleElement(boost::shared_ptr element) { if (boost::shared_ptr stanza = boost::dynamic_pointer_cast(element)) { if (getState() == Initialized) { onStanzaReceived(stanza); } else { finishSession(Error::UnexpectedElementError); } } else if (boost::dynamic_pointer_cast(element)) { if (!checkState(Authenticating)) { return; } stream->setWhitespacePingEnabled(true); state = Initialized; onInitialized(); } else if (getState() == Authenticating) { if (boost::dynamic_pointer_cast(element)) { // M-Link sends stream features, so swallow that. } else { // FIXME: We should actually check the element received finishSession(Error::AuthenticationFailedError); } } else { finishSession(Error::UnexpectedElementError); } } bool ComponentSession::checkState(State state) { if (this->state != state) { finishSession(Error::UnexpectedElementError); return false; } return true; } void ComponentSession::handleStreamClosed(boost::shared_ptr streamError) { State oldState = state; state = Finished; stream->setWhitespacePingEnabled(false); stream->onStreamStartReceived.disconnect(boost::bind(&ComponentSession::handleStreamStart, shared_from_this(), _1)); stream->onElementReceived.disconnect(boost::bind(&ComponentSession::handleElement, shared_from_this(), _1)); stream->onClosed.disconnect(boost::bind(&ComponentSession::handleStreamClosed, shared_from_this(), _1)); if (oldState == Finishing) { onFinished(error); } else { onFinished(streamError); } } void ComponentSession::finish() { finishSession(boost::shared_ptr()); } void ComponentSession::finishSession(Error::Type error) { finishSession(boost::make_shared(error)); } void ComponentSession::finishSession(boost::shared_ptr finishError) { state = Finishing; error = finishError; assert(stream->isOpen()); stream->writeFooter(); stream->close(); } } swift-im-2.0+dev6/Swiften/Component/ComponentXMLTracer.h0000644000175000017500000000074412227051774023106 0ustar kismithkismith/* * 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 #include namespace Swift { class SWIFTEN_API ComponentXMLTracer { public: ComponentXMLTracer(CoreComponent* component); private: static void printData(char direction, const SafeByteArray& data); static void printLine(char c); }; } swift-im-2.0+dev6/Swiften/Component/ComponentXMLTracer.cpp0000644000175000017500000000150712227051774023437 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include namespace Swift { ComponentXMLTracer::ComponentXMLTracer(CoreComponent* client) { client->onDataRead.connect(boost::bind(&ComponentXMLTracer::printData, '<', _1)); client->onDataWritten.connect(boost::bind(&ComponentXMLTracer::printData, '>', _1)); } void ComponentXMLTracer::printData(char direction, const SafeByteArray& data) { printLine(direction); std::cerr << byteArrayToString(ByteArray(data.begin(), data.end())) << std::endl; } void ComponentXMLTracer::printLine(char c) { for (unsigned int i = 0; i < 80; ++i) { std::cerr << c; } std::cerr << std::endl; } } swift-im-2.0+dev6/Swiften/Component/Component.cpp0000644000175000017500000000147412227051774021720 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include namespace Swift { Component::Component(EventLoop* eventLoop, NetworkFactories* networkFactories, const JID& jid, const std::string& secret) : CoreComponent(eventLoop, networkFactories, jid, secret) { softwareVersionResponder = new SoftwareVersionResponder(getIQRouter()); softwareVersionResponder->start(); } Component::~Component() { softwareVersionResponder->stop(); delete softwareVersionResponder; } void Component::setSoftwareVersion(const std::string& name, const std::string& version) { softwareVersionResponder->setVersion(name, version); } } swift-im-2.0+dev6/Swiften/Presence/0000755000175000017500000000000012227051774017046 5ustar kismithkismithswift-im-2.0+dev6/Swiften/Presence/SConscript0000644000175000017500000000045712227051774021066 0ustar kismithkismithImport("swiften_env") objects = swiften_env.SwiftenObject([ "PresenceOracle.cpp", "PresenceSender.cpp", "DirectedPresenceSender.cpp", "PayloadAddingPresenceSender.cpp", "StanzaChannelPresenceSender.cpp", "SubscriptionManager.cpp", ]) swiften_env.Append(SWIFTEN_OBJECTS = [objects]) swift-im-2.0+dev6/Swiften/Presence/PayloadAddingPresenceSender.cpp0000644000175000017500000000212212227051774025075 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { PayloadAddingPresenceSender::PayloadAddingPresenceSender(PresenceSender* sender) : sender(sender) { } void PayloadAddingPresenceSender::sendPresence(Presence::ref presence) { if (presence->isAvailable()) { if (!presence->getTo().isValid()) { lastSentPresence = presence; } } else { lastSentPresence.reset(); } if (payload) { Presence::ref sentPresence = Presence::create(presence); sentPresence->updatePayload(payload); sender->sendPresence(sentPresence); } else { sender->sendPresence(presence); } } bool PayloadAddingPresenceSender::isAvailable() const { return sender->isAvailable(); } void PayloadAddingPresenceSender::setPayload(boost::shared_ptr payload) { this->payload = payload; if (lastSentPresence) { sendPresence(lastSentPresence); } } void PayloadAddingPresenceSender::reset() { lastSentPresence.reset(); } } swift-im-2.0+dev6/Swiften/Presence/StanzaChannelPresenceSender.cpp0000644000175000017500000000111412227051774025126 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include namespace Swift { StanzaChannelPresenceSender::StanzaChannelPresenceSender(StanzaChannel* channel) : channel(channel) { } void StanzaChannelPresenceSender::sendPresence(Presence::ref presence) { channel->sendPresence(presence); } bool StanzaChannelPresenceSender::isAvailable() const { return channel->isAvailable(); } } swift-im-2.0+dev6/Swiften/Presence/DirectedPresenceSender.cpp0000644000175000017500000000466212227051774024133 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include namespace Swift { DirectedPresenceSender::DirectedPresenceSender(PresenceSender* sender) : sender(sender) { } void DirectedPresenceSender::sendPresence(boost::shared_ptr presence) { if (!sender->isAvailable()) { return; } sender->sendPresence(presence); if (!presence->getTo().isValid()) { boost::shared_ptr presenceCopy(new Presence(*presence)); foreach(const JID& jid, directedPresenceReceivers) { presenceCopy->setTo(jid); sender->sendPresence(presenceCopy); } lastSentUndirectedPresence = presence; } } /** * Gets either the last broadcast presence, or an empty stanza if none has been sent. */ boost::shared_ptr DirectedPresenceSender::getLastSentUndirectedPresence() { boost::shared_ptr presenceCopy(lastSentUndirectedPresence ? new Presence(*lastSentUndirectedPresence) : new Presence()); return presenceCopy; } /** * Send future broadcast presence also to this JID. * @param jid Non-roster JID to receive global presence updates. * @param sendPresence Also send the current global presence immediately. */ void DirectedPresenceSender::addDirectedPresenceReceiver(const JID& jid, SendPresence sendPresence) { directedPresenceReceivers.insert(jid); if (sendPresence == AndSendPresence && sender->isAvailable()) { if (lastSentUndirectedPresence && lastSentUndirectedPresence->getType() == Presence::Available) { boost::shared_ptr presenceCopy(new Presence(*lastSentUndirectedPresence)); presenceCopy->setTo(jid); sender->sendPresence(presenceCopy); } } } /** * Send future broadcast presence also to this JID. * @param jid Non-roster JID to stop receiving global presence updates. * @param sendPresence Also send presence type=unavailable immediately to jid. */ void DirectedPresenceSender::removeDirectedPresenceReceiver(const JID& jid, SendPresence sendPresence) { directedPresenceReceivers.erase(jid); if (sendPresence == AndSendPresence && sender->isAvailable()) { boost::shared_ptr presence(new Presence()); presence->setType(Presence::Unavailable); presence->setTo(jid); sender->sendPresence(presence); } } bool DirectedPresenceSender::isAvailable() const { return sender->isAvailable(); } } swift-im-2.0+dev6/Swiften/Presence/SubscriptionManager.cpp0000644000175000017500000000324712227051774023537 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include namespace Swift { SubscriptionManager::SubscriptionManager(StanzaChannel* channel) : stanzaChannel(channel) { stanzaChannel->onPresenceReceived.connect(boost::bind(&SubscriptionManager::handleIncomingPresence, this, _1)); } SubscriptionManager::~SubscriptionManager() { stanzaChannel->onPresenceReceived.disconnect(boost::bind(&SubscriptionManager::handleIncomingPresence, this, _1)); } void SubscriptionManager::cancelSubscription(const JID& jid) { Presence::ref stanza(new Presence()); stanza->setType(Presence::Unsubscribed); stanza->setTo(jid); stanzaChannel->sendPresence(stanza); } void SubscriptionManager::confirmSubscription(const JID& jid) { Presence::ref stanza(new Presence()); stanza->setType(Presence::Subscribed); stanza->setTo(jid); stanzaChannel->sendPresence(stanza); } void SubscriptionManager::requestSubscription(const JID& jid) { Presence::ref stanza(new Presence()); stanza->setType(Presence::Subscribe); stanza->setTo(jid); stanzaChannel->sendPresence(stanza); } void SubscriptionManager::handleIncomingPresence(Presence::ref presence) { JID bareJID(presence->getFrom().toBare()); if (presence->getType() == Presence::Subscribe) { onPresenceSubscriptionRequest(bareJID, presence->getStatus(), presence); } else if (presence->getType() == Presence::Unsubscribe) { onPresenceSubscriptionRevoked(bareJID, presence->getStatus()); } } } swift-im-2.0+dev6/Swiften/Presence/PresenceSender.h0000644000175000017500000000061212227051774022123 0ustar kismithkismith/* * 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 namespace Swift { class PresenceSender { public: virtual ~PresenceSender(); virtual void sendPresence(Presence::ref) = 0; virtual bool isAvailable() const = 0; }; } swift-im-2.0+dev6/Swiften/Presence/PresenceOracle.h0000644000175000017500000000202512227051774022110 0ustar kismithkismith/* * 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 #include #include #include #include namespace Swift { class StanzaChannel; class SWIFTEN_API PresenceOracle { public: PresenceOracle(StanzaChannel* stanzaChannel); ~PresenceOracle(); Presence::ref getLastPresence(const JID&) const; Presence::ref getHighestPriorityPresence(const JID& bareJID) const; std::vector getAllPresence(const JID& bareJID) const; public: boost::signal onPresenceChange; private: void handleIncomingPresence(Presence::ref presence); void handleStanzaChannelAvailableChanged(bool); private: typedef std::map PresenceMap; typedef std::map PresencesMap; PresencesMap entries_; StanzaChannel* stanzaChannel_; }; } swift-im-2.0+dev6/Swiften/Presence/PayloadAddingPresenceSender.h0000644000175000017500000000252512227051774024551 0ustar kismithkismith/* * 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 #include #include namespace Swift { class StanzaChannel; /** * This presence sender adds payloads to outgoing presences. * * This class isn't meant to be used with directed presence. */ class SWIFTEN_API PayloadAddingPresenceSender : public PresenceSender { public: PayloadAddingPresenceSender(PresenceSender*); void sendPresence(boost::shared_ptr); bool isAvailable() const; /** * Sets the payload to be added to outgoing presences. * If initial presence has been sent, this will resend the last sent presence * with an updated payload. Initial presence is reset when unavailable presence is * sent, or when reset() is called. */ void setPayload(boost::shared_ptr); /** * Resets the presence sender. * This puts the presence sender back in the initial state (before initial * presence has been sent). * This also resets the chained sender. */ void reset(); private: boost::shared_ptr lastSentPresence; PresenceSender* sender; boost::shared_ptr payload; }; } swift-im-2.0+dev6/Swiften/Presence/UnitTest/0000755000175000017500000000000012227051774020625 5ustar kismithkismithswift-im-2.0+dev6/Swiften/Presence/UnitTest/DirectedPresenceSenderTest.cpp0000644000175000017500000001531512227051774026547 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include using namespace Swift; class DirectedPresenceSenderTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(DirectedPresenceSenderTest); CPPUNIT_TEST(testSendPresence); CPPUNIT_TEST(testSendPresence_UndirectedPresenceWithDirectedPresenceReceivers); CPPUNIT_TEST(testSendPresence_DirectedPresenceWithDirectedPresenceReceivers); CPPUNIT_TEST(testAddDirectedPresenceReceiver); CPPUNIT_TEST(testAddDirectedPresenceReceiver_WithoutSendingPresence); CPPUNIT_TEST(testAddDirectedPresenceReceiver_AfterSendingDirectedPresence); CPPUNIT_TEST(testRemoveDirectedPresenceReceiver); CPPUNIT_TEST(testRemoveDirectedPresenceReceiver_WithoutSendingPresence); CPPUNIT_TEST_SUITE_END(); public: void setUp() { channel = new DummyStanzaChannel(); testPresence = boost::make_shared(); testPresence->setStatus("Foo"); secondTestPresence = boost::make_shared(); secondTestPresence->setStatus("Bar"); stanzaChannelPresenceSender = new StanzaChannelPresenceSender(channel); } void tearDown() { delete stanzaChannelPresenceSender; delete channel; } void testSendPresence() { boost::shared_ptr testling(createPresenceSender()); testling->sendPresence(testPresence); CPPUNIT_ASSERT_EQUAL(1, static_cast(channel->sentStanzas.size())); boost::shared_ptr presence = boost::dynamic_pointer_cast(channel->sentStanzas[0]); CPPUNIT_ASSERT(testPresence == presence); } void testSendPresence_UndirectedPresenceWithDirectedPresenceReceivers() { boost::shared_ptr testling(createPresenceSender()); testling->addDirectedPresenceReceiver(JID("alice@wonderland.lit/teaparty"), DirectedPresenceSender::AndSendPresence); testling->sendPresence(testPresence); CPPUNIT_ASSERT_EQUAL(2, static_cast(channel->sentStanzas.size())); boost::shared_ptr presence = boost::dynamic_pointer_cast(channel->sentStanzas[0]); CPPUNIT_ASSERT(testPresence == presence); presence = boost::dynamic_pointer_cast(channel->sentStanzas[1]); CPPUNIT_ASSERT_EQUAL(testPresence->getStatus(), presence->getStatus()); CPPUNIT_ASSERT_EQUAL(JID("alice@wonderland.lit/teaparty"), presence->getTo()); } void testSendPresence_DirectedPresenceWithDirectedPresenceReceivers() { boost::shared_ptr testling(createPresenceSender()); testling->addDirectedPresenceReceiver(JID("alice@wonderland.lit/teaparty"), DirectedPresenceSender::AndSendPresence); channel->sentStanzas.clear(); testPresence->setTo(JID("foo@bar.com")); testling->sendPresence(testPresence); CPPUNIT_ASSERT_EQUAL(1, static_cast(channel->sentStanzas.size())); boost::shared_ptr presence = boost::dynamic_pointer_cast(channel->sentStanzas[0]); CPPUNIT_ASSERT(testPresence == presence); } void testAddDirectedPresenceReceiver() { boost::shared_ptr testling(createPresenceSender()); testling->sendPresence(testPresence); channel->sentStanzas.clear(); testling->addDirectedPresenceReceiver(JID("alice@wonderland.lit/teaparty"), DirectedPresenceSender::AndSendPresence); CPPUNIT_ASSERT_EQUAL(1, static_cast(channel->sentStanzas.size())); boost::shared_ptr presence = boost::dynamic_pointer_cast(channel->sentStanzas[0]); CPPUNIT_ASSERT_EQUAL(testPresence->getStatus(), presence->getStatus()); CPPUNIT_ASSERT_EQUAL(JID("alice@wonderland.lit/teaparty"), presence->getTo()); } void testAddDirectedPresenceReceiver_WithoutSendingPresence() { boost::shared_ptr testling(createPresenceSender()); testling->sendPresence(testPresence); channel->sentStanzas.clear(); testling->addDirectedPresenceReceiver(JID("alice@wonderland.lit/teaparty"), DirectedPresenceSender::DontSendPresence); CPPUNIT_ASSERT_EQUAL(0, static_cast(channel->sentStanzas.size())); } void testAddDirectedPresenceReceiver_AfterSendingDirectedPresence() { boost::shared_ptr testling(createPresenceSender()); testling->sendPresence(testPresence); secondTestPresence->setTo(JID("foo@bar.com")); testling->sendPresence(secondTestPresence); channel->sentStanzas.clear(); testling->addDirectedPresenceReceiver(JID("alice@wonderland.lit/teaparty"), DirectedPresenceSender::AndSendPresence); CPPUNIT_ASSERT_EQUAL(1, static_cast(channel->sentStanzas.size())); boost::shared_ptr presence = boost::dynamic_pointer_cast(channel->sentStanzas[0]); CPPUNIT_ASSERT_EQUAL(testPresence->getStatus(), presence->getStatus()); CPPUNIT_ASSERT_EQUAL(JID("alice@wonderland.lit/teaparty"), presence->getTo()); } void testRemoveDirectedPresenceReceiver() { boost::shared_ptr testling(createPresenceSender()); testling->addDirectedPresenceReceiver(JID("alice@wonderland.lit/teaparty"), DirectedPresenceSender::DontSendPresence); testling->removeDirectedPresenceReceiver(JID("alice@wonderland.lit/teaparty"), DirectedPresenceSender::AndSendPresence); testling->sendPresence(testPresence); CPPUNIT_ASSERT_EQUAL(2, static_cast(channel->sentStanzas.size())); CPPUNIT_ASSERT_EQUAL(boost::dynamic_pointer_cast(channel->sentStanzas[0])->getType(), Presence::Unavailable); CPPUNIT_ASSERT(channel->sentStanzas[1] == testPresence); } void testRemoveDirectedPresenceReceiver_WithoutSendingPresence() { boost::shared_ptr testling(createPresenceSender()); testling->addDirectedPresenceReceiver(JID("alice@wonderland.lit/teaparty"), DirectedPresenceSender::AndSendPresence); channel->sentStanzas.clear(); testling->removeDirectedPresenceReceiver(JID("alice@wonderland.lit/teaparty"), DirectedPresenceSender::DontSendPresence); testling->sendPresence(testPresence); CPPUNIT_ASSERT_EQUAL(1, static_cast(channel->sentStanzas.size())); CPPUNIT_ASSERT(channel->sentStanzas[0] == testPresence); } private: DirectedPresenceSender* createPresenceSender() { return new DirectedPresenceSender(stanzaChannelPresenceSender); } private: DummyStanzaChannel* channel; StanzaChannelPresenceSender* stanzaChannelPresenceSender; boost::shared_ptr testPresence; boost::shared_ptr secondTestPresence; }; CPPUNIT_TEST_SUITE_REGISTRATION(DirectedPresenceSenderTest); swift-im-2.0+dev6/Swiften/Presence/UnitTest/PresenceOracleTest.cpp0000644000175000017500000001651112227051774025067 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include using namespace Swift; class PresenceOracleTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(PresenceOracleTest); CPPUNIT_TEST(testReceivePresence); CPPUNIT_TEST(testReceivePresenceFromDifferentResources); CPPUNIT_TEST(testSubscriptionRequest); CPPUNIT_TEST(testReconnectResetsPresences); CPPUNIT_TEST(testHighestPresenceSingle); CPPUNIT_TEST(testHighestPresenceMultiple); CPPUNIT_TEST(testHighestPresenceGlobal); CPPUNIT_TEST(testHighestPresenceChangePriority); CPPUNIT_TEST_SUITE_END(); public: void setUp() { stanzaChannel_ = new DummyStanzaChannel(); oracle_ = new PresenceOracle(stanzaChannel_); oracle_->onPresenceChange.connect(boost::bind(&PresenceOracleTest::handlePresenceChange, this, _1)); subscriptionManager_ = new SubscriptionManager(stanzaChannel_); subscriptionManager_->onPresenceSubscriptionRequest.connect(boost::bind(&PresenceOracleTest::handlePresenceSubscriptionRequest, this, _1, _2)); user1 = JID("user1@foo.com/Foo"); user1alt = JID("user1@foo.com/Bar"); user2 = JID("user2@bar.com/Bar"); } void tearDown() { delete subscriptionManager_; delete oracle_; delete stanzaChannel_; } void testHighestPresenceSingle() { JID bareJID("alice@wonderland.lit"); Presence::ref fiveOn = makeOnline("blah", 5); Presence::ref fiveOff = makeOffline("/blah"); CPPUNIT_ASSERT_EQUAL(Presence::ref(), oracle_->getHighestPriorityPresence(bareJID)); stanzaChannel_->onPresenceReceived(fiveOn); CPPUNIT_ASSERT_EQUAL(fiveOn, oracle_->getHighestPriorityPresence(bareJID)); stanzaChannel_->onPresenceReceived(fiveOff); CPPUNIT_ASSERT_EQUAL(fiveOff, oracle_->getHighestPriorityPresence(bareJID)); } void testHighestPresenceMultiple() { JID bareJID("alice@wonderland.lit"); Presence::ref fiveOn = makeOnline("blah", 5); Presence::ref fiveOff = makeOffline("/blah"); Presence::ref tenOn = makeOnline("bert", 10); Presence::ref tenOff = makeOffline("/bert"); stanzaChannel_->onPresenceReceived(fiveOn); stanzaChannel_->onPresenceReceived(tenOn); CPPUNIT_ASSERT_EQUAL(tenOn, oracle_->getHighestPriorityPresence(bareJID)); stanzaChannel_->onPresenceReceived(fiveOff); CPPUNIT_ASSERT_EQUAL(tenOn, oracle_->getHighestPriorityPresence(bareJID)); stanzaChannel_->onPresenceReceived(fiveOn); stanzaChannel_->onPresenceReceived(tenOff); CPPUNIT_ASSERT_EQUAL(fiveOn, oracle_->getHighestPriorityPresence(bareJID)); } void testHighestPresenceGlobal() { JID bareJID("alice@wonderland.lit"); Presence::ref fiveOn = makeOnline("blah", 5); Presence::ref fiveOff = makeOffline("/blah"); Presence::ref tenOn = makeOnline("bert", 10); Presence::ref allOff = makeOffline(""); stanzaChannel_->onPresenceReceived(fiveOn); stanzaChannel_->onPresenceReceived(tenOn); stanzaChannel_->onPresenceReceived(allOff); CPPUNIT_ASSERT_EQUAL(allOff, oracle_->getHighestPriorityPresence(bareJID)); } void testHighestPresenceChangePriority() { JID bareJID("alice@wonderland.lit"); Presence::ref fiveOn = makeOnline("blah", 5); Presence::ref fiveOff = makeOffline("/blah"); Presence::ref tenOn = makeOnline("bert", 10); Presence::ref tenOnThree = makeOnline("bert", 3); Presence::ref tenOff = makeOffline("/bert"); stanzaChannel_->onPresenceReceived(fiveOn); stanzaChannel_->onPresenceReceived(tenOn); stanzaChannel_->onPresenceReceived(tenOnThree); CPPUNIT_ASSERT_EQUAL(fiveOn, oracle_->getHighestPriorityPresence(bareJID)); stanzaChannel_->onPresenceReceived(fiveOff); CPPUNIT_ASSERT_EQUAL(tenOnThree, oracle_->getHighestPriorityPresence(bareJID)); stanzaChannel_->onPresenceReceived(fiveOn); CPPUNIT_ASSERT_EQUAL(fiveOn, oracle_->getHighestPriorityPresence(bareJID)); } void testReceivePresence() { boost::shared_ptr sentPresence(createPresence(user1)); stanzaChannel_->onPresenceReceived(sentPresence); CPPUNIT_ASSERT_EQUAL(1, static_cast(changes.size())); CPPUNIT_ASSERT_EQUAL(0, static_cast(subscriptionRequests.size())); CPPUNIT_ASSERT_EQUAL(sentPresence, changes[0]); CPPUNIT_ASSERT_EQUAL(sentPresence, oracle_->getLastPresence(user1)); } void testReceivePresenceFromDifferentResources() { boost::shared_ptr sentPresence1(createPresence(user1)); boost::shared_ptr sentPresence2(createPresence(user1alt)); stanzaChannel_->onPresenceReceived(sentPresence1); stanzaChannel_->onPresenceReceived(sentPresence2); CPPUNIT_ASSERT_EQUAL(sentPresence1, oracle_->getLastPresence(user1)); CPPUNIT_ASSERT_EQUAL(sentPresence2, oracle_->getLastPresence(user1alt)); } void testSubscriptionRequest() { std::string reasonText = "Because I want to"; JID sentJID = JID("me@example.com"); boost::shared_ptr sentPresence(new Presence()); sentPresence->setType(Presence::Subscribe); sentPresence->setFrom(sentJID); sentPresence->setStatus(reasonText); stanzaChannel_->onPresenceReceived(sentPresence); CPPUNIT_ASSERT_EQUAL(0, static_cast(changes.size())); CPPUNIT_ASSERT_EQUAL(1, static_cast(subscriptionRequests.size())); CPPUNIT_ASSERT_EQUAL(sentJID, subscriptionRequests[0].jid); CPPUNIT_ASSERT_EQUAL(reasonText, subscriptionRequests[0].reason); } void testReconnectResetsPresences() { boost::shared_ptr sentPresence(createPresence(user1)); stanzaChannel_->onPresenceReceived(sentPresence); stanzaChannel_->setAvailable(false); stanzaChannel_->setAvailable(true); CPPUNIT_ASSERT(!oracle_->getLastPresence(user1)); } private: Presence::ref makeOnline(const std::string& resource, int priority) { Presence::ref presence(new Presence()); presence->setPriority(priority); presence->setFrom(JID("alice@wonderland.lit/" + resource)); return presence; } Presence::ref makeOffline(const std::string& resource) { Presence::ref presence(new Presence()); presence->setFrom(JID("alice@wonderland.lit" + resource)); presence->setType(Presence::Unavailable); return presence; } void handlePresenceChange(boost::shared_ptr newPresence) { changes.push_back(newPresence); } void handlePresenceSubscriptionRequest(const JID& jid, const std::string& reason) { SubscriptionRequestInfo subscriptionRequest; subscriptionRequest.jid = jid; subscriptionRequest.reason = reason; subscriptionRequests.push_back(subscriptionRequest); } boost::shared_ptr createPresence(const JID& jid) { boost::shared_ptr sentPresence(new Presence("blarb")); sentPresence->setFrom(jid); return sentPresence; } private: struct SubscriptionRequestInfo { JID jid; std::string reason; }; PresenceOracle* oracle_; SubscriptionManager* subscriptionManager_; DummyStanzaChannel* stanzaChannel_; std::vector changes; std::vector subscriptionRequests; JID user1; JID user1alt; JID user2; }; CPPUNIT_TEST_SUITE_REGISTRATION(PresenceOracleTest); swift-im-2.0+dev6/Swiften/Presence/UnitTest/PayloadAddingPresenceSenderTest.cpp0000644000175000017500000001220412227051774027516 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include using namespace Swift; class PayloadAddingPresenceSenderTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(PayloadAddingPresenceSenderTest); CPPUNIT_TEST(testSetPayloadAddsPayloadOnPresenceSend); CPPUNIT_TEST(testSetNullPayloadDoesNotAddPayloadOnPresenceSend); CPPUNIT_TEST(testSendPresenceDoesNotAlterOriginalPayload); CPPUNIT_TEST(testSetPayloadAfterInitialPresenceResendsPresence); CPPUNIT_TEST(testSetPayloadAfterUnavailablePresenceDoesNotResendPresence); CPPUNIT_TEST(testSetPayloadAfterResetDoesNotResendPresence); CPPUNIT_TEST(testSendDirectedPresenceIsNotResent); CPPUNIT_TEST_SUITE_END(); public: void setUp() { stanzaChannel = new DummyStanzaChannel(); presenceSender = new StanzaChannelPresenceSender(stanzaChannel); } void tearDown() { delete presenceSender; delete stanzaChannel; } void testSetPayloadAddsPayloadOnPresenceSend() { boost::shared_ptr testling(createSender()); testling->setPayload(MyPayload::create("foo")); testling->sendPresence(Presence::create("bar")); CPPUNIT_ASSERT_EQUAL(1, static_cast(stanzaChannel->sentStanzas.size())); CPPUNIT_ASSERT_EQUAL(std::string("bar"), stanzaChannel->getStanzaAtIndex(0)->getStatus()); CPPUNIT_ASSERT(stanzaChannel->getStanzaAtIndex(0)->getPayload()); } void testSetNullPayloadDoesNotAddPayloadOnPresenceSend() { boost::shared_ptr testling(createSender()); testling->setPayload(MyPayload::ref()); testling->sendPresence(Presence::create("bar")); CPPUNIT_ASSERT_EQUAL(1, static_cast(stanzaChannel->sentStanzas.size())); CPPUNIT_ASSERT_EQUAL(std::string("bar"), stanzaChannel->getStanzaAtIndex(0)->getStatus()); CPPUNIT_ASSERT(!stanzaChannel->getStanzaAtIndex(0)->getPayload()); } void testSendPresenceDoesNotAlterOriginalPayload() { boost::shared_ptr testling(createSender()); testling->setPayload(MyPayload::create("foo")); Presence::ref presence(Presence::create("bar")); testling->sendPresence(presence); CPPUNIT_ASSERT(!presence->getPayload()); } void testSetPayloadAfterInitialPresenceResendsPresence() { boost::shared_ptr testling(createSender()); testling->sendPresence(Presence::create("bar")); testling->setPayload(MyPayload::create("foo")); CPPUNIT_ASSERT_EQUAL(2, static_cast(stanzaChannel->sentStanzas.size())); CPPUNIT_ASSERT_EQUAL(std::string("bar"), stanzaChannel->getStanzaAtIndex(1)->getStatus()); CPPUNIT_ASSERT(stanzaChannel->getStanzaAtIndex(1)->getPayload()); } void testSetPayloadAfterUnavailablePresenceDoesNotResendPresence() { boost::shared_ptr testling(createSender()); testling->sendPresence(Presence::create("bar")); Presence::ref presence = Presence::create("bar"); presence->setType(Presence::Unavailable); testling->sendPresence(presence); testling->setPayload(MyPayload::create("foo")); CPPUNIT_ASSERT_EQUAL(2, static_cast(stanzaChannel->sentStanzas.size())); } void testSetPayloadAfterResetDoesNotResendPresence() { boost::shared_ptr testling(createSender()); testling->sendPresence(Presence::create("bar")); testling->reset(); testling->setPayload(MyPayload::create("foo")); CPPUNIT_ASSERT_EQUAL(1, static_cast(stanzaChannel->sentStanzas.size())); } void testSendDirectedPresenceIsNotResent() { boost::shared_ptr testling(createSender()); testling->sendPresence(Presence::create("bar")); Presence::ref directedPresence = Presence::create("baz"); directedPresence->setTo(JID("foo@bar.com")); testling->sendPresence(directedPresence); testling->setPayload(MyPayload::create("foo")); CPPUNIT_ASSERT_EQUAL(3, static_cast(stanzaChannel->sentStanzas.size())); CPPUNIT_ASSERT_EQUAL(std::string("bar"), stanzaChannel->getStanzaAtIndex(2)->getStatus()); } private: boost::shared_ptr createSender() { boost::shared_ptr sender(new PayloadAddingPresenceSender(presenceSender)); return sender; } struct MyPayload : public Payload { typedef boost::shared_ptr ref; MyPayload(const std::string& body) : body(body) {} static ref create(const std::string& body) { return ref(new MyPayload(body)); } std::string body; }; private: DummyStanzaChannel* stanzaChannel; StanzaChannelPresenceSender* presenceSender; }; CPPUNIT_TEST_SUITE_REGISTRATION(PayloadAddingPresenceSenderTest); swift-im-2.0+dev6/Swiften/Presence/SubscriptionManager.h0000644000175000017500000000232212227051774023175 0ustar kismithkismith/* * 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 #include #include #include #include #include namespace Swift { class StanzaChannel; class SWIFTEN_API SubscriptionManager { public: SubscriptionManager(StanzaChannel* stanzaChannel); ~SubscriptionManager(); void cancelSubscription(const JID& jid); void confirmSubscription(const JID& jid); void requestSubscription(const JID& jid); /** * This signal is emitted when a presence subscription request is * received. * * The third parameter of this signal is the original presence stanza * received. This is useful when the subscriber adds extensions to * the request. */ boost::signal onPresenceSubscriptionRequest; boost::signal onPresenceSubscriptionRevoked; private: void handleIncomingPresence(Presence::ref presence); private: StanzaChannel* stanzaChannel; }; } swift-im-2.0+dev6/Swiften/Presence/StanzaChannelPresenceSender.h0000644000175000017500000000102712227051774024576 0ustar kismithkismith/* * 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 #include namespace Swift { class StanzaChannel; class SWIFTEN_API StanzaChannelPresenceSender : public PresenceSender { public: StanzaChannelPresenceSender(StanzaChannel*); void sendPresence(Presence::ref); bool isAvailable() const; private: StanzaChannel* channel; }; } swift-im-2.0+dev6/Swiften/Presence/PresenceSender.cpp0000644000175000017500000000041212227051774022454 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { PresenceSender::~PresenceSender() { } } swift-im-2.0+dev6/Swiften/Presence/DirectedPresenceSender.h0000644000175000017500000000156612227051774023600 0ustar kismithkismith/* * 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 #include #include #include namespace Swift { class SWIFTEN_API DirectedPresenceSender : public PresenceSender { public: enum SendPresence {AndSendPresence, DontSendPresence}; DirectedPresenceSender(PresenceSender*); void addDirectedPresenceReceiver(const JID&, SendPresence); void removeDirectedPresenceReceiver(const JID&, SendPresence); void sendPresence(Presence::ref); Presence::ref getLastSentUndirectedPresence(); bool isAvailable() const; private: Presence::ref lastSentUndirectedPresence; PresenceSender* sender; std::set directedPresenceReceivers; }; } swift-im-2.0+dev6/Swiften/Presence/PresenceOracle.cpp0000644000175000017500000000731112227051774022446 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "PresenceOracle.h" #include #include namespace Swift { PresenceOracle::PresenceOracle(StanzaChannel* stanzaChannel) { stanzaChannel_ = stanzaChannel; stanzaChannel_->onPresenceReceived.connect(boost::bind(&PresenceOracle::handleIncomingPresence, this, _1)); stanzaChannel_->onAvailableChanged.connect(boost::bind(&PresenceOracle::handleStanzaChannelAvailableChanged, this, _1)); } PresenceOracle::~PresenceOracle() { stanzaChannel_->onPresenceReceived.disconnect(boost::bind(&PresenceOracle::handleIncomingPresence, this, _1)); stanzaChannel_->onAvailableChanged.disconnect(boost::bind(&PresenceOracle::handleStanzaChannelAvailableChanged, this, _1)); } void PresenceOracle::handleStanzaChannelAvailableChanged(bool available) { if (available) { entries_.clear(); } } void PresenceOracle::handleIncomingPresence(Presence::ref presence) { JID bareJID(presence->getFrom().toBare()); if (presence->getType() == Presence::Subscribe) { } else { Presence::ref passedPresence = presence; if (presence->getType() == Presence::Unsubscribe) { /* 3921bis says that we don't follow up with an unavailable, so simulate this ourselves */ passedPresence = Presence::ref(new Presence()); passedPresence->setType(Presence::Unavailable); passedPresence->setFrom(bareJID); passedPresence->setStatus(presence->getStatus()); } std::map > jidMap = entries_[bareJID]; if (passedPresence->getFrom().isBare() && presence->getType() == Presence::Unavailable) { /* Have a bare-JID only presence of offline */ jidMap.clear(); } else if (passedPresence->getType() == Presence::Available) { /* Don't have a bare-JID only offline presence once there are available presences */ jidMap.erase(bareJID); } if (passedPresence->getType() == Presence::Unavailable && jidMap.size() > 1) { jidMap.erase(passedPresence->getFrom()); } else { jidMap[passedPresence->getFrom()] = passedPresence; } entries_[bareJID] = jidMap; onPresenceChange(passedPresence); } } Presence::ref PresenceOracle::getLastPresence(const JID& jid) const { PresencesMap::const_iterator i = entries_.find(jid.toBare()); if (i == entries_.end()) { return Presence::ref(); } PresenceMap presenceMap = i->second; PresenceMap::const_iterator j = presenceMap.find(jid); if (j != presenceMap.end()) { return j->second; } else { return Presence::ref(); } } std::vector PresenceOracle::getAllPresence(const JID& bareJID) const { std::vector results; PresencesMap::const_iterator i = entries_.find(bareJID); if (i == entries_.end()) { return results; } PresenceMap presenceMap = i->second; PresenceMap::const_iterator j = presenceMap.begin(); for (; j != presenceMap.end(); ++j) { Presence::ref current = j->second; results.push_back(current); } return results; } Presence::ref PresenceOracle::getHighestPriorityPresence(const JID& bareJID) const { PresencesMap::const_iterator i = entries_.find(bareJID); if (i == entries_.end()) { return Presence::ref(); } PresenceMap presenceMap = i->second; PresenceMap::const_iterator j = presenceMap.begin(); Presence::ref highest; for (; j != presenceMap.end(); ++j) { Presence::ref current = j->second; if (!highest || current->getPriority() > highest->getPriority() || (current->getPriority() == highest->getPriority() && StatusShow::typeToAvailabilityOrdering(current->getShow()) > StatusShow::typeToAvailabilityOrdering(highest->getShow()))) { highest = current; } } return highest; } } swift-im-2.0+dev6/Swiften/Avatars/0000755000175000017500000000000012227051774016703 5ustar kismithkismithswift-im-2.0+dev6/Swiften/Avatars/AvatarStorage.h0000644000175000017500000000152212227051774021617 0ustar kismithkismith/* * 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 #include #include #include namespace Swift { class JID; class SWIFTEN_API AvatarStorage { public: virtual ~AvatarStorage(); virtual bool hasAvatar(const std::string& hash) const = 0; virtual void addAvatar(const std::string& hash, const ByteArray& avatar) = 0; virtual ByteArray getAvatar(const std::string& hash) const = 0; virtual boost::filesystem::path getAvatarPath(const std::string& hash) const = 0; virtual void setAvatarForJID(const JID& jid, const std::string& hash) = 0; virtual std::string getAvatarForJID(const JID& jid) const = 0; }; } swift-im-2.0+dev6/Swiften/Avatars/SConscript0000644000175000017500000000052712227051774020721 0ustar kismithkismithImport("swiften_env") objects = swiften_env.SwiftenObject([ "VCardUpdateAvatarManager.cpp", "VCardAvatarManager.cpp", "OfflineAvatarManager.cpp", "AvatarManager.cpp", "AvatarManagerImpl.cpp", "AvatarStorage.cpp", "AvatarProvider.cpp", "CombinedAvatarProvider.cpp", ]) swiften_env.Append(SWIFTEN_OBJECTS = [objects]) swift-im-2.0+dev6/Swiften/Avatars/CombinedAvatarProvider.h0000644000175000017500000000137512227051774023454 0ustar kismithkismith/* * 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 #include #include #include #include namespace Swift { class SWIFTEN_API CombinedAvatarProvider : public AvatarProvider { public: virtual std::string getAvatarHash(const JID&) const; void addProvider(AvatarProvider*); void removeProvider(AvatarProvider*); private: void handleAvatarChanged(const JID&); std::string getCombinedAvatarAndCache(const JID&) const; private: std::vector providers; mutable std::map avatars; }; } swift-im-2.0+dev6/Swiften/Avatars/AvatarStorage.cpp0000644000175000017500000000040612227051774022152 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { AvatarStorage::~AvatarStorage() { } } swift-im-2.0+dev6/Swiften/Avatars/AvatarManager.cpp0000644000175000017500000000040612227051774022120 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { AvatarManager::~AvatarManager() { } } swift-im-2.0+dev6/Swiften/Avatars/VCardUpdateAvatarManager.cpp0000644000175000017500000000673112227051774024212 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include #include namespace Swift { VCardUpdateAvatarManager::VCardUpdateAvatarManager(VCardManager* vcardManager, StanzaChannel* stanzaChannel, AvatarStorage* avatarStorage, MUCRegistry* mucRegistry) : vcardManager_(vcardManager), avatarStorage_(avatarStorage), mucRegistry_(mucRegistry) { stanzaChannel->onPresenceReceived.connect(boost::bind(&VCardUpdateAvatarManager::handlePresenceReceived, this, _1)); stanzaChannel->onAvailableChanged.connect(boost::bind(&VCardUpdateAvatarManager::handleStanzaChannelAvailableChanged, this, _1)); vcardManager_->onVCardChanged.connect(boost::bind(&VCardUpdateAvatarManager::handleVCardChanged, this, _1, _2)); } void VCardUpdateAvatarManager::handlePresenceReceived(boost::shared_ptr presence) { boost::shared_ptr update = presence->getPayload(); if (!update || presence->getPayload()) { return; } JID from = getAvatarJID(presence->getFrom()); if (getAvatarHash(from) == update->getPhotoHash()) { return; } SWIFT_LOG(debug) << "Updated hash: " << from << " -> " << update->getPhotoHash() << std::endl; if (avatarStorage_->hasAvatar(update->getPhotoHash())) { setAvatarHash(from, update->getPhotoHash()); } else { vcardManager_->requestVCard(from); } } void VCardUpdateAvatarManager::handleVCardChanged(const JID& from, VCard::ref vCard) { if (!vCard) { std::cerr << "Warning: " << from << ": null vcard payload" << std::endl; return; } if (vCard->getPhoto().empty()) { setAvatarHash(from, ""); } else { std::string hash = Hexify::hexify(SHA1::getHash(vCard->getPhoto())); if (!avatarStorage_->hasAvatar(hash)) { avatarStorage_->addAvatar(hash, vCard->getPhoto()); } setAvatarHash(from, hash); } } void VCardUpdateAvatarManager::setAvatarHash(const JID& from, const std::string& hash) { SWIFT_LOG(debug) << "Updating hash: " << from << " -> " << hash << std::endl; avatarHashes_[from] = hash; onAvatarChanged(from); } /* void VCardUpdateAvatarManager::setAvatar(const JID& jid, const ByteArray& avatar) { std::string hash = Hexify::hexify(SHA1::getHash(avatar)); avatarStorage_->addAvatar(hash, avatar); setAvatarHash(getAvatarJID(jid), hash); } */ std::string VCardUpdateAvatarManager::getAvatarHash(const JID& jid) const { std::map::const_iterator i = avatarHashes_.find(getAvatarJID(jid)); if (i != avatarHashes_.end()) { return i->second; } else { return ""; } } JID VCardUpdateAvatarManager::getAvatarJID(const JID& jid) const { JID bareFrom = jid.toBare(); return (mucRegistry_ && mucRegistry_->isMUC(bareFrom)) ? jid : bareFrom; } void VCardUpdateAvatarManager::handleStanzaChannelAvailableChanged(bool available) { if (available) { std::map oldAvatarHashes; avatarHashes_.swap(oldAvatarHashes); for(std::map::const_iterator i = oldAvatarHashes.begin(); i != oldAvatarHashes.end(); ++i) { onAvatarChanged(i->first); } } } } swift-im-2.0+dev6/Swiften/Avatars/NullAvatarManager.h0000644000175000017500000000075712227051774022431 0ustar kismithkismith/* * 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 namespace Swift { class NullAvatarManager : public AvatarManager { public: virtual boost::filesystem::path getAvatarPath(const JID&) const { return boost::filesystem::path(); } virtual ByteArray getAvatar(const JID&) const { return ByteArray(); } }; } swift-im-2.0+dev6/Swiften/Avatars/AvatarManagerImpl.cpp0000644000175000017500000000444612227051774022752 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include namespace Swift { AvatarManagerImpl::AvatarManagerImpl(VCardManager* vcardManager, StanzaChannel* stanzaChannel, AvatarStorage* avatarStorage, MUCRegistry* mucRegistry) : avatarStorage(avatarStorage) { vcardUpdateAvatarManager = new VCardUpdateAvatarManager(vcardManager, stanzaChannel, avatarStorage, mucRegistry); combinedAvatarProvider.addProvider(vcardUpdateAvatarManager); vcardAvatarManager = new VCardAvatarManager(vcardManager, avatarStorage, mucRegistry); combinedAvatarProvider.addProvider(vcardAvatarManager); offlineAvatarManager = new OfflineAvatarManager(avatarStorage); combinedAvatarProvider.addProvider(offlineAvatarManager); combinedAvatarProvider.onAvatarChanged.connect(boost::bind(&AvatarManagerImpl::handleCombinedAvatarChanged, this, _1)); } AvatarManagerImpl::~AvatarManagerImpl() { combinedAvatarProvider.onAvatarChanged.disconnect(boost::bind(&AvatarManagerImpl::handleCombinedAvatarChanged, this, _1)); combinedAvatarProvider.removeProvider(offlineAvatarManager); delete offlineAvatarManager; combinedAvatarProvider.removeProvider(vcardAvatarManager); delete vcardAvatarManager; combinedAvatarProvider.removeProvider(vcardUpdateAvatarManager); delete vcardUpdateAvatarManager; } boost::filesystem::path AvatarManagerImpl::getAvatarPath(const JID& jid) const { std::string hash = combinedAvatarProvider.getAvatarHash(jid); if (!hash.empty()) { return avatarStorage->getAvatarPath(hash); } return boost::filesystem::path(); } ByteArray AvatarManagerImpl::getAvatar(const JID& jid) const { std::string hash = combinedAvatarProvider.getAvatarHash(jid); if (!hash.empty()) { return avatarStorage->getAvatar(hash); } return ByteArray(); } void AvatarManagerImpl::handleCombinedAvatarChanged(const JID& jid) { offlineAvatarManager->setAvatar(jid, combinedAvatarProvider.getAvatarHash(jid)); onAvatarChanged(jid); } } swift-im-2.0+dev6/Swiften/Avatars/AvatarProvider.h0000644000175000017500000000075512227051774022014 0ustar kismithkismith/* * 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 #include #include namespace Swift { class JID; class SWIFTEN_API AvatarProvider { public: virtual ~AvatarProvider(); virtual std::string getAvatarHash(const JID&) const = 0; boost::signal onAvatarChanged; }; } swift-im-2.0+dev6/Swiften/Avatars/AvatarMemoryStorage.h0000644000175000017500000000251712227051774023015 0ustar kismithkismith/* * 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 #include #include #include #include namespace Swift { class AvatarMemoryStorage : public AvatarStorage { public: virtual bool hasAvatar(const std::string& hash) const { return avatars.find(hash) != avatars.end(); } virtual void addAvatar(const std::string& hash, const ByteArray& avatar) { avatars[hash] = avatar; } virtual ByteArray getAvatar(const std::string& hash) const { std::map::const_iterator i = avatars.find(hash); return i == avatars.end() ? ByteArray() : i->second; } virtual boost::filesystem::path getAvatarPath(const std::string& hash) const { return boost::filesystem::path("/avatars") / hash; } virtual void setAvatarForJID(const JID& jid, const std::string& hash) { jidAvatars[jid] = hash; } virtual std::string getAvatarForJID(const JID& jid) const { std::map::const_iterator i = jidAvatars.find(jid); return i == jidAvatars.end() ? "" : i->second; } private: std::map avatars; std::map jidAvatars; }; } swift-im-2.0+dev6/Swiften/Avatars/OfflineAvatarManager.h0000644000175000017500000000105512227051774023071 0ustar kismithkismith/* * 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 namespace Swift { class AvatarStorage; class OfflineAvatarManager : public AvatarProvider { public: OfflineAvatarManager(AvatarStorage*); ~OfflineAvatarManager(); virtual std::string getAvatarHash(const JID&) const; void setAvatar(const JID&, const std::string& hash); private: AvatarStorage* avatarStorage; }; } swift-im-2.0+dev6/Swiften/Avatars/CombinedAvatarProvider.cpp0000644000175000017500000000347612227051774024013 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include namespace Swift { std::string CombinedAvatarProvider::getAvatarHash(const JID& jid) const { return getCombinedAvatarAndCache(jid); } void CombinedAvatarProvider::addProvider(AvatarProvider* provider) { provider->onAvatarChanged.connect(boost::bind(&CombinedAvatarProvider::handleAvatarChanged, this, _1)); providers.push_back(provider); } void CombinedAvatarProvider::removeProvider(AvatarProvider* provider) { std::vector::iterator i = std::remove(providers.begin(), providers.end(), provider); for(std::vector::iterator j = i; j < providers.end(); ++j) { provider->onAvatarChanged.disconnect(boost::bind(&CombinedAvatarProvider::handleAvatarChanged, this, _1)); } providers.erase(i, providers.end()); } void CombinedAvatarProvider::handleAvatarChanged(const JID& jid) { std::string oldHash; std::map::const_iterator i = avatars.find(jid); if (i != avatars.end()) { oldHash = i->second; } std::string newHash = getCombinedAvatarAndCache(jid); if (newHash != oldHash) { SWIFT_LOG(debug) << "Avatar changed: " << jid << ": " << oldHash << " -> " << newHash << std::endl; onAvatarChanged(jid); } } std::string CombinedAvatarProvider::getCombinedAvatarAndCache(const JID& jid) const { SWIFT_LOG(debug) << "JID: " << jid << std::endl; std::string hash; for (size_t i = 0; i < providers.size() && hash.empty(); ++i) { hash = providers[i]->getAvatarHash(jid); SWIFT_LOG(debug) << "Provider " << providers[i] << ": " << hash << std::endl; } avatars[jid] = hash; return hash; } } swift-im-2.0+dev6/Swiften/Avatars/UnitTest/0000755000175000017500000000000012227051774020462 5ustar kismithkismithswift-im-2.0+dev6/Swiften/Avatars/UnitTest/VCardAvatarManagerTest.cpp0000644000175000017500000001375712227051774025474 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Swift; class VCardAvatarManagerTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(VCardAvatarManagerTest); CPPUNIT_TEST(testGetAvatarHashKnownAvatar); CPPUNIT_TEST(testGetAvatarHashEmptyAvatar); CPPUNIT_TEST(testGetAvatarHashUnknownAvatarKnownVCardStoresAvatar); CPPUNIT_TEST(testGetAvatarHashUnknownAvatarUnknownVCard); CPPUNIT_TEST(testVCardUpdateTriggersUpdate); CPPUNIT_TEST(testGetAvatarHashKnownAvatarUnknownVCard); CPPUNIT_TEST_SUITE_END(); public: class TestVCardStorage : public VCardStorage { public: virtual VCard::ref getVCard(const JID& jid) const { VCardMap::const_iterator i = vcards.find(jid); if (i != vcards.end()) { return i->second; } else { return VCard::ref(); } } virtual void setVCard(const JID& jid, VCard::ref v) { vcards[jid] = v; } std::string getPhotoHash(const JID& jid) const { if (photoHashes.find(jid) != photoHashes.end()) { return photoHashes.find(jid)->second; } VCard::ref vCard = getVCard(jid); if (vCard && !vCard->getPhoto().empty()) { return Hexify::hexify(SHA1::getHash(vCard->getPhoto())); } else { return ""; } } std::map photoHashes; private: typedef std::map VCardMap; VCardMap vcards; }; void setUp() { ownJID = JID("foo@fum.com/bum"); stanzaChannel = new DummyStanzaChannel(); stanzaChannel->setAvailable(true); iqRouter = new IQRouter(stanzaChannel); mucRegistry = new DummyMUCRegistry(); avatarStorage = new AvatarMemoryStorage(); vcardStorage = new TestVCardStorage(); vcardManager = new VCardManager(ownJID, iqRouter, vcardStorage); avatar1 = createByteArray("abcdefg"); avatar1Hash = Hexify::hexify(SHA1::getHash(avatar1)); user1 = JID("user1@bar.com/bla"); user2 = JID("user2@foo.com/baz"); } void tearDown() { delete vcardManager; delete vcardStorage; delete avatarStorage; delete mucRegistry; delete iqRouter; delete stanzaChannel; } void testGetAvatarHashKnownAvatar() { boost::shared_ptr testling = createManager(); storeVCardWithPhoto(user1.toBare(), avatar1); avatarStorage->addAvatar(avatar1Hash, avatar1); std::string result = testling->getAvatarHash(user1); CPPUNIT_ASSERT_EQUAL(avatar1Hash, result); } void testGetAvatarHashEmptyAvatar() { boost::shared_ptr testling = createManager(); storeEmptyVCard(user1.toBare()); std::string result = testling->getAvatarHash(user1); CPPUNIT_ASSERT_EQUAL(std::string(), result); } void testGetAvatarHashUnknownAvatarKnownVCardStoresAvatar() { boost::shared_ptr testling = createManager(); storeVCardWithPhoto(user1.toBare(), avatar1); std::string result = testling->getAvatarHash(user1); CPPUNIT_ASSERT_EQUAL(avatar1Hash, result); CPPUNIT_ASSERT(avatarStorage->hasAvatar(avatar1Hash)); CPPUNIT_ASSERT_EQUAL(avatar1, avatarStorage->getAvatar(avatar1Hash)); } void testGetAvatarHashUnknownAvatarUnknownVCard() { boost::shared_ptr testling = createManager(); std::string result = testling->getAvatarHash(user1); CPPUNIT_ASSERT_EQUAL(std::string(), result); } void testGetAvatarHashKnownAvatarUnknownVCard() { boost::shared_ptr testling = createManager(); vcardStorage->photoHashes[user1.toBare()] = avatar1Hash; std::string result = testling->getAvatarHash(user1); CPPUNIT_ASSERT_EQUAL(std::string(), result); } void testVCardUpdateTriggersUpdate() { boost::shared_ptr testling = createManager(); vcardManager->requestVCard(user1); sendVCardResult(); CPPUNIT_ASSERT_EQUAL(1, static_cast(changes.size())); } private: boost::shared_ptr createManager() { boost::shared_ptr result(new VCardAvatarManager(vcardManager, avatarStorage, mucRegistry)); result->onAvatarChanged.connect(boost::bind(&VCardAvatarManagerTest::handleAvatarChanged, this, _1)); return result; } void storeVCardWithPhoto(const JID& jid, const ByteArray& avatar) { VCard::ref vcard(new VCard()); vcard->setPhoto(avatar); vcardStorage->setVCard(jid, vcard); } void storeEmptyVCard(const JID& jid) { VCard::ref vcard(new VCard()); vcardStorage->setVCard(jid, vcard); } void handleAvatarChanged(const JID& jid) { changes.push_back(jid); } void sendVCardResult() { VCard::ref vcard(new VCard()); vcard->setFullName("Foo Bar"); stanzaChannel->onIQReceived(IQ::createResult(JID("baz@fum.com/dum"), stanzaChannel->sentStanzas[0]->getTo(), stanzaChannel->sentStanzas[0]->getID(), vcard)); } private: struct DummyMUCRegistry : public MUCRegistry { bool isMUC(const JID& jid) const { return std::find(mucs_.begin(), mucs_.end(), jid) != mucs_.end(); } std::vector mucs_; }; JID ownJID; DummyStanzaChannel* stanzaChannel; IQRouter* iqRouter; DummyMUCRegistry* mucRegistry; AvatarMemoryStorage* avatarStorage; VCardManager* vcardManager; TestVCardStorage* vcardStorage; ByteArray avatar1; std::string avatar1Hash; std::vector changes; JID user1; JID user2; }; CPPUNIT_TEST_SUITE_REGISTRATION(VCardAvatarManagerTest); swift-im-2.0+dev6/Swiften/Avatars/UnitTest/VCardUpdateAvatarManagerTest.cpp0000644000175000017500000001706112227051774026627 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Swift; class VCardUpdateAvatarManagerTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(VCardUpdateAvatarManagerTest); CPPUNIT_TEST(testUpdate_NewHashNewVCardRequestsVCard); CPPUNIT_TEST(testUpdate_NewHashStoresAvatarAndEmitsNotificationOnVCardReceive); CPPUNIT_TEST(testUpdate_KnownHash); CPPUNIT_TEST(testUpdate_KnownHashFromDifferentUserDoesNotRequestVCardButTriggersNotification); CPPUNIT_TEST(testVCardWithEmptyPhoto); CPPUNIT_TEST(testStanzaChannelReset_ClearsHash); CPPUNIT_TEST(testStanzaChannelReset_ReceiveHashAfterResetUpdatesHash); CPPUNIT_TEST_SUITE_END(); public: void setUp() { ownJID = JID("foo@fum.com/bum"); stanzaChannel = new DummyStanzaChannel(); stanzaChannel->setAvailable(true); iqRouter = new IQRouter(stanzaChannel); mucRegistry = new DummyMUCRegistry(); avatarStorage = new AvatarMemoryStorage(); vcardStorage = new VCardMemoryStorage(); vcardManager = new VCardManager(ownJID, iqRouter, vcardStorage); avatar1 = createByteArray("abcdefg"); avatar1Hash = Hexify::hexify(SHA1::getHash(avatar1)); user1 = JID("user1@bar.com/bla"); user2 = JID("user2@foo.com/baz"); } void tearDown() { delete vcardManager; delete vcardStorage; delete avatarStorage; delete mucRegistry; delete iqRouter; delete stanzaChannel; } void testUpdate_NewHashNewVCardRequestsVCard() { boost::shared_ptr testling = createManager(); stanzaChannel->onPresenceReceived(createPresenceWithPhotoHash(user1, avatar1Hash)); CPPUNIT_ASSERT_EQUAL(1, static_cast(stanzaChannel->sentStanzas.size())); CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex(0, user1.toBare(), IQ::Get)); } void testUpdate_NewHashStoresAvatarAndEmitsNotificationOnVCardReceive() { boost::shared_ptr testling = createManager(); stanzaChannel->onPresenceReceived(createPresenceWithPhotoHash(user1, avatar1Hash)); stanzaChannel->onIQReceived(createVCardResult(avatar1)); CPPUNIT_ASSERT_EQUAL(1, static_cast(changes.size())); CPPUNIT_ASSERT_EQUAL(user1.toBare(), changes[0]); CPPUNIT_ASSERT_EQUAL(avatar1Hash, testling->getAvatarHash(user1.toBare())); CPPUNIT_ASSERT(avatarStorage->hasAvatar(avatar1Hash)); CPPUNIT_ASSERT_EQUAL(avatar1, avatarStorage->getAvatar(avatar1Hash)); } void testUpdate_KnownHash() { boost::shared_ptr testling = createManager(); stanzaChannel->onPresenceReceived(createPresenceWithPhotoHash(user1, avatar1Hash)); stanzaChannel->onIQReceived(createVCardResult(avatar1)); changes.clear(); stanzaChannel->sentStanzas.clear(); stanzaChannel->onPresenceReceived(createPresenceWithPhotoHash(user1, avatar1Hash)); CPPUNIT_ASSERT_EQUAL(0, static_cast(stanzaChannel->sentStanzas.size())); CPPUNIT_ASSERT_EQUAL(0, static_cast(changes.size())); } void testUpdate_KnownHashFromDifferentUserDoesNotRequestVCardButTriggersNotification() { boost::shared_ptr testling = createManager(); stanzaChannel->onPresenceReceived(createPresenceWithPhotoHash(user1, avatar1Hash)); stanzaChannel->onIQReceived(createVCardResult(avatar1)); changes.clear(); stanzaChannel->sentStanzas.clear(); stanzaChannel->onPresenceReceived(createPresenceWithPhotoHash(user2, avatar1Hash)); CPPUNIT_ASSERT_EQUAL(0, static_cast(stanzaChannel->sentStanzas.size())); CPPUNIT_ASSERT_EQUAL(1, static_cast(changes.size())); CPPUNIT_ASSERT_EQUAL(user2.toBare(), changes[0]); CPPUNIT_ASSERT_EQUAL(avatar1Hash, testling->getAvatarHash(user2.toBare())); } void testVCardWithEmptyPhoto() { boost::shared_ptr testling = createManager(); vcardManager->requestVCard(JID("foo@bar.com")); stanzaChannel->onIQReceived(createVCardResult(ByteArray())); CPPUNIT_ASSERT(!avatarStorage->hasAvatar(Hexify::hexify(SHA1::getHash(ByteArray())))); CPPUNIT_ASSERT_EQUAL(std::string(), testling->getAvatarHash(JID("foo@bar.com"))); } void testStanzaChannelReset_ClearsHash() { boost::shared_ptr testling = createManager(); stanzaChannel->onPresenceReceived(createPresenceWithPhotoHash(user1, avatar1Hash)); stanzaChannel->onIQReceived(createVCardResult(avatar1)); changes.clear(); stanzaChannel->sentStanzas.clear(); stanzaChannel->setAvailable(false); stanzaChannel->setAvailable(true); CPPUNIT_ASSERT_EQUAL(1, static_cast(changes.size())); CPPUNIT_ASSERT_EQUAL(user1.toBare(), changes[0]); CPPUNIT_ASSERT_EQUAL(std::string(""), testling->getAvatarHash(user1.toBare())); } void testStanzaChannelReset_ReceiveHashAfterResetUpdatesHash() { boost::shared_ptr testling = createManager(); stanzaChannel->onPresenceReceived(createPresenceWithPhotoHash(user1, avatar1Hash)); stanzaChannel->onIQReceived(createVCardResult(avatar1)); changes.clear(); stanzaChannel->sentStanzas.clear(); stanzaChannel->setAvailable(false); stanzaChannel->setAvailable(true); stanzaChannel->onPresenceReceived(createPresenceWithPhotoHash(user1, avatar1Hash)); CPPUNIT_ASSERT_EQUAL(2, static_cast(changes.size())); CPPUNIT_ASSERT_EQUAL(user1.toBare(), changes[1]); CPPUNIT_ASSERT_EQUAL(avatar1Hash, testling->getAvatarHash(user1.toBare())); } private: boost::shared_ptr createManager() { boost::shared_ptr result(new VCardUpdateAvatarManager(vcardManager, stanzaChannel, avatarStorage, mucRegistry)); result->onAvatarChanged.connect(boost::bind(&VCardUpdateAvatarManagerTest::handleAvatarChanged, this, _1)); return result; } boost::shared_ptr createPresenceWithPhotoHash(const JID& jid, const std::string& hash) { boost::shared_ptr presence(new Presence()); presence->setFrom(jid); presence->addPayload(boost::make_shared(hash)); return presence; } IQ::ref createVCardResult(const ByteArray& avatar) { VCard::ref vcard(new VCard()); if (!avatar.empty()) { vcard->setPhoto(avatar); } return IQ::createResult(JID("baz@fum.com"), stanzaChannel->sentStanzas[0]->getTo(), stanzaChannel->sentStanzas[0]->getID(), vcard); } void handleAvatarChanged(const JID& jid) { changes.push_back(jid); } private: struct DummyMUCRegistry : public MUCRegistry { bool isMUC(const JID& jid) const { return std::find(mucs_.begin(), mucs_.end(), jid) != mucs_.end(); } std::vector mucs_; }; JID ownJID; DummyStanzaChannel* stanzaChannel; IQRouter* iqRouter; DummyMUCRegistry* mucRegistry; AvatarMemoryStorage* avatarStorage; VCardManager* vcardManager; VCardMemoryStorage* vcardStorage; ByteArray avatar1; std::string avatar1Hash; std::vector changes; JID user1; JID user2; }; CPPUNIT_TEST_SUITE_REGISTRATION(VCardUpdateAvatarManagerTest); swift-im-2.0+dev6/Swiften/Avatars/UnitTest/CombinedAvatarProviderTest.cpp0000644000175000017500000001722012227051774026422 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include using namespace Swift; class CombinedAvatarProviderTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(CombinedAvatarProviderTest); CPPUNIT_TEST(testGetAvatarWithNoAvatarProviderReturnsEmpty); CPPUNIT_TEST(testGetAvatarWithSingleAvatarProvider); CPPUNIT_TEST(testGetAvatarWithMultipleAvatarProviderReturnsFirstAvatar); CPPUNIT_TEST(testGetAvatarWithMultipleAvatarProviderAndFailingFirstProviderReturnsSecondAvatar); CPPUNIT_TEST(testProviderUpdateTriggersChange); CPPUNIT_TEST(testProviderUpdateWithoutChangeDoesNotTriggerChange); CPPUNIT_TEST(testProviderSecondUpdateTriggersChange); CPPUNIT_TEST(testProviderUpdateWithAvatarDisappearingTriggersChange); CPPUNIT_TEST(testProviderUpdateAfterAvatarDisappearedTriggersChange); CPPUNIT_TEST(testProviderUpdateAfterGetDoesNotTriggerChange); CPPUNIT_TEST(testProviderUpdateBareJIDAfterGetFullJID); CPPUNIT_TEST(testRemoveProviderDisconnectsUpdates); CPPUNIT_TEST_SUITE_END(); public: void setUp() { avatarProvider1 = new DummyAvatarProvider(); avatarProvider2 = new DummyAvatarProvider(); user1 = JID("user1@bar.com/bla"); user2 = JID("user2@foo.com/baz"); avatarHash1 = "ABCDEFG"; avatarHash2 = "XYZU"; avatarHash3 = "IDGH"; } void tearDown() { delete avatarProvider1; delete avatarProvider2; } void testGetAvatarWithNoAvatarProviderReturnsEmpty() { boost::shared_ptr testling(createProvider()); CPPUNIT_ASSERT(testling->getAvatarHash(user1).empty()); } void testGetAvatarWithSingleAvatarProvider() { boost::shared_ptr testling(createProvider()); avatarProvider1->avatars[user1] = avatarHash1; testling->addProvider(avatarProvider1); CPPUNIT_ASSERT_EQUAL(avatarHash1, testling->getAvatarHash(user1)); } void testGetAvatarWithMultipleAvatarProviderReturnsFirstAvatar() { boost::shared_ptr testling(createProvider()); avatarProvider1->avatars[user1] = avatarHash1; avatarProvider2->avatars[user1] = avatarHash2; testling->addProvider(avatarProvider1); testling->addProvider(avatarProvider2); CPPUNIT_ASSERT_EQUAL(avatarHash1, testling->getAvatarHash(user1)); } void testGetAvatarWithMultipleAvatarProviderAndFailingFirstProviderReturnsSecondAvatar() { boost::shared_ptr testling(createProvider()); avatarProvider2->avatars[user1] = avatarHash2; testling->addProvider(avatarProvider1); testling->addProvider(avatarProvider2); CPPUNIT_ASSERT_EQUAL(avatarHash2, testling->getAvatarHash(user1)); } void testProviderUpdateTriggersChange() { boost::shared_ptr testling(createProvider()); testling->addProvider(avatarProvider1); avatarProvider1->avatars[user1] = avatarHash1; avatarProvider1->onAvatarChanged(user1); CPPUNIT_ASSERT_EQUAL(1, static_cast(changes.size())); CPPUNIT_ASSERT_EQUAL(user1, changes[0]); } void testProviderUpdateWithoutChangeDoesNotTriggerChange() { boost::shared_ptr testling(createProvider()); testling->addProvider(avatarProvider1); testling->addProvider(avatarProvider2); avatarProvider1->avatars[user1] = avatarHash1; avatarProvider1->onAvatarChanged(user1); changes.clear(); avatarProvider2->avatars[user1] = avatarHash2; avatarProvider2->onAvatarChanged(user1); CPPUNIT_ASSERT_EQUAL(0, static_cast(changes.size())); } void testProviderSecondUpdateTriggersChange() { boost::shared_ptr testling(createProvider()); testling->addProvider(avatarProvider1); avatarProvider1->avatars[user1] = avatarHash1; avatarProvider1->onAvatarChanged(user1); changes.clear(); avatarProvider1->avatars[user1] = avatarHash2; avatarProvider1->onAvatarChanged(user1); CPPUNIT_ASSERT_EQUAL(1, static_cast(changes.size())); CPPUNIT_ASSERT_EQUAL(user1, changes[0]); } void testProviderUpdateWithAvatarDisappearingTriggersChange() { boost::shared_ptr testling(createProvider()); testling->addProvider(avatarProvider1); avatarProvider1->avatars[user1] = avatarHash1; avatarProvider1->onAvatarChanged(user1); changes.clear(); avatarProvider1->avatars.clear(); avatarProvider1->onAvatarChanged(user1); CPPUNIT_ASSERT_EQUAL(1, static_cast(changes.size())); CPPUNIT_ASSERT_EQUAL(user1, changes[0]); } void testProviderUpdateAfterAvatarDisappearedTriggersChange() { boost::shared_ptr testling(createProvider()); testling->addProvider(avatarProvider1); avatarProvider1->avatars[user1] = avatarHash1; avatarProvider1->onAvatarChanged(user1); avatarProvider1->avatars.clear(); avatarProvider1->onAvatarChanged(user1); changes.clear(); avatarProvider1->avatars[user1] = avatarHash1; avatarProvider1->onAvatarChanged(user1); CPPUNIT_ASSERT_EQUAL(1, static_cast(changes.size())); CPPUNIT_ASSERT_EQUAL(user1, changes[0]); } void testProviderUpdateAfterGetDoesNotTriggerChange() { boost::shared_ptr testling(createProvider()); testling->addProvider(avatarProvider1); avatarProvider1->avatars[user1] = avatarHash1; testling->getAvatarHash(user1); avatarProvider1->onAvatarChanged(user1); CPPUNIT_ASSERT_EQUAL(0, static_cast(changes.size())); } void testRemoveProviderDisconnectsUpdates() { boost::shared_ptr testling(createProvider()); testling->addProvider(avatarProvider1); testling->addProvider(avatarProvider2); testling->removeProvider(avatarProvider1); avatarProvider1->avatars[user1] = avatarHash1; avatarProvider2->avatars[user1] = avatarHash2; avatarProvider1->onAvatarChanged(user1); CPPUNIT_ASSERT_EQUAL(0, static_cast(changes.size())); } void testProviderUpdateBareJIDAfterGetFullJID() { boost::shared_ptr testling(createProvider()); avatarProvider1->useBare = true; testling->addProvider(avatarProvider1); avatarProvider1->avatars[user1.toBare()] = avatarHash1; testling->getAvatarHash(user1); avatarProvider1->avatars[user1.toBare()] = avatarHash2; avatarProvider1->onAvatarChanged(user1.toBare()); CPPUNIT_ASSERT_EQUAL(avatarHash2, testling->getAvatarHash(user1)); } private: boost::shared_ptr createProvider() { boost::shared_ptr result(new CombinedAvatarProvider()); result->onAvatarChanged.connect(boost::bind(&CombinedAvatarProviderTest::handleAvatarChanged, this, _1)); return result; } void handleAvatarChanged(const JID& jid) { changes.push_back(jid); } private: struct DummyAvatarProvider : public AvatarProvider { DummyAvatarProvider() : useBare(false) { } std::string getAvatarHash(const JID& jid) const { JID actualJID = useBare ? jid.toBare() : jid; std::map::const_iterator i = avatars.find(actualJID); if (i != avatars.end()) { return i->second; } else { return std::string(); } } bool useBare; std::map avatars; }; DummyAvatarProvider* avatarProvider1; DummyAvatarProvider* avatarProvider2; JID user1; JID user2; std::string avatarHash1; std::string avatarHash2; std::string avatarHash3; std::vector changes; }; CPPUNIT_TEST_SUITE_REGISTRATION(CombinedAvatarProviderTest); swift-im-2.0+dev6/Swiften/Avatars/VCardAvatarManager.h0000644000175000017500000000140212227051774022502 0ustar kismithkismith/* * 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 #include #include namespace Swift { class MUCRegistry; class AvatarStorage; class VCardManager; class SWIFTEN_API VCardAvatarManager : public AvatarProvider { public: VCardAvatarManager(VCardManager*, AvatarStorage*, MUCRegistry* = NULL); std::string getAvatarHash(const JID&) const; private: void handleVCardChanged(const JID& from); JID getAvatarJID(const JID& o) const; private: VCardManager* vcardManager_; AvatarStorage* avatarStorage_; MUCRegistry* mucRegistry_; }; } swift-im-2.0+dev6/Swiften/Avatars/OfflineAvatarManager.cpp0000644000175000017500000000140512227051774023423 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include namespace Swift { OfflineAvatarManager::OfflineAvatarManager(AvatarStorage* avatarStorage) : avatarStorage(avatarStorage) { } OfflineAvatarManager::~OfflineAvatarManager() { } std::string OfflineAvatarManager::getAvatarHash(const JID& jid) const { return avatarStorage->getAvatarForJID(jid); } void OfflineAvatarManager::setAvatar(const JID& jid, const std::string& hash) { if (getAvatarHash(jid) != hash) { avatarStorage->setAvatarForJID(jid, hash); onAvatarChanged(jid); } } } swift-im-2.0+dev6/Swiften/Avatars/AvatarManagerImpl.h0000644000175000017500000000206512227051774022412 0ustar kismithkismith/* * 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 #include namespace Swift { class MUCRegistry; class AvatarStorage; class StanzaChannel; class VCardManager; class VCardUpdateAvatarManager; class VCardAvatarManager; class OfflineAvatarManager; class AvatarManagerImpl : public AvatarManager { public: AvatarManagerImpl(VCardManager*, StanzaChannel*, AvatarStorage*, MUCRegistry* = NULL); virtual ~AvatarManagerImpl(); virtual boost::filesystem::path getAvatarPath(const JID&) const; virtual ByteArray getAvatar(const JID&) const; private: void handleCombinedAvatarChanged(const JID& jid); private: CombinedAvatarProvider combinedAvatarProvider; AvatarStorage* avatarStorage; VCardUpdateAvatarManager* vcardUpdateAvatarManager; VCardAvatarManager* vcardAvatarManager; OfflineAvatarManager* offlineAvatarManager; }; } swift-im-2.0+dev6/Swiften/Avatars/AvatarProvider.cpp0000644000175000017500000000041112227051774022334 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { AvatarProvider::~AvatarProvider() { } } swift-im-2.0+dev6/Swiften/Avatars/DummyAvatarManager.h0000644000175000017500000000136312227051774022604 0ustar kismithkismith/* * 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 #include #include namespace Swift { class DummyAvatarManager : public AvatarManager { public: virtual boost::filesystem::path getAvatarPath(const JID& j) const { return boost::filesystem::path("/avatars") / j.toString(); } virtual ByteArray getAvatar(const JID& jid) const { std::map::const_iterator i = avatars.find(jid); if (i != avatars.end()) { return i->second; } else { return ByteArray(); } } std::map avatars; }; } swift-im-2.0+dev6/Swiften/Avatars/AvatarManager.h0000644000175000017500000000114412227051774021565 0ustar kismithkismith/* * 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 #include #include #include namespace Swift { class JID; class SWIFTEN_API AvatarManager { public: virtual ~AvatarManager(); virtual ByteArray getAvatar(const JID&) const = 0; virtual boost::filesystem::path getAvatarPath(const JID&) const = 0; boost::signal onAvatarChanged; }; } swift-im-2.0+dev6/Swiften/Avatars/VCardUpdateAvatarManager.h0000644000175000017500000000234712227051774023656 0ustar kismithkismith/* * 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 #include #include #include #include #include #include #include namespace Swift { class MUCRegistry; class AvatarStorage; class StanzaChannel; class VCardManager; class SWIFTEN_API VCardUpdateAvatarManager : public AvatarProvider, public boost::bsignals::trackable { public: VCardUpdateAvatarManager(VCardManager*, StanzaChannel*, AvatarStorage*, MUCRegistry* = NULL); std::string getAvatarHash(const JID&) const; private: void handlePresenceReceived(boost::shared_ptr); void handleStanzaChannelAvailableChanged(bool); void handleVCardChanged(const JID& from, VCard::ref); void setAvatarHash(const JID& from, const std::string& hash); JID getAvatarJID(const JID& o) const; private: VCardManager* vcardManager_; AvatarStorage* avatarStorage_; MUCRegistry* mucRegistry_; std::map avatarHashes_; }; } swift-im-2.0+dev6/Swiften/Avatars/VCardAvatarManager.cpp0000644000175000017500000000403412227051774023041 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include namespace Swift { VCardAvatarManager::VCardAvatarManager(VCardManager* vcardManager, AvatarStorage* avatarStorage, MUCRegistry* mucRegistry) : vcardManager_(vcardManager), avatarStorage_(avatarStorage), mucRegistry_(mucRegistry) { vcardManager_->onVCardChanged.connect(boost::bind(&VCardAvatarManager::handleVCardChanged, this, _1)); } void VCardAvatarManager::handleVCardChanged(const JID& from) { // We don't check whether the avatar actually changed. Direct use of this // manager could cause unnecessary updates, but in practice, this will be // caught by the wrapping CombinedAvatarManager anyway. onAvatarChanged(from); } std::string VCardAvatarManager::getAvatarHash(const JID& jid) const { JID avatarJID = getAvatarJID(jid); std::string hash = vcardManager_->getPhotoHash(avatarJID); if (!hash.empty()) { if (!avatarStorage_->hasAvatar(hash)) { VCard::ref vCard = vcardManager_->getVCard(avatarJID); if (vCard) { std::string newHash = Hexify::hexify(SHA1::getHash(vCard->getPhoto())); if (newHash != hash) { // Shouldn't happen, but sometimes seem to. Might be fixed if we // move to a safer backend. SWIFT_LOG(warning) << "Inconsistent vCard photo hash cache"; hash = newHash; } avatarStorage_->addAvatar(hash, vCard->getPhoto()); } else { // Can happen if the cache is inconsistent. hash = ""; } } } return hash; } JID VCardAvatarManager::getAvatarJID(const JID& jid) const { JID bareFrom = jid.toBare(); return (mucRegistry_ && mucRegistry_->isMUC(bareFrom)) ? jid : bareFrom; } } swift-im-2.0+dev6/Swiften/Elements/0000755000175000017500000000000012227051774017056 5ustar kismithkismithswift-im-2.0+dev6/Swiften/Elements/EnableStreamManagement.h0000644000175000017500000000051012227051774023562 0ustar kismithkismith/* * 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 namespace Swift { class EnableStreamManagement : public Element { public: EnableStreamManagement() {} }; } swift-im-2.0+dev6/Swiften/Elements/JingleDescription.h0000644000175000017500000000057112227051774022646 0ustar kismithkismith/* * 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 #include namespace Swift { class JingleDescription : public Payload { public: typedef boost::shared_ptr ref; }; } swift-im-2.0+dev6/Swiften/Elements/Form.cpp0000644000175000017500000000175312227051774020473 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include namespace Swift { std::string Form::getFormType() const { FormField::ref field = getField("FORM_TYPE"); boost::shared_ptr f = boost::dynamic_pointer_cast(field); return (f ? f->getValue() : ""); } FormField::ref Form::getField(const std::string& name) const { foreach(FormField::ref field, fields_) { if (field->getName() == name) { return field; } } return FormField::ref(); } void Form::addReportedField(FormField::ref field) { reportedFields_.push_back(field); } const std::vector& Form::getReportedFields() const { return reportedFields_; } void Form::addItem(const Form::FormItem& item) { items_.push_back(item); } const std::vector& Form::getItems() const { return items_; } } swift-im-2.0+dev6/Swiften/Elements/StanzaAckRequest.h0000644000175000017500000000043112227051774022455 0ustar kismithkismith/* * 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 namespace Swift { class StanzaAckRequest : public Element { }; } swift-im-2.0+dev6/Swiften/Elements/StartTLSFailure.h0000644000175000017500000000047112227051774022221 0ustar kismithkismith/* * 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 namespace Swift { class StartTLSFailure : public Element { public: StartTLSFailure() {} }; } swift-im-2.0+dev6/Swiften/Elements/ComponentHandshake.h0000644000175000017500000000116212227051774023000 0ustar kismithkismith/* * 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 #include #include namespace Swift { class ComponentHandshake : public Element { public: typedef boost::shared_ptr ref; ComponentHandshake(const std::string& data = "") : data(data) { } void setData(const std::string& d) { data = d; } const std::string& getData() const { return data; } private: std::string data; }; } swift-im-2.0+dev6/Swiften/Elements/StreamResume.cpp0000644000175000017500000000045012227051774022175 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include using namespace Swift; StreamResume::StreamResume() { } StreamResume::~StreamResume() { } swift-im-2.0+dev6/Swiften/Elements/CompressRequest.h0000644000175000017500000000103612227051774022373 0ustar kismithkismith/* * 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 namespace Swift { class CompressRequest : public Element { public: CompressRequest(const std::string& method = "") : method_(method) {} const std::string& getMethod() const { return method_; } void setMethod(const std::string& method) { method_ = method; } private: std::string method_; }; } swift-im-2.0+dev6/Swiften/Elements/BlockPayload.h0000644000175000017500000000102612227051774021572 0ustar kismithkismith/* * 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 #include #include namespace Swift { class BlockPayload : public Payload { public: BlockPayload() { } void addItem(const JID& jid) { items.push_back(jid); } const std::vector& getItems() const { return items; } private: std::vector items; }; } swift-im-2.0+dev6/Swiften/Elements/StreamInitiation.h0000644000175000017500000000302112227051774022506 0ustar kismithkismith/* * 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 #include #include #include #include #include namespace Swift { class StreamInitiation : public Payload { public: typedef boost::shared_ptr ref; StreamInitiation() : isFileTransfer(true) {} const std::string& getID() const { return id; } void setID(const std::string& id) { this->id = id; } const boost::optional& getFileInfo() const { return fileInfo; } void setFileInfo(const StreamInitiationFileInfo& info) { fileInfo = info; } const std::vector& getProvidedMethods() const { return providedMethods; } void addProvidedMethod(const std::string& method) { providedMethods.push_back(method); } void setRequestedMethod(const std::string& method) { requestedMethod = method; } const std::string& getRequestedMethod() const { return requestedMethod; } bool getIsFileTransfer() const { return isFileTransfer; } void setIsFileTransfer(bool b) { isFileTransfer = b; } private: bool isFileTransfer; std::string id; boost::optional fileInfo; std::vector providedMethods; std::string requestedMethod; }; } swift-im-2.0+dev6/Swiften/Elements/S5BProxyRequest.h0000644000175000017500000000213512227051774022234 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include #include #include #include namespace Swift { class S5BProxyRequest : public Payload { public: typedef boost::shared_ptr ref; public: struct StreamHost { HostAddressPort addressPort; JID jid; }; public: const boost::optional& getStreamHost() const { return streamHost; } void setStreamHost(const StreamHost& streamHost) { this->streamHost = boost::optional(streamHost); } const std::string& getSID() const { return sid; } void setSID(const std::string& sid) { this->sid = sid; } const boost::optional& getActivate() const { return activate; } void setActivate(const JID& activate) { this->activate = activate; } private: boost::optional streamHost; std::string sid; boost::optional activate; }; } swift-im-2.0+dev6/Swiften/Elements/DiscoInfo.cpp0000644000175000017500000000372412227051774021445 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include namespace Swift { const std::string DiscoInfo::ChatStatesFeature = std::string("http://jabber.org/protocol/chatstates"); const std::string DiscoInfo::SecurityLabelsFeature = std::string("urn:xmpp:sec-label:0"); const std::string DiscoInfo::SecurityLabelsCatalogFeature = std::string("urn:xmpp:sec-label:catalog:2"); const std::string DiscoInfo::JabberSearchFeature = std::string("jabber:iq:search"); const std::string DiscoInfo::CommandsFeature = std::string("http://jabber.org/protocol/commands"); const std::string DiscoInfo::MessageCorrectionFeature = std::string("urn:xmpp:message-correct:0"); const std::string DiscoInfo::JingleFeature = std::string("urn:xmpp:jingle:1"); const std::string DiscoInfo::JingleFTFeature = std::string("urn:xmpp:jingle:apps:file-transfer:3"); const std::string DiscoInfo::JingleTransportsIBBFeature = std::string("urn:xmpp:jingle:transports:ibb:1"); const std::string DiscoInfo::JingleTransportsS5BFeature = std::string("urn:xmpp:jingle:transports:s5b:1"); const std::string DiscoInfo::Bytestream = std::string("http://jabber.org/protocol/bytestreams"); const std::string DiscoInfo::MessageDeliveryReceiptsFeature = std::string("urn:xmpp:receipts"); const std::string DiscoInfo::WhiteboardFeature = std::string("http://swift.im/whiteboard"); bool DiscoInfo::Identity::operator<(const Identity& other) const { if (category_ == other.category_) { if (type_ == other.type_) { if (lang_ == other.lang_) { return name_ < other.name_; } else { return lang_ < other.lang_; } } else { return type_ < other.type_; } } else { return category_ < other.category_; } } bool DiscoInfo::hasFeature(const std::string& feature) const { return std::find(features_.begin(), features_.end(), feature) != features_.end(); } } swift-im-2.0+dev6/Swiften/Elements/MUCAdminPayload.h0000644000175000017500000000135512227051774022142 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include #include #include #include #include namespace Swift { class MUCAdminPayload : public Payload { public: typedef boost::shared_ptr ref; MUCAdminPayload() { } void addItem(const MUCItem& item) {items_.push_back(item);} const std::vector& getItems() const {return items_;} private: std::vector items_; }; } swift-im-2.0+dev6/Swiften/Elements/CapsInfo.h0000644000175000017500000000255612227051774020741 0ustar kismithkismith/* * 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 #include #include namespace Swift { class CapsInfo : public Payload { public: typedef boost::shared_ptr ref; CapsInfo(const std::string& node = "", const std::string& version = "", const std::string& hash = "sha-1") : node_(node), version_(version), hash_(hash) {} bool operator==(const CapsInfo& o) const { return o.node_ == node_ && o.version_ == version_ && o.hash_ == hash_; } bool operator<(const CapsInfo& o) const { if (o.node_ == node_) { if (o.version_ == version_) { return hash_ < o.hash_; } else { return version_ < o.version_; } } else { return node_ < o.node_; } } const std::string& getNode() const { return node_; } void setNode(const std::string& node) { node_ = node; } const std::string& getVersion() const { return version_; } void setVersion(const std::string& version) { version_ = version; } const std::string& getHash() const { return hash_; } void setHash(const std::string& hash) { hash_ = hash; } private: std::string node_; std::string version_; std::string hash_; }; } swift-im-2.0+dev6/Swiften/Elements/Presence.cpp0000644000175000017500000000176212227051774021334 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include namespace Swift { Presence::Presence() : type_(Available) /*, showType_(Online)*/ { } Presence::Presence(const std::string& status) : type_(Available) { setStatus(status); } Presence::~Presence() { } int Presence::getPriority() const { boost::shared_ptr priority(getPayload()); return (priority ? priority->getPriority() : 0); } void Presence::setPriority(int priority) { updatePayload(boost::make_shared(priority)); } std::string Presence::getStatus() const { boost::shared_ptr status(getPayload()); if (status) { return status->getText(); } return ""; } void Presence::setStatus(const std::string& status) { updatePayload(boost::make_shared(status)); } } swift-im-2.0+dev6/Swiften/Elements/JingleContentPayload.h0000644000175000017500000000432212227051774023305 0ustar kismithkismith/* * 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 #include #include #include #include #include #include namespace Swift { class JingleContentPayload : public Payload { public: typedef boost::shared_ptr ref; enum Creator { UnknownCreator, InitiatorCreator, ResponderCreator, }; /*enum Senders { NoSenders, InitiatorSender, ResponderSender, BothSenders, };*/ Creator getCreator() const { return creator; } void setCreator(Creator creator) { this->creator = creator; } const std::string& getName() const { return name; } void setName(const std::string& name) { this->name = name; } const std::vector& getDescriptions() const { return descriptions; } void addDescription(JingleDescription::ref description) { descriptions.push_back(description); } const std::vector >& getTransports() const { return transports; } void addTransport(boost::shared_ptr transport) { transports.push_back(transport); } template boost::shared_ptr getDescription() const { for (size_t i = 0; i < descriptions.size(); ++i) { boost::shared_ptr result(boost::dynamic_pointer_cast(descriptions[i])); if (result) { return result; } } return boost::shared_ptr(); } template boost::shared_ptr getTransport() const { for (size_t i = 0; i < transports.size(); ++i) { boost::shared_ptr result(boost::dynamic_pointer_cast(transports[i])); if (result) { return result; } } return boost::shared_ptr(); } private: Creator creator; std::string name; //Senders senders; std::vector descriptions; std::vector > transports; }; } swift-im-2.0+dev6/Swiften/Elements/Version.h0000644000175000017500000000127112227051774020655 0ustar kismithkismith/* * 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 #include namespace Swift { class Version : public Payload { public: Version(const std::string& name = "", const std::string& version = "", const std::string& os = "") : name_(name), version_(version), os_(os) { } const std::string& getName() const { return name_; } const std::string& getVersion() const { return version_; } const std::string& getOS() const { return os_; } private: std::string name_; std::string version_; std::string os_; }; } swift-im-2.0+dev6/Swiften/Elements/Presence.h0000644000175000017500000000325012227051774020773 0ustar kismithkismith/* * 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 #include #include #include namespace Swift { class SWIFTEN_API Presence : public Stanza { public: typedef boost::shared_ptr ref; enum Type { Available, Error, Probe, Subscribe, Subscribed, Unavailable, Unsubscribe, Unsubscribed }; Presence(); Presence(const std::string& status); virtual ~Presence(); static ref create() { return boost::make_shared(); } static ref create(const std::string& status) { return boost::make_shared(status); } static ref create(Presence::ref presence) { return boost::make_shared(*presence); } Type getType() const { return type_; } void setType(Type type) { type_ = type; } StatusShow::Type getShow() const { boost::shared_ptr show(getPayload()); if (show) { return show->getType(); } return type_ == Available ? StatusShow::Online : StatusShow::None; } void setShow(const StatusShow::Type &show) { updatePayload(boost::make_shared(show)); } std::string getStatus() const; void setStatus(const std::string& status); int getPriority() const; void setPriority(int priority); boost::shared_ptr clone() const { return boost::make_shared(*this); } bool isAvailable() const { return type_ == Available; } private: Presence::Type type_; }; } swift-im-2.0+dev6/Swiften/Elements/JingleFileTransferReceived.h0000644000175000017500000000127612227051774024421 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include #include #include namespace Swift { class JingleFileTransferReceived : public Payload { public: typedef boost::shared_ptr ref; void setFileInfo(const StreamInitiationFileInfo& fileInfo) { this->fileInfo = fileInfo; } const StreamInitiationFileInfo& getFileInfo() const { return this->fileInfo; } private: StreamInitiationFileInfo fileInfo; }; } swift-im-2.0+dev6/Swiften/Elements/UnknownElement.h0000644000175000017500000000046712227051774022207 0ustar kismithkismith/* * 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 namespace Swift { class UnknownElement : public Element { public: UnknownElement() {} }; } swift-im-2.0+dev6/Swiften/Elements/StartTLSRequest.h0000644000175000017500000000047112227051774022262 0ustar kismithkismith/* * 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 namespace Swift { class StartTLSRequest : public Element { public: StartTLSRequest() {} }; } swift-im-2.0+dev6/Swiften/Elements/SecurityLabelsCatalog.h0000644000175000017500000000346412227051774023463 0ustar kismithkismith/* * 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 #include #include #include #include #include #include namespace Swift { class SWIFTEN_API SecurityLabelsCatalog : public Payload { public: typedef boost::shared_ptr ref; class Item { public: Item() : default_(false) {} SecurityLabel::ref getLabel() const { return label_; } void setLabel(SecurityLabel::ref label) { label_ = label; } const std::string& getSelector() const { return selector_; } void setSelector(const std::string& selector) { selector_ = selector; } bool getIsDefault() const { return default_; } void setIsDefault(bool isDefault) { default_ = isDefault; } private: SecurityLabel::ref label_; std::string selector_; bool default_; }; SecurityLabelsCatalog(const JID& to = JID()) : to_(to) {} const std::vector& getItems() const { return items_; } void addItem(const Item& item) { items_.push_back(item); } const JID& getTo() const { return to_; } void setTo(const JID& to) { to_ = to; } const std::string& getName() const { return name_; } void setName(const std::string& name) { name_ = name; } const std::string& getDescription() const { return description_; } void setDescription(const std::string& description) { description_ = description; } private: JID to_; std::string name_; std::string description_; std::vector items_; }; } swift-im-2.0+dev6/Swiften/Elements/IBB.h0000644000175000017500000000443412227051774017630 0ustar kismithkismith/* * 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 #include #include #include #include namespace Swift { class IBB : public Payload { public: typedef boost::shared_ptr ref; enum Action { Open, Close, Data, }; enum StanzaType { IQStanza, MessageStanza, }; IBB(Action action = Open, const std::string& streamID = "") : action(action), streamID(streamID), stanzaType(IQStanza), blockSize(-1), sequenceNumber(-1) { } static IBB::ref createIBBOpen(const std::string& streamID, int blockSize) { IBB::ref result = boost::make_shared(Open, streamID); result->setBlockSize(blockSize); return result; } static IBB::ref createIBBData(const std::string& streamID, int sequenceNumber, const std::vector& data) { IBB::ref result = boost::make_shared(Data, streamID); result->setSequenceNumber(sequenceNumber); result->setData(data); return result; } static IBB::ref createIBBClose(const std::string& streamID) { return boost::make_shared(Close, streamID); } void setAction(Action action) { this->action = action; } Action getAction() const { return action; } void setStanzaType(StanzaType stanzaType) { this->stanzaType = stanzaType; } StanzaType getStanzaType() const { return stanzaType; } void setStreamID(const std::string& id) { streamID = id; } const std::string& getStreamID() const { return streamID; } const std::vector& getData() const { return data; } void setData(const std::vector& data) { this->data = data; } int getBlockSize() const { return blockSize; } void setBlockSize(int blockSize) { this->blockSize = blockSize; } int getSequenceNumber() const { return sequenceNumber; } void setSequenceNumber(int i) { sequenceNumber = i; } private: Action action; std::string streamID; std::vector data; StanzaType stanzaType; int blockSize; int sequenceNumber; }; } swift-im-2.0+dev6/Swiften/Elements/AuthSuccess.h0000644000175000017500000000122012227051774021454 0ustar kismithkismith/* * 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 #include #include namespace Swift { class AuthSuccess : public Element { public: AuthSuccess() {} const boost::optional >& getValue() const { return value; } void setValue(const std::vector& value) { this->value = boost::optional >(value); } private: boost::optional > value; }; } swift-im-2.0+dev6/Swiften/Elements/JingleFileTransferDescription.h0000644000175000017500000000175012227051774025153 0ustar kismithkismith/* * 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 #include #include #include namespace Swift { class JingleFileTransferDescription : public JingleDescription { public: typedef boost::shared_ptr ref; void addOffer(const StreamInitiationFileInfo& offer) { offers.push_back(offer); } const std::vector& getOffers() const { return offers; } void addRequest(const StreamInitiationFileInfo& request) { reqeusts.push_back(request); } const std::vector& getRequests() const { return reqeusts; } private: std::vector offers; std::vector reqeusts; }; } swift-im-2.0+dev6/Swiften/Elements/VCard.h0000644000175000017500000000562712227051774020240 0ustar kismithkismith/* * 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 #include #include #include namespace Swift { class VCard : public Payload { public: typedef boost::shared_ptr ref; struct EMailAddress { EMailAddress() : isHome(false), isWork(false), isInternet(false), isPreferred(false), isX400(false) { } bool isHome; bool isWork; bool isInternet; bool isPreferred; bool isX400; std::string address; }; VCard() {} void setVersion(const std::string& version) { version_ = version; } const std::string& getVersion() const { return version_; } void setFullName(const std::string& fullName) { fullName_ = fullName; } const std::string& getFullName() const { return fullName_; } void setFamilyName(const std::string& familyName) { familyName_ = familyName; } const std::string& getFamilyName() const { return familyName_; } void setGivenName(const std::string& givenName) { givenName_ = givenName; } const std::string& getGivenName() const { return givenName_; } void setMiddleName(const std::string& middleName) { middleName_ = middleName; } const std::string& getMiddleName() const { return middleName_; } void setPrefix(const std::string& prefix) { prefix_ = prefix; } const std::string& getPrefix() const { return prefix_; } void setSuffix(const std::string& suffix) { suffix_ = suffix; } const std::string& getSuffix() const { return suffix_; } //void setEMailAddress(const std::string& email) { email_ = email; } //const std::string& getEMailAddress() const { return email_; } void setNickname(const std::string& nick) { nick_ = nick; } const std::string& getNickname() const { return nick_; } void setPhoto(const ByteArray& photo) { photo_ = photo; } const ByteArray& getPhoto() const { return photo_; } void setPhotoType(const std::string& photoType) { photoType_ = photoType; } const std::string& getPhotoType() const { return photoType_; } const std::string& getUnknownContent() const { return unknownContent_; } void addUnknownContent(const std::string& c) { unknownContent_ += c; } const std::vector& getEMailAddresses() const { return emailAddresses_; } void addEMailAddress(const EMailAddress& email) { emailAddresses_.push_back(email); } EMailAddress getPreferredEMailAddress() const; private: std::string version_; std::string fullName_; std::string familyName_; std::string givenName_; std::string middleName_; std::string prefix_; std::string suffix_; //std::string email_; ByteArray photo_; std::string photoType_; std::string nick_; std::string unknownContent_; std::vector emailAddresses_; }; } swift-im-2.0+dev6/Swiften/Elements/RosterItemPayload.h0000644000175000017500000000323712227051774022643 0ustar kismithkismith/* * 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 #include #include namespace Swift { class RosterItemPayload { public: enum Subscription { None, To, From, Both, Remove }; RosterItemPayload() : subscription_(None), ask_(false) {} RosterItemPayload(const JID& jid, const std::string& name, Subscription subscription, const std::vector& groups = std::vector()) : jid_(jid), name_(name), subscription_(subscription), groups_(groups), ask_(false) { } void setJID(const JID& jid) { jid_ = jid; } const JID& getJID() const { return jid_; } void setName(const std::string& name) { name_ = name; } const std::string& getName() const { return name_; } void setSubscription(Subscription subscription) { subscription_ = subscription; } const Subscription& getSubscription() const { return subscription_; } void addGroup(const std::string& group) { groups_.push_back(group); } void setGroups(const std::vector& groups) { groups_ = groups; } const std::vector& getGroups() const { return groups_; } void setSubscriptionRequested() { ask_ = true; } bool getSubscriptionRequested() const { return ask_; } const std::string& getUnknownContent() const { return unknownContent_; } void addUnknownContent(const std::string& c) { unknownContent_ += c; } private: JID jid_; std::string name_; Subscription subscription_; std::vector groups_; bool ask_; std::string unknownContent_; }; } swift-im-2.0+dev6/Swiften/Elements/Priority.h0000644000175000017500000000076012227051774021053 0ustar kismithkismith/* * 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 namespace Swift { class Priority : public Payload { public: Priority(int priority = 0) : priority_(priority) { } void setPriority(int priority) { priority_ = priority; } int getPriority() const { return priority_; } private: int priority_; }; } swift-im-2.0+dev6/Swiften/Elements/VCard.cpp0000644000175000017500000000101012227051774020551 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include namespace Swift { VCard::EMailAddress VCard::getPreferredEMailAddress() const { foreach(const EMailAddress& address, emailAddresses_) { if (address.isPreferred) { return address; } } if (!emailAddresses_.empty()) { return emailAddresses_[0]; } return EMailAddress(); } } swift-im-2.0+dev6/Swiften/Elements/RosterItemExchangePayload.cpp0000644000175000017500000000062512227051774024637 0ustar kismithkismith/* * Copyright (c) 2011 Jan Kaluza * Licensed under the Simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include #include namespace Swift { RosterItemExchangePayload::Item::Item(Action action) : action(action) { } RosterItemExchangePayload::RosterItemExchangePayload() { } } swift-im-2.0+dev6/Swiften/Elements/Compressed.h0000644000175000017500000000045712227051774021341 0ustar kismithkismith/* * 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 namespace Swift { class Compressed : public Element { public: Compressed() {} }; } swift-im-2.0+dev6/Swiften/Elements/MUCOwnerPayload.h0000644000175000017500000000132012227051774022174 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include namespace Swift { class MUCOwnerPayload : public Payload { public: typedef boost::shared_ptr ref; MUCOwnerPayload() { } boost::shared_ptr getPayload() const { return payload; } void setPayload(boost::shared_ptr p) { payload = p; } Form::ref getForm() { return boost::dynamic_pointer_cast(payload); } private: boost::shared_ptr payload; }; } swift-im-2.0+dev6/Swiften/Elements/RawXMLPayload.h0000644000175000017500000000104312227051774021651 0ustar kismithkismith/* * 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 #include namespace Swift { class RawXMLPayload : public Payload { public: RawXMLPayload(const std::string& data = "") : rawXML_(data) {} void setRawXML(const std::string& data) { rawXML_ = data; } const std::string& getRawXML() const { return rawXML_; } private: std::string rawXML_; }; } swift-im-2.0+dev6/Swiften/Elements/DeliveryReceiptRequest.h0000644000175000017500000000061112227051774023675 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the BSD license. * See http://www.opensource.org/licenses/bsd-license.php for more information. */ #pragma once #include namespace Swift { class DeliveryReceiptRequest : public Payload { public: typedef boost::shared_ptr ref; public: DeliveryReceiptRequest() {} }; } swift-im-2.0+dev6/Swiften/Elements/SearchPayload.h0000644000175000017500000000355412227051774021755 0ustar kismithkismith/* * 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 #include #include #include #include namespace Swift { /** * XEP-0055 search payload. */ class SearchPayload : public Payload { public: typedef boost::shared_ptr ref; struct Item { std::string first; std::string last; std::string nick; std::string email; JID jid; }; SearchPayload() {} Form::ref getForm() const { return form; } void setForm(Form::ref f) { form = f; } const boost::optional& getInstructions() const { return instructions; } const boost::optional& getNick() const { return nick; } const boost::optional& getFirst() const { return first; } const boost::optional& getLast() const { return last; } const boost::optional& getEMail() const { return email; } void setInstructions(const std::string& v) { this->instructions = v; } void setNick(const std::string& v) { this->nick = v; } void setFirst(const std::string& v) { this->first = v; } void setLast(const std::string& v) { this->last = v; } void setEMail(const std::string& v) { this->email = v; } const std::vector& getItems() const { return items; } void addItem(const Item& item) { items.push_back(item); } private: Form::ref form; boost::optional instructions; boost::optional nick; boost::optional first; boost::optional last; boost::optional email; std::vector items; }; } swift-im-2.0+dev6/Swiften/Elements/ResourceBind.h0000644000175000017500000000124012227051774021610 0ustar kismithkismith/* * 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 #include #include namespace Swift { class ResourceBind : public Payload { public: ResourceBind() {} void setJID(const JID& jid) { jid_ = jid; } const JID& getJID() const { return jid_; } void setResource(const std::string& resource) { resource_ = resource; } const std::string& getResource() const { return resource_; } private: JID jid_; std::string resource_; }; } swift-im-2.0+dev6/Swiften/Elements/Element.cpp0000644000175000017500000000036512227051774021157 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { Element::~Element() { } } swift-im-2.0+dev6/Swiften/Elements/StreamFeatures.h0000644000175000017500000000424512227051774022166 0ustar kismithkismith/* * 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 #include #include #include #include namespace Swift { class SWIFTEN_API StreamFeatures : public Element { public: typedef boost::shared_ptr ref; StreamFeatures() : hasStartTLS_(false), hasResourceBind_(false), hasSession_(false), hasStreamManagement_(false), hasRosterVersioning_(false) {} void setHasStartTLS() { hasStartTLS_ = true; } bool hasStartTLS() const { return hasStartTLS_; } void setHasSession() { hasSession_ = true; } bool hasSession() const { return hasSession_; } void setHasResourceBind() { hasResourceBind_ = true; } bool hasResourceBind() const { return hasResourceBind_; } const std::vector& getCompressionMethods() const { return compressionMethods_; } void addCompressionMethod(const std::string& mechanism) { compressionMethods_.push_back(mechanism); } bool hasCompressionMethod(const std::string& mechanism) const; const std::vector& getAuthenticationMechanisms() const { return authenticationMechanisms_; } void addAuthenticationMechanism(const std::string& mechanism) { authenticationMechanisms_.push_back(mechanism); } bool hasAuthenticationMechanism(const std::string& mechanism) const; bool hasAuthenticationMechanisms() const { return !authenticationMechanisms_.empty(); } bool hasStreamManagement() const { return hasStreamManagement_; } void setHasStreamManagement() { hasStreamManagement_ = true; } bool hasRosterVersioning() const { return hasRosterVersioning_; } void setHasRosterVersioning() { hasRosterVersioning_ = true; } private: bool hasStartTLS_; std::vector compressionMethods_; std::vector authenticationMechanisms_; bool hasResourceBind_; bool hasSession_; bool hasStreamManagement_; bool hasRosterVersioning_; }; } swift-im-2.0+dev6/Swiften/Elements/RosterItemExchangePayload.h0000644000175000017500000000335712227051774024311 0ustar kismithkismith/* * Copyright (c) 2011 Jan Kaluza * Licensed under the Simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include #include #include #include #include namespace Swift { class SWIFTEN_API RosterItemExchangePayload : public Payload { public: typedef boost::shared_ptr ref; class SWIFTEN_API Item { public: enum Action { Add, Modify, Delete }; Item(Action action = Add); Action getAction() const { return action; } void setAction(Action action) { this->action = action; } const JID& getJID() const { return jid; } void setJID(const JID& jid) { this->jid = jid; } const std::string& getName() const { return name; } void setName(const std::string& name) { this->name = name; } const std::vector& getGroups() const { return groups; } void setGroups(const std::vector &groups) { this->groups = groups; } void addGroup(const std::string& group) { groups.push_back(group); } private: Action action; JID jid; std::string name; std::vector groups; }; typedef std::vector RosterItemExchangePayloadItems; public: RosterItemExchangePayload(); void addItem(const RosterItemExchangePayload::Item& item) { items_.push_back(item); } const RosterItemExchangePayloadItems& getItems() const { return items_; } private: RosterItemExchangePayloadItems items_; }; } swift-im-2.0+dev6/Swiften/Elements/StreamResume.h0000644000175000017500000000144012227051774021642 0ustar kismithkismith/* * 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 #include #include namespace Swift { class StreamResume : public Element { public: StreamResume(); ~StreamResume(); void setResumeID(const std::string& id) { resumeID = id; } const std::string& getResumeID() const { return resumeID; } const boost::optional getHandledStanzasCount() const { return handledStanzasCount; } void setHandledStanzasCount(unsigned int i) { handledStanzasCount = i; } private: std::string resumeID; boost::optional handledStanzasCount; }; } swift-im-2.0+dev6/Swiften/Elements/DiscoItems.h0000644000175000017500000000234212227051774021273 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include namespace Swift { /** * Service discovery disco#items from XEP-0030. */ class DiscoItems : public Payload { public: /** * A single result item. */ class Item { public: Item(const std::string& name, const JID& jid, const std::string& node="") : name_(name), jid_(jid), node_(node) { } const std::string& getName() const { return name_; } const std::string& getNode() const { return node_; } const JID& getJID() const { return jid_; } private: std::string name_; JID jid_; std::string node_; }; DiscoItems() { } const std::string& getNode() const { return node_; } void setNode(const std::string& node) { node_ = node; } const std::vector& getItems() const { return items_; } void addItem(const Item& item) { items_.push_back(item); } private: std::string node_; std::vector items_; }; } swift-im-2.0+dev6/Swiften/Elements/StreamResumed.h0000644000175000017500000000144312227051774022011 0ustar kismithkismith/* * 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 #include #include namespace Swift { class StreamResumed : public Element { public: StreamResumed(); ~StreamResumed(); void setResumeID(const std::string& id) { resumeID = id; } const std::string& getResumeID() const { return resumeID; } const boost::optional getHandledStanzasCount() const { return handledStanzasCount; } void setHandledStanzasCount(unsigned int i) { handledStanzasCount = i; } private: std::string resumeID; boost::optional handledStanzasCount; }; } swift-im-2.0+dev6/Swiften/Elements/Stanza.cpp0000644000175000017500000000257612227051774021034 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include namespace Swift { Stanza::Stanza() { } Stanza::~Stanza() { payloads_.clear(); } void Stanza::updatePayload(boost::shared_ptr payload) { foreach (boost::shared_ptr& i, payloads_) { if (typeid(*i.get()) == typeid(*payload.get())) { i = payload; return; } } addPayload(payload); } boost::shared_ptr Stanza::getPayloadOfSameType(boost::shared_ptr payload) const { foreach (const boost::shared_ptr& i, payloads_) { if (typeid(*i.get()) == typeid(*payload.get())) { return i; } } return boost::shared_ptr(); } boost::optional Stanza::getTimestamp() const { boost::shared_ptr delay = getPayload(); return delay ? delay->getStamp() : boost::optional(); } boost::optional Stanza::getTimestampFrom(const JID& jid) const { std::vector< boost::shared_ptr > delays = getPayloads(); for (size_t i = 0; i < delays.size(); ++i) { if (delays[i]->getFrom() == jid) { return delays[i]->getStamp(); } } return getTimestamp(); } } swift-im-2.0+dev6/Swiften/Elements/StartSession.h0000644000175000017500000000050512227051774021670 0ustar kismithkismith/* * 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 #include namespace Swift { class StartSession : public Payload { public: StartSession() {} }; } swift-im-2.0+dev6/Swiften/Elements/StreamFeatures.cpp0000644000175000017500000000123512227051774022515 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include namespace Swift { bool StreamFeatures::hasCompressionMethod(const std::string& mechanism) const { return std::find(compressionMethods_.begin(), compressionMethods_.end(), mechanism) != compressionMethods_.end(); } bool StreamFeatures::hasAuthenticationMechanism(const std::string& mechanism) const { return std::find(authenticationMechanisms_.begin(), authenticationMechanisms_.end(), mechanism) != authenticationMechanisms_.end(); } } swift-im-2.0+dev6/Swiften/Elements/AuthRequest.h0000644000175000017500000000225612227051774021506 0ustar kismithkismith/* * 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 #include #include #include #include namespace Swift { class AuthRequest : public Element { public: AuthRequest(const std::string& mechanism = "") : mechanism_(mechanism) { } AuthRequest(const std::string& mechanism, const SafeByteArray& message) : mechanism_(mechanism), message_(message) { } AuthRequest(const std::string& mechanism, const boost::optional& message) : mechanism_(mechanism), message_(message) { } const boost::optional& getMessage() const { return message_; } void setMessage(const SafeByteArray& message) { message_ = boost::optional(message); } const std::string& getMechanism() const { return mechanism_; } void setMechanism(const std::string& mechanism) { mechanism_ = mechanism; } private: std::string mechanism_; boost::optional message_; }; } swift-im-2.0+dev6/Swiften/Elements/Replace.h0000644000175000017500000000114512227051774020603 0ustar kismithkismith/* * Copyright (c) 2011 Vlad Voicu * Licensed under the Simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include #include namespace Swift { class Replace : public Payload { public: typedef boost::shared_ptr ref; Replace(const std::string& id = std::string()) : replaceID_(id) {}; const std::string& getID() const { return replaceID_; } void setID(const std::string& id) { replaceID_ = id; } private: std::string replaceID_; }; } swift-im-2.0+dev6/Swiften/Elements/StatusShow.cpp0000644000175000017500000000043512227051774021710 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include using namespace Swift; StatusShow::StatusShow(const Type& type) : type_(type) { } swift-im-2.0+dev6/Swiften/Elements/MUCItem.h0000644000175000017500000000102012227051774020463 0ustar kismithkismith/* * Copyright (c) 2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include namespace Swift { struct MUCItem { MUCItem() {} boost::optional realJID; boost::optional nick; boost::optional affiliation; boost::optional role; boost::optional actor; boost::optional reason; }; } swift-im-2.0+dev6/Swiften/Elements/FormField.h0000644000175000017500000000614412227051774021103 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ // FIXME: We currently keep 2 values: the raw values, and the actual value. // We should only store the raw values, and deduce the actual values from this #pragma once #include #include #include #include namespace Swift { class FormField { public: typedef boost::shared_ptr ref; virtual ~FormField() {} struct Option { Option(const std::string& label, const std::string& value) : label(label), value(value) {} std::string label; std::string value; }; void setName(const std::string& name) { this->name = name; } const std::string& getName() const { return name; } void setLabel(const std::string& label) { this->label = label; } const std::string& getLabel() const { return label; } void setDescription(const std::string& description) { this->description = description; } const std::string& getDescription() const { return description; } void setRequired(bool required) { this->required = required; } bool getRequired() const { return required; } void addOption(const Option& option) { options.push_back(option); } const std::vector"; StreamInitiationFileInfo::ref fileInfo = boost::make_shared(); fileInfo->setDate(stringToDateTime("1969-07-21T02:56:15Z")); fileInfo->setHash("552da749930852c69ae5d2141d3766b1"); fileInfo->setSize(1022); fileInfo->setName("test.txt"); fileInfo->setDescription("This is a test. If this were a real file..."); fileInfo->setSupportsRangeRequests(true); boost::shared_ptr serializer = boost::make_shared(); CPPUNIT_ASSERT_EQUAL(expected, serializer->serializePayload(fileInfo)); } void testSerialize_StreamInitiationFileInfoRange() { std::string expected = "" "" ""; StreamInitiationFileInfo::ref fileInfo = boost::make_shared(); fileInfo->setHash("552da749930852c69ae5d2141d3766b1"); fileInfo->setSupportsRangeRequests(true); fileInfo->setRangeOffset(270336); boost::shared_ptr serializer = boost::make_shared(); CPPUNIT_ASSERT_EQUAL(expected, serializer->serializePayload(fileInfo)); } // IBB Transport Method Examples // http://xmpp.org/extensions/xep-0261.html#example-1 void testSerialize_Xep0261_Example1() { std::string expected = "" "" "" "" ""; JinglePayload::ref payload = boost::make_shared(); payload->setAction(JinglePayload::SessionInitiate); payload->setSessionID("a73sjjvkla37jfea"); payload->setInitiator(JID("romeo@montague.lit/orchard")); JingleIBBTransportPayload::ref transport = boost::make_shared(); transport->setBlockSize(4096); transport->setSessionID("ch3d9s71"); JingleContentPayload::ref content = boost::make_shared(); content->setCreator(JingleContentPayload::InitiatorCreator); content->setName("ex"); content->addTransport(transport); payload->addPayload(content); CPPUNIT_ASSERT_EQUAL(expected, createTestling()->serialize(payload)); } // http://xmpp.org/extensions/xep-0261.html#example-9 void testSerialize_Xep0261_Example9() { std::string expected = "" "" "" "" ""; JinglePayload::ref payload = boost::make_shared(); payload->setAction(JinglePayload::TransportInfo); payload->setInitiator(JID("romeo@montague.lit/orchard")); payload->setSessionID("a73sjjvkla37jfea"); JingleContentPayload::ref content = boost::make_shared(); content->setCreator(JingleContentPayload::InitiatorCreator); content->setName("ex"); JingleIBBTransportPayload::ref transport = boost::make_shared(); transport->setBlockSize(2048); transport->setSessionID("bt8a71h6"); content->addTransport(transport); payload->addPayload(content); CPPUNIT_ASSERT_EQUAL(expected, createTestling()->serialize(payload)); } // http://xmpp.org/extensions/xep-0261.html#example-13 void testSerialize_Xep0261_Example13() { std::string expected = "" "" ""; JinglePayload::ref payload = boost::make_shared(); payload->setAction(JinglePayload::SessionTerminate); payload->setInitiator(JID("romeo@montague.lit/orchard")); payload->setSessionID("a73sjjvkla37jfea"); payload->setReason(JinglePayload::Reason(JinglePayload::Reason::Success)); CPPUNIT_ASSERT_EQUAL(expected, createTestling()->serialize(payload)); } // http://xmpp.org/extensions/xep-0234.html#example-1 void testSerialize_Xep0234_Example1() { std::string expected = "" "" "" "This is a test. If this were a real file..." "" "" "" ""; JingleFileTransferDescription::ref desc = boost::make_shared(); StreamInitiationFileInfo fileInfo; fileInfo.setDate(stringToDateTime("1969-07-21T02:56:15Z")); fileInfo.setHash("552da749930852c69ae5d2141d3766b1"); fileInfo.setSize(1022); fileInfo.setName("test.txt"); fileInfo.setDescription("This is a test. If this were a real file..."); fileInfo.setSupportsRangeRequests(true); desc->addOffer(fileInfo); CPPUNIT_ASSERT_EQUAL(expected, boost::make_shared()->serialize(desc)); } // http://xmpp.org/extensions/xep-0234.html#example-3 void testSerialize_Xep0234_Example3() { std::string expected = "" "" "" "" "" "This is a test. If this were a real file..." "" "" "" "" /*"" "" "" "" ""*/ "" ""; JinglePayload::ref payload = boost::make_shared(); payload->setAction(JinglePayload::SessionAccept); payload->setInitiator(JID("romeo@montague.lit/orchard")); payload->setSessionID("851ba2"); JingleContentPayload::ref content = boost::make_shared(); content->setCreator(JingleContentPayload::InitiatorCreator); content->setName("a-file-offer"); JingleFileTransferDescription::ref description = boost::make_shared(); StreamInitiationFileInfo fileInfo; fileInfo.setName("test.txt"); fileInfo.setSize(1022); fileInfo.setHash("552da749930852c69ae5d2141d3766b1"); fileInfo.setDate(stringToDateTime("1969-07-21T02:56:15Z")); fileInfo.setDescription("This is a test. If this were a real file..."); fileInfo.setSupportsRangeRequests(true); description->addOffer(fileInfo); content->addDescription(description); payload->addPayload(content); CPPUNIT_ASSERT_EQUAL(expected, createTestling()->serialize(payload)); } // http://xmpp.org/extensions/xep-0234.html#example-5 void testSerialize_Xep0234_Example5() { std::string expected = "" "" /*"" "" ""*/ //"" ""; JinglePayload::ref payload = boost::make_shared(); payload->setAction(JinglePayload::TransportInfo); payload->setInitiator(JID("romeo@montague.lit/orchard")); payload->setSessionID("a73sjjvkla37jfea"); JingleContentPayload::ref content = boost::make_shared(); content->setCreator(JingleContentPayload::InitiatorCreator); content->setName("ex"); payload->addPayload(content); CPPUNIT_ASSERT_EQUAL(expected, createTestling()->serialize(payload)); } // http://xmpp.org/extensions/xep-0234.html#example-8 void testSerialize_Xep0234_Example8() { std::string expected = "" "" "" "" "552da749930852c69ae5d2141d3766b1" "" "" "" ""; JinglePayload::ref payload = boost::make_shared(); payload->setAction(JinglePayload::SessionInfo); payload->setInitiator(JID("romeo@montague.lit/orchard")); payload->setSessionID("a73sjjvkla37jfea"); JingleFileTransferHash::ref hash = boost::make_shared(); hash->setHash("sha-1", "552da749930852c69ae5d2141d3766b1"); payload->addPayload(hash); CPPUNIT_ASSERT_EQUAL(expected, createTestling()->serialize(payload)); } // http://xmpp.org/extensions/xep-0234.html#example-10 void testSerialize_Xep0234_Example10() { std::string expected = "" "" "" "" "" "" "" "" "" /*"" "" "" "" ""*/ "" ""; JinglePayload::ref payload = boost::make_shared(); payload->setAction(JinglePayload::SessionInitiate); payload->setInitiator(JID("romeo@montague.lit/orchard")); payload->setSessionID("uj3b2"); StreamInitiationFileInfo fileInfo; fileInfo.setHash("552da749930852c69ae5d2141d3766b1"); fileInfo.setRangeOffset(270336); JingleFileTransferDescription::ref desc = boost::make_shared(); desc->addRequest(fileInfo); JingleContentPayload::ref content = boost::make_shared(); content->setCreator(JingleContentPayload::InitiatorCreator); content->setName("a-file-request"); content->addDescription(desc); payload->addPayload(content); CPPUNIT_ASSERT_EQUAL(expected, createTestling()->serialize(payload)); } // http://xmpp.org/extensions/xep-0234.html#example-10 void testSerialize_Xep0234_Example13() { std::string expected = "" "" "" "" ""; JinglePayload::ref payload = boost::make_shared(); payload->setAction(JinglePayload::SessionInfo); payload->setInitiator(JID("romeo@montague.lit/orchard")); payload->setSessionID("a73sjjvkla37jfea"); JingleFileTransferReceived::ref received = boost::make_shared(); StreamInitiationFileInfo fileInfo; fileInfo.setHash("a749930852c69ae5d2141d3766b1552d"); received->setFileInfo(fileInfo); payload->addPayload(received); CPPUNIT_ASSERT_EQUAL(expected, createTestling()->serialize(payload)); } // http://xmpp.org/extensions/xep-0260.html#example-1 void testSerialize_Xep0260_Example1() { std::string expected = "" "" "" "" "" "" "" ""; JinglePayload::ref payload = boost::make_shared(); payload->setAction(JinglePayload::SessionInitiate); payload->setInitiator(JID("romeo@montague.lit/orchard")); payload->setSessionID("a73sjjvkla37jfea"); JingleContentPayload::ref content = boost::make_shared(); content->setCreator(JingleContentPayload::InitiatorCreator); content->setName("ex"); JingleS5BTransportPayload::ref transport = boost::make_shared(); transport->setMode(JingleS5BTransportPayload::TCPMode); transport->setSessionID("vj3hs98y"); JingleS5BTransportPayload::Candidate candidate1; candidate1.cid = "hft54dqy"; candidate1.hostPort = HostAddressPort(HostAddress("192.168.4.1"), 5086); candidate1.jid = JID("romeo@montague.lit/orchard"); candidate1.priority = 8257636; candidate1.type = JingleS5BTransportPayload::Candidate::DirectType; transport->addCandidate(candidate1); JingleS5BTransportPayload::Candidate candidate2; candidate2.cid = "hutr46fe"; candidate2.hostPort = HostAddressPort(HostAddress("24.24.24.1"), 5087); candidate2.jid = JID("romeo@montague.lit/orchard"); candidate2.priority = 8258636; candidate2.type = JingleS5BTransportPayload::Candidate::DirectType; transport->addCandidate(candidate2); content->addTransport(transport); payload->addPayload(content); CPPUNIT_ASSERT_EQUAL(expected, createTestling()->serialize(payload)); } private: FullPayloadSerializerCollection collection; }; CPPUNIT_TEST_SUITE_REGISTRATION(JingleSerializersTest); swift-im-2.0+dev6/Swiften/Serializer/PayloadSerializers/UnitTest/StorageSerializerTest.cpp0000644000175000017500000000424012227051774032002 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include using namespace Swift; class StorageSerializerTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(StorageSerializerTest); CPPUNIT_TEST(testSerialize); CPPUNIT_TEST(testSerialize_NoNickOrPassword); CPPUNIT_TEST_SUITE_END(); public: StorageSerializerTest() {} void testSerialize() { PayloadsSerializer serializer; boost::shared_ptr storage(new Storage()); Storage::Room room; room.name = "Council of Oberon"; room.autoJoin = true; room.jid = JID("council@conference.underhill.org"); room.nick = "Puck"; room.password = "MyPass"; storage->addRoom(room); Storage::URL url; url.name = "Complete Works of Shakespeare"; url.url = "http://the-tech.mit.edu/Shakespeare/"; storage->addURL(url); CPPUNIT_ASSERT_EQUAL(std::string( "" "" "Puck" "MyPass" "" "" ""), serializer.serialize(storage)); } void testSerialize_NoNickOrPassword() { PayloadsSerializer serializer; boost::shared_ptr storage(new Storage()); Storage::Room room; room.name = "Council of Oberon"; room.autoJoin = true; room.jid = JID("council@conference.underhill.org"); storage->addRoom(room); CPPUNIT_ASSERT_EQUAL(std::string( "" "" ""), serializer.serialize(storage)); } }; CPPUNIT_TEST_SUITE_REGISTRATION(StorageSerializerTest); ././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootswift-im-2.0+dev6/Swiften/Serializer/PayloadSerializers/UnitTest/RosterItemExchangeSerializerTest.cppswift-im-2.0+dev6/Swiften/Serializer/PayloadSerializers/UnitTest/RosterItemExchangeSerializerTest.cp0000644000175000017500000000316212227051774033760 0ustar kismithkismith/* * Copyright (c) 2011 Jan Kaluza * Licensed under the Simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include #include #include using namespace Swift; class RosterItemExchangeSerializerTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(RosterItemExchangeSerializerTest); CPPUNIT_TEST(testSerialize); CPPUNIT_TEST_SUITE_END(); public: RosterItemExchangeSerializerTest() {} void testSerialize() { RosterItemExchangeSerializer testling; boost::shared_ptr roster(new RosterItemExchangePayload()); RosterItemExchangePayload::Item item1; item1.setJID("foo@bar.com"); item1.setName("Foo @ Bar"); item1.setAction(RosterItemExchangePayload::Item::Add); item1.addGroup("Group 1"); item1.addGroup("Group 2"); roster->addItem(item1); RosterItemExchangePayload::Item item2; item2.setJID("baz@blo.com"); item2.setName("Baz"); item2.setAction(RosterItemExchangePayload::Item::Modify); roster->addItem(item2); std::string expectedResult = "" "" "Group 1" "Group 2" "" "" ""; CPPUNIT_ASSERT_EQUAL(expectedResult, testling.serialize(roster)); } }; CPPUNIT_TEST_SUITE_REGISTRATION(RosterItemExchangeSerializerTest); swift-im-2.0+dev6/Swiften/Serializer/PayloadSerializers/FormSerializer.h0000644000175000017500000000150212227051774026325 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include #include namespace Swift { class SWIFTEN_API FormSerializer : public GenericPayloadSerializer { public: FormSerializer(); virtual std::string serializePayload(boost::shared_ptr) const; private: boost::shared_ptr fieldToXML(boost::shared_ptr field, bool withTypeAttribute) const; void multiLineify(const std::string& text, const std::string& elementName, boost::shared_ptr parent) const; }; } swift-im-2.0+dev6/Swiften/Serializer/PayloadSerializers/WhiteboardSerializer.h0000644000175000017500000000302712227051774027516 0ustar kismithkismith/* * Copyright (c) 2012 Mateusz Piękos * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include #include #include #include #include #include #include #include #include namespace Swift { class WhiteboardElementSerializingVisitor : public WhiteboardElementVisitor { public: void visit(WhiteboardLineElement& line); void visit(WhiteboardFreehandPathElement& path); void visit(WhiteboardRectElement& rect); void visit(WhiteboardPolygonElement& polygon); void visit(WhiteboardTextElement& text); void visit(WhiteboardEllipseElement& ellipse); XMLElement::ref getResult() const; private: std::string alphaToOpacity(int alpha) const; XMLElement::ref element; }; class WhiteboardSerializer : public GenericPayloadSerializer { public: std::string serializePayload(boost::shared_ptr payload) const; private: std::string typeToString(WhiteboardPayload::Type type) const; }; } swift-im-2.0+dev6/Swiften/Serializer/PayloadSerializers/SearchPayloadSerializer.cpp0000644000175000017500000000420512227051774030477 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include namespace Swift { SearchPayloadSerializer::SearchPayloadSerializer() { } std::string SearchPayloadSerializer::serializePayload(boost::shared_ptr searchPayload) const { XMLElement searchElement("query", "jabber:iq:search"); if (searchPayload->getInstructions()) { searchElement.addNode(XMLElement::ref(new XMLElement("instructions", "", *searchPayload->getInstructions()))); } if (searchPayload->getNick()) { searchElement.addNode(XMLElement::ref(new XMLElement("nick", "", *searchPayload->getNick()))); } if (searchPayload->getFirst()) { searchElement.addNode(XMLElement::ref(new XMLElement("first", "", *searchPayload->getFirst()))); } if (searchPayload->getLast()) { searchElement.addNode(XMLElement::ref(new XMLElement("last", "", *searchPayload->getLast()))); } if (searchPayload->getEMail()) { searchElement.addNode(XMLElement::ref(new XMLElement("email", "", *searchPayload->getEMail()))); } foreach(const SearchPayload::Item& item, searchPayload->getItems()) { XMLElement::ref itemElement(new XMLElement("item")); itemElement->setAttribute("jid", item.jid); itemElement->addNode(XMLElement::ref(new XMLElement("first", "", item.first))); itemElement->addNode(XMLElement::ref(new XMLElement("last", "", item.last))); itemElement->addNode(XMLElement::ref(new XMLElement("nick", "", item.nick))); itemElement->addNode(XMLElement::ref(new XMLElement("email", "", item.email))); searchElement.addNode(itemElement); } if (Form::ref form = searchPayload->getForm()) { searchElement.addNode(boost::make_shared(FormSerializer().serialize(form))); } return searchElement.serialize(); } } swift-im-2.0+dev6/Swiften/Serializer/PayloadSerializers/CommandSerializer.h0000644000175000017500000000104112227051774026776 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include namespace Swift { class CommandSerializer : public GenericPayloadSerializer { public: CommandSerializer(); virtual std::string serializePayload(boost::shared_ptr) const; private: std::string actionToString(Command::Action action) const; }; } swift-im-2.0+dev6/Swiften/Serializer/PayloadSerializers/SoftwareVersionSerializer.cpp0000644000175000017500000000153612227051774031124 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { SoftwareVersionSerializer::SoftwareVersionSerializer() : GenericPayloadSerializer() { } std::string SoftwareVersionSerializer::serializePayload(boost::shared_ptr version) const { std::string result(""); if (!version->getName().empty()) { result += "" + version->getName() + ""; } if (!version->getVersion().empty()) { result += "" + version->getVersion() + ""; } if (!version->getOS().empty()) { result += "" + version->getOS() + ""; } result += ""; return result; } } swift-im-2.0+dev6/Swiften/Serializer/PayloadSerializers/MUCUserPayloadSerializer.h0000644000175000017500000000121012227051774030213 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include namespace Swift { class PayloadSerializerCollection; class MUCUserPayloadSerializer : public GenericPayloadSerializer { public: MUCUserPayloadSerializer(PayloadSerializerCollection* serializers); virtual std::string serializePayload(boost::shared_ptr version) const; private: PayloadSerializerCollection* serializers; }; } swift-im-2.0+dev6/Swiften/Serializer/PayloadSerializers/SecurityLabelSerializer.cpp0000644000175000017500000000334212227051774030530 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include namespace Swift { SecurityLabelSerializer::SecurityLabelSerializer() : GenericPayloadSerializer() { } std::string SecurityLabelSerializer::serializePayload(boost::shared_ptr label) const { XMLElement element("securitylabel", "urn:xmpp:sec-label:0"); if (!label->getDisplayMarking().empty()) { boost::shared_ptr displayMarking(new XMLElement("displaymarking")); if (!label->getForegroundColor().empty()) { displayMarking->setAttribute("fgcolor", label->getForegroundColor()); } if (!label->getBackgroundColor().empty()) { displayMarking->setAttribute("bgcolor", label->getBackgroundColor()); } displayMarking->addNode(boost::make_shared(label->getDisplayMarking())); element.addNode(displayMarking); } boost::shared_ptr labelElement(new XMLElement("label")); labelElement->addNode(boost::make_shared(label->getLabel())); element.addNode(labelElement); foreach(const std::string& equivalentLabel, label->getEquivalentLabels()) { boost::shared_ptr equivalentLabelElement(new XMLElement("equivalentlabel")); equivalentLabelElement->addNode(boost::make_shared(equivalentLabel)); element.addNode(equivalentLabelElement); } return element.serialize(); } } swift-im-2.0+dev6/Swiften/Serializer/PayloadSerializers/StatusShowSerializer.h0000644000175000017500000000215112227051774027547 0ustar kismithkismith/* * 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 #include namespace Swift { class StatusShowSerializer : public GenericPayloadSerializer { public: StatusShowSerializer() : GenericPayloadSerializer() {} virtual std::string serializePayload(boost::shared_ptr statusShow) const { if (statusShow->getType () == StatusShow::Online || statusShow->getType() == StatusShow::None) { return ""; } else { std::string result(""); switch (statusShow->getType()) { case StatusShow::Away: result += "away"; break; case StatusShow::XA: result += "xa"; break; case StatusShow::FFC: result += "chat"; break; case StatusShow::DND: result += "dnd"; break; case StatusShow::Online: assert(false); break; case StatusShow::None: assert(false); break; } result += ""; return result; } } }; } swift-im-2.0+dev6/Swiften/Serializer/PayloadSerializers/JingleContentPayloadSerializer.h0000644000175000017500000000124512227051774031503 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include namespace Swift { class PayloadSerializerCollection; class JingleContentPayloadSerializer : public GenericPayloadSerializer { public: JingleContentPayloadSerializer(); virtual std::string serializePayload(boost::shared_ptr) const; private: std::string creatorToString(JingleContentPayload::Creator creator) const; }; } swift-im-2.0+dev6/Swiften/Serializer/PayloadSerializers/InBandRegistrationPayloadSerializer.h0000644000175000017500000000114012227051774032460 0ustar kismithkismith/* * 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 #include #include namespace Swift { class SWIFTEN_API InBandRegistrationPayloadSerializer : public GenericPayloadSerializer { public: InBandRegistrationPayloadSerializer(); virtual std::string serializePayload(boost::shared_ptr) const; }; } swift-im-2.0+dev6/Swiften/Serializer/PayloadSerializers/RosterItemExchangeSerializer.h0000644000175000017500000000111712227051774031164 0ustar kismithkismith/* * Copyright (c) 2011 Jan Kaluza * Licensed under the Simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include #include namespace Swift { class SWIFTEN_API RosterItemExchangeSerializer : public GenericPayloadSerializer { public: RosterItemExchangeSerializer(); virtual std::string serializePayload(boost::shared_ptr) const; }; } swift-im-2.0+dev6/Swiften/Serializer/PayloadSerializers/CapsInfoSerializer.cpp0000644000175000017500000000142112227051774027457 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include namespace Swift { CapsInfoSerializer::CapsInfoSerializer() : GenericPayloadSerializer() { } std::string CapsInfoSerializer::serializePayload(boost::shared_ptr capsInfo) const { XMLElement capsElement("c", "http://jabber.org/protocol/caps"); capsElement.setAttribute("node", capsInfo->getNode()); capsElement.setAttribute("hash", capsInfo->getHash()); capsElement.setAttribute("ver", capsInfo->getVersion()); return capsElement.serialize(); } } swift-im-2.0+dev6/Swiften/Serializer/PayloadSerializers/PrioritySerializer.h0000644000175000017500000000124012227051774027242 0ustar kismithkismith/* * 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 #include #include namespace Swift { class PrioritySerializer : public GenericPayloadSerializer { public: PrioritySerializer() : GenericPayloadSerializer() {} virtual std::string serializePayload(boost::shared_ptr priority) const { return "" + boost::lexical_cast(priority->getPriority()) + ""; } }; } swift-im-2.0+dev6/Swiften/Serializer/PayloadSerializers/CapsInfoSerializer.h0000644000175000017500000000101212227051774027120 0ustar kismithkismith/* * 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 #include #include namespace Swift { class SWIFTEN_API CapsInfoSerializer : public GenericPayloadSerializer { public: CapsInfoSerializer(); virtual std::string serializePayload(boost::shared_ptr) const; }; } swift-im-2.0+dev6/Swiften/Serializer/PayloadSerializers/StreamInitiationSerializer.h0000644000175000017500000000106212227051774030706 0ustar kismithkismith/* * 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 #include #include namespace Swift { class SWIFTEN_API StreamInitiationSerializer : public GenericPayloadSerializer { public: StreamInitiationSerializer(); virtual std::string serializePayload(boost::shared_ptr) const; }; } swift-im-2.0+dev6/Swiften/Serializer/PayloadSerializers/MUCDestroyPayloadSerializer.cpp0000644000175000017500000000211112227051774031262 0ustar kismithkismith/* * Copyright (c) 2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include namespace Swift { MUCDestroyPayloadSerializer::MUCDestroyPayloadSerializer() : GenericPayloadSerializer() { } std::string MUCDestroyPayloadSerializer::serializePayload(boost::shared_ptr payload) const { XMLElement mucElement("destroy", ""); if (!payload->getReason().empty()) { XMLElement::ref reason = boost::make_shared("reason", ""); reason->addNode(boost::make_shared(payload->getReason())); mucElement.addNode(reason); } if (payload->getNewVenue().isValid()) { mucElement.setAttribute("jid", payload->getNewVenue().toString()); } return mucElement.serialize(); } } swift-im-2.0+dev6/Swiften/Serializer/PayloadSerializers/VCardUpdateSerializer.h0000644000175000017500000000103112227051774027561 0ustar kismithkismith/* * 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 #include #include namespace Swift { class SWIFTEN_API VCardUpdateSerializer : public GenericPayloadSerializer { public: VCardUpdateSerializer(); virtual std::string serializePayload(boost::shared_ptr) const; }; } swift-im-2.0+dev6/Swiften/Serializer/PayloadSerializers/ResourceBindSerializer.h0000644000175000017500000000103612227051774030010 0ustar kismithkismith/* * 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 #include #include namespace Swift { class SWIFTEN_API ResourceBindSerializer : public GenericPayloadSerializer { public: ResourceBindSerializer(); virtual std::string serializePayload(boost::shared_ptr) const; }; } swift-im-2.0+dev6/Swiften/Serializer/PayloadSerializers/MUCPayloadSerializer.h0000644000175000017500000000075712227051774027373 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include namespace Swift { class MUCPayloadSerializer : public GenericPayloadSerializer { public: MUCPayloadSerializer(); virtual std::string serializePayload(boost::shared_ptr version) const; }; } swift-im-2.0+dev6/Swiften/Serializer/PayloadSerializers/ChatStateSerializer.cpp0000644000175000017500000000160212227051774027636 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { ChatStateSerializer::ChatStateSerializer() : GenericPayloadSerializer() { } std::string ChatStateSerializer::serializePayload(boost::shared_ptr chatState) const { std::string result("<"); switch (chatState->getChatState()) { case ChatState::Active: result += "active"; break; case ChatState::Composing: result += "composing"; break; case ChatState::Paused: result += "paused"; break; case ChatState::Inactive: result += "inactive"; break; case ChatState::Gone: result += "gone"; break; default: result += "gone"; break; } result += " xmlns=\"http://jabber.org/protocol/chatstates\"/>"; return result; } } swift-im-2.0+dev6/Swiften/Serializer/PayloadSerializers/MUCInvitationPayloadSerializer.cpp0000644000175000017500000000252112227051774031762 0ustar kismithkismith/* * Copyright (c) 2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include namespace Swift { MUCInvitationPayloadSerializer::MUCInvitationPayloadSerializer() : GenericPayloadSerializer() { } std::string MUCInvitationPayloadSerializer::serializePayload(boost::shared_ptr payload) const { XMLElement mucElement("x", "jabber:x:conference"); if (payload->getIsContinuation()) { mucElement.setAttribute("continue", "true"); } if (payload->getJID().isValid()) { mucElement.setAttribute("jid", payload->getJID().toString()); } if (!payload->getPassword().empty()) { mucElement.setAttribute("password", payload->getPassword()); } if (!payload->getReason().empty()) { mucElement.setAttribute("reason", payload->getReason()); } if (!payload->getThread().empty()) { mucElement.setAttribute("thread", payload->getThread()); } return mucElement.serialize(); } } swift-im-2.0+dev6/Swiften/Serializer/PayloadSerializers/DiscoItemsSerializer.h0000644000175000017500000000075012227051774027471 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include namespace Swift { class DiscoItemsSerializer : public GenericPayloadSerializer { public: DiscoItemsSerializer(); virtual std::string serializePayload(boost::shared_ptr) const; }; } swift-im-2.0+dev6/Swiften/Serializer/PayloadSerializers/MUCUserPayloadSerializer.cpp0000644000175000017500000000503012227051774030552 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include namespace Swift { MUCUserPayloadSerializer::MUCUserPayloadSerializer(PayloadSerializerCollection* serializers) : GenericPayloadSerializer(), serializers(serializers) { } std::string MUCUserPayloadSerializer::serializePayload(boost::shared_ptr payload) const { XMLElement mucElement("x", "http://jabber.org/protocol/muc#user"); foreach (const MUCUserPayload::StatusCode statusCode, payload->getStatusCodes()) { boost::shared_ptr statusElement(new XMLElement("status")); std::ostringstream code; code << statusCode.code; statusElement->setAttribute("code", code.str()); mucElement.addNode(statusElement); } foreach (const MUCItem& item, payload->getItems()) { mucElement.addNode(MUCItemSerializer::itemToElement(item)); } if (payload->getPassword()) { boost::shared_ptr passwordElement = boost::make_shared("password"); passwordElement->addNode(boost::make_shared(*payload->getPassword())); } if (payload->getInvite()) { MUCUserPayload::Invite invite = *payload->getInvite(); boost::shared_ptr inviteElement = boost::make_shared("invite"); if (invite.to.isValid()) { inviteElement->setAttribute("to", invite.to.toString()); } if (invite.from.isValid()) { inviteElement->setAttribute("from", invite.from.toString()); } if (!invite.reason.empty()) { boost::shared_ptr reasonElement = boost::make_shared("reason"); reasonElement->addNode(boost::make_shared(invite.reason)); } mucElement.addNode(inviteElement); } boost::shared_ptr childPayload = payload->getPayload(); if (childPayload) { PayloadSerializer* serializer = serializers->getPayloadSerializer(childPayload); if (serializer) { mucElement.addNode(boost::make_shared(serializer->serialize(childPayload))); } } return mucElement.serialize(); } } swift-im-2.0+dev6/Swiften/Serializer/PayloadSerializers/StreamInitiationFileInfoSerializer.h0000644000175000017500000000126312227051774032325 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include #include #include namespace Swift { class PayloadSerializerCollection; class SWIFTEN_API StreamInitiationFileInfoSerializer : public GenericPayloadSerializer { public: StreamInitiationFileInfoSerializer(); virtual std::string serializePayload(boost::shared_ptr) const; }; } swift-im-2.0+dev6/Swiften/Serializer/PayloadSerializers/StartSessionSerializer.h0000644000175000017500000000123712227051774030070 0ustar kismithkismith/* * 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 #include #include namespace Swift { class StartSessionSerializer : public GenericPayloadSerializer { public: StartSessionSerializer() : GenericPayloadSerializer() {} virtual std::string serializePayload(boost::shared_ptr) const { return XMLElement("session", "urn:ietf:params:xml:ns:xmpp-session").serialize(); } }; } swift-im-2.0+dev6/Swiften/Serializer/PayloadSerializers/RosterItemExchangeSerializer.cpp0000644000175000017500000000330512227051774031520 0ustar kismithkismith/* * Copyright (c) 2011 Jan Kaluza * Licensed under the Simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include #include #include #include #include #include #include namespace Swift { RosterItemExchangeSerializer::RosterItemExchangeSerializer() : GenericPayloadSerializer() { } std::string RosterItemExchangeSerializer::serializePayload(boost::shared_ptr roster) const { XMLElement queryElement("x", "http://jabber.org/protocol/rosterx"); foreach(const RosterItemExchangePayload::Item& item, roster->getItems()) { boost::shared_ptr itemElement(new XMLElement("item")); itemElement->setAttribute("jid", item.getJID()); itemElement->setAttribute("name", item.getName()); switch (item.getAction()) { case RosterItemExchangePayload::Item::Add: itemElement->setAttribute("action", "add"); break; case RosterItemExchangePayload::Item::Modify: itemElement->setAttribute("action", "modify"); break; case RosterItemExchangePayload::Item::Delete: itemElement->setAttribute("action", "delete"); break; } foreach(const std::string& group, item.getGroups()) { boost::shared_ptr groupElement(new XMLElement("group")); groupElement->addNode(boost::make_shared(group)); itemElement->addNode(groupElement); } queryElement.addNode(itemElement); } return queryElement.serialize(); } } swift-im-2.0+dev6/Swiften/Serializer/PayloadSerializers/VCardSerializer.cpp0000644000175000017500000001106412227051774026760 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include namespace Swift { VCardSerializer::VCardSerializer() : GenericPayloadSerializer() { } std::string VCardSerializer::serializePayload(boost::shared_ptr vcard) const { XMLElement queryElement("vCard", "vcard-temp"); if (!vcard->getVersion().empty()) { boost::shared_ptr versionElement(new XMLElement("VERSION")); versionElement->addNode(boost::make_shared(vcard->getVersion())); queryElement.addNode(versionElement); } if (!vcard->getFullName().empty()) { boost::shared_ptr fullNameElement(new XMLElement("FN")); fullNameElement->addNode(boost::make_shared(vcard->getFullName())); queryElement.addNode(fullNameElement); } if (!vcard->getGivenName().empty() || !vcard->getFamilyName().empty() || !vcard->getMiddleName().empty() || !vcard->getPrefix().empty() || !vcard->getSuffix().empty()) { boost::shared_ptr nameElement(new XMLElement("N")); if (!vcard->getFamilyName().empty()) { boost::shared_ptr familyNameElement(new XMLElement("FAMILY")); familyNameElement->addNode(boost::make_shared(vcard->getFamilyName())); nameElement->addNode(familyNameElement); } if (!vcard->getGivenName().empty()) { boost::shared_ptr givenNameElement(new XMLElement("GIVEN")); givenNameElement->addNode(boost::make_shared(vcard->getGivenName())); nameElement->addNode(givenNameElement); } if (!vcard->getMiddleName().empty()) { boost::shared_ptr middleNameElement(new XMLElement("MIDDLE")); middleNameElement->addNode(boost::make_shared(vcard->getMiddleName())); nameElement->addNode(middleNameElement); } if (!vcard->getPrefix().empty()) { boost::shared_ptr prefixElement(new XMLElement("PREFIX")); prefixElement->addNode(boost::make_shared(vcard->getPrefix())); nameElement->addNode(prefixElement); } if (!vcard->getSuffix().empty()) { boost::shared_ptr suffixElement(new XMLElement("SUFFIX")); suffixElement->addNode(boost::make_shared(vcard->getSuffix())); nameElement->addNode(suffixElement); } queryElement.addNode(nameElement); } foreach(const VCard::EMailAddress& emailAddress, vcard->getEMailAddresses()) { boost::shared_ptr emailElement(new XMLElement("EMAIL")); boost::shared_ptr userIDElement(new XMLElement("USERID")); userIDElement->addNode(boost::make_shared(emailAddress.address)); emailElement->addNode(userIDElement); if (emailAddress.isHome) { emailElement->addNode(boost::make_shared("HOME")); } if (emailAddress.isWork) { emailElement->addNode(boost::make_shared("WORK")); } if (emailAddress.isInternet) { emailElement->addNode(boost::make_shared("INTERNET")); } if (emailAddress.isPreferred) { emailElement->addNode(boost::make_shared("PREF")); } if (emailAddress.isX400) { emailElement->addNode(boost::make_shared("X400")); } queryElement.addNode(emailElement); } if (!vcard->getNickname().empty()) { boost::shared_ptr nickElement(new XMLElement("NICKNAME")); nickElement->addNode(boost::make_shared(vcard->getNickname())); queryElement.addNode(nickElement); } if (!vcard->getPhoto().empty() || !vcard->getPhotoType().empty()) { XMLElement::ref photoElement(new XMLElement("PHOTO")); if (!vcard->getPhotoType().empty()) { XMLElement::ref typeElement(new XMLElement("TYPE")); typeElement->addNode(XMLTextNode::ref(new XMLTextNode(vcard->getPhotoType()))); photoElement->addNode(typeElement); } if (!vcard->getPhoto().empty()) { XMLElement::ref binvalElement(new XMLElement("BINVAL")); binvalElement->addNode(XMLTextNode::ref(new XMLTextNode(Base64::encode(vcard->getPhoto())))); photoElement->addNode(binvalElement); } queryElement.addNode(photoElement); } if (!vcard->getUnknownContent().empty()) { queryElement.addNode(boost::make_shared(vcard->getUnknownContent())); } return queryElement.serialize(); } } swift-im-2.0+dev6/Swiften/Serializer/PayloadSerializers/DeliveryReceiptRequestSerializer.h0000644000175000017500000000113012227051774032067 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the BSD license. * See http://www.opensource.org/licenses/bsd-license.php for more information. */ #pragma once #include #include #include namespace Swift { class SWIFTEN_API DeliveryReceiptRequestSerializer : public GenericPayloadSerializer { public: DeliveryReceiptRequestSerializer(); virtual std::string serializePayload(boost::shared_ptr request) const; }; } swift-im-2.0+dev6/Swiften/Serializer/PayloadSerializers/JingleS5BTransportPayloadSerializer.cpp0000644000175000017500000000614312227051774032734 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include #include #include #include #include #include #include #include #include namespace Swift { JingleS5BTransportPayloadSerializer::JingleS5BTransportPayloadSerializer() { } std::string JingleS5BTransportPayloadSerializer::serializePayload(boost::shared_ptr payload) const { XMLElement payloadXML("transport", "urn:xmpp:jingle:transports:s5b:1"); payloadXML.setAttribute("sid", payload->getSessionID()); payloadXML.setAttribute("mode", modeToString(payload->getMode())); foreach(JingleS5BTransportPayload::Candidate candidate, payload->getCandidates()) { boost::shared_ptr candidateXML = boost::make_shared("candidate"); candidateXML->setAttribute("cid", candidate.cid); candidateXML->setAttribute("host", candidate.hostPort.getAddress().toString()); candidateXML->setAttribute("jid", candidate.jid.toString()); candidateXML->setAttribute("port", boost::lexical_cast(candidate.hostPort.getPort())); candidateXML->setAttribute("priority", boost::lexical_cast(candidate.priority)); candidateXML->setAttribute("type", typeToString(candidate.type)); payloadXML.addNode(candidateXML); } if (payload->hasCandidateError()) { payloadXML.addNode(boost::make_shared("candidate-error")); } if (payload->hasProxyError()) { payloadXML.addNode(boost::make_shared("proxy-error")); } if (!payload->getActivated().empty()) { boost::shared_ptr activatedXML = boost::make_shared("activated"); activatedXML->setAttribute("cid", payload->getActivated()); payloadXML.addNode(activatedXML); } if (!payload->getCandidateUsed().empty()) { boost::shared_ptr candusedXML = boost::make_shared("candidate-used"); candusedXML->setAttribute("cid", payload->getCandidateUsed()); payloadXML.addNode(candusedXML); } return payloadXML.serialize(); } std::string JingleS5BTransportPayloadSerializer::modeToString(JingleS5BTransportPayload::Mode mode) const { switch(mode) { case JingleS5BTransportPayload::TCPMode: return "tcp"; case JingleS5BTransportPayload::UDPMode: return "udp"; } assert(false); return ""; } std::string JingleS5BTransportPayloadSerializer::typeToString(JingleS5BTransportPayload::Candidate::Type type) const { switch(type) { case JingleS5BTransportPayload::Candidate::AssistedType: return "assisted"; case JingleS5BTransportPayload::Candidate::DirectType: return "direct"; case JingleS5BTransportPayload::Candidate::ProxyType: return "proxy"; case JingleS5BTransportPayload::Candidate::TunnelType: return "tunnel"; } assert(false); return ""; } } swift-im-2.0+dev6/Swiften/Serializer/PayloadSerializers/ReplaceSerializer.h0000644000175000017500000000146712227051774027007 0ustar kismithkismith/* * Copyright (c) 2011 Vlad Voicu * Licensed under the Simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ /* * Copyright (c) 2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include namespace Swift { class ReplaceSerializer : public GenericPayloadSerializer { public: ReplaceSerializer() : GenericPayloadSerializer() {} virtual std::string serializePayload(boost::shared_ptr replace) const { return ""; } }; } swift-im-2.0+dev6/Swiften/Serializer/PayloadSerializers/IBBSerializer.h0000644000175000017500000000070712227051774026024 0ustar kismithkismith/* * 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 #include namespace Swift { class IBBSerializer : public GenericPayloadSerializer { public: IBBSerializer(); virtual std::string serializePayload(boost::shared_ptr) const; }; } swift-im-2.0+dev6/Swiften/Serializer/PayloadSerializers/StatusSerializer.h0000644000175000017500000000144012227051774026706 0ustar kismithkismith/* * 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 #include #include #include #include namespace Swift { class StatusSerializer : public GenericPayloadSerializer { public: StatusSerializer() : GenericPayloadSerializer() {} virtual std::string serializePayload(boost::shared_ptr status) const { XMLElement element("status"); element.addNode(boost::make_shared(status->getText())); return element.serialize(); } }; } swift-im-2.0+dev6/Swiften/Serializer/PayloadSerializers/JingleFileTransferReceivedSerializer.h0000644000175000017500000000117212227051774032611 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include namespace Swift { class PayloadSerializerCollection; class XMLElement; class JingleFileTransferReceivedSerializer : public GenericPayloadSerializer { public: JingleFileTransferReceivedSerializer(); virtual std::string serializePayload(boost::shared_ptr) const; }; } swift-im-2.0+dev6/Swiften/Serializer/StreamFeaturesSerializer.cpp0000644000175000017500000000502712227051774025107 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include namespace Swift { StreamFeaturesSerializer::StreamFeaturesSerializer() { } SafeByteArray StreamFeaturesSerializer::serialize(boost::shared_ptr element) const { boost::shared_ptr streamFeatures(boost::dynamic_pointer_cast(element)); XMLElement streamFeaturesElement("stream:features"); if (streamFeatures->hasStartTLS()) { streamFeaturesElement.addNode(boost::make_shared("starttls", "urn:ietf:params:xml:ns:xmpp-tls")); } if (!streamFeatures->getCompressionMethods().empty()) { boost::shared_ptr compressionElement(new XMLElement("compression", "http://jabber.org/features/compress")); foreach(const std::string& method, streamFeatures->getCompressionMethods()) { boost::shared_ptr methodElement(new XMLElement("method")); methodElement->addNode(boost::make_shared(method)); compressionElement->addNode(methodElement); } streamFeaturesElement.addNode(compressionElement); } if (!streamFeatures->getAuthenticationMechanisms().empty()) { boost::shared_ptr mechanismsElement(new XMLElement("mechanisms", "urn:ietf:params:xml:ns:xmpp-sasl")); foreach(const std::string& mechanism, streamFeatures->getAuthenticationMechanisms()) { boost::shared_ptr mechanismElement(new XMLElement("mechanism")); mechanismElement->addNode(boost::make_shared(mechanism)); mechanismsElement->addNode(mechanismElement); } streamFeaturesElement.addNode(mechanismsElement); } if (streamFeatures->hasResourceBind()) { streamFeaturesElement.addNode(boost::make_shared("bind", "urn:ietf:params:xml:ns:xmpp-bind")); } if (streamFeatures->hasSession()) { streamFeaturesElement.addNode(boost::make_shared("session", "urn:ietf:params:xml:ns:xmpp-session")); } if (streamFeatures->hasStreamManagement()) { streamFeaturesElement.addNode(boost::make_shared("sm", "urn:xmpp:sm:2")); } if (streamFeatures->hasRosterVersioning()) { streamFeaturesElement.addNode(boost::make_shared("ver", "urn:xmpp:features:rosterver")); } return createSafeByteArray(streamFeaturesElement.serialize()); } } swift-im-2.0+dev6/Swiften/Serializer/AuthSuccessSerializer.cpp0000644000175000017500000000165012227051774024405 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include namespace Swift { AuthSuccessSerializer::AuthSuccessSerializer() { } SafeByteArray AuthSuccessSerializer::serialize(boost::shared_ptr element) const { boost::shared_ptr authSuccess(boost::dynamic_pointer_cast(element)); std::string value; boost::optional > message = authSuccess->getValue(); if (message) { if ((*message).empty()) { value = "="; } else { value = Base64::encode(ByteArray(*message)); } } return createSafeByteArray("" + value + ""); } } swift-im-2.0+dev6/Swiften/Serializer/AuthChallengeSerializer.cpp0000644000175000017500000000167612227051774024667 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include namespace Swift { AuthChallengeSerializer::AuthChallengeSerializer() { } SafeByteArray AuthChallengeSerializer::serialize(boost::shared_ptr element) const { boost::shared_ptr authChallenge(boost::dynamic_pointer_cast(element)); std::string value; boost::optional > message = authChallenge->getValue(); if (message) { if ((*message).empty()) { value = "="; } else { value = Base64::encode(ByteArray(*message)); } } return createSafeByteArray("" + value + ""); } } swift-im-2.0+dev6/Swiften/Serializer/MessageSerializer.cpp0000644000175000017500000000167312227051774023544 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include namespace Swift { MessageSerializer::MessageSerializer(PayloadSerializerCollection* payloadSerializers) : GenericStanzaSerializer("message", payloadSerializers) { } void MessageSerializer::setStanzaSpecificAttributesGeneric( boost::shared_ptr message, XMLElement& element) const { if (message->getType() == Message::Chat) { element.setAttribute("type", "chat"); } else if (message->getType() == Message::Groupchat) { element.setAttribute("type", "groupchat"); } else if (message->getType() == Message::Headline) { element.setAttribute("type", "headline"); } else if (message->getType() == Message::Error) { element.setAttribute("type", "error"); } } } swift-im-2.0+dev6/Swiften/Serializer/AuthResponseSerializer.cpp0000644000175000017500000000176012227051774024575 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include namespace Swift { AuthResponseSerializer::AuthResponseSerializer() { } SafeByteArray AuthResponseSerializer::serialize(boost::shared_ptr element) const { boost::shared_ptr authResponse(boost::dynamic_pointer_cast(element)); SafeByteArray value; boost::optional message = authResponse->getValue(); if (message) { if ((*message).empty()) { value = createSafeByteArray("="); } else { value = Base64::encode(*message); } } return concat(createSafeByteArray(""), value, createSafeByteArray("")); } } swift-im-2.0+dev6/Swiften/Serializer/PayloadSerializer.h0000644000175000017500000000101012227051774023177 0ustar kismithkismith/* * 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 #include #include namespace Swift { class Payload; class SWIFTEN_API PayloadSerializer { public: virtual ~PayloadSerializer(); virtual bool canSerialize(boost::shared_ptr) const = 0; virtual std::string serialize(boost::shared_ptr) const = 0; }; } swift-im-2.0+dev6/Swiften/Serializer/StanzaSerializer.cpp0000644000175000017500000000347312227051774023420 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include namespace Swift { StanzaSerializer::StanzaSerializer(const std::string& tag, PayloadSerializerCollection* payloadSerializers) : tag_(tag), payloadSerializers_(payloadSerializers) { } SafeByteArray StanzaSerializer::serialize(boost::shared_ptr element) const { boost::shared_ptr stanza(boost::dynamic_pointer_cast(element)); XMLElement stanzaElement(tag_); if (stanza->getFrom().isValid()) { stanzaElement.setAttribute("from", stanza->getFrom()); } if (stanza->getTo().isValid()) { stanzaElement.setAttribute("to", stanza->getTo()); } if (!stanza->getID().empty()) { stanzaElement.setAttribute("id", stanza->getID()); } setStanzaSpecificAttributes(stanza, stanzaElement); std::string serializedPayloads; foreach (const boost::shared_ptr& payload, stanza->getPayloads()) { PayloadSerializer* serializer = payloadSerializers_->getPayloadSerializer(payload); if (serializer) { serializedPayloads += serializer->serialize(payload); } else { std::cerr << "Could not find serializer for " << typeid(*(payload.get())).name() << std::endl; } } if (!serializedPayloads.empty()) { stanzaElement.addNode(boost::shared_ptr(new XMLRawTextNode(serializedPayloads))); } return createSafeByteArray(stanzaElement.serialize()); } } swift-im-2.0+dev6/Swiften/Serializer/PayloadSerializer.cpp0000644000175000017500000000042512227051774023543 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { PayloadSerializer::~PayloadSerializer() { } } swift-im-2.0+dev6/Swiften/Serializer/AuthFailureSerializer.h0000644000175000017500000000132212227051774024025 0ustar kismithkismith/* * 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 #include #include #include namespace Swift { class AuthFailureSerializer : public GenericElementSerializer { public: AuthFailureSerializer() : GenericElementSerializer() { } virtual SafeByteArray serialize(boost::shared_ptr) const { return createSafeByteArray(XMLElement("failure", "urn:ietf:params:xml:ns:xmpp-sasl").serialize()); } }; } swift-im-2.0+dev6/Swiften/Serializer/StreamFeaturesSerializer.h0000644000175000017500000000110512227051774024545 0ustar kismithkismith/* * 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 #include #include #include namespace Swift { class SWIFTEN_API StreamFeaturesSerializer : public GenericElementSerializer { public: StreamFeaturesSerializer(); virtual SafeByteArray serialize(boost::shared_ptr element) const; }; } swift-im-2.0+dev6/Swiften/Serializer/UnitTest/0000755000175000017500000000000012227051774021172 5ustar kismithkismithswift-im-2.0+dev6/Swiften/Serializer/UnitTest/AuthSuccessSerializerTest.cpp0000644000175000017500000000337512227051774027032 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include using namespace Swift; class AuthSuccessSerializerTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(AuthSuccessSerializerTest); CPPUNIT_TEST(testSerialize); CPPUNIT_TEST(testSerialize_NoMessage); CPPUNIT_TEST(testSerialize_EmptyMessage); CPPUNIT_TEST_SUITE_END(); public: void testSerialize() { AuthSuccessSerializer testling; boost::shared_ptr authSuccess(new AuthSuccess()); authSuccess->setValue(createByteArray("foo")); CPPUNIT_ASSERT_EQUAL(createSafeByteArray( "" "Zm9v" ""), testling.serialize(authSuccess)); } void testSerialize_NoMessage() { AuthSuccessSerializer testling; boost::shared_ptr authSuccess(new AuthSuccess()); CPPUNIT_ASSERT_EQUAL(createSafeByteArray( "" ""), testling.serialize(authSuccess)); } void testSerialize_EmptyMessage() { AuthSuccessSerializer testling; boost::shared_ptr authSuccess(new AuthSuccess()); authSuccess->setValue(std::vector()); CPPUNIT_ASSERT_EQUAL(createSafeByteArray( "" "=" ""), testling.serialize(authSuccess)); } }; CPPUNIT_TEST_SUITE_REGISTRATION(AuthSuccessSerializerTest); swift-im-2.0+dev6/Swiften/Serializer/UnitTest/XMPPSerializerTest.cpp0000644000175000017500000000552212227051774025360 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include using namespace Swift; class XMPPSerializerTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(XMPPSerializerTest); CPPUNIT_TEST(testSerializeHeader_Client); CPPUNIT_TEST(testSerializeHeader_Component); CPPUNIT_TEST(testSerializeHeader_Server); CPPUNIT_TEST_SUITE_END(); public: void setUp() { payloadSerializerCollection = new PayloadSerializerCollection(); } void tearDown() { delete payloadSerializerCollection; } void testSerializeHeader_Client() { boost::shared_ptr testling(createSerializer(ClientStreamType)); ProtocolHeader protocolHeader; protocolHeader.setFrom("bla@foo.com"); protocolHeader.setTo("foo.com"); protocolHeader.setID("myid"); protocolHeader.setVersion("0.99"); CPPUNIT_ASSERT_EQUAL(std::string(""), testling->serializeHeader(protocolHeader)); } void testSerializeHeader_Component() { boost::shared_ptr testling(createSerializer(ComponentStreamType)); ProtocolHeader protocolHeader; protocolHeader.setFrom("bla@foo.com"); protocolHeader.setTo("foo.com"); protocolHeader.setID("myid"); protocolHeader.setVersion("0.99"); CPPUNIT_ASSERT_EQUAL(std::string(""), testling->serializeHeader(protocolHeader)); } void testSerializeHeader_Server() { boost::shared_ptr testling(createSerializer(ServerStreamType)); ProtocolHeader protocolHeader; protocolHeader.setFrom("bla@foo.com"); protocolHeader.setTo("foo.com"); protocolHeader.setID("myid"); protocolHeader.setVersion("0.99"); CPPUNIT_ASSERT_EQUAL(std::string(""), testling->serializeHeader(protocolHeader)); } private: XMPPSerializer* createSerializer(StreamType type) { return new XMPPSerializer(payloadSerializerCollection, type); } private: PayloadSerializerCollection* payloadSerializerCollection; }; CPPUNIT_TEST_SUITE_REGISTRATION(XMPPSerializerTest); swift-im-2.0+dev6/Swiften/Serializer/UnitTest/StreamFeaturesSerializerTest.cpp0000644000175000017500000000367612227051774027536 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include using namespace Swift; class StreamFeaturesSerializerTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(StreamFeaturesSerializerTest); CPPUNIT_TEST(testSerialize); CPPUNIT_TEST_SUITE_END(); public: StreamFeaturesSerializerTest() {} void testSerialize() { StreamFeaturesSerializer testling; boost::shared_ptr streamFeatures(new StreamFeatures()); streamFeatures->setHasStartTLS(); streamFeatures->addCompressionMethod("zlib"); streamFeatures->addCompressionMethod("lzw"); streamFeatures->addAuthenticationMechanism("DIGEST-MD5"); streamFeatures->addAuthenticationMechanism("PLAIN"); streamFeatures->setHasResourceBind(); streamFeatures->setHasSession(); streamFeatures->setHasStreamManagement(); streamFeatures->setHasRosterVersioning(); CPPUNIT_ASSERT_EQUAL(createSafeByteArray( "" "" "" "zlib" "lzw" "" "" "DIGEST-MD5" "PLAIN" "" "" "" "" "" ""), testling.serialize(streamFeatures)); } }; CPPUNIT_TEST_SUITE_REGISTRATION(StreamFeaturesSerializerTest); swift-im-2.0+dev6/Swiften/Serializer/UnitTest/AuthResponseSerializerTest.cpp0000644000175000017500000000342012227051774027207 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include using namespace Swift; class AuthResponseSerializerTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(AuthResponseSerializerTest); CPPUNIT_TEST(testSerialize); CPPUNIT_TEST(testSerialize_NoMessage); CPPUNIT_TEST(testSerialize_EmptyMessage); CPPUNIT_TEST_SUITE_END(); public: void testSerialize() { AuthResponseSerializer testling; boost::shared_ptr authResponse(new AuthResponse()); authResponse->setValue(createSafeByteArray("foo")); CPPUNIT_ASSERT_EQUAL(createSafeByteArray( "" "Zm9v" ""), testling.serialize(authResponse)); } void testSerialize_NoMessage() { AuthResponseSerializer testling; boost::shared_ptr authResponse(new AuthResponse()); CPPUNIT_ASSERT_EQUAL(createSafeByteArray( "" ""), testling.serialize(authResponse)); } void testSerialize_EmptyMessage() { AuthResponseSerializer testling; boost::shared_ptr authResponse(new AuthResponse()); authResponse->setValue(SafeByteArray()); CPPUNIT_ASSERT_EQUAL(createSafeByteArray( "" "=" ""), testling.serialize(authResponse)); } }; CPPUNIT_TEST_SUITE_REGISTRATION(AuthResponseSerializerTest); swift-im-2.0+dev6/Swiften/Serializer/UnitTest/AuthRequestSerializerTest.cpp0000644000175000017500000000346712227051774027054 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include using namespace Swift; class AuthRequestSerializerTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(AuthRequestSerializerTest); CPPUNIT_TEST(testSerialize); CPPUNIT_TEST(testSerialize_NoMessage); CPPUNIT_TEST(testSerialize_EmptyMessage); CPPUNIT_TEST_SUITE_END(); public: void testSerialize() { AuthRequestSerializer testling; boost::shared_ptr authRequest(new AuthRequest("PLAIN")); authRequest->setMessage(createSafeByteArray("foo")); CPPUNIT_ASSERT_EQUAL(createSafeByteArray( "" "Zm9v" ""), testling.serialize(authRequest)); } void testSerialize_NoMessage() { AuthRequestSerializer testling; boost::shared_ptr authRequest(new AuthRequest("PLAIN")); CPPUNIT_ASSERT_EQUAL(createSafeByteArray( "" ""), testling.serialize(authRequest)); } void testSerialize_EmptyMessage() { AuthRequestSerializer testling; boost::shared_ptr authRequest(new AuthRequest("PLAIN")); authRequest->setMessage(SafeByteArray()); CPPUNIT_ASSERT_EQUAL(createSafeByteArray( "" "=" ""), testling.serialize(authRequest)); } }; CPPUNIT_TEST_SUITE_REGISTRATION(AuthRequestSerializerTest); swift-im-2.0+dev6/Swiften/Serializer/UnitTest/AuthChallengeSerializerTest.cpp0000644000175000017500000000346512227051774027304 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include using namespace Swift; class AuthChallengeSerializerTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(AuthChallengeSerializerTest); CPPUNIT_TEST(testSerialize); CPPUNIT_TEST(testSerialize_NoMessage); CPPUNIT_TEST(testSerialize_EmptyMessage); CPPUNIT_TEST_SUITE_END(); public: void testSerialize() { AuthChallengeSerializer testling; boost::shared_ptr authChallenge(new AuthChallenge()); authChallenge->setValue(createByteArray("foo")); CPPUNIT_ASSERT_EQUAL(createSafeByteArray( "" "Zm9v" ""), testling.serialize(authChallenge)); } void testSerialize_NoMessage() { AuthChallengeSerializer testling; boost::shared_ptr authChallenge(new AuthChallenge()); CPPUNIT_ASSERT_EQUAL(createSafeByteArray( "" ""), testling.serialize(authChallenge)); } void testSerialize_EmptyMessage() { AuthChallengeSerializer testling; boost::shared_ptr authChallenge(new AuthChallenge()); authChallenge->setValue(std::vector()); CPPUNIT_ASSERT_EQUAL(createSafeByteArray( "" "=" ""), testling.serialize(authChallenge)); } }; CPPUNIT_TEST_SUITE_REGISTRATION(AuthChallengeSerializerTest); swift-im-2.0+dev6/Swiften/Serializer/PayloadSerializerCollection.cpp0000644000175000017500000000203712227051774025560 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include namespace Swift { PayloadSerializerCollection::PayloadSerializerCollection() { } void PayloadSerializerCollection::addSerializer(PayloadSerializer* serializer) { serializers_.push_back(serializer); } void PayloadSerializerCollection::removeSerializer(PayloadSerializer* serializer) { serializers_.erase(remove(serializers_.begin(), serializers_.end(), serializer), serializers_.end()); } PayloadSerializer* PayloadSerializerCollection::getPayloadSerializer(boost::shared_ptr payload) const { std::vector::const_iterator i = std::find_if( serializers_.begin(), serializers_.end(), boost::bind(&PayloadSerializer::canSerialize, _1, payload)); return (i != serializers_.end() ? *i : NULL); } } swift-im-2.0+dev6/Swiften/Serializer/StartTLSFailureSerializer.h0000644000175000017500000000134512227051774024611 0ustar kismithkismith/* * 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 #include #include #include namespace Swift { class StartTLSFailureSerializer : public GenericElementSerializer { public: StartTLSFailureSerializer() : GenericElementSerializer() { } virtual SafeByteArray serialize(boost::shared_ptr) const { return createSafeByteArray(XMLElement("failure", "urn:ietf:params:xml:ns:xmpp-tls").serialize()); } }; } swift-im-2.0+dev6/Swiften/Serializer/ComponentHandshakeSerializer.h0000644000175000017500000000105312227051774025366 0ustar kismithkismith/* * 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 #include #include namespace Swift { class ComponentHandshakeSerializer : public GenericElementSerializer { public: ComponentHandshakeSerializer(); virtual SafeByteArray serialize(boost::shared_ptr element) const; }; } swift-im-2.0+dev6/Swiften/Serializer/ElementSerializer.cpp0000644000175000017500000000042512227051774023543 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { ElementSerializer::~ElementSerializer() { } } swift-im-2.0+dev6/Swiften/Serializer/XMPPSerializer.cpp0000644000175000017500000001162112227051774022736 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Swift { XMPPSerializer::XMPPSerializer(PayloadSerializerCollection* payloadSerializers, StreamType type) : type_(type) { serializers_.push_back(boost::make_shared(payloadSerializers)); serializers_.push_back(boost::make_shared(payloadSerializers)); serializers_.push_back(boost::make_shared(payloadSerializers)); serializers_.push_back(boost::make_shared()); serializers_.push_back(boost::make_shared()); serializers_.push_back(boost::make_shared()); serializers_.push_back(boost::make_shared()); serializers_.push_back(boost::make_shared()); serializers_.push_back(boost::make_shared()); serializers_.push_back(boost::make_shared()); serializers_.push_back(boost::make_shared()); serializers_.push_back(boost::make_shared()); serializers_.push_back(boost::make_shared()); serializers_.push_back(boost::make_shared()); serializers_.push_back(boost::make_shared()); serializers_.push_back(boost::make_shared()); serializers_.push_back(boost::make_shared()); serializers_.push_back(boost::make_shared()); serializers_.push_back(boost::make_shared()); serializers_.push_back(boost::make_shared()); serializers_.push_back(boost::make_shared()); serializers_.push_back(boost::make_shared()); serializers_.push_back(boost::make_shared()); } std::string XMPPSerializer::serializeHeader(const ProtocolHeader& header) const { std::string result = " element) const { std::vector< boost::shared_ptr >::const_iterator i = std::find_if(serializers_.begin(), serializers_.end(), boost::bind(&ElementSerializer::canSerialize, _1, element)); if (i != serializers_.end()) { return (*i)->serialize(element); } else { std::cerr << "Could not find serializer for " << typeid(*(element.get())).name() << std::endl; return createSafeByteArray(""); } } std::string XMPPSerializer::serializeFooter() const { return ""; } std::string XMPPSerializer::getDefaultNamespace() const { switch (type_) { case ClientStreamType: return "jabber:client"; case ServerStreamType: return "jabber:server"; case ComponentStreamType: return "jabber:component:accept"; } assert(false); return ""; } } swift-im-2.0+dev6/Swiften/Serializer/StanzaAckRequestSerializer.h0000644000175000017500000000132212227051774025044 0ustar kismithkismith/* * 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 #include #include #include namespace Swift { class StanzaAckRequestSerializer : public GenericElementSerializer { public: StanzaAckRequestSerializer() : GenericElementSerializer() { } virtual SafeByteArray serialize(boost::shared_ptr) const { return createSafeByteArray(XMLElement("r", "urn:xmpp:sm:2").serialize()); } }; } swift-im-2.0+dev6/Swiften/Serializer/StreamResumeSerializer.cpp0000644000175000017500000000166312227051774024573 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include using namespace Swift; StreamResumeSerializer::StreamResumeSerializer() : GenericElementSerializer() { } SafeByteArray StreamResumeSerializer::serialize(boost::shared_ptr el) const { boost::shared_ptr e(boost::dynamic_pointer_cast(el)); XMLElement element("resume", "urn:xmpp:sm:2"); element.setAttribute("previd", e->getResumeID()); if (e->getHandledStanzasCount()) { element.setAttribute("h", boost::lexical_cast(e->getHandledStanzasCount())); } return createSafeByteArray(element.serialize()); } swift-im-2.0+dev6/Swiften/Serializer/XML/0000755000175000017500000000000012227051774020053 5ustar kismithkismithswift-im-2.0+dev6/Swiften/Serializer/XML/XMLRawTextNode.h0000644000175000017500000000067512227051774023021 0ustar kismithkismith/* * 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 namespace Swift { class XMLRawTextNode : public XMLNode { public: XMLRawTextNode(const std::string& text) : text_(text) { } std::string serialize() { return text_; } private: std::string text_; }; } swift-im-2.0+dev6/Swiften/Serializer/XML/XMLElement.cpp0000644000175000017500000000275512227051774022542 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include namespace Swift { XMLElement::XMLElement(const std::string& tag, const std::string& xmlns, const std::string& text) : tag_(tag) { if (!xmlns.empty()) { setAttribute("xmlns", xmlns); } if (!text.empty()) { addNode(XMLTextNode::ref(new XMLTextNode(text))); } } std::string XMLElement::serialize() { std::string result; result += "<" + tag_; typedef std::pair Pair; foreach(const Pair& p, attributes_) { result += " " + p.first + "=\"" + p.second + "\""; } if (!childNodes_.empty()) { result += ">"; foreach (boost::shared_ptr node, childNodes_) { result += node->serialize(); } result += ""; } else { result += "/>"; } return result; } void XMLElement::setAttribute(const std::string& attribute, const std::string& value) { std::string escapedValue(value); String::replaceAll(escapedValue, '&', "&"); String::replaceAll(escapedValue, '<', "<"); String::replaceAll(escapedValue, '>', ">"); String::replaceAll(escapedValue, '\'', "'"); String::replaceAll(escapedValue, '"', """); attributes_[attribute] = escapedValue; } void XMLElement::addNode(boost::shared_ptr node) { childNodes_.push_back(node); } } swift-im-2.0+dev6/Swiften/Serializer/XML/UnitTest/0000755000175000017500000000000012227051774021632 5ustar kismithkismithswift-im-2.0+dev6/Swiften/Serializer/XML/UnitTest/XMLElementTest.cpp0000644000175000017500000000420012227051774025144 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include using namespace Swift; class XMLElementTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(XMLElementTest); CPPUNIT_TEST(testSerialize); CPPUNIT_TEST(testSerialize_NoChildren); CPPUNIT_TEST(testSerialize_SpecialAttributeCharacters); CPPUNIT_TEST(testSerialize_EmptyAttributeValue); CPPUNIT_TEST_SUITE_END(); public: XMLElementTest() {} void testSerialize() { XMLElement testling("foo", "http://example.com"); testling.setAttribute("myatt", "myval"); boost::shared_ptr barElement(new XMLElement("bar")); barElement->addNode(boost::make_shared("Blo")); testling.addNode(barElement); boost::shared_ptr bazElement(new XMLElement("baz")); bazElement->addNode(boost::make_shared("Bli&")); testling.addNode(bazElement); std::string result = testling.serialize(); std::string expectedResult = "" "Blo" "Bli&</stream>" ""; CPPUNIT_ASSERT_EQUAL(expectedResult, result); } void testSerialize_NoChildren() { XMLElement testling("foo", "http://example.com"); CPPUNIT_ASSERT_EQUAL(std::string(""), testling.serialize()); } void testSerialize_SpecialAttributeCharacters() { XMLElement testling("foo"); testling.setAttribute("myatt", "<\"'&>"); CPPUNIT_ASSERT_EQUAL(std::string(""), testling.serialize()); } void testSerialize_EmptyAttributeValue() { XMLElement testling("foo"); testling.setAttribute("myatt", ""); CPPUNIT_ASSERT_EQUAL(std::string(""), testling.serialize()); } }; CPPUNIT_TEST_SUITE_REGISTRATION(XMLElementTest); swift-im-2.0+dev6/Swiften/Serializer/XML/XMLNode.cpp0000644000175000017500000000037312227051774022030 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { XMLNode::~XMLNode() { } } swift-im-2.0+dev6/Swiften/Serializer/XML/XMLTextNode.h0000644000175000017500000000137712227051774022347 0ustar kismithkismith/* * 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 #include namespace Swift { class XMLTextNode : public XMLNode { public: typedef boost::shared_ptr ref; XMLTextNode(const std::string& text) : text_(text) { String::replaceAll(text_, '&', "&"); // Should come first String::replaceAll(text_, '<', "<"); String::replaceAll(text_, '>', ">"); } std::string serialize() { return text_; } static ref create(const std::string& text) { return ref(new XMLTextNode(text)); } private: std::string text_; }; } swift-im-2.0+dev6/Swiften/Serializer/XML/XMLNode.h0000644000175000017500000000053712227051774021477 0ustar kismithkismith/* * 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 #include namespace Swift { class SWIFTEN_API XMLNode { public: virtual ~XMLNode(); virtual std::string serialize() = 0; }; } swift-im-2.0+dev6/Swiften/Serializer/XML/XMLElement.h0000644000175000017500000000155012227051774022177 0ustar kismithkismith/* * 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 #include #include #include #include #include namespace Swift { class SWIFTEN_API XMLElement : public XMLNode { public: typedef boost::shared_ptr ref; XMLElement(const std::string& tag, const std::string& xmlns = "", const std::string& text = ""); void setAttribute(const std::string& attribute, const std::string& value); void addNode(boost::shared_ptr node); virtual std::string serialize(); private: std::string tag_; std::map attributes_; std::vector< boost::shared_ptr > childNodes_; }; } swift-im-2.0+dev6/Swiften/Serializer/StanzaSerializer.h0000644000175000017500000000140212227051774023053 0ustar kismithkismith/* * 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 #include #include namespace Swift { class PayloadSerializerCollection; class XMLElement; class StanzaSerializer : public ElementSerializer { public: StanzaSerializer(const std::string& tag, PayloadSerializerCollection* payloadSerializers); virtual SafeByteArray serialize(boost::shared_ptr) const; virtual void setStanzaSpecificAttributes(boost::shared_ptr, XMLElement&) const = 0; private: std::string tag_; PayloadSerializerCollection* payloadSerializers_; }; } swift-im-2.0+dev6/Swiften/Serializer/AuthChallengeSerializer.h0000644000175000017500000000110112227051774024313 0ustar kismithkismith/* * 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 #include #include #include namespace Swift { class SWIFTEN_API AuthChallengeSerializer : public GenericElementSerializer { public: AuthChallengeSerializer(); virtual SafeByteArray serialize(boost::shared_ptr element) const; }; } swift-im-2.0+dev6/Swiften/Serializer/PayloadSerializerCollection.h0000644000175000017500000000125512227051774025226 0ustar kismithkismith/* * 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 #include #include #include namespace Swift { class PayloadSerializer; class SWIFTEN_API PayloadSerializerCollection { public: PayloadSerializerCollection(); void addSerializer(PayloadSerializer* factory); void removeSerializer(PayloadSerializer* factory); PayloadSerializer* getPayloadSerializer(boost::shared_ptr) const; private: std::vector serializers_; }; } swift-im-2.0+dev6/Swiften/Serializer/CompressFailureSerializer.h0000644000175000017500000000135112227051774024721 0ustar kismithkismith/* * 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 #include #include #include namespace Swift { class CompressFailureSerializer : public GenericElementSerializer { public: CompressFailureSerializer() : GenericElementSerializer() { } virtual SafeByteArray serialize(boost::shared_ptr) const { return createSafeByteArray(XMLElement("failure", "http://jabber.org/protocol/compress").serialize()); } }; } swift-im-2.0+dev6/Swiften/Serializer/StreamErrorSerializer.h0000644000175000017500000000075312227051774024070 0ustar kismithkismith/* * 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 #include namespace Swift { class StreamErrorSerializer : public GenericElementSerializer { public: StreamErrorSerializer(); virtual SafeByteArray serialize(boost::shared_ptr error) const; }; } swift-im-2.0+dev6/Swiften/Serializer/StanzaAckSerializer.h0000644000175000017500000000171712227051774023503 0ustar kismithkismith/* * 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 #include #include #include #include namespace Swift { class StanzaAckSerializer : public GenericElementSerializer { public: StanzaAckSerializer() : GenericElementSerializer() { } virtual SafeByteArray serialize(boost::shared_ptr element) const { StanzaAck::ref stanzaAck(boost::dynamic_pointer_cast(element)); assert(stanzaAck->isValid()); XMLElement result("a", "urn:xmpp:sm:2"); result.setAttribute("h", std::string(boost::lexical_cast(stanzaAck->getHandledStanzasCount()))); return createSafeByteArray(result.serialize()); } }; } swift-im-2.0+dev6/Swiften/Serializer/PresenceSerializer.h0000644000175000017500000000111412227051774023357 0ustar kismithkismith/* * 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 #include namespace Swift { class PresenceSerializer : public GenericStanzaSerializer { public: PresenceSerializer(PayloadSerializerCollection* payloadSerializers); private: virtual void setStanzaSpecificAttributesGeneric( boost::shared_ptr presence, XMLElement& element) const; }; } swift-im-2.0+dev6/Swiften/Serializer/StreamErrorSerializer.cpp0000644000175000017500000000544512227051774024426 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include namespace Swift { StreamErrorSerializer::StreamErrorSerializer() : GenericElementSerializer() { } SafeByteArray StreamErrorSerializer::serialize(boost::shared_ptr element) const { StreamError::ref error = boost::dynamic_pointer_cast(element); XMLElement errorElement("error", "http://etherx.jabber.org/streams"); std::string typeTag; switch (error->getType()) { case StreamError::BadFormat: typeTag = "bad-format"; break; case StreamError::BadNamespacePrefix: typeTag = "bad-namespace-prefix"; break; case StreamError::Conflict: typeTag = "conflict"; break; case StreamError::ConnectionTimeout: typeTag = "connection-timeout"; break; case StreamError::HostGone: typeTag = "host-gone"; break; case StreamError::HostUnknown: typeTag = "host-unknown"; break; case StreamError::ImproperAddressing: typeTag = "improper-addressing"; break; case StreamError::InternalServerError: typeTag = "internal-server-error"; break; case StreamError::InvalidFrom: typeTag = "invalid-from"; break; case StreamError::InvalidID: typeTag = "invalid-id"; break; case StreamError::InvalidNamespace: typeTag = "invalid-namespace"; break; case StreamError::InvalidXML: typeTag = "invalid-xml"; break; case StreamError::NotAuthorized: typeTag = "not-authorized"; break; case StreamError::NotWellFormed: typeTag = "not-well-formed"; break; case StreamError::PolicyViolation: typeTag = "policy-violation"; break; case StreamError::RemoteConnectionFailed: typeTag = "remote-connection-failed"; break; case StreamError::Reset: typeTag = "reset"; break; case StreamError::ResourceConstraint: typeTag = "resource-constraint"; break; case StreamError::RestrictedXML: typeTag = "restricted-xml"; break; case StreamError::SeeOtherHost: typeTag = "see-other-host"; break; case StreamError::SystemShutdown: typeTag = "system-shutdown"; break; case StreamError::UndefinedCondition: typeTag = "undefined-condition"; break; case StreamError::UnsupportedEncoding: typeTag = "unsupported-encoding"; break; case StreamError::UnsupportedStanzaType: typeTag = "unsupported-stanza-type"; break; case StreamError::UnsupportedVersion: typeTag = "unsupported-version"; break; } errorElement.addNode(boost::make_shared(typeTag, "urn:ietf:params:xml:ns:xmpp-streams")); if (!error->getText().empty()) { errorElement.addNode(boost::make_shared("text", "urn:ietf:params:xml:ns:xmpp-streams", error->getText())); } return createSafeByteArray(errorElement.serialize()); } } swift-im-2.0+dev6/Swiften/Serializer/StreamResumedSerializer.cpp0000644000175000017500000000167412227051774024741 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include using namespace Swift; StreamResumedSerializer::StreamResumedSerializer() : GenericElementSerializer() { } SafeByteArray StreamResumedSerializer::serialize(boost::shared_ptr el) const { boost::shared_ptr e(boost::dynamic_pointer_cast(el)); XMLElement element("resumed", "urn:xmpp:sm:2"); element.setAttribute("previd", e->getResumeID()); if (e->getHandledStanzasCount()) { element.setAttribute("h", boost::lexical_cast(e->getHandledStanzasCount())); } return createSafeByteArray(element.serialize()); } swift-im-2.0+dev6/Swiften/Serializer/TLSProceedSerializer.h0000644000175000017500000000131412227051774023561 0ustar kismithkismith/* * 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 #include #include #include namespace Swift { class TLSProceedSerializer : public GenericElementSerializer { public: TLSProceedSerializer() : GenericElementSerializer() { } virtual SafeByteArray serialize(boost::shared_ptr) const { return createSafeByteArray(XMLElement("proceed", "urn:ietf:params:xml:ns:xmpp-tls").serialize()); } }; } swift-im-2.0+dev6/Swiften/VCards/0000755000175000017500000000000012227051774016464 5ustar kismithkismithswift-im-2.0+dev6/Swiften/VCards/SConscript0000644000175000017500000000024012227051774020472 0ustar kismithkismithImport("swiften_env") objects = swiften_env.SwiftenObject([ "VCardManager.cpp", "VCardStorage.cpp", ]) swiften_env.Append(SWIFTEN_OBJECTS = [objects]) swift-im-2.0+dev6/Swiften/VCards/VCardStorage.h0000644000175000017500000000107212227051774021161 0ustar kismithkismith/* * 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 #include #include #include namespace Swift { class JID; class SWIFTEN_API VCardStorage { public: virtual ~VCardStorage(); virtual VCard::ref getVCard(const JID& jid) const = 0; virtual void setVCard(const JID&, VCard::ref) = 0; virtual std::string getPhotoHash(const JID&) const; }; } swift-im-2.0+dev6/Swiften/VCards/VCardStorage.cpp0000644000175000017500000000107212227051774021514 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include namespace Swift { VCardStorage::~VCardStorage() { } std::string VCardStorage::getPhotoHash(const JID& jid) const { VCard::ref vCard = getVCard(jid); if (vCard && !vCard->getPhoto().empty()) { return Hexify::hexify(SHA1::getHash(vCard->getPhoto())); } else { return ""; } } } swift-im-2.0+dev6/Swiften/VCards/VCardManager.cpp0000644000175000017500000000445512227051774021472 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include namespace Swift { VCardManager::VCardManager(const JID& ownJID, IQRouter* iqRouter, VCardStorage* vcardStorage) : ownJID(ownJID), iqRouter(iqRouter), storage(vcardStorage) { } VCardManager::~VCardManager() { } VCard::ref VCardManager::getVCard(const JID& jid) const { return storage->getVCard(jid); } VCard::ref VCardManager::getVCardAndRequestWhenNeeded(const JID& jid) { VCard::ref vcard = storage->getVCard(jid); if (!vcard) { requestVCard(jid); } return vcard; } void VCardManager::requestVCard(const JID& requestedJID) { JID jid = requestedJID.equals(ownJID, JID::WithoutResource) ? JID() : requestedJID; if (requestedVCards.find(jid) != requestedVCards.end()) { return; } GetVCardRequest::ref request = GetVCardRequest::create(jid, iqRouter); request->onResponse.connect(boost::bind(&VCardManager::handleVCardReceived, this, jid, _1, _2)); request->send(); requestedVCards.insert(jid); } void VCardManager::requestOwnVCard() { requestVCard(JID()); } void VCardManager::handleVCardReceived(const JID& actualJID, VCard::ref vcard, ErrorPayload::ref error) { if (error || !vcard) { vcard = VCard::ref(new VCard()); } requestedVCards.erase(actualJID); JID jid = actualJID.isValid() ? actualJID : ownJID.toBare(); setVCard(jid, vcard); } SetVCardRequest::ref VCardManager::createSetVCardRequest(VCard::ref vcard) { SetVCardRequest::ref request = SetVCardRequest::create(vcard, iqRouter); request->onResponse.connect(boost::bind(&VCardManager::handleSetVCardResponse, this, vcard, _2)); return request; } void VCardManager::handleSetVCardResponse(VCard::ref vcard, ErrorPayload::ref error) { if (!error) { setVCard(ownJID.toBare(), vcard); } } void VCardManager::setVCard(const JID& jid, VCard::ref vcard) { storage->setVCard(jid, vcard); onVCardChanged(jid, vcard); if (jid.compare(ownJID, JID::WithoutResource) == 0) { onOwnVCardChanged(vcard); } } std::string VCardManager::getPhotoHash(const JID& jid) const { return storage->getPhotoHash(jid); } } swift-im-2.0+dev6/Swiften/VCards/UnitTest/0000755000175000017500000000000012227051774020243 5ustar kismithkismithswift-im-2.0+dev6/Swiften/VCards/UnitTest/VCardManagerTest.cpp0000644000175000017500000001752512227051774024113 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include using namespace Swift; class VCardManagerTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(VCardManagerTest); CPPUNIT_TEST(testGet_NewVCardRequestsVCard); CPPUNIT_TEST(testGet_ExistingVCard); CPPUNIT_TEST(testRequest_RequestsVCard); CPPUNIT_TEST(testRequest_ReceiveEmitsNotification); CPPUNIT_TEST(testRequest_Error); CPPUNIT_TEST(testRequest_VCardAlreadyRequested); CPPUNIT_TEST(testRequest_AfterPreviousRequest); CPPUNIT_TEST(testRequestOwnVCard); CPPUNIT_TEST(testCreateSetVCardRequest); CPPUNIT_TEST(testCreateSetVCardRequest_Error); CPPUNIT_TEST_SUITE_END(); public: void setUp() { ownJID = JID("baz@fum.com/dum"); stanzaChannel = new DummyStanzaChannel(); iqRouter = new IQRouter(stanzaChannel); vcardStorage = new VCardMemoryStorage(); } void tearDown() { delete vcardStorage; delete iqRouter; delete stanzaChannel; } void testGet_NewVCardRequestsVCard() { boost::shared_ptr testling = createManager(); VCard::ref result = testling->getVCardAndRequestWhenNeeded(JID("foo@bar.com/baz")); CPPUNIT_ASSERT(!result); CPPUNIT_ASSERT_EQUAL(1, static_cast(stanzaChannel->sentStanzas.size())); CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex(0, JID("foo@bar.com/baz"), IQ::Get)); } void testGet_ExistingVCard() { boost::shared_ptr testling = createManager(); VCard::ref vcard(new VCard()); vcard->setFullName("Foo Bar"); vcardStorage->setVCard(JID("foo@bar.com/baz"), vcard); VCard::ref result = testling->getVCardAndRequestWhenNeeded(JID("foo@bar.com/baz")); CPPUNIT_ASSERT_EQUAL(std::string("Foo Bar"), result->getFullName()); CPPUNIT_ASSERT_EQUAL(0, static_cast(stanzaChannel->sentStanzas.size())); } void testRequest_RequestsVCard() { boost::shared_ptr testling = createManager(); testling->requestVCard(JID("foo@bar.com/baz")); CPPUNIT_ASSERT_EQUAL(1, static_cast(stanzaChannel->sentStanzas.size())); CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex(0, JID("foo@bar.com/baz"), IQ::Get)); } void testRequest_ReceiveEmitsNotification() { boost::shared_ptr testling = createManager(); testling->requestVCard(JID("foo@bar.com/baz")); stanzaChannel->onIQReceived(createVCardResult()); CPPUNIT_ASSERT_EQUAL(1, static_cast(changes.size())); CPPUNIT_ASSERT_EQUAL(JID("foo@bar.com/baz"), changes[0].first); CPPUNIT_ASSERT_EQUAL(std::string("Foo Bar"), changes[0].second->getFullName()); CPPUNIT_ASSERT_EQUAL(std::string("Foo Bar"), vcardStorage->getVCard(JID("foo@bar.com/baz"))->getFullName()); CPPUNIT_ASSERT_EQUAL(0, static_cast(ownChanges.size())); } void testRequest_Error() { boost::shared_ptr testling = createManager(); testling->requestVCard(JID("foo@bar.com/baz")); stanzaChannel->onIQReceived(IQ::createError(JID("baz@fum.com/foo"), stanzaChannel->sentStanzas[0]->getTo(), stanzaChannel->sentStanzas[0]->getID())); CPPUNIT_ASSERT_EQUAL(1, static_cast(changes.size())); CPPUNIT_ASSERT_EQUAL(JID("foo@bar.com/baz"), changes[0].first); CPPUNIT_ASSERT_EQUAL(std::string(""), changes[0].second->getFullName()); CPPUNIT_ASSERT_EQUAL(std::string(""), vcardStorage->getVCard(JID("foo@bar.com/baz"))->getFullName()); } void testRequest_VCardAlreadyRequested() { boost::shared_ptr testling = createManager(); testling->requestVCard(JID("foo@bar.com/baz")); VCard::ref result = testling->getVCardAndRequestWhenNeeded(JID("foo@bar.com/baz")); CPPUNIT_ASSERT(!result); CPPUNIT_ASSERT_EQUAL(1, static_cast(stanzaChannel->sentStanzas.size())); } void testRequest_AfterPreviousRequest() { boost::shared_ptr testling = createManager(); testling->requestVCard(JID("foo@bar.com/baz")); stanzaChannel->onIQReceived(createVCardResult()); testling->requestVCard(JID("foo@bar.com/baz")); CPPUNIT_ASSERT_EQUAL(2, static_cast(stanzaChannel->sentStanzas.size())); CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex(1, JID("foo@bar.com/baz"), IQ::Get)); } void testRequestOwnVCard() { boost::shared_ptr testling = createManager(); testling->requestVCard(ownJID); stanzaChannel->onIQReceived(createOwnVCardResult()); CPPUNIT_ASSERT_EQUAL(1, static_cast(stanzaChannel->sentStanzas.size())); CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex(0, JID(), IQ::Get)); CPPUNIT_ASSERT_EQUAL(1, static_cast(changes.size())); CPPUNIT_ASSERT_EQUAL(ownJID.toBare(), changes[0].first); CPPUNIT_ASSERT_EQUAL(std::string("Myself"), changes[0].second->getFullName()); CPPUNIT_ASSERT_EQUAL(std::string("Myself"), vcardStorage->getVCard(ownJID.toBare())->getFullName()); CPPUNIT_ASSERT_EQUAL(1, static_cast(ownChanges.size())); CPPUNIT_ASSERT_EQUAL(std::string("Myself"), ownChanges[0]->getFullName()); } void testCreateSetVCardRequest() { boost::shared_ptr testling = createManager(); VCard::ref vcard = boost::make_shared(); vcard->setFullName("New Name"); SetVCardRequest::ref request = testling->createSetVCardRequest(vcard); request->send(); stanzaChannel->onIQReceived(createSetVCardResult()); CPPUNIT_ASSERT_EQUAL(1, static_cast(changes.size())); CPPUNIT_ASSERT_EQUAL(ownJID.toBare(), changes[0].first); CPPUNIT_ASSERT_EQUAL(std::string("New Name"), changes[0].second->getFullName()); } void testCreateSetVCardRequest_Error() { boost::shared_ptr testling = createManager(); VCard::ref vcard = boost::make_shared(); vcard->setFullName("New Name"); SetVCardRequest::ref request = testling->createSetVCardRequest(vcard); request->send(); stanzaChannel->onIQReceived(IQ::createError(JID("baz@fum.com/foo"), stanzaChannel->sentStanzas[0]->getID())); CPPUNIT_ASSERT_EQUAL(0, static_cast(changes.size())); } private: boost::shared_ptr createManager() { boost::shared_ptr manager(new VCardManager(ownJID, iqRouter, vcardStorage)); manager->onVCardChanged.connect(boost::bind(&VCardManagerTest::handleVCardChanged, this, _1, _2)); manager->onOwnVCardChanged.connect(boost::bind(&VCardManagerTest::handleOwnVCardChanged, this, _1)); return manager; } void handleVCardChanged(const JID& jid, VCard::ref vcard) { changes.push_back(std::pair(jid, vcard)); } void handleOwnVCardChanged(VCard::ref vcard) { ownChanges.push_back(vcard); } IQ::ref createVCardResult() { VCard::ref vcard(new VCard()); vcard->setFullName("Foo Bar"); return IQ::createResult(JID("baz@fum.com/dum"), stanzaChannel->sentStanzas[0]->getTo(), stanzaChannel->sentStanzas[0]->getID(), vcard); } IQ::ref createOwnVCardResult() { VCard::ref vcard(new VCard()); vcard->setFullName("Myself"); return IQ::createResult(JID(), stanzaChannel->sentStanzas[0]->getTo(), stanzaChannel->sentStanzas[0]->getID(), vcard); } IQ::ref createSetVCardResult() { return IQ::createResult(JID("baz@fum.com/dum"), stanzaChannel->sentStanzas[0]->getTo(), stanzaChannel->sentStanzas[0]->getID(), VCard::ref()); } private: JID ownJID; DummyStanzaChannel* stanzaChannel; IQRouter* iqRouter; VCardMemoryStorage* vcardStorage; std::vector< std::pair > changes; std::vector ownChanges; }; CPPUNIT_TEST_SUITE_REGISTRATION(VCardManagerTest); swift-im-2.0+dev6/Swiften/VCards/GetVCardRequest.h0000644000175000017500000000123512227051774021646 0ustar kismithkismith/* * 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 #include namespace Swift { class GetVCardRequest : public GenericRequest { public: typedef boost::shared_ptr ref; static ref create(const JID& jid, IQRouter* router) { return ref(new GetVCardRequest(jid, router)); } private: GetVCardRequest(const JID& jid, IQRouter* router) : GenericRequest(IQ::Get, jid, boost::shared_ptr(new VCard()), router) { } }; } swift-im-2.0+dev6/Swiften/VCards/VCardManager.h0000644000175000017500000000302512227051774021127 0ustar kismithkismith/* * 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 #include #include #include #include #include #include namespace Swift { class JID; class VCardStorage; class IQRouter; class SWIFTEN_API VCardManager : public boost::bsignals::trackable { public: VCardManager(const JID& ownJID, IQRouter* iqRouter, VCardStorage* vcardStorage); ~VCardManager(); VCard::ref getVCard(const JID& jid) const; VCard::ref getVCardAndRequestWhenNeeded(const JID& jid); void requestVCard(const JID& jid); void requestOwnVCard(); std::string getPhotoHash(const JID& jid) const; SetVCardRequest::ref createSetVCardRequest(VCard::ref); public: /** * The JID will always be bare. */ boost::signal onVCardChanged; /** * Emitted when our own vcard changes. * * onVCardChanged will also be emitted. */ boost::signal onOwnVCardChanged; private: void handleVCardReceived(const JID& from, VCard::ref, ErrorPayload::ref); void handleSetVCardResponse(VCard::ref, ErrorPayload::ref); void setVCard(const JID& jid, VCard::ref vcard); private: JID ownJID; IQRouter* iqRouter; VCardStorage* storage; std::set requestedVCards; }; } swift-im-2.0+dev6/Swiften/VCards/VCardMemoryStorage.h0000644000175000017500000000141412227051774022352 0ustar kismithkismith/* * 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 #include #include #include namespace Swift { class VCardMemoryStorage : public VCardStorage { public: VCardMemoryStorage() {} virtual VCard::ref getVCard(const JID& jid) const { VCardMap::const_iterator i = vcards.find(jid); if (i != vcards.end()) { return i->second; } else { return VCard::ref(); } } virtual void setVCard(const JID& jid, VCard::ref v) { vcards[jid] = v; } private: typedef std::map VCardMap; VCardMap vcards; }; } swift-im-2.0+dev6/Swiften/VCards/SetVCardRequest.h0000644000175000017500000000120312227051774021655 0ustar kismithkismith/* * 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 #include namespace Swift { class SetVCardRequest : public GenericRequest { public: typedef boost::shared_ptr ref; static ref create(VCard::ref vcard, IQRouter* router) { return ref(new SetVCardRequest(vcard, router)); } private: SetVCardRequest(VCard::ref vcard, IQRouter* router) : GenericRequest(IQ::Set, JID(), vcard, router) { } }; } swift-im-2.0+dev6/Swiften/StreamManagement/0000755000175000017500000000000012227051774020532 5ustar kismithkismithswift-im-2.0+dev6/Swiften/StreamManagement/SConscript0000644000175000017500000000024712227051774022547 0ustar kismithkismithImport("swiften_env") sources = [ "StanzaAckRequester.cpp", "StanzaAckResponder.cpp", ] swiften_env.Append(SWIFTEN_OBJECTS = swiften_env.SwiftenObject(sources))swift-im-2.0+dev6/Swiften/StreamManagement/StanzaAckRequester.cpp0000644000175000017500000000231612227051774025017 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include namespace Swift { static const unsigned int MAX_HANDLED_STANZA_COUNT = boost::numeric_cast((1ULL<<32) - 1); StanzaAckRequester::StanzaAckRequester() : lastHandledStanzasCount(0) { } void StanzaAckRequester::handleStanzaSent(boost::shared_ptr stanza) { unackedStanzas.push_back(stanza); if (boost::dynamic_pointer_cast(stanza)) { onRequestAck(); } } void StanzaAckRequester::handleAckReceived(unsigned int handledStanzasCount) { unsigned int i = lastHandledStanzasCount; while (i != handledStanzasCount) { if (unackedStanzas.empty()) { std::cerr << "Warning: Server acked more stanzas than we sent" << std::endl; break; } boost::shared_ptr ackedStanza = unackedStanzas.front(); unackedStanzas.pop_front(); onStanzaAcked(ackedStanza); i = (i == MAX_HANDLED_STANZA_COUNT ? 0 : i + 1); } lastHandledStanzasCount = handledStanzasCount; } } swift-im-2.0+dev6/Swiften/StreamManagement/UnitTest/0000755000175000017500000000000012227051774022311 5ustar kismithkismithswift-im-2.0+dev6/Swiften/StreamManagement/UnitTest/StanzaAckRequesterTest.cpp0000644000175000017500000001266512227051774027446 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include using namespace Swift; namespace Swift { class StanzaAckRequesterTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(StanzaAckRequesterTest); CPPUNIT_TEST(testHandleStanzaSent_MessageRequestsAck); CPPUNIT_TEST(testHandleStanzaSent_IQDoesNotRequestAck); CPPUNIT_TEST(testHandleStanzaSent_PresenceDoesNotRequestAck); CPPUNIT_TEST(testHandleAckReceived_AcksStanza); CPPUNIT_TEST(testHandleAckReceived_AcksMultipleMessages); CPPUNIT_TEST(testHandleAckReceived_AcksMultipleStanzas); CPPUNIT_TEST(testHandleAckReceived_MultipleAcks); CPPUNIT_TEST(testHandleAckReceived_WrapAround); CPPUNIT_TEST_SUITE_END(); public: void setUp() { acksRequested = 0; } void testHandleStanzaSent_MessageRequestsAck() { boost::shared_ptr testling(createRequester()); testling->handleStanzaSent(createMessage("m1")); CPPUNIT_ASSERT_EQUAL(1, acksRequested); } void testHandleStanzaSent_IQDoesNotRequestAck() { boost::shared_ptr testling(createRequester()); testling->handleStanzaSent(createIQ("iq1")); CPPUNIT_ASSERT_EQUAL(0, acksRequested); } void testHandleStanzaSent_PresenceDoesNotRequestAck() { boost::shared_ptr testling(createRequester()); testling->handleStanzaSent(createPresence("p1")); CPPUNIT_ASSERT_EQUAL(0, acksRequested); } void testHandleAckReceived_AcksStanza() { boost::shared_ptr testling(createRequester()); testling->handleStanzaSent(createMessage("m1")); testling->handleAckReceived(1); CPPUNIT_ASSERT_EQUAL(1, static_cast(ackedStanzas.size())); CPPUNIT_ASSERT_EQUAL(std::string("m1"), ackedStanzas[0]->getID()); } void testHandleAckReceived_AcksMultipleMessages() { boost::shared_ptr testling(createRequester()); testling->handleStanzaSent(createMessage("m1")); testling->handleStanzaSent(createMessage("m2")); testling->handleAckReceived(2); CPPUNIT_ASSERT_EQUAL(2, static_cast(ackedStanzas.size())); CPPUNIT_ASSERT_EQUAL(std::string("m1"), ackedStanzas[0]->getID()); CPPUNIT_ASSERT_EQUAL(std::string("m2"), ackedStanzas[1]->getID()); } void testHandleAckReceived_AcksMultipleStanzas() { boost::shared_ptr testling(createRequester()); testling->handleStanzaSent(createIQ("iq1")); testling->handleStanzaSent(createPresence("p1")); testling->handleStanzaSent(createMessage("m1")); testling->handleAckReceived(3); CPPUNIT_ASSERT_EQUAL(3, static_cast(ackedStanzas.size())); CPPUNIT_ASSERT_EQUAL(std::string("iq1"), ackedStanzas[0]->getID()); CPPUNIT_ASSERT_EQUAL(std::string("p1"), ackedStanzas[1]->getID()); CPPUNIT_ASSERT_EQUAL(std::string("m1"), ackedStanzas[2]->getID()); } void testHandleAckReceived_MultipleAcks() { boost::shared_ptr testling(createRequester()); testling->handleStanzaSent(createMessage("m1")); testling->handleAckReceived(1); testling->handleStanzaSent(createMessage("m2")); testling->handleStanzaSent(createMessage("m3")); testling->handleAckReceived(3); CPPUNIT_ASSERT_EQUAL(3, static_cast(ackedStanzas.size())); CPPUNIT_ASSERT_EQUAL(std::string("m1"), ackedStanzas[0]->getID()); CPPUNIT_ASSERT_EQUAL(std::string("m2"), ackedStanzas[1]->getID()); CPPUNIT_ASSERT_EQUAL(std::string("m3"), ackedStanzas[2]->getID()); } // Handle stanza ack count wrapping, as per the XEP void testHandleAckReceived_WrapAround() { boost::shared_ptr testling(createRequester()); testling->lastHandledStanzasCount = boost::numeric_cast((1ULL<<32) - 1); testling->handleStanzaSent(createMessage("m1")); testling->handleStanzaSent(createMessage("m2")); testling->handleAckReceived(1); CPPUNIT_ASSERT_EQUAL(2, static_cast(ackedStanzas.size())); CPPUNIT_ASSERT_EQUAL(std::string("m1"), ackedStanzas[0]->getID()); CPPUNIT_ASSERT_EQUAL(std::string("m2"), ackedStanzas[1]->getID()); } private: Message::ref createMessage(const std::string& id) { Message::ref result(new Message()); result->setID(id); return result; } IQ::ref createIQ(const std::string& id) { IQ::ref result(new IQ()); result->setID(id); return result; } Presence::ref createPresence(const std::string& id) { Presence::ref result(new Presence()); result->setID(id); return result; } StanzaAckRequester* createRequester() { StanzaAckRequester* requester = new StanzaAckRequester(); requester->onRequestAck.connect(boost::bind(&StanzaAckRequesterTest::handleRequestAck, this)); requester->onStanzaAcked.connect(boost::bind(&StanzaAckRequesterTest::handleStanzaAcked, this, _1)); return requester; } void handleRequestAck() { acksRequested++; } void handleStanzaAcked(boost::shared_ptr stanza) { ackedStanzas.push_back(stanza); } private: int acksRequested; std::vector< boost::shared_ptr > ackedStanzas; }; } CPPUNIT_TEST_SUITE_REGISTRATION(Swift::StanzaAckRequesterTest); swift-im-2.0+dev6/Swiften/StreamManagement/UnitTest/StanzaAckResponderTest.cpp0000644000175000017500000000560312227051774027422 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include using namespace Swift; namespace Swift { class StanzaAckResponderTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(StanzaAckResponderTest); CPPUNIT_TEST(testHandleAckRequestReceived_AcksStanza); CPPUNIT_TEST(testHandleAckRequestReceived_AcksMultipleStanzas); CPPUNIT_TEST(testHandleAckRequestReceived_MultipleAcks); CPPUNIT_TEST(testHandleAckRequestReceived_WrapAround); CPPUNIT_TEST_SUITE_END(); public: void testHandleAckRequestReceived_AcksStanza() { boost::shared_ptr testling(createResponder()); testling->handleStanzaReceived(); testling->handleAckRequestReceived(); CPPUNIT_ASSERT_EQUAL(1, static_cast(acks.size())); CPPUNIT_ASSERT_EQUAL(1U, acks[0]); } void testHandleAckRequestReceived_AcksMultipleStanzas() { boost::shared_ptr testling(createResponder()); testling->handleStanzaReceived(); testling->handleStanzaReceived(); testling->handleAckRequestReceived(); CPPUNIT_ASSERT_EQUAL(1, static_cast(acks.size())); CPPUNIT_ASSERT_EQUAL(2U, acks[0]); } void testHandleAckRequestReceived_MultipleAcks() { boost::shared_ptr testling(createResponder()); testling->handleStanzaReceived(); testling->handleAckRequestReceived(); testling->handleStanzaReceived(); testling->handleAckRequestReceived(); CPPUNIT_ASSERT_EQUAL(2, static_cast(acks.size())); CPPUNIT_ASSERT_EQUAL(1U, acks[0]); CPPUNIT_ASSERT_EQUAL(2U, acks[1]); } // Handle stanza ack count wrapping, as per the XEP void testHandleAckRequestReceived_WrapAround() { boost::shared_ptr testling(createResponder()); testling->handledStanzasCount = boost::numeric_cast((1ULL<<32) - 1); testling->handleStanzaReceived(); testling->handleStanzaReceived(); testling->handleAckRequestReceived(); CPPUNIT_ASSERT_EQUAL(1, static_cast(acks.size())); CPPUNIT_ASSERT_EQUAL(1U, acks[0]); } private: Message::ref createMessage(const std::string& id) { Message::ref result(new Message()); result->setID(id); return result; } StanzaAckResponder* createResponder() { StanzaAckResponder* responder = new StanzaAckResponder(); responder->onAck.connect(boost::bind(&StanzaAckResponderTest::handleAck, this, _1)); return responder; } void handleAck(unsigned int h) { acks.push_back(h); } private: std::vector acks; }; } CPPUNIT_TEST_SUITE_REGISTRATION(Swift::StanzaAckResponderTest); swift-im-2.0+dev6/Swiften/StreamManagement/StanzaAckResponder.cpp0000644000175000017500000000130012227051774024771 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include namespace Swift { static const unsigned int MAX_HANDLED_STANZA_COUNT = boost::numeric_cast((1ULL<<32) - 1); StanzaAckResponder::StanzaAckResponder() : handledStanzasCount(0) { } void StanzaAckResponder::handleStanzaReceived() { handledStanzasCount = (handledStanzasCount == MAX_HANDLED_STANZA_COUNT ? 0 : handledStanzasCount + 1); } void StanzaAckResponder::handleAckRequestReceived() { onAck(handledStanzasCount); } } swift-im-2.0+dev6/Swiften/StreamManagement/StanzaAckRequester.h0000644000175000017500000000146712227051774024472 0ustar kismithkismith/* * 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 #include #include #include #include namespace Swift { class SWIFTEN_API StanzaAckRequester { public: StanzaAckRequester(); void handleStanzaSent(boost::shared_ptr stanza); void handleAckReceived(unsigned int handledStanzasCount); public: boost::signal onRequestAck; boost::signal)> onStanzaAcked; private: friend class StanzaAckRequesterTest; unsigned int lastHandledStanzasCount; std::deque > unackedStanzas; }; } swift-im-2.0+dev6/Swiften/StreamManagement/StanzaAckResponder.h0000644000175000017500000000121712227051774024445 0ustar kismithkismith/* * 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 #include #include #include namespace Swift { class SWIFTEN_API StanzaAckResponder { public: StanzaAckResponder(); void handleStanzaReceived(); void handleAckRequestReceived(); public: boost::signal onAck; private: friend class StanzaAckResponderTest; unsigned int handledStanzasCount; }; } swift-im-2.0+dev6/Swiften/Network/0000755000175000017500000000000012227051774016733 5ustar kismithkismithswift-im-2.0+dev6/Swiften/Network/SOCKS5ProxiedConnection.h0000644000175000017500000000214712227051774023432 0ustar kismithkismith/* * Copyright (c) 2010-2011 Thilo Cestonaro * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include namespace Swift { class ConnectionFactory; class DomainNameResolver; class TimerFactory; class SOCKS5ProxiedConnection : public ProxiedConnection { public: typedef boost::shared_ptr ref; static ref create(DomainNameResolver* resolver, ConnectionFactory* connectionFactory, TimerFactory* timerFactory, const std::string& proxyHost, int proxyPort) { return ref(new SOCKS5ProxiedConnection(resolver, connectionFactory, timerFactory, proxyHost, proxyPort)); } private: SOCKS5ProxiedConnection(DomainNameResolver* resolver, ConnectionFactory* connectionFactory, TimerFactory* timerFactory, const std::string& proxyHost, int proxyPort); virtual void initializeProxy(); virtual void handleProxyInitializeData(boost::shared_ptr data); private: enum { ProxyAuthenticating = 0, ProxyConnecting, } proxyState_; }; } swift-im-2.0+dev6/Swiften/Network/BOSHConnectionPool.h0000644000175000017500000000501512227051774022512 0ustar kismithkismith/* * Copyright (c) 2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include namespace Swift { class HTTPConnectProxiedConnectionFactory; class TLSConnectionFactory; class CachingDomainNameResolver; class EventLoop; class SWIFTEN_API BOSHConnectionPool : public boost::bsignals::trackable { public: BOSHConnectionPool(const URL& boshURL, DomainNameResolver* resolver, ConnectionFactory* connectionFactory, XMLParserFactory* parserFactory, TLSContextFactory* tlsFactory, TimerFactory* timerFactory, EventLoop* eventLoop, const std::string& to, unsigned long long initialRID, const URL& boshHTTPConnectProxyURL, const SafeString& boshHTTPConnectProxyAuthID, const SafeString& boshHTTPConnectProxyAuthPassword); ~BOSHConnectionPool(); void write(const SafeByteArray& data); void writeFooter(); void close(); void restartStream(); boost::signal onSessionTerminated; boost::signal onSessionStarted; boost::signal onXMPPDataRead; boost::signal onBOSHDataRead; boost::signal onBOSHDataWritten; private: void handleDataRead(const SafeByteArray& data); void handleSessionStarted(const std::string& sid, size_t requests); void handleBOSHDataRead(const SafeByteArray& data); void handleBOSHDataWritten(const SafeByteArray& data); void handleSessionTerminated(BOSHError::ref condition); void handleConnectFinished(bool, BOSHConnection::ref connection); void handleConnectionDisconnected(bool error, BOSHConnection::ref connection); void handleHTTPError(const std::string& errorCode); private: BOSHConnection::ref createConnection(); void destroyConnection(BOSHConnection::ref connection); void tryToSendQueuedData(); BOSHConnection::ref getSuitableConnection(); private: URL boshURL; ConnectionFactory* connectionFactory; XMLParserFactory* xmlParserFactory; TimerFactory* timerFactory; std::vector connections; std::string sid; unsigned long long rid; std::vector dataQueue; bool pendingTerminate; std::string to; size_t requestLimit; int restartCount; bool pendingRestart; std::vector myConnectionFactories; CachingDomainNameResolver* resolver; }; } swift-im-2.0+dev6/Swiften/Network/SConscript0000644000175000017500000000542712227051774020755 0ustar kismithkismithImport("swiften_env") myenv = swiften_env.Clone() sourceList = [ "ProxiedConnection.cpp", "HTTPConnectProxiedConnection.cpp", "HTTPConnectProxiedConnectionFactory.cpp", "SOCKS5ProxiedConnection.cpp", "SOCKS5ProxiedConnectionFactory.cpp", "BoostConnection.cpp", "BoostConnectionFactory.cpp", "BoostConnectionServer.cpp", "BoostConnectionServerFactory.cpp", "BoostIOServiceThread.cpp", "BOSHConnection.cpp", "BOSHConnectionPool.cpp", "CachingDomainNameResolver.cpp", "ConnectionFactory.cpp", "ConnectionServer.cpp", "ConnectionServerFactory.cpp", "DummyConnection.cpp", "FakeConnection.cpp", "ChainedConnector.cpp", "Connector.cpp", "Connection.cpp", "TimerFactory.cpp", "DummyTimerFactory.cpp", "BoostTimerFactory.cpp", "DomainNameResolver.cpp", "DomainNameAddressQuery.cpp", "DomainNameServiceQuery.cpp", "PlatformDomainNameResolver.cpp", "PlatformDomainNameServiceQuery.cpp", "PlatformDomainNameAddressQuery.cpp", "StaticDomainNameResolver.cpp", "HostAddress.cpp", "HostAddressPort.cpp", "HostNameOrAddress.cpp", "NetworkFactories.cpp", "BoostNetworkFactories.cpp", "NetworkEnvironment.cpp", "Timer.cpp", "TLSConnection.cpp", "TLSConnectionFactory.cpp", "BoostTimer.cpp", "ProxyProvider.cpp", "NullProxyProvider.cpp", "NATTraverser.cpp", "NullNATTraverser.cpp", "NATTraversalGetPublicIPRequest.cpp", "NATTraversalForwardPortRequest.cpp", "NATTraversalRemovePortForwardingRequest.cpp", "NATTraversalInterface.cpp", ] if myenv["PLATFORM"] == "darwin" : myenv.Append(FRAMEWORKS = ["CoreServices", "SystemConfiguration"]) sourceList += [ "MacOSXProxyProvider.cpp" ] sourceList += [ "UnixNetworkEnvironment.cpp" ] elif myenv["PLATFORM"] == "win32" : sourceList += [ "WindowsProxyProvider.cpp" ] sourceList += [ "WindowsNetworkEnvironment.cpp" ] else : sourceList += [ "UnixNetworkEnvironment.cpp" ] sourceList += [ "UnixProxyProvider.cpp" ] sourceList += [ "EnvironmentProxyProvider.cpp" ] if myenv.get("HAVE_GCONF", 0) : myenv.Append(CPPDEFINES = "HAVE_GCONF") myenv.MergeFlags(myenv["GCONF_FLAGS"]) sourceList += [ "GConfProxyProvider.cpp" ] objects = myenv.SwiftenObject(sourceList) if myenv["experimental"] : # LibNATPMP classes natpmp_env = myenv.Clone() natpmp_env.Append(CPPDEFINES = natpmp_env["LIBNATPMP_FLAGS"].get("INTERNAL_CPPDEFINES", [])) objects += natpmp_env.SwiftenObject([ "NATPMPInterface.cpp", ]) # LibMINIUPnP classes upnp_env = myenv.Clone() upnp_env.Append(CPPDEFINES = upnp_env["LIBMINIUPNPC_FLAGS"].get("INTERNAL_CPPDEFINES", [])) objects += upnp_env.SwiftenObject([ "MiniUPnPInterface.cpp", ]) objects += myenv.SwiftenObject([ "PlatformNATTraversalWorker.cpp", ]) swiften_env.Append(SWIFTEN_OBJECTS = [objects]) swift-im-2.0+dev6/Swiften/Network/HostAddressPort.h0000644000175000017500000000157712227051774022206 0ustar kismithkismith/* * 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 #include #include namespace Swift { class SWIFTEN_API HostAddressPort { public: HostAddressPort(const HostAddress& address = HostAddress(), int port = -1); HostAddressPort(const boost::asio::ip::tcp::endpoint& endpoint); const HostAddress& getAddress() const { return address_; } int getPort() const { return port_; } bool operator==(const HostAddressPort& o) const { return address_ == o.address_ && port_ == o.port_; } bool isValid() const { return address_.isValid() && port_ > 0; } std::string toString() const; private: HostAddress address_; int port_; }; } swift-im-2.0+dev6/Swiften/Network/HostNameOrAddress.h0000644000175000017500000000063112227051774022431 0ustar kismithkismith/* * Copyright (c) 2012 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include namespace Swift { typedef boost::variant HostNameOrAddress; std::string toString(const HostNameOrAddress& address); } swift-im-2.0+dev6/Swiften/Network/NetworkEnvironment.h0000644000175000017500000000102712227051774022762 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include #include #include namespace Swift { class SWIFTEN_API NetworkEnvironment { public: virtual ~NetworkEnvironment(); virtual std::vector getNetworkInterfaces() const = 0; HostAddress getLocalAddress() const; }; } swift-im-2.0+dev6/Swiften/Network/EnvironmentProxyProvider.h0000644000175000017500000000114312227051774024164 0ustar kismithkismith/* * Copyright (c) 2010-2011 Thilo Cestonaro * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include namespace Swift { class EnvironmentProxyProvider : public ProxyProvider { public: EnvironmentProxyProvider(); virtual HostAddressPort getHTTPConnectProxy() const; virtual HostAddressPort getSOCKS5Proxy() const; private: HostAddressPort getFromEnv(const char* envVarName, std::string proxyProtocol); HostAddressPort socksProxy; HostAddressPort httpProxy; }; } swift-im-2.0+dev6/Swiften/Network/ProxyProvider.h0000644000175000017500000000102012227051774021731 0ustar kismithkismith/* * Copyright (c) 2010-2011 Thilo Cestonaro * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include #include #include namespace Swift { class ProxyProvider { public: ProxyProvider(); virtual ~ProxyProvider(); virtual HostAddressPort getHTTPConnectProxy() const = 0; virtual HostAddressPort getSOCKS5Proxy() const = 0; }; } swift-im-2.0+dev6/Swiften/Network/NullProxyProvider.cpp0000644000175000017500000000071612227051774023132 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include using namespace Swift; NullProxyProvider::NullProxyProvider() { } HostAddressPort NullProxyProvider::getHTTPConnectProxy() const { return HostAddressPort(); } HostAddressPort NullProxyProvider::getSOCKS5Proxy() const { return HostAddressPort(); } swift-im-2.0+dev6/Swiften/Network/Connector.h0000644000175000017500000000500312227051774021034 0ustar kismithkismith/* * 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 #include #include #include #include #include #include #include #include #include namespace Swift { class DomainNameAddressQuery; class DomainNameResolver; class ConnectionFactory; class TimerFactory; class SWIFTEN_API Connector : public boost::bsignals::trackable, public boost::enable_shared_from_this { public: typedef boost::shared_ptr ref; static Connector::ref create(const std::string& hostname, int port, bool doServiceLookups, DomainNameResolver* resolver, ConnectionFactory* connectionFactory, TimerFactory* timerFactory) { return ref(new Connector(hostname, port, doServiceLookups, resolver, connectionFactory, timerFactory)); } void setTimeoutMilliseconds(int milliseconds); void start(); void stop(); boost::signal, boost::shared_ptr)> onConnectFinished; private: Connector(const std::string& hostname, int port, bool doServiceLookups, DomainNameResolver*, ConnectionFactory*, TimerFactory*); void handleServiceQueryResult(const std::vector& result); void handleAddressQueryResult(const std::vector& address, boost::optional error); void queryAddress(const std::string& hostname); void tryNextServiceOrFallback(); void tryNextAddress(); void tryConnect(const HostAddressPort& target); void handleConnectionConnectFinished(bool error); void finish(boost::shared_ptr); void handleTimeout(); private: std::string hostname; int port; bool doServiceLookups; DomainNameResolver* resolver; ConnectionFactory* connectionFactory; TimerFactory* timerFactory; int timeoutMilliseconds; boost::shared_ptr timer; boost::shared_ptr serviceQuery; std::deque serviceQueryResults; boost::shared_ptr addressQuery; std::deque addressQueryResults; bool queriedAllServices; boost::shared_ptr currentConnection; bool foundSomeDNS; }; }; swift-im-2.0+dev6/Swiften/Network/CachingDomainNameResolver.cpp0000644000175000017500000000144712227051774024454 0ustar kismithkismith/* * Copyright (c) 2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include namespace Swift { CachingDomainNameResolver::CachingDomainNameResolver(DomainNameResolver* realResolver, EventLoop*) : realResolver(realResolver) { } CachingDomainNameResolver::~CachingDomainNameResolver() { } DomainNameServiceQuery::ref CachingDomainNameResolver::createServiceQuery(const std::string& name) { //TODO: Cache return realResolver->createServiceQuery(name); } DomainNameAddressQuery::ref CachingDomainNameResolver::createAddressQuery(const std::string& name) { //TODO: Cache return realResolver->createAddressQuery(name); } } swift-im-2.0+dev6/Swiften/Network/PlatformNATTraversalWorker.h0000644000175000017500000000376212227051774024321 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include #include #include #include #include #include #include #include #include namespace Swift { class EventLoop; class NATTraversalGetPublicIPRequest; class NATTraversalForwardPortRequest; class NATTraversalRemovePortForwardingRequest; class PlatformNATTraversalRequest; class NATPMPInterface; class MiniUPnPInterface; class NATTraversalInterface; class NATPortMapping; class SWIFTEN_API PlatformNATTraversalWorker : public NATTraverser { friend class PlatformNATTraversalRequest; public: PlatformNATTraversalWorker(EventLoop* eventLoop); ~PlatformNATTraversalWorker(); boost::shared_ptr createGetPublicIPRequest(); boost::shared_ptr createForwardPortRequest(int localPort, int publicPort); boost::shared_ptr createRemovePortForwardingRequest(int localPort, int publicPort); private: NATTraversalInterface* getNATTraversalInterface() const; void addRequestToQueue(boost::shared_ptr); void run(); private: EventLoop* eventLoop; bool stopRequested; boost::thread* thread; std::deque > queue; boost::mutex queueMutex; boost::condition_variable queueNonEmpty; NullNATTraversalInterface* nullNATTraversalInterface; mutable boost::logic::tribool natPMPSupported; mutable NATPMPInterface* natPMPInterface; mutable boost::logic::tribool miniUPnPSupported; mutable MiniUPnPInterface* miniUPnPInterface; }; } swift-im-2.0+dev6/Swiften/Network/ConnectionServerFactory.cpp0000644000175000017500000000044212227051774024255 0ustar kismithkismith/* * Copyright (c) 2011 Jan Kaluza * Licensed under the Simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include namespace Swift { ConnectionServerFactory::~ConnectionServerFactory() { } } swift-im-2.0+dev6/Swiften/Network/FakeConnection.cpp0000644000175000017500000000241312227051774022325 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include namespace Swift { FakeConnection::FakeConnection(EventLoop* eventLoop) : eventLoop(eventLoop), state(Initial), delayConnect(false) { } FakeConnection::~FakeConnection() { } void FakeConnection::listen() { assert(false); } void FakeConnection::setError(const Error& e) { error = boost::optional(e); state = DisconnectedWithError; if (connectedTo) { eventLoop->postEvent( boost::bind(boost::ref(onDisconnected), error), shared_from_this()); } } void FakeConnection::connect(const HostAddressPort& address) { if (delayConnect) { state = Connecting; } else { if (!error) { connectedTo = address; state = Connected; } else { state = DisconnectedWithError; } eventLoop->postEvent( boost::bind(boost::ref(onConnectFinished), error), shared_from_this()); } } void FakeConnection::disconnect() { if (!error) { state = Disconnected; } else { state = DisconnectedWithError; } connectedTo.reset(); eventLoop->postEvent( boost::bind(boost::ref(onDisconnected), error), shared_from_this()); } } swift-im-2.0+dev6/Swiften/Network/Connection.h0000644000175000017500000000202412227051774021201 0ustar kismithkismith/* * 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 #include #include #include namespace Swift { class HostAddressPort; class SWIFTEN_API Connection { public: typedef boost::shared_ptr ref; enum Error { ReadError, WriteError }; Connection(); virtual ~Connection(); virtual void listen() = 0; virtual void connect(const HostAddressPort& address) = 0; virtual void disconnect() = 0; virtual void write(const SafeByteArray& data) = 0; virtual HostAddressPort getLocalAddress() const = 0; public: boost::signal onConnectFinished; boost::signal&)> onDisconnected; boost::signal)> onDataRead; boost::signal onDataWritten; }; } swift-im-2.0+dev6/Swiften/Network/BoostConnectionServerFactory.h0000644000175000017500000000151212227051774024730 0ustar kismithkismith/* * Copyright (c) 2011 Jan Kaluza * Licensed under the Simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include #include namespace Swift { class ConnectionServer; class BoostConnectionServerFactory : public ConnectionServerFactory { public: BoostConnectionServerFactory(boost::shared_ptr, EventLoop* eventLoop); virtual boost::shared_ptr createConnectionServer(int port); virtual boost::shared_ptr createConnectionServer(const Swift::HostAddress &hostAddress, int port); private: boost::shared_ptr ioService; EventLoop* eventLoop; }; } swift-im-2.0+dev6/Swiften/Network/WindowsProxyProvider.cpp0000644000175000017500000000660612227051774023656 0ustar kismithkismith/* * Copyright (c) 2010-2011 Thilo Cestonaro * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include #include #include #include #include #include #include #include #include #include namespace Swift { WindowsProxyProvider::WindowsProxyProvider() : ProxyProvider() { HKEY hKey = (HKEY)INVALID_HANDLE_VALUE; long result; result = RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", 0, KEY_READ, &hKey); if (result == ERROR_SUCCESS && hKey != INVALID_HANDLE_VALUE && proxyEnabled(hKey)) { DWORD dataType = REG_SZ; DWORD dataSize = 0; ByteArray dataBuffer; result = RegQueryValueEx(hKey, "ProxyServer", NULL, &dataType, NULL, &dataSize); if(result != ERROR_SUCCESS) { return; } dataBuffer.resize(dataSize); result = RegQueryValueEx(hKey, "ProxyServer", NULL, &dataType, reinterpret_cast(vecptr(dataBuffer)), &dataSize); if(result == ERROR_SUCCESS) { std::vector proxies = String::split(byteArrayToString(dataBuffer), ';'); std::pair protocolAndProxy; foreach(std::string proxy, proxies) { if(proxy.find('=') != std::string::npos) { protocolAndProxy = String::getSplittedAtFirst(proxy, '='); SWIFT_LOG(debug) << "Found proxy: " << protocolAndProxy.first << " => " << protocolAndProxy.second << std::endl; if(protocolAndProxy.first.compare("socks") == 0) { socksProxy = getAsHostAddressPort(protocolAndProxy.second); } else if (protocolAndProxy.first.compare("http") == 0) { httpProxy = getAsHostAddressPort(protocolAndProxy.second); } } } } } } HostAddressPort WindowsProxyProvider::getHTTPConnectProxy() const { return httpProxy; } HostAddressPort WindowsProxyProvider::getSOCKS5Proxy() const { return socksProxy; } HostAddressPort WindowsProxyProvider::getAsHostAddressPort(std::string proxy) { HostAddressPort ret(HostAddress(), 0); try { std::pair tmp; int port = 0; tmp = String::getSplittedAtFirst(proxy, ':'); // .c_str() is needed as tmp.second can include a \0 char which will end in an exception of the lexical cast. // with .c_str() the \0 will not be part of the string which is to be casted port = boost::lexical_cast (tmp.second.c_str()); ret = HostAddressPort(HostAddress(tmp.first), port); } catch(...) { std::cerr << "Exception occured while parsing windows proxy \"getHostAddressPort\"." << std::endl; } return ret; } bool WindowsProxyProvider::proxyEnabled(HKEY hKey) const { bool ret = false; long result; DWORD dataType = REG_DWORD; DWORD dataSize = 0; DWORD data = 0; ByteArray dataBuffer; if(hKey == INVALID_HANDLE_VALUE) return ret; result = RegQueryValueEx(hKey, "ProxyEnable", NULL, &dataType, NULL, &dataSize); if(result != ERROR_SUCCESS) return ret; dataBuffer.resize(dataSize); result = RegQueryValueEx(hKey, "ProxyEnable", NULL, &dataType, reinterpret_cast(vecptr(dataBuffer)), &dataSize); if(result != ERROR_SUCCESS) return ret; for(size_t t = 0; t < dataBuffer.size(); t++) { data += static_cast (dataBuffer[t]) * pow(256, static_cast(t)); } return (data == 1); } } swift-im-2.0+dev6/Swiften/Network/HostAddressPort.cpp0000644000175000017500000000123212227051774022525 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include using namespace Swift; HostAddressPort::HostAddressPort(const HostAddress& address, int port) : address_(address), port_(port) { } HostAddressPort::HostAddressPort(const boost::asio::ip::tcp::endpoint& endpoint) { address_ = HostAddress(endpoint.address()); port_ = endpoint.port(); } std::string HostAddressPort::toString() const { return getAddress().toString() + ":" + boost::lexical_cast(getPort()); } swift-im-2.0+dev6/Swiften/Network/BoostTimerFactory.cpp0000644000175000017500000000107712227051774023063 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include namespace Swift { BoostTimerFactory::BoostTimerFactory(boost::shared_ptr ioService, EventLoop* eventLoop) : ioService(ioService), eventLoop(eventLoop) { } boost::shared_ptr BoostTimerFactory::createTimer(int milliseconds) { return BoostTimer::create(milliseconds, ioService, eventLoop); } } swift-im-2.0+dev6/Swiften/Network/BOSHConnection.cpp0000644000175000017500000002201312227051774022210 0ustar kismithkismith/* * Copyright (c) 2011 Thilo Cestonaro * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ /* * Copyright (c) 2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include #include namespace Swift { BOSHConnection::BOSHConnection(const URL& boshURL, Connector::ref connector, XMLParserFactory* parserFactory) : boshURL_(boshURL), connector_(connector), parserFactory_(parserFactory), sid_(), waitingForStartResponse_(false), pending_(false), connectionReady_(false) { } BOSHConnection::~BOSHConnection() { cancelConnector(); if (connection_) { connection_->onDataRead.disconnect(boost::bind(&BOSHConnection::handleDataRead, shared_from_this(), _1)); connection_->onDisconnected.disconnect(boost::bind(&BOSHConnection::handleDisconnected, shared_from_this(), _1)); } disconnect(); } void BOSHConnection::connect() { connector_->onConnectFinished.connect(boost::bind(&BOSHConnection::handleConnectFinished, shared_from_this(), _1)); connector_->start(); } void BOSHConnection::cancelConnector() { if (connector_) { connector_->onConnectFinished.disconnect(boost::bind(&BOSHConnection::handleConnectFinished, shared_from_this(), _1)); connector_->stop(); connector_.reset(); } } void BOSHConnection::disconnect() { if (connection_) { connection_->disconnect(); sid_ = ""; } else { /* handleDisconnected takes care of the connector_ as well */ handleDisconnected(boost::optional()); } } void BOSHConnection::restartStream() { write(createSafeByteArray(""), true, false); } void BOSHConnection::terminateStream() { write(createSafeByteArray(""), false, true); } void BOSHConnection::write(const SafeByteArray& data) { write(data, false, false); } std::pair BOSHConnection::createHTTPRequest(const SafeByteArray& data, bool streamRestart, bool terminate, unsigned long long rid, const std::string& sid, const URL& boshURL) { size_t size; std::stringstream content; SafeByteArray contentTail = createSafeByteArray(""); std::stringstream header; content << ""; SafeByteArray safeContent = createSafeByteArray(content.str()); safeContent.insert(safeContent.end(), data.begin(), data.end()); safeContent.insert(safeContent.end(), contentTail.begin(), contentTail.end()); size = safeContent.size(); header << "POST " << boshURL.getPath() << " HTTP/1.1\r\n" << "Host: " << boshURL.getHost(); if (boshURL.getPort()) { header << ":" << *boshURL.getPort(); } header << "\r\n" /*<< "Accept-Encoding: deflate\r\n"*/ << "Content-Type: text/xml; charset=utf-8\r\n" << "Content-Length: " << size << "\r\n\r\n"; SafeByteArray safeHeader = createSafeByteArray(header.str()); safeHeader.insert(safeHeader.end(), safeContent.begin(), safeContent.end()); return std::pair(safeHeader, size); } void BOSHConnection::write(const SafeByteArray& data, bool streamRestart, bool terminate) { assert(connectionReady_); assert(!sid_.empty()); SafeByteArray safeHeader = createHTTPRequest(data, streamRestart, terminate, rid_, sid_, boshURL_).first; onBOSHDataWritten(safeHeader); connection_->write(safeHeader); pending_ = true; SWIFT_LOG(debug) << "write data: " << safeByteArrayToString(safeHeader) << std::endl; } void BOSHConnection::handleConnectFinished(Connection::ref connection) { cancelConnector(); connectionReady_ = connection; if (connectionReady_) { connection_ = connection; connection_->onDataRead.connect(boost::bind(&BOSHConnection::handleDataRead, shared_from_this(), _1)); connection_->onDisconnected.connect(boost::bind(&BOSHConnection::handleDisconnected, shared_from_this(), _1)); } onConnectFinished(!connectionReady_); } void BOSHConnection::startStream(const std::string& to, unsigned long long rid) { assert(connectionReady_); // Session Creation Request std::stringstream content; std::stringstream header; content << ""; std::string contentString = content.str(); header << "POST " << boshURL_.getPath() << " HTTP/1.1\r\n" << "Host: " << boshURL_.getHost(); if (boshURL_.getPort()) { header << ":" << *boshURL_.getPort(); } header << "\r\n" /*<< "Accept-Encoding: deflate\r\n"*/ << "Content-Type: text/xml; charset=utf-8\r\n" << "Content-Length: " << contentString.size() << "\r\n\r\n" << contentString; waitingForStartResponse_ = true; SafeByteArray safeHeader = createSafeByteArray(header.str()); onBOSHDataWritten(safeHeader); connection_->write(safeHeader); SWIFT_LOG(debug) << "write stream header: " << safeByteArrayToString(safeHeader) << std::endl; } void BOSHConnection::handleDataRead(boost::shared_ptr data) { onBOSHDataRead(*data); buffer_ = concat(buffer_, *data); std::string response = safeByteArrayToString(buffer_); if (response.find("\r\n\r\n") == std::string::npos) { onBOSHDataRead(createSafeByteArray("[[Previous read incomplete, pending]]")); return; } std::string httpCode = response.substr(response.find(" ") + 1, 3); if (httpCode != "200") { onHTTPError(httpCode); return; } BOSHBodyExtractor parser(parserFactory_, createByteArray(response.substr(response.find("\r\n\r\n") + 4))); if (parser.getBody()) { if (parser.getBody()->attributes.getAttribute("type") == "terminate") { BOSHError::Type errorType = parseTerminationCondition(parser.getBody()->attributes.getAttribute("condition")); onSessionTerminated(errorType == BOSHError::NoError ? boost::shared_ptr() : boost::make_shared(errorType)); } buffer_.clear(); if (waitingForStartResponse_) { waitingForStartResponse_ = false; sid_ = parser.getBody()->attributes.getAttribute("sid"); std::string requestsString = parser.getBody()->attributes.getAttribute("requests"); int requests = 2; if (!requestsString.empty()) { try { requests = boost::lexical_cast(requestsString); } catch (const boost::bad_lexical_cast&) { } } onSessionStarted(sid_, requests); } SafeByteArray payload = createSafeByteArray(parser.getBody()->content); /* Say we're good to go again, so don't add anything after here in the method */ pending_ = false; onXMPPDataRead(payload); } } BOSHError::Type BOSHConnection::parseTerminationCondition(const std::string& text) { BOSHError::Type condition = BOSHError::UndefinedCondition; if (text == "bad-request") { condition = BOSHError::BadRequest; } else if (text == "host-gone") { condition = BOSHError::HostGone; } else if (text == "host-unknown") { condition = BOSHError::HostUnknown; } else if (text == "improper-addressing") { condition = BOSHError::ImproperAddressing; } else if (text == "internal-server-error") { condition = BOSHError::InternalServerError; } else if (text == "item-not-found") { condition = BOSHError::ItemNotFound; } else if (text == "other-request") { condition = BOSHError::OtherRequest; } else if (text == "policy-violation") { condition = BOSHError::PolicyViolation; } else if (text == "remote-connection-failed") { condition = BOSHError::RemoteConnectionFailed; } else if (text == "remote-stream-error") { condition = BOSHError::RemoteStreamError; } else if (text == "see-other-uri") { condition = BOSHError::SeeOtherURI; } else if (text == "system-shutdown") { condition = BOSHError::SystemShutdown; } else if (text == "") { condition = BOSHError::NoError; } return condition; } const std::string& BOSHConnection::getSID() { return sid_; } void BOSHConnection::setRID(unsigned long long rid) { rid_ = rid; } void BOSHConnection::setSID(const std::string& sid) { sid_ = sid; } void BOSHConnection::handleDisconnected(const boost::optional& error) { cancelConnector(); onDisconnected(error); sid_ = ""; connectionReady_ = false; } bool BOSHConnection::isReadyToSend() { /* Without pipelining you need to not send more without first receiving the response */ /* With pipelining you can. Assuming we can't, here */ return connectionReady_ && !pending_ && !waitingForStartResponse_ && !sid_.empty(); } } swift-im-2.0+dev6/Swiften/Network/BoostConnectionServerFactory.cpp0000644000175000017500000000154412227051774025270 0ustar kismithkismith/* * Copyright (c) 2011 Jan Kaluza * Licensed under the Simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include #include namespace Swift { BoostConnectionServerFactory::BoostConnectionServerFactory(boost::shared_ptr ioService, EventLoop* eventLoop) : ioService(ioService), eventLoop(eventLoop) { } boost::shared_ptr BoostConnectionServerFactory::createConnectionServer(int port) { return BoostConnectionServer::create(port, ioService, eventLoop); } boost::shared_ptr BoostConnectionServerFactory::createConnectionServer(const Swift::HostAddress &hostAddress, int port) { return BoostConnectionServer::create(hostAddress, port, ioService, eventLoop); } } swift-im-2.0+dev6/Swiften/Network/DomainNameResolver.h0000644000175000017500000000117212227051774022637 0ustar kismithkismith/* * 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 #include #include namespace Swift { class DomainNameServiceQuery; class DomainNameAddressQuery; class SWIFTEN_API DomainNameResolver { public: virtual ~DomainNameResolver(); virtual boost::shared_ptr createServiceQuery(const std::string& name) = 0; virtual boost::shared_ptr createAddressQuery(const std::string& name) = 0; }; } swift-im-2.0+dev6/Swiften/Network/NATTraversalInterface.h0000644000175000017500000000113612227051774023234 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include namespace Swift { class NATTraversalInterface { public: virtual ~NATTraversalInterface(); virtual bool isAvailable() = 0; virtual boost::optional getPublicIP() = 0; virtual boost::optional addPortForward(int localPort, int publicPort) = 0; virtual bool removePortForward(const NATPortMapping&) = 0; }; } swift-im-2.0+dev6/Swiften/Network/TLSConnection.cpp0000644000175000017500000000574112227051774022130 0ustar kismithkismith/* * Copyright (c) 2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include namespace Swift { TLSConnection::TLSConnection(Connection::ref connection, TLSContextFactory* tlsFactory) : connection(connection) { context = tlsFactory->createTLSContext(); context->onDataForNetwork.connect(boost::bind(&TLSConnection::handleTLSDataForNetwork, this, _1)); context->onDataForApplication.connect(boost::bind(&TLSConnection::handleTLSDataForApplication, this, _1)); context->onConnected.connect(boost::bind(&TLSConnection::handleTLSConnectFinished, this, false)); context->onError.connect(boost::bind(&TLSConnection::handleTLSConnectFinished, this, true)); connection->onConnectFinished.connect(boost::bind(&TLSConnection::handleRawConnectFinished, this, _1)); connection->onDataRead.connect(boost::bind(&TLSConnection::handleRawDataRead, this, _1)); connection->onDataWritten.connect(boost::bind(&TLSConnection::handleRawDataWritten, this)); connection->onDisconnected.connect(boost::bind(&TLSConnection::handleRawDisconnected, this, _1)); } TLSConnection::~TLSConnection() { connection->onConnectFinished.disconnect(boost::bind(&TLSConnection::handleRawConnectFinished, this, _1)); connection->onDataRead.disconnect(boost::bind(&TLSConnection::handleRawDataRead, this, _1)); connection->onDataWritten.disconnect(boost::bind(&TLSConnection::handleRawDataWritten, this)); connection->onDisconnected.disconnect(boost::bind(&TLSConnection::handleRawDisconnected, this, _1)); delete context; } void TLSConnection::handleTLSConnectFinished(bool error) { onConnectFinished(error); if (error) { disconnect(); } } void TLSConnection::handleTLSDataForNetwork(const SafeByteArray& data) { connection->write(data); } void TLSConnection::handleTLSDataForApplication(const SafeByteArray& data) { onDataRead(boost::make_shared(data)); } void TLSConnection::connect(const HostAddressPort& address) { connection->connect(address); } void TLSConnection::disconnect() { connection->disconnect(); } void TLSConnection::write(const SafeByteArray& data) { context->handleDataFromApplication(data); } HostAddressPort TLSConnection::getLocalAddress() const { return connection->getLocalAddress(); } void TLSConnection::handleRawConnectFinished(bool error) { connection->onConnectFinished.disconnect(boost::bind(&TLSConnection::handleRawConnectFinished, this, _1)); if (error) { onConnectFinished(true); } else { context->connect(); } } void TLSConnection::handleRawDisconnected(const boost::optional& error) { onDisconnected(error); } void TLSConnection::handleRawDataRead(boost::shared_ptr data) { context->handleDataFromNetwork(*data); } void TLSConnection::handleRawDataWritten() { onDataWritten(); } } swift-im-2.0+dev6/Swiften/Network/ConnectionServer.h0000644000175000017500000000122012227051774022365 0ustar kismithkismith/* * 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 #include #include #include #include namespace Swift { class SWIFTEN_API ConnectionServer { public: virtual ~ConnectionServer(); virtual HostAddressPort getAddressPort() const = 0; virtual void start() = 0; virtual void stop() = 0; boost::signal)> onNewConnection; }; } swift-im-2.0+dev6/Swiften/Network/PlatformProxyProvider.h0000644000175000017500000000123312227051774023444 0ustar kismithkismith/* * Copyright (c) 2010-2011 Thilo Cestonaro * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #if defined(SWIFTEN_PLATFORM_MACOSX) #include namespace Swift { typedef MacOSXProxyProvider PlatformProxyProvider; } #elif defined(SWIFTEN_PLATFORM_WIN32) #include namespace Swift { typedef WindowsProxyProvider PlatformProxyProvider; } #else #include namespace Swift { typedef UnixProxyProvider PlatformProxyProvider; } #endif swift-im-2.0+dev6/Swiften/Network/DomainNameServiceQuery.h0000644000175000017500000000205212227051774023462 0ustar kismithkismith/* * 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 #include #include #include #include #include #include namespace Swift { class RandomGenerator; class SWIFTEN_API DomainNameServiceQuery { public: typedef boost::shared_ptr ref; struct Result { Result(const std::string& hostname = "", int port = -1, int priority = -1, int weight = -1) : hostname(hostname), port(port), priority(priority), weight(weight) {} std::string hostname; int port; int priority; int weight; }; virtual ~DomainNameServiceQuery(); virtual void run() = 0; static void sortResults(std::vector& queries, RandomGenerator& generator); boost::signal&)> onResult; }; } swift-im-2.0+dev6/Swiften/Network/UnixProxyProvider.cpp0000644000175000017500000000256512227051774023147 0ustar kismithkismith/* * Copyright (c) 2010-2011 Thilo Cestonaro * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include #include #include #include #include #if defined(HAVE_GCONF) # include "Swiften/Network/GConfProxyProvider.h" #endif namespace Swift { UnixProxyProvider::UnixProxyProvider() : gconfProxyProvider(0), environmentProxyProvider() { #if defined(HAVE_GCONF) gconfProxyProvider = new GConfProxyProvider(); #endif } UnixProxyProvider::~UnixProxyProvider() { #if defined(HAVE_GCONF) delete gconfProxyProvider; #endif } HostAddressPort UnixProxyProvider::getSOCKS5Proxy() const { HostAddressPort proxy; #if defined(HAVE_GCONF) proxy = gconfProxyProvider->getSOCKS5Proxy(); if(proxy.isValid()) { return proxy; } #endif proxy = environmentProxyProvider.getSOCKS5Proxy(); if(proxy.isValid()) { return proxy; } return HostAddressPort(HostAddress(), 0); } HostAddressPort UnixProxyProvider::getHTTPConnectProxy() const { HostAddressPort proxy; #if defined(HAVE_GCONF) proxy = gconfProxyProvider->getHTTPConnectProxy(); if(proxy.isValid()) { return proxy; } #endif proxy = environmentProxyProvider.getHTTPConnectProxy(); if(proxy.isValid()) { return proxy; } return HostAddressPort(HostAddress(), 0); } } swift-im-2.0+dev6/Swiften/Network/ProxiedConnection.h0000644000175000017500000000342512227051774022542 0ustar kismithkismith/* * Copyright (c) 2012 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include #include namespace boost { class thread; namespace system { class error_code; } } namespace Swift { class ConnectionFactory; class ProxiedConnection : public Connection, public boost::enable_shared_from_this { public: ProxiedConnection(DomainNameResolver* resolver, ConnectionFactory* connectionFactory, TimerFactory* timerFactory, const std::string& proxyHost, int proxyPort); ~ProxiedConnection(); virtual void listen(); virtual void connect(const HostAddressPort& address); virtual void disconnect(); virtual void write(const SafeByteArray& data); virtual HostAddressPort getLocalAddress() const; private: void handleConnectFinished(Connection::ref connection); void handleDataRead(boost::shared_ptr data); void handleDisconnected(const boost::optional& error); void cancelConnector(); protected: void setProxyInitializeFinished(bool success); virtual void initializeProxy() = 0; virtual void handleProxyInitializeData(boost::shared_ptr data) = 0; const HostAddressPort& getServer() const { return server_; } private: bool connected_; DomainNameResolver* resolver_; ConnectionFactory* connectionFactory_; TimerFactory* timerFactory_; std::string proxyHost_; int proxyPort_; HostAddressPort server_; Connector::ref connector_; boost::shared_ptr connection_; }; } swift-im-2.0+dev6/Swiften/Network/BoostConnectionServer.h0000644000175000017500000000374612227051774023413 0ustar kismithkismith/* * 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 #include #include #include #include #include #include #include #include namespace Swift { class SWIFTEN_API BoostConnectionServer : public ConnectionServer, public EventOwner, public boost::enable_shared_from_this { public: typedef boost::shared_ptr ref; enum Error { Conflict, UnknownError }; static ref create(int port, boost::shared_ptr ioService, EventLoop* eventLoop) { return ref(new BoostConnectionServer(port, ioService, eventLoop)); } static ref create(const HostAddress &address, int port, boost::shared_ptr ioService, EventLoop* eventLoop) { return ref(new BoostConnectionServer(address, port, ioService, eventLoop)); } virtual void start(); virtual void stop(); virtual HostAddressPort getAddressPort() const; boost::signal)> onStopped; private: BoostConnectionServer(int port, boost::shared_ptr ioService, EventLoop* eventLoop); BoostConnectionServer(const HostAddress &address, int port, boost::shared_ptr ioService, EventLoop* eventLoop); void stop(boost::optional e); void acceptNextConnection(); void handleAccept(boost::shared_ptr newConnection, const boost::system::error_code& error); private: HostAddress address_; int port_; boost::shared_ptr ioService_; EventLoop* eventLoop; boost::asio::ip::tcp::acceptor* acceptor_; }; } swift-im-2.0+dev6/Swiften/Network/NATTraversalForwardPortRequest.cpp0000644000175000017500000000047412227051774025515 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include namespace Swift { NATTraversalForwardPortRequest::~NATTraversalForwardPortRequest() { } } swift-im-2.0+dev6/Swiften/Network/BoostTimer.h0000644000175000017500000000217612227051774021201 0ustar kismithkismith/* * 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 #include #include #include #include namespace Swift { class EventLoop; class BoostTimer : public Timer, public EventOwner, public boost::enable_shared_from_this { public: typedef boost::shared_ptr ref; static ref create(int milliseconds, boost::shared_ptr service, EventLoop* eventLoop) { return ref(new BoostTimer(milliseconds, service, eventLoop)); } virtual void start(); virtual void stop(); private: BoostTimer(int milliseconds, boost::shared_ptr service, EventLoop* eventLoop); void handleTimerTick(const boost::system::error_code& error); private: int timeout; boost::shared_ptr ioService; boost::asio::deadline_timer timer; EventLoop* eventLoop; }; } swift-im-2.0+dev6/Swiften/Network/NATTraversalRemovePortForwardingRequest.cpp0000644000175000017500000000052712227051774027370 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include namespace Swift { NATTraversalRemovePortForwardingRequest::~NATTraversalRemovePortForwardingRequest() { } } swift-im-2.0+dev6/Swiften/Network/NATTraversalInterface.cpp0000644000175000017500000000050012227051774023561 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include #include namespace Swift { NATTraversalInterface::~NATTraversalInterface() { } } swift-im-2.0+dev6/Swiften/Network/NATTraverser.h0000644000175000017500000000140612227051774021425 0ustar kismithkismith/* * 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 namespace Swift { class NATTraversalGetPublicIPRequest; class NATTraversalForwardPortRequest; class NATTraversalRemovePortForwardingRequest; class NATTraverser { public: virtual ~NATTraverser(); virtual boost::shared_ptr createGetPublicIPRequest() = 0; virtual boost::shared_ptr createForwardPortRequest(int localPort, int publicPort) = 0; virtual boost::shared_ptr createRemovePortForwardingRequest(int localPort, int publicPort) = 0; }; } swift-im-2.0+dev6/Swiften/Network/PlatformDomainNameQuery.h0000644000175000017500000000124412227051774023650 0ustar kismithkismith/* * 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 namespace Swift { class PlatformDomainNameResolver; class PlatformDomainNameQuery { public: typedef boost::shared_ptr ref; PlatformDomainNameQuery(PlatformDomainNameResolver* resolver) : resolver(resolver) {} virtual ~PlatformDomainNameQuery() {} virtual void runBlocking() = 0; protected: PlatformDomainNameResolver* getResolver() { return resolver; } private: PlatformDomainNameResolver* resolver; }; } swift-im-2.0+dev6/Swiften/Network/ProxiedConnection.cpp0000644000175000017500000000577312227051774023105 0ustar kismithkismith/* * Copyright (c) 2012 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include using namespace Swift; ProxiedConnection::ProxiedConnection( DomainNameResolver* resolver, ConnectionFactory* connectionFactory, TimerFactory* timerFactory, const std::string& proxyHost, int proxyPort) : resolver_(resolver), connectionFactory_(connectionFactory), timerFactory_(timerFactory), proxyHost_(proxyHost), proxyPort_(proxyPort), server_(HostAddressPort(HostAddress("0.0.0.0"), 0)) { connected_ = false; } ProxiedConnection::~ProxiedConnection() { cancelConnector(); if (connection_) { connection_->onDataRead.disconnect(boost::bind(&ProxiedConnection::handleDataRead, shared_from_this(), _1)); connection_->onDisconnected.disconnect(boost::bind(&ProxiedConnection::handleDisconnected, shared_from_this(), _1)); } if (connected_) { std::cerr << "Warning: Connection was still established." << std::endl; } } void ProxiedConnection::cancelConnector() { if (connector_) { connector_->onConnectFinished.disconnect(boost::bind(&ProxiedConnection::handleConnectFinished, shared_from_this(), _1)); connector_->stop(); connector_.reset(); } } void ProxiedConnection::connect(const HostAddressPort& server) { server_ = server; connector_ = Connector::create(proxyHost_, proxyPort_, false, resolver_, connectionFactory_, timerFactory_); connector_->onConnectFinished.connect(boost::bind(&ProxiedConnection::handleConnectFinished, shared_from_this(), _1)); connector_->start(); } void ProxiedConnection::listen() { assert(false); connection_->listen(); } void ProxiedConnection::disconnect() { connected_ = false; connection_->disconnect(); } void ProxiedConnection::handleDisconnected(const boost::optional& error) { onDisconnected(error); } void ProxiedConnection::write(const SafeByteArray& data) { connection_->write(data); } void ProxiedConnection::handleConnectFinished(Connection::ref connection) { cancelConnector(); if (connection) { connection_ = connection; connection_->onDataRead.connect(boost::bind(&ProxiedConnection::handleDataRead, shared_from_this(), _1)); connection_->onDisconnected.connect(boost::bind(&ProxiedConnection::handleDisconnected, shared_from_this(), _1)); initializeProxy(); } else { onConnectFinished(true); } } void ProxiedConnection::handleDataRead(boost::shared_ptr data) { if (!connected_) { handleProxyInitializeData(data); } else { onDataRead(data); } } HostAddressPort ProxiedConnection::getLocalAddress() const { return connection_->getLocalAddress(); } void ProxiedConnection::setProxyInitializeFinished(bool success) { connected_ = success; if (!success) { disconnect(); } onConnectFinished(!success); } swift-im-2.0+dev6/Swiften/Network/Timer.cpp0000644000175000017500000000035612227051774020523 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { Timer::~Timer() { } } swift-im-2.0+dev6/Swiften/Network/NetworkEnvironment.cpp0000644000175000017500000000143312227051774023316 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include namespace Swift { NetworkEnvironment::~NetworkEnvironment() { } HostAddress NetworkEnvironment::getLocalAddress() const { std::vector networkInterfaces = getNetworkInterfaces(); foreach (const NetworkInterface& iface, networkInterfaces) { if (!iface.isLoopback()) { foreach (const HostAddress& address, iface.getAddresses()) { if (address.getRawAddress().is_v4()) { return address; } } } } return HostAddress(); } } swift-im-2.0+dev6/Swiften/Network/HTTPConnectProxiedConnectionFactory.h0000644000175000017500000000247512227051774026110 0ustar kismithkismith/* * Copyright (c) 2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ /* * Copyright (c) 2010-2011 Thilo Cestonaro * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include #include namespace Swift { class DomainNameResolver; class TimerFactory; class EventLoop; class HTTPConnectProxiedConnectionFactory : public ConnectionFactory { public: HTTPConnectProxiedConnectionFactory(DomainNameResolver* resolver, ConnectionFactory* connectionFactory, TimerFactory* timerFactory, const std::string& proxyHost, int proxyPort); HTTPConnectProxiedConnectionFactory(DomainNameResolver* resolver, ConnectionFactory* connectionFactory, TimerFactory* timerFactory, const std::string& proxyHost, int proxyPort, const SafeString& authID, const SafeString& authPassword); virtual boost::shared_ptr createConnection(); private: DomainNameResolver* resolver_; ConnectionFactory* connectionFactory_; TimerFactory* timerFactory_; std::string proxyHost_; int proxyPort_; SafeString authID_; SafeString authPassword_; }; } swift-im-2.0+dev6/Swiften/Network/HostAddress.cpp0000644000175000017500000000272712227051774021672 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include namespace Swift { HostAddress::HostAddress() { } HostAddress::HostAddress(const std::string& address) { try { address_ = boost::asio::ip::address::from_string(address); } catch (const std::exception&) { } } HostAddress::HostAddress(const unsigned char* address, int length) { assert(length == 4 || length == 16); if (length == 4) { boost::asio::ip::address_v4::bytes_type data; for (int i = 0; i < length; ++i) { data[i] = address[i]; } address_ = boost::asio::ip::address(boost::asio::ip::address_v4(data)); } else { boost::asio::ip::address_v6::bytes_type data; for (int i = 0; i < length; ++i) { data[i] = address[i]; } address_ = boost::asio::ip::address(boost::asio::ip::address_v6(data)); } } HostAddress::HostAddress(const boost::asio::ip::address& address) : address_(address) { } std::string HostAddress::toString() const { return address_.to_string(); } bool HostAddress::isValid() const { return !(address_.is_v4() && address_.to_v4().to_ulong() == 0); } boost::asio::ip::address HostAddress::getRawAddress() const { return address_; } } swift-im-2.0+dev6/Swiften/Network/BoostConnectionServer.cpp0000644000175000017500000000545012227051774023740 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include namespace Swift { BoostConnectionServer::BoostConnectionServer(int port, boost::shared_ptr ioService, EventLoop* eventLoop) : port_(port), ioService_(ioService), eventLoop(eventLoop), acceptor_(NULL) { } BoostConnectionServer::BoostConnectionServer(const HostAddress &address, int port, boost::shared_ptr ioService, EventLoop* eventLoop) : address_(address), port_(port), ioService_(ioService), eventLoop(eventLoop), acceptor_(NULL) { } void BoostConnectionServer::start() { try { assert(!acceptor_); if (address_.isValid()) { acceptor_ = new boost::asio::ip::tcp::acceptor( *ioService_, boost::asio::ip::tcp::endpoint(address_.getRawAddress(), port_)); } else { acceptor_ = new boost::asio::ip::tcp::acceptor( *ioService_, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port_)); } acceptNextConnection(); } catch (const boost::system::system_error& e) { if (e.code() == boost::asio::error::address_in_use) { eventLoop->postEvent(boost::bind(boost::ref(onStopped), Conflict), shared_from_this()); } else { eventLoop->postEvent(boost::bind(boost::ref(onStopped), UnknownError), shared_from_this()); } } } void BoostConnectionServer::stop() { stop(boost::optional()); } void BoostConnectionServer::stop(boost::optional e) { if (acceptor_) { acceptor_->close(); delete acceptor_; acceptor_ = NULL; } eventLoop->postEvent(boost::bind(boost::ref(onStopped), e), shared_from_this()); } void BoostConnectionServer::acceptNextConnection() { BoostConnection::ref newConnection(BoostConnection::create(ioService_, eventLoop)); acceptor_->async_accept(newConnection->getSocket(), boost::bind(&BoostConnectionServer::handleAccept, shared_from_this(), newConnection, boost::asio::placeholders::error)); } void BoostConnectionServer::handleAccept(boost::shared_ptr newConnection, const boost::system::error_code& error) { if (error) { eventLoop->postEvent( boost::bind( &BoostConnectionServer::stop, shared_from_this(), UnknownError), shared_from_this()); } else { eventLoop->postEvent( boost::bind(boost::ref(onNewConnection), newConnection), shared_from_this()); newConnection->listen(); acceptNextConnection(); } } HostAddressPort BoostConnectionServer::getAddressPort() const { if (acceptor_) { return HostAddressPort(acceptor_->local_endpoint()); } else { return HostAddressPort(); } } } swift-im-2.0+dev6/Swiften/Network/PlatformDomainNameServiceQuery.cpp0000644000175000017500000001152512227051774025527 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #pragma GCC diagnostic ignored "-Wold-style-cast" #include #include #ifdef SWIFTEN_PLATFORM_WINDOWS #undef UNICODE #include #include #ifndef DNS_TYPE_SRV #define DNS_TYPE_SRV 33 #endif #else #include #include #include #endif #include #include #include #include #include #include #include using namespace Swift; namespace Swift { PlatformDomainNameServiceQuery::PlatformDomainNameServiceQuery(const std::string& service, EventLoop* eventLoop, PlatformDomainNameResolver* resolver) : PlatformDomainNameQuery(resolver), eventLoop(eventLoop), service(service) { } void PlatformDomainNameServiceQuery::run() { getResolver()->addQueryToQueue(shared_from_this()); } void PlatformDomainNameServiceQuery::runBlocking() { SWIFT_LOG(debug) << "Querying " << service << std::endl; std::vector records; #if defined(SWIFTEN_PLATFORM_WINDOWS) DNS_RECORD* responses; // FIXME: This conversion doesn't work if unicode is deffed above if (DnsQuery(service.c_str(), DNS_TYPE_SRV, DNS_QUERY_STANDARD, NULL, &responses, NULL) != ERROR_SUCCESS) { emitError(); return; } DNS_RECORD* currentEntry = responses; while (currentEntry) { if (currentEntry->wType == DNS_TYPE_SRV) { DomainNameServiceQuery::Result record; record.priority = currentEntry->Data.SRV.wPriority; record.weight = currentEntry->Data.SRV.wWeight; record.port = currentEntry->Data.SRV.wPort; // The pNameTarget is actually a PCWSTR, so I would have expected this // conversion to not work at all, but it does. // Actually, it doesn't. Fix this and remove explicit cast // Remove unicode undef above as well record.hostname = std::string((const char*) currentEntry->Data.SRV.pNameTarget); records.push_back(record); } currentEntry = currentEntry->pNext; } DnsRecordListFree(responses, DnsFreeRecordList); #else // Make sure we reinitialize the domain list every time res_init(); ByteArray response; response.resize(NS_PACKETSZ); int responseLength = res_query(const_cast(service.c_str()), ns_c_in, ns_t_srv, reinterpret_cast(vecptr(response)), response.size()); if (responseLength == -1) { SWIFT_LOG(debug) << "Error" << std::endl; emitError(); return; } // Parse header HEADER* header = reinterpret_cast(vecptr(response)); unsigned char* messageStart = vecptr(response); unsigned char* messageEnd = messageStart + responseLength; unsigned char* currentEntry = messageStart + NS_HFIXEDSZ; // Skip over the queries int queriesCount = ntohs(header->qdcount); while (queriesCount > 0) { int entryLength = dn_skipname(currentEntry, messageEnd); if (entryLength < 0) { emitError(); return; } currentEntry += entryLength + NS_QFIXEDSZ; queriesCount--; } // Process the SRV answers int answersCount = ntohs(header->ancount); while (answersCount > 0) { DomainNameServiceQuery::Result record; int entryLength = dn_skipname(currentEntry, messageEnd); currentEntry += entryLength; currentEntry += NS_RRFIXEDSZ; // Priority if (currentEntry + 2 >= messageEnd) { emitError(); return; } record.priority = ns_get16(currentEntry); currentEntry += 2; // Weight if (currentEntry + 2 >= messageEnd) { emitError(); return; } record.weight = ns_get16(currentEntry); currentEntry += 2; // Port if (currentEntry + 2 >= messageEnd) { emitError(); return; } record.port = ns_get16(currentEntry); currentEntry += 2; // Hostname if (currentEntry >= messageEnd) { emitError(); return; } ByteArray entry; entry.resize(NS_MAXDNAME); entryLength = dn_expand(messageStart, messageEnd, currentEntry, reinterpret_cast(vecptr(entry)), entry.size()); if (entryLength < 0) { emitError(); return; } record.hostname = std::string(reinterpret_cast(vecptr(entry))); records.push_back(record); currentEntry += entryLength; answersCount--; } #endif BoostRandomGenerator generator; DomainNameServiceQuery::sortResults(records, generator); //std::cout << "Sending out " << records.size() << " SRV results " << std::endl; eventLoop->postEvent(boost::bind(boost::ref(onResult), records), shared_from_this()); } void PlatformDomainNameServiceQuery::emitError() { eventLoop->postEvent(boost::bind(boost::ref(onResult), std::vector()), shared_from_this()); } } swift-im-2.0+dev6/Swiften/Network/SOCKS5ProxiedConnection.cpp0000644000175000017500000001007012227051774023757 0ustar kismithkismith/* * Copyright (c) 2010-2011 Thilo Cestonaro * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include #include #include #include #include #include #include #include #include using namespace Swift; SOCKS5ProxiedConnection::SOCKS5ProxiedConnection( DomainNameResolver* resolver, ConnectionFactory* connectionFactory, TimerFactory* timerFactory, const std::string& proxyHost, int proxyPort) : ProxiedConnection(resolver, connectionFactory, timerFactory, proxyHost, proxyPort) { } void SOCKS5ProxiedConnection::initializeProxy() { proxyState_ = ProxyAuthenticating; SafeByteArray socksConnect; socksConnect.push_back(0x05); // VER = SOCKS5 = 0x05 socksConnect.push_back(0x01); // Number of authentication methods after this byte. socksConnect.push_back(0x00); // 0x00 == no authentication // buffer.push_back(0x01); // 0x01 == GSSAPI // buffer.push_back(0x02); // 0x02 == Username/Password // rest see RFC 1928 (http://tools.ietf.org/html/rfc1928) write(socksConnect); } void SOCKS5ProxiedConnection::handleProxyInitializeData(boost::shared_ptr data) { SafeByteArray socksConnect; boost::asio::ip::address rawAddress = getServer().getAddress().getRawAddress(); assert(rawAddress.is_v4() || rawAddress.is_v6()); if (proxyState_ == ProxyAuthenticating) { SWIFT_LOG(debug) << "ProxyAuthenticating response received, reply with the connect BYTEs" << std::endl; unsigned char choosenMethod = static_cast ((*data)[1]); if ((*data)[0] == 0x05 && choosenMethod != 0xFF) { switch(choosenMethod) { // use the correct Method case 0x00: try { proxyState_ = ProxyConnecting; socksConnect.push_back(0x05); // VER = SOCKS5 = 0x05 socksConnect.push_back(0x01); // Construct a TCP connection. (CMD) socksConnect.push_back(0x00); // reserved. socksConnect.push_back(rawAddress.is_v4() ? 0x01 : 0x04); // IPv4 == 0x01, Hostname == 0x02, IPv6 == 0x04. (ATYP) size_t size = rawAddress.is_v4() ? rawAddress.to_v4().to_bytes().size() : rawAddress.to_v6().to_bytes().size(); for (size_t s = 0; s < size; s++) { unsigned char uc; if(rawAddress.is_v4()) { uc = rawAddress.to_v4().to_bytes()[s]; // the address. } else { uc = rawAddress.to_v6().to_bytes()[s]; // the address. } socksConnect.push_back(static_cast(uc)); } socksConnect.push_back(static_cast ((getServer().getPort() >> 8) & 0xFF)); // highbyte of the port. socksConnect.push_back(static_cast (getServer().getPort() & 0xFF)); // lowbyte of the port. write(socksConnect); return; } catch(...) { std::cerr << "exception caught" << std::endl; } write(socksConnect); break; default: setProxyInitializeFinished(true); break; } return; } setProxyInitializeFinished(false); } else if (proxyState_ == ProxyConnecting) { SWIFT_LOG(debug) << "Connect response received, check if successfully." << std::endl; SWIFT_LOG(debug) << "Errorbyte: 0x" << std::hex << static_cast ((*data)[1]) << std::dec << std::endl; /* data.at(1) can be one of the following: 0x00 succeeded 0x01 general SOCKS server failure 0x02 connection not allowed by ruleset 0x03 Network unreachable 0x04 Host unreachable 0x05 Connection refused 0x06 TTL expired 0x07 Command not supported (CMD) 0x08 Address type not supported (ATYP) 0x09 bis 0xFF unassigned */ if ((*data)[0] == 0x05 && (*data)[1] == 0x0) { SWIFT_LOG(debug) << "Successfully connected the server via the proxy." << std::endl; setProxyInitializeFinished(true); } else { std::cerr << "SOCKS Proxy returned an error: " << std::hex << (*data)[1] << std::endl; setProxyInitializeFinished(false); } } } swift-im-2.0+dev6/Swiften/Network/PlatformDomainNameAddressQuery.cpp0000644000175000017500000000440012227051774025506 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include namespace Swift { PlatformDomainNameAddressQuery::PlatformDomainNameAddressQuery(const std::string& host, EventLoop* eventLoop, PlatformDomainNameResolver* resolver) : PlatformDomainNameQuery(resolver), hostname(host), eventLoop(eventLoop) { } void PlatformDomainNameAddressQuery::run() { getResolver()->addQueryToQueue(shared_from_this()); } void PlatformDomainNameAddressQuery::runBlocking() { //std::cout << "PlatformDomainNameResolver::doRun()" << std::endl; boost::asio::ip::tcp::resolver resolver(ioService); boost::asio::ip::tcp::resolver::query query(hostname, "5222"); try { //std::cout << "PlatformDomainNameResolver::doRun(): Resolving" << std::endl; boost::asio::ip::tcp::resolver::iterator endpointIterator = resolver.resolve(query); //std::cout << "PlatformDomainNameResolver::doRun(): Resolved" << std::endl; if (endpointIterator == boost::asio::ip::tcp::resolver::iterator()) { //std::cout << "PlatformDomainNameResolver::doRun(): Error 1" << std::endl; emitError(); } else { std::vector results; for ( ; endpointIterator != boost::asio::ip::tcp::resolver::iterator(); ++endpointIterator) { boost::asio::ip::address address = (*endpointIterator).endpoint().address(); results.push_back(address.is_v4() ? HostAddress(&address.to_v4().to_bytes()[0], 4) : HostAddress(&address.to_v6().to_bytes()[0], 16)); } //std::cout << "PlatformDomainNameResolver::doRun(): Success" << std::endl; eventLoop->postEvent( boost::bind(boost::ref(onResult), results, boost::optional()), shared_from_this()); } } catch (...) { //std::cout << "PlatformDomainNameResolver::doRun(): Error 2" << std::endl; emitError(); } } void PlatformDomainNameAddressQuery::emitError() { eventLoop->postEvent(boost::bind(boost::ref(onResult), std::vector(), boost::optional(DomainNameResolveError())), shared_from_this()); } } swift-im-2.0+dev6/Swiften/Network/NATTraversalGetPublicIPRequest.cpp0000644000175000017500000000047412227051774025353 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include namespace Swift { NATTraversalGetPublicIPRequest::~NATTraversalGetPublicIPRequest() { } } swift-im-2.0+dev6/Swiften/Network/FakeConnection.h0000644000175000017500000000255712227051774022003 0ustar kismithkismith/* * 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 #include #include #include #include #include #include #include namespace Swift { class SWIFTEN_API FakeConnection : public Connection, public EventOwner, public boost::enable_shared_from_this { public: enum State { Initial, Connecting, Connected, Disconnected, DisconnectedWithError }; FakeConnection(EventLoop* eventLoop); ~FakeConnection(); virtual void listen(); virtual HostAddressPort getLocalAddress() const { return HostAddressPort(); } void setError(const Error& e); virtual void connect(const HostAddressPort& address); virtual void disconnect(); virtual void write(const SafeByteArray& data) { dataWritten.push_back(data); } void setDelayConnect() { delayConnect = true; } EventLoop* eventLoop; boost::optional connectedTo; std::vector dataWritten; boost::optional error; State state; bool delayConnect; }; } swift-im-2.0+dev6/Swiften/Network/StaticDomainNameResolver.cpp0000644000175000017500000001044212227051774024342 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include using namespace Swift; namespace { struct ServiceQuery : public DomainNameServiceQuery, public boost::enable_shared_from_this { ServiceQuery(const std::string& service, Swift::StaticDomainNameResolver* resolver, EventLoop* eventLoop, boost::shared_ptr owner) : eventLoop(eventLoop), service(service), resolver(resolver), owner(owner) {} virtual void run() { if (!resolver->getIsResponsive()) { return; } std::vector results; for(StaticDomainNameResolver::ServicesCollection::const_iterator i = resolver->getServices().begin(); i != resolver->getServices().end(); ++i) { if (i->first == service) { results.push_back(i->second); } } eventLoop->postEvent(boost::bind(&ServiceQuery::emitOnResult, shared_from_this(), results), owner); } void emitOnResult(std::vector results) { onResult(results); } EventLoop* eventLoop; std::string service; StaticDomainNameResolver* resolver; boost::shared_ptr owner; }; struct AddressQuery : public DomainNameAddressQuery, public boost::enable_shared_from_this { AddressQuery(const std::string& host, StaticDomainNameResolver* resolver, EventLoop* eventLoop, boost::shared_ptr owner) : eventLoop(eventLoop), host(host), resolver(resolver), owner(owner) {} virtual void run() { if (!resolver->getIsResponsive()) { return; } StaticDomainNameResolver::AddressesMap::const_iterator i = resolver->getAddresses().find(host); if (i != resolver->getAddresses().end()) { eventLoop->postEvent( boost::bind(&AddressQuery::emitOnResult, shared_from_this(), i->second, boost::optional())); } else { eventLoop->postEvent(boost::bind(&AddressQuery::emitOnResult, shared_from_this(), std::vector(), boost::optional(DomainNameResolveError())), owner); } } void emitOnResult(std::vector results, boost::optional error) { onResult(results, error); } EventLoop* eventLoop; std::string host; StaticDomainNameResolver* resolver; boost::shared_ptr owner; }; } class StaticDomainNameResolverEventOwner : public EventOwner { public: ~StaticDomainNameResolverEventOwner() { } }; namespace Swift { StaticDomainNameResolver::StaticDomainNameResolver(EventLoop* eventLoop) : eventLoop(eventLoop), isResponsive(true), owner(new StaticDomainNameResolverEventOwner()) { } StaticDomainNameResolver::~StaticDomainNameResolver() { eventLoop->removeEventsFromOwner(owner); } void StaticDomainNameResolver::addAddress(const std::string& domain, const HostAddress& address) { addresses[domain].push_back(address); } void StaticDomainNameResolver::addService(const std::string& service, const DomainNameServiceQuery::Result& result) { services.push_back(std::make_pair(service, result)); } void StaticDomainNameResolver::addXMPPClientService(const std::string& domain, const HostAddressPort& address) { static int hostid = 0; std::string hostname(std::string("host-") + boost::lexical_cast(hostid)); hostid++; addService("_xmpp-client._tcp." + domain, ServiceQuery::Result(hostname, address.getPort(), 0, 0)); addAddress(hostname, address.getAddress()); } void StaticDomainNameResolver::addXMPPClientService(const std::string& domain, const std::string& hostname, int port) { addService("_xmpp-client._tcp." + domain, ServiceQuery::Result(hostname, port, 0, 0)); } boost::shared_ptr StaticDomainNameResolver::createServiceQuery(const std::string& name) { return boost::shared_ptr(new ServiceQuery(name, this, eventLoop, owner)); } boost::shared_ptr StaticDomainNameResolver::createAddressQuery(const std::string& name) { return boost::shared_ptr(new AddressQuery(name, this, eventLoop, owner)); } } swift-im-2.0+dev6/Swiften/Network/CachingDomainNameResolver.h0000644000175000017500000000144612227051774024120 0ustar kismithkismith/* * Copyright (c) 2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include /* * FIXME: Does not do any caching yet. */ namespace Swift { class EventLoop; class CachingDomainNameResolver : public DomainNameResolver { public: CachingDomainNameResolver(DomainNameResolver* realResolver, EventLoop* eventLoop); ~CachingDomainNameResolver(); virtual DomainNameServiceQuery::ref createServiceQuery(const std::string& name); virtual DomainNameAddressQuery::ref createAddressQuery(const std::string& name); private: DomainNameResolver* realResolver; }; } swift-im-2.0+dev6/Swiften/Network/Connection.cpp0000644000175000017500000000043512227051774021540 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include using namespace Swift; Connection::Connection() { } Connection::~Connection() { } swift-im-2.0+dev6/Swiften/Network/NetworkInterface.h0000644000175000017500000000141612227051774022360 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include namespace Swift { class NetworkInterface { public: NetworkInterface(const std::string& name, bool loopback) : name(name), loopback(loopback) { } void addAddress(const HostAddress& address) { addresses.push_back(address); } const std::vector& getAddresses() const { return addresses; } const std::string& getName() const { return name; } bool isLoopback() const { return loopback; } private: std::string name; bool loopback; std::vector addresses; }; } swift-im-2.0+dev6/Swiften/Network/TLSConnection.h0000644000175000017500000000244712227051774021575 0ustar kismithkismith/* * Copyright (c) 2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include #include namespace Swift { class HostAddressPort; class TLSContextFactory; class TLSContext; class TLSConnection : public Connection { public: TLSConnection(Connection::ref connection, TLSContextFactory* tlsFactory); virtual ~TLSConnection(); virtual void listen() {assert(false);}; virtual void connect(const HostAddressPort& address); virtual void disconnect(); virtual void write(const SafeByteArray& data); virtual HostAddressPort getLocalAddress() const; private: void handleRawConnectFinished(bool error); void handleRawDisconnected(const boost::optional& error); void handleRawDataRead(boost::shared_ptr data); void handleRawDataWritten(); void handleTLSConnectFinished(bool error); void handleTLSDataForNetwork(const SafeByteArray& data); void handleTLSDataForApplication(const SafeByteArray& data); private: TLSContext* context; Connection::ref connection; }; } swift-im-2.0+dev6/Swiften/Network/MiniUPnPInterface.cpp0000644000175000017500000000573312227051774022727 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include #include #include #include #include #include #include namespace Swift { struct MiniUPnPInterface::Private { bool isValid; std::string localAddress; UPNPDev* deviceList; UPNPUrls urls; IGDdatas data; }; MiniUPnPInterface::MiniUPnPInterface() : p(boost::make_shared()) { p->isValid = false; int error = 0; p->deviceList = upnpDiscover(1500 /* timeout in ms */, 0, 0, 0, 0 /* do IPv6? */, &error); if (!p->deviceList) { return; } char lanAddress[64]; if (!UPNP_GetValidIGD(p->deviceList, &p->urls, &p->data, lanAddress, sizeof(lanAddress))) { return; } p->localAddress = std::string(lanAddress); p->isValid = true; } MiniUPnPInterface::~MiniUPnPInterface() { if (p->isValid) { FreeUPNPUrls(&p->urls); } freeUPNPDevlist(p->deviceList); } boost::optional MiniUPnPInterface::getPublicIP() { if (!p->isValid) { return boost::optional(); } char externalIPAddress[40]; int ret = UPNP_GetExternalIPAddress(p->urls.controlURL, p->data.first.servicetype, externalIPAddress); if (ret != UPNPCOMMAND_SUCCESS) { return boost::optional(); } else { return HostAddress(std::string(externalIPAddress)); } } boost::optional MiniUPnPInterface::addPortForward(int actualLocalPort, int actualPublicPort) { if (!p->isValid) { return boost::optional(); } NATPortMapping mapping(actualLocalPort, actualPublicPort, NATPortMapping::TCP); std::string publicPort = boost::lexical_cast(mapping.getPublicPort()); std::string localPort = boost::lexical_cast(mapping.getLocalPort()); std::string leaseSeconds = boost::lexical_cast(mapping.getLeaseInSeconds()); int ret = UPNP_AddPortMapping(p->urls.controlURL, p->data.first.servicetype, publicPort.c_str(), localPort.c_str(), p->localAddress.c_str(), 0, mapping.getPublicPort() == NATPortMapping::TCP ? "TCP" : "UDP", 0, leaseSeconds.c_str()); if (ret == UPNPCOMMAND_SUCCESS) { return mapping; } else { return boost::optional(); } } bool MiniUPnPInterface::removePortForward(const NATPortMapping& mapping) { if (!p->isValid) { return false; } std::string publicPort = boost::lexical_cast(mapping.getPublicPort()); std::string localPort = boost::lexical_cast(mapping.getLocalPort()); std::string leaseSeconds = boost::lexical_cast(mapping.getLeaseInSeconds()); int ret = UPNP_DeletePortMapping(p->urls.controlURL, p->data.first.servicetype, publicPort.c_str(), mapping.getProtocol() == NATPortMapping::TCP ? "TCP" : "UDP", 0); return ret == UPNPCOMMAND_SUCCESS; } bool MiniUPnPInterface::isAvailable() { return p->isValid; } } swift-im-2.0+dev6/Swiften/Network/NullNATTraverser.cpp0000644000175000017500000000426212227051774022616 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include namespace Swift { class NullNATTraversalGetPublicIPRequest : public NATTraversalGetPublicIPRequest { public: NullNATTraversalGetPublicIPRequest(EventLoop* eventLoop) : eventLoop(eventLoop) { } virtual void run() { eventLoop->postEvent(boost::bind(boost::ref(onResult), boost::optional())); } private: EventLoop* eventLoop; }; class NullNATTraversalForwardPortRequest : public NATTraversalForwardPortRequest { public: NullNATTraversalForwardPortRequest(EventLoop* eventLoop) : eventLoop(eventLoop) { } virtual void run() { eventLoop->postEvent(boost::bind(boost::ref(onResult), boost::optional())); } private: EventLoop* eventLoop; }; class NullNATTraversalRemovePortForwardingRequest : public NATTraversalRemovePortForwardingRequest { public: NullNATTraversalRemovePortForwardingRequest(EventLoop* eventLoop) : eventLoop(eventLoop) { } virtual void run() { eventLoop->postEvent(boost::bind(boost::ref(onResult), boost::optional(true))); } private: EventLoop* eventLoop; }; NullNATTraverser::NullNATTraverser(EventLoop* eventLoop) : eventLoop(eventLoop) { } boost::shared_ptr NullNATTraverser::createGetPublicIPRequest() { return boost::make_shared(eventLoop); } boost::shared_ptr NullNATTraverser::createForwardPortRequest(int, int) { return boost::make_shared(eventLoop); } boost::shared_ptr NullNATTraverser::createRemovePortForwardingRequest(int, int) { return boost::make_shared(eventLoop); } } swift-im-2.0+dev6/Swiften/Network/SOCKS5ProxiedConnectionFactory.cpp0000644000175000017500000000151612227051774025314 0ustar kismithkismith/* * Copyright (c) 2010-2011 Thilo Cestonaro * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include #include namespace Swift { SOCKS5ProxiedConnectionFactory::SOCKS5ProxiedConnectionFactory(DomainNameResolver* resolver, ConnectionFactory* connectionFactory, TimerFactory* timerFactory, const std::string& proxyHost, int proxyPort) : resolver_(resolver), connectionFactory_(connectionFactory), timerFactory_(timerFactory), proxyHost_(proxyHost), proxyPort_(proxyPort) { } boost::shared_ptr SOCKS5ProxiedConnectionFactory::createConnection() { return SOCKS5ProxiedConnection::create(resolver_, connectionFactory_, timerFactory_, proxyHost_, proxyPort_); } } swift-im-2.0+dev6/Swiften/Network/StaticDomainNameResolver.h0000644000175000017500000000351712227051774024014 0ustar kismithkismith/* * 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 #include #include #include #include #include #include #include #include namespace Swift { class SWIFTEN_API StaticDomainNameResolver : public DomainNameResolver { public: typedef std::map > AddressesMap; typedef std::vector< std::pair > ServicesCollection; public: StaticDomainNameResolver(EventLoop* eventLoop); ~StaticDomainNameResolver(); void addAddress(const std::string& domain, const HostAddress& address); void addService(const std::string& service, const DomainNameServiceQuery::Result& result); void addXMPPClientService(const std::string& domain, const HostAddressPort&); void addXMPPClientService(const std::string& domain, const std::string& host, int port); const AddressesMap& getAddresses() const { return addresses; } const ServicesCollection& getServices() const { return services; } bool getIsResponsive() const { return isResponsive; } void setIsResponsive(bool b) { isResponsive = b; } virtual boost::shared_ptr createServiceQuery(const std::string& name); virtual boost::shared_ptr createAddressQuery(const std::string& name); private: EventLoop* eventLoop; bool isResponsive; AddressesMap addresses; ServicesCollection services; boost::shared_ptr owner; }; } swift-im-2.0+dev6/Swiften/Network/WindowsProxyProvider.h0000644000175000017500000000122412227051774023312 0ustar kismithkismith/* * Copyright (c) 2010-2011 Thilo Cestonaro * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include namespace Swift { class SWIFTEN_API WindowsProxyProvider : public ProxyProvider { public: WindowsProxyProvider(); virtual HostAddressPort getHTTPConnectProxy() const; virtual HostAddressPort getSOCKS5Proxy() const; private: HostAddressPort getAsHostAddressPort(std::string proxy); bool proxyEnabled(HKEY hKey) const; HostAddressPort socksProxy; HostAddressPort httpProxy; }; } swift-im-2.0+dev6/Swiften/Network/NATPMPInterface.cpp0000644000175000017500000000676712227051774022277 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include #include #include // This has to be included after the previous headers, because of WIN32 macro // being defined somewhere. #include #pragma GCC diagnostic ignored "-Wold-style-cast" namespace Swift { struct NATPMPInterface::Private { natpmp_t natpmp; }; NATPMPInterface::NATPMPInterface() : p(boost::make_shared()) { initnatpmp(&p->natpmp, 0, 0); } NATPMPInterface::~NATPMPInterface() { closenatpmp(&p->natpmp); } bool NATPMPInterface::isAvailable() { return getPublicIP(); } boost::optional NATPMPInterface::getPublicIP() { if (sendpublicaddressrequest(&p->natpmp) < 0) { SWIFT_LOG(debug) << "Failed to send NAT-PMP public address request!" << std::endl; return boost::optional(); } int r = 0; natpmpresp_t response; do { fd_set fds; struct timeval timeout; FD_ZERO(&fds); FD_SET(p->natpmp.s, &fds); getnatpmprequesttimeout(&p->natpmp, &timeout); select(FD_SETSIZE, &fds, NULL, NULL, &timeout); r = readnatpmpresponseorretry(&p->natpmp, &response); } while (r == NATPMP_TRYAGAIN); if (r == 0) { return boost::optional(HostAddress(reinterpret_cast(&(response.pnu.publicaddress.addr)), 4)); } else { SWIFT_LOG(debug) << "Inavlid NAT-PMP response." << std::endl; return boost::optional(); } } boost::optional NATPMPInterface::addPortForward(int localPort, int publicPort) { NATPortMapping mapping(localPort, publicPort, NATPortMapping::TCP); if (sendnewportmappingrequest(&p->natpmp, mapping.getProtocol() == NATPortMapping::TCP ? NATPMP_PROTOCOL_TCP : NATPMP_PROTOCOL_UDP, mapping.getLeaseInSeconds(), mapping.getPublicPort(), mapping.getLocalPort()) < 0) { SWIFT_LOG(debug) << "Failed to send NAT-PMP port forwarding request!" << std::endl; return boost::optional(); } int r = 0; natpmpresp_t response; do { fd_set fds; struct timeval timeout; FD_ZERO(&fds); FD_SET(p->natpmp.s, &fds); getnatpmprequesttimeout(&p->natpmp, &timeout); select(FD_SETSIZE, &fds, NULL, NULL, &timeout); r = readnatpmpresponseorretry(&p->natpmp, &response); } while(r == NATPMP_TRYAGAIN); if (r == 0) { NATPortMapping result(response.pnu.newportmapping.privateport, response.pnu.newportmapping.mappedpublicport, NATPortMapping::TCP, response.pnu.newportmapping.lifetime); return result; } else { SWIFT_LOG(debug) << "Invalid NAT-PMP response." << std::endl; return boost::optional(); } } bool NATPMPInterface::removePortForward(const NATPortMapping& mapping) { if (sendnewportmappingrequest(&p->natpmp, mapping.getProtocol() == NATPortMapping::TCP ? NATPMP_PROTOCOL_TCP : NATPMP_PROTOCOL_UDP, 0, 0, mapping.getLocalPort()) < 0) { SWIFT_LOG(debug) << "Failed to send NAT-PMP remove forwarding request!" << std::endl; return false; } int r = 0; natpmpresp_t response; do { fd_set fds; struct timeval timeout; FD_ZERO(&fds); FD_SET(p->natpmp.s, &fds); getnatpmprequesttimeout(&p->natpmp, &timeout); select(FD_SETSIZE, &fds, NULL, NULL, &timeout); r = readnatpmpresponseorretry(&p->natpmp, &response); } while(r == NATPMP_TRYAGAIN); if (r == 0) { return true; } else { SWIFT_LOG(debug) << "Invalid NAT-PMP response." << std::endl; return false; } } } swift-im-2.0+dev6/Swiften/Network/DomainNameResolver.cpp0000644000175000017500000000042512227051774023172 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { DomainNameResolver::~DomainNameResolver() { } } swift-im-2.0+dev6/Swiften/Network/HTTPConnectProxiedConnectionFactory.cpp0000644000175000017500000000301612227051774026433 0ustar kismithkismith/* * Copyright (c) 2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ /* * Copyright (c) 2010-2011 Thilo Cestonaro * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include #include namespace Swift { HTTPConnectProxiedConnectionFactory::HTTPConnectProxiedConnectionFactory(DomainNameResolver* resolver, ConnectionFactory* connectionFactory, TimerFactory* timerFactory, const std::string& proxyHost, int proxyPort) : resolver_(resolver), connectionFactory_(connectionFactory), timerFactory_(timerFactory), proxyHost_(proxyHost), proxyPort_(proxyPort), authID_(""), authPassword_("") { } HTTPConnectProxiedConnectionFactory::HTTPConnectProxiedConnectionFactory(DomainNameResolver* resolver, ConnectionFactory* connectionFactory, TimerFactory* timerFactory, const std::string& proxyHost, int proxyPort, const SafeString& authID, const SafeString& authPassword) : resolver_(resolver), connectionFactory_(connectionFactory), timerFactory_(timerFactory), proxyHost_(proxyHost), proxyPort_(proxyPort), authID_(authID), authPassword_(authPassword) { } boost::shared_ptr HTTPConnectProxiedConnectionFactory::createConnection() { return HTTPConnectProxiedConnection::create(resolver_, connectionFactory_, timerFactory_, proxyHost_, proxyPort_, authID_, authPassword_); } } swift-im-2.0+dev6/Swiften/Network/GConfProxyProvider.cpp0000644000175000017500000000274412227051774023217 0ustar kismithkismith/* * Copyright (c) 2010-2011 Thilo Cestonaro * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include #include #include #include #include #include namespace Swift { GConfProxyProvider::GConfProxyProvider() { // Ensure static GLib initialization methods are called static bool glibInitialized = false; if (!glibInitialized) { g_type_init(); glibInitialized = true; } socksProxy = getFromGConf("/system/proxy/socks_host", "/system/proxy/socks_port"); httpProxy = getFromGConf("/system/http_proxy/host", "/system/http_proxy/port"); SWIFT_LOG(debug) << "GConf: SOCKS5 => " << socksProxy.toString() << "; HTTP Connect => " << httpProxy.toString() << std::endl; } HostAddressPort GConfProxyProvider::getHTTPConnectProxy() const { return httpProxy; } HostAddressPort GConfProxyProvider::getSOCKS5Proxy() const { return socksProxy; } HostAddressPort GConfProxyProvider::getFromGConf(const char* gcHost, const char* gcPort) { std::string address; int port = 0; gchar* str; GConfClient* client = gconf_client_get_default(); str = gconf_client_get_string(client, gcHost, NULL); port = static_cast (gconf_client_get_int(client, gcPort, NULL)); if(str) { address = static_cast (str); g_free(str); } g_object_unref(client); return HostAddressPort(HostAddress(address), port); } } swift-im-2.0+dev6/Swiften/Network/DummyConnectionFactory.h0000644000175000017500000000125512227051774023552 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include #include namespace Swift { class EventLoop; class DummyConnectionFactory : public ConnectionFactory { public: DummyConnectionFactory(EventLoop *eventLoop) : eventLoop(eventLoop) {} virtual ~DummyConnectionFactory() {} virtual boost::shared_ptr createConnection() { return boost::make_shared(eventLoop); } private: EventLoop* eventLoop; }; } swift-im-2.0+dev6/Swiften/Network/ChainedConnector.cpp0000644000175000017500000000557712227051774022663 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include using namespace Swift; ChainedConnector::ChainedConnector( const std::string& hostname, int port, bool doServiceLookups, DomainNameResolver* resolver, const std::vector& connectionFactories, TimerFactory* timerFactory) : hostname(hostname), port(port), doServiceLookups(doServiceLookups), resolver(resolver), connectionFactories(connectionFactories), timerFactory(timerFactory), timeoutMilliseconds(0) { } void ChainedConnector::setTimeoutMilliseconds(int milliseconds) { timeoutMilliseconds = milliseconds; } void ChainedConnector::start() { SWIFT_LOG(debug) << "Starting queued connector for " << hostname << std::endl; connectionFactoryQueue = std::deque(connectionFactories.begin(), connectionFactories.end()); tryNextConnectionFactory(); } void ChainedConnector::stop() { if (currentConnector) { currentConnector->onConnectFinished.disconnect(boost::bind(&ChainedConnector::handleConnectorFinished, this, _1, _2)); currentConnector->stop(); currentConnector.reset(); } finish(boost::shared_ptr(), boost::shared_ptr()); } void ChainedConnector::tryNextConnectionFactory() { assert(!currentConnector); if (connectionFactoryQueue.empty()) { SWIFT_LOG(debug) << "No more connection factories" << std::endl; finish(boost::shared_ptr(), lastError); } else { ConnectionFactory* connectionFactory = connectionFactoryQueue.front(); SWIFT_LOG(debug) << "Trying next connection factory: " << typeid(*connectionFactory).name() << std::endl; connectionFactoryQueue.pop_front(); currentConnector = Connector::create(hostname, port, doServiceLookups, resolver, connectionFactory, timerFactory); currentConnector->setTimeoutMilliseconds(timeoutMilliseconds); currentConnector->onConnectFinished.connect(boost::bind(&ChainedConnector::handleConnectorFinished, this, _1, _2)); currentConnector->start(); } } void ChainedConnector::handleConnectorFinished(boost::shared_ptr connection, boost::shared_ptr error) { SWIFT_LOG(debug) << "Connector finished" << std::endl; currentConnector->onConnectFinished.disconnect(boost::bind(&ChainedConnector::handleConnectorFinished, this, _1, _2)); lastError = error; currentConnector.reset(); if (connection) { finish(connection, error); } else { tryNextConnectionFactory(); } } void ChainedConnector::finish(boost::shared_ptr connection, boost::shared_ptr error) { onConnectFinished(connection, error); } swift-im-2.0+dev6/Swiften/Network/PlatformNATTraversalWorker.cpp0000644000175000017500000001227112227051774024647 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include "PlatformNATTraversalWorker.h" #include #include #include #include #include #include #include #include namespace Swift { class PlatformNATTraversalRequest : public boost::enable_shared_from_this { public: typedef boost::shared_ptr ref; public: PlatformNATTraversalRequest(PlatformNATTraversalWorker* worker) : worker(worker) { } virtual ~PlatformNATTraversalRequest() { } virtual void doRun() { worker->addRequestToQueue(shared_from_this()); } NATTraversalInterface* getNATTraversalInterface() const { return worker->getNATTraversalInterface(); } virtual void runBlocking() = 0; private: PlatformNATTraversalWorker* worker; }; class PlatformNATTraversalGetPublicIPRequest : public NATTraversalGetPublicIPRequest, public PlatformNATTraversalRequest { public: PlatformNATTraversalGetPublicIPRequest(PlatformNATTraversalWorker* worker) : PlatformNATTraversalRequest(worker) { } virtual void run() { doRun(); } virtual void runBlocking() { onResult(getNATTraversalInterface()->getPublicIP()); } }; class PlatformNATTraversalForwardPortRequest : public NATTraversalForwardPortRequest, public PlatformNATTraversalRequest { public: PlatformNATTraversalForwardPortRequest(PlatformNATTraversalWorker* worker, unsigned int localIP, unsigned int publicIP) : PlatformNATTraversalRequest(worker), localIP(localIP), publicIP(publicIP) { } virtual void run() { doRun(); } virtual void runBlocking() { onResult(getNATTraversalInterface()->addPortForward(localIP, publicIP)); } private: unsigned int localIP; unsigned int publicIP; }; class PlatformNATTraversalRemovePortForwardingRequest : public NATTraversalRemovePortForwardingRequest, public PlatformNATTraversalRequest { public: PlatformNATTraversalRemovePortForwardingRequest(PlatformNATTraversalWorker* worker, const NATPortMapping& mapping) : PlatformNATTraversalRequest(worker), mapping(mapping) { } virtual void run() { doRun(); } virtual void runBlocking() { onResult(getNATTraversalInterface()->removePortForward(mapping)); } private: NATPortMapping mapping; }; PlatformNATTraversalWorker::PlatformNATTraversalWorker(EventLoop* eventLoop) : eventLoop(eventLoop), stopRequested(false), natPMPSupported(boost::logic::indeterminate), natPMPInterface(NULL), miniUPnPSupported(boost::logic::indeterminate), miniUPnPInterface(NULL) { nullNATTraversalInterface = new NullNATTraversalInterface(); thread = new boost::thread(boost::bind(&PlatformNATTraversalWorker::run, this)); } PlatformNATTraversalWorker::~PlatformNATTraversalWorker() { stopRequested = true; addRequestToQueue(boost::shared_ptr()); thread->join(); delete thread; delete natPMPInterface; delete miniUPnPInterface; delete nullNATTraversalInterface; } NATTraversalInterface* PlatformNATTraversalWorker::getNATTraversalInterface() const { if (boost::logic::indeterminate(miniUPnPSupported)) { miniUPnPInterface = new MiniUPnPInterface(); miniUPnPSupported = miniUPnPInterface->isAvailable(); } if (miniUPnPSupported) { return miniUPnPInterface; } if (boost::logic::indeterminate(natPMPSupported)) { natPMPInterface = new NATPMPInterface(); natPMPSupported = natPMPInterface->isAvailable(); } if (natPMPSupported) { return natPMPInterface; } return nullNATTraversalInterface; } boost::shared_ptr PlatformNATTraversalWorker::createGetPublicIPRequest() { return boost::make_shared(this); } boost::shared_ptr PlatformNATTraversalWorker::createForwardPortRequest(int localPort, int publicPort) { return boost::make_shared(this, localPort, publicPort); } boost::shared_ptr PlatformNATTraversalWorker::createRemovePortForwardingRequest(int localPort, int publicPort) { NATPortMapping mapping(localPort, publicPort, NATPortMapping::TCP); // FIXME return boost::make_shared(this, mapping); } void PlatformNATTraversalWorker::run() { while (!stopRequested) { PlatformNATTraversalRequest::ref request; { boost::unique_lock lock(queueMutex); while (queue.empty()) { queueNonEmpty.wait(lock); } request = queue.front(); queue.pop_front(); } // Check whether we don't have a non-null request (used to stop the // worker) if (request) { request->runBlocking(); } } } void PlatformNATTraversalWorker::addRequestToQueue(PlatformNATTraversalRequest::ref request) { { boost::lock_guard lock(queueMutex); queue.push_back(request); } queueNonEmpty.notify_one(); } } swift-im-2.0+dev6/Swiften/Network/ConnectionFactory.cpp0000644000175000017500000000042212227051774023064 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { ConnectionFactory::~ConnectionFactory() { } } swift-im-2.0+dev6/Swiften/Network/BOSHConnectionPool.cpp0000644000175000017500000002330312227051774023045 0ustar kismithkismith/* * Copyright (c) 2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include namespace Swift { BOSHConnectionPool::BOSHConnectionPool(const URL& boshURL, DomainNameResolver* realResolver, ConnectionFactory* connectionFactoryParameter, XMLParserFactory* parserFactory, TLSContextFactory* tlsFactory, TimerFactory* timerFactory, EventLoop* eventLoop, const std::string& to, unsigned long long initialRID, const URL& boshHTTPConnectProxyURL, const SafeString& boshHTTPConnectProxyAuthID, const SafeString& boshHTTPConnectProxyAuthPassword) : boshURL(boshURL), connectionFactory(connectionFactoryParameter), xmlParserFactory(parserFactory), timerFactory(timerFactory), rid(initialRID), pendingTerminate(false), to(to), requestLimit(2), restartCount(0), pendingRestart(false) { if (!boshHTTPConnectProxyURL.isEmpty()) { if (boshHTTPConnectProxyURL.getScheme() == "https") { connectionFactory = new TLSConnectionFactory(tlsFactory, connectionFactory); myConnectionFactories.push_back(connectionFactory); } connectionFactory = new HTTPConnectProxiedConnectionFactory(realResolver, connectionFactory, timerFactory, boshHTTPConnectProxyURL.getHost(), URL::getPortOrDefaultPort(boshHTTPConnectProxyURL), boshHTTPConnectProxyAuthID, boshHTTPConnectProxyAuthPassword); } if (boshURL.getScheme() == "https") { connectionFactory = new TLSConnectionFactory(tlsFactory, connectionFactory); myConnectionFactories.push_back(connectionFactory); } resolver = new CachingDomainNameResolver(realResolver, eventLoop); createConnection(); } BOSHConnectionPool::~BOSHConnectionPool() { /* Don't do a normal close here. Instead kill things forcibly, as close() or writeFooter() will already have been called */ std::vector connectionCopies = connections; foreach (BOSHConnection::ref connection, connectionCopies) { if (connection) { destroyConnection(connection); connection->disconnect(); } } foreach (ConnectionFactory* factory, myConnectionFactories) { delete factory; } delete resolver; } void BOSHConnectionPool::write(const SafeByteArray& data) { dataQueue.push_back(data); tryToSendQueuedData(); } void BOSHConnectionPool::handleDataRead(const SafeByteArray& data) { onXMPPDataRead(data); tryToSendQueuedData(); /* Will rebalance the connections */ } void BOSHConnectionPool::restartStream() { BOSHConnection::ref connection = getSuitableConnection(); if (connection) { pendingRestart = false; rid++; connection->setRID(rid); connection->restartStream(); restartCount++; } else { pendingRestart = true; } } void BOSHConnectionPool::writeFooter() { pendingTerminate = true; tryToSendQueuedData(); } void BOSHConnectionPool::close() { if (!sid.empty()) { writeFooter(); } else { pendingTerminate = true; std::vector connectionCopies = connections; foreach (BOSHConnection::ref connection, connectionCopies) { if (connection) { connection->disconnect(); } } } } void BOSHConnectionPool::handleSessionStarted(const std::string& sessionID, size_t requests) { sid = sessionID; requestLimit = requests; onSessionStarted(); } void BOSHConnectionPool::handleConnectFinished(bool error, BOSHConnection::ref connection) { if (error) { onSessionTerminated(boost::make_shared(BOSHError::UndefinedCondition)); /*TODO: We can probably manage to not terminate the stream here and use the rid/ack retry * logic to just swallow the error and try again (some number of tries). */ } else { if (sid.empty()) { connection->startStream(to, rid); } if (pendingRestart) { restartStream(); } tryToSendQueuedData(); } } BOSHConnection::ref BOSHConnectionPool::getSuitableConnection() { BOSHConnection::ref suitableConnection; foreach (BOSHConnection::ref connection, connections) { if (connection->isReadyToSend()) { suitableConnection = connection; break; } } if (!suitableConnection && connections.size() < requestLimit) { /* This is not a suitable connection because it won't have yet connected and added TLS if needed. */ BOSHConnection::ref newConnection = createConnection(); newConnection->setSID(sid); } assert(connections.size() <= requestLimit); assert((!suitableConnection) || suitableConnection->isReadyToSend()); return suitableConnection; } void BOSHConnectionPool::tryToSendQueuedData() { if (sid.empty()) { /* If we've not got as far as stream start yet, pend */ return; } BOSHConnection::ref suitableConnection = getSuitableConnection(); bool toSend = !dataQueue.empty(); if (suitableConnection) { if (toSend) { rid++; suitableConnection->setRID(rid); SafeByteArray data; foreach (const SafeByteArray& datum, dataQueue) { data.insert(data.end(), datum.begin(), datum.end()); } suitableConnection->write(data); dataQueue.clear(); } else if (pendingTerminate) { rid++; suitableConnection->setRID(rid); suitableConnection->terminateStream(); sid = ""; close(); } } if (!pendingTerminate) { /* Ensure there's always a session waiting to read data for us */ bool pending = false; foreach (BOSHConnection::ref connection, connections) { if (connection && !connection->isReadyToSend()) { pending = true; } } if (!pending) { if (restartCount >= 1) { /* Don't open a second connection until we've restarted the stream twice - i.e. we've authed and resource bound.*/ if (suitableConnection) { rid++; suitableConnection->setRID(rid); suitableConnection->write(createSafeByteArray("")); } else { /* My thought process I went through when writing this, to aid anyone else confused why this can happen... * * What to do here? I think this isn't possible. If you didn't have two connections, suitable would have made one. If you have two connections and neither is suitable, pending would be true. If you have a non-pending connection, it's suitable. If I decide to do something here, remove assert above. Ah! Yes, because there's a period between creating the connection and it being connected. */ } } } } } void BOSHConnectionPool::handleHTTPError(const std::string& /*errorCode*/) { handleSessionTerminated(boost::make_shared(BOSHError::UndefinedCondition)); } void BOSHConnectionPool::handleConnectionDisconnected(bool error, BOSHConnection::ref connection) { destroyConnection(connection); if (pendingTerminate && sid.empty() && connections.empty()) { handleSessionTerminated(BOSHError::ref()); } else if (false && error) { handleSessionTerminated(boost::make_shared(BOSHError::UndefinedCondition)); } else { /* We might have just freed up a connection slot to send with */ tryToSendQueuedData(); } } boost::shared_ptr BOSHConnectionPool::createConnection() { Connector::ref connector = Connector::create(boshURL.getHost(), URL::getPortOrDefaultPort(boshURL), false, resolver, connectionFactory, timerFactory); BOSHConnection::ref connection = BOSHConnection::create(boshURL, connector, xmlParserFactory); connection->onXMPPDataRead.connect(boost::bind(&BOSHConnectionPool::handleDataRead, this, _1)); connection->onSessionStarted.connect(boost::bind(&BOSHConnectionPool::handleSessionStarted, this, _1, _2)); connection->onBOSHDataRead.connect(boost::bind(&BOSHConnectionPool::handleBOSHDataRead, this, _1)); connection->onBOSHDataWritten.connect(boost::bind(&BOSHConnectionPool::handleBOSHDataWritten, this, _1)); connection->onDisconnected.connect(boost::bind(&BOSHConnectionPool::handleConnectionDisconnected, this, _1, connection)); connection->onConnectFinished.connect(boost::bind(&BOSHConnectionPool::handleConnectFinished, this, _1, connection)); connection->onSessionTerminated.connect(boost::bind(&BOSHConnectionPool::handleSessionTerminated, this, _1)); connection->onHTTPError.connect(boost::bind(&BOSHConnectionPool::handleHTTPError, this, _1)); connection->connect(); connections.push_back(connection); return connection; } void BOSHConnectionPool::destroyConnection(boost::shared_ptr connection) { connections.erase(std::remove(connections.begin(), connections.end(), connection), connections.end()); connection->onXMPPDataRead.disconnect(boost::bind(&BOSHConnectionPool::handleDataRead, this, _1)); connection->onSessionStarted.disconnect(boost::bind(&BOSHConnectionPool::handleSessionStarted, this, _1, _2)); connection->onBOSHDataRead.disconnect(boost::bind(&BOSHConnectionPool::handleBOSHDataRead, this, _1)); connection->onBOSHDataWritten.disconnect(boost::bind(&BOSHConnectionPool::handleBOSHDataWritten, this, _1)); connection->onDisconnected.disconnect(boost::bind(&BOSHConnectionPool::handleConnectionDisconnected, this, _1, connection)); connection->onConnectFinished.disconnect(boost::bind(&BOSHConnectionPool::handleConnectFinished, this, _1, connection)); connection->onSessionTerminated.disconnect(boost::bind(&BOSHConnectionPool::handleSessionTerminated, this, _1)); connection->onHTTPError.disconnect(boost::bind(&BOSHConnectionPool::handleHTTPError, this, _1)); } void BOSHConnectionPool::handleSessionTerminated(BOSHError::ref error) { onSessionTerminated(error); } void BOSHConnectionPool::handleBOSHDataRead(const SafeByteArray& data) { onBOSHDataRead(data); } void BOSHConnectionPool::handleBOSHDataWritten(const SafeByteArray& data) { onBOSHDataWritten(data); } } swift-im-2.0+dev6/Swiften/Network/MiniUPnPInterface.h0000644000175000017500000000144512227051774022370 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include #include #include #include namespace Swift { class MiniUPnPInterface : public NATTraversalInterface, boost::noncopyable { public: MiniUPnPInterface(); ~MiniUPnPInterface(); virtual bool isAvailable(); boost::optional getPublicIP(); boost::optional addPortForward(int localPort, int publicPort); bool removePortForward(const NATPortMapping&); private: struct Private; boost::shared_ptr p; }; } swift-im-2.0+dev6/Swiften/Network/WindowsNetworkEnvironment.h0000644000175000017500000000075012227051774024337 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include #include #include namespace Swift { class SWIFTEN_API WindowsNetworkEnvironment : public NetworkEnvironment { public: std::vector getNetworkInterfaces() const; }; } swift-im-2.0+dev6/Swiften/Network/ConnectionServerFactory.h0000644000175000017500000000107012227051774023720 0ustar kismithkismith/* * Copyright (c) 2011 Jan Kaluza * Licensed under the Simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include namespace Swift { class ConnectionServer; class HostAddress; class ConnectionServerFactory { public: virtual ~ConnectionServerFactory(); virtual boost::shared_ptr createConnectionServer(int port) = 0; virtual boost::shared_ptr createConnectionServer(const Swift::HostAddress& hostAddress, int port) = 0; }; } swift-im-2.0+dev6/Swiften/Network/EnvironmentProxyProvider.cpp0000644000175000017500000000261712227051774024526 0ustar kismithkismith/* * Copyright (c) 2010-2011 Thilo Cestonaro * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include #include #include #include #include namespace Swift { EnvironmentProxyProvider::EnvironmentProxyProvider() { socksProxy = getFromEnv("all_proxy", "socks"); httpProxy = getFromEnv("http_proxy", "http"); SWIFT_LOG(debug) << "Environment: SOCKS5 => " << socksProxy.toString() << "; HTTP Connect => " << httpProxy.toString() << std::endl; } HostAddressPort EnvironmentProxyProvider::getHTTPConnectProxy() const { return httpProxy; } HostAddressPort EnvironmentProxyProvider::getSOCKS5Proxy() const { return socksProxy; } HostAddressPort EnvironmentProxyProvider::getFromEnv(const char* envVarName, std::string proxyProtocol) { char* envVar = NULL; std::string address; int port = 0; envVar = getenv(envVarName); proxyProtocol += "://"; address = envVar != NULL ? envVar : "0.0.0.0"; if(envVar != NULL && address.compare(0, proxyProtocol.length(), proxyProtocol) == 0) { address = address.substr(proxyProtocol.length(), address.length()); port = atoi(address.substr(address.find(':') + 1, address.length()).c_str()); address = address.substr(0, address.find(':')); } return HostAddressPort(HostAddress(address), port); } } swift-im-2.0+dev6/Swiften/Network/UnixNetworkEnvironment.cpp0000644000175000017500000000340112227051774024157 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Swift { std::vector UnixNetworkEnvironment::getNetworkInterfaces() const { std::map interfaces; ifaddrs* addrs = 0; int ret = getifaddrs(&addrs); if (ret != 0) { return std::vector(); } for (ifaddrs* a = addrs; a != 0; a = a->ifa_next) { std::string name(a->ifa_name); boost::optional address; if (a->ifa_addr->sa_family == PF_INET) { sockaddr_in* sa = reinterpret_cast(a->ifa_addr); address = HostAddress(reinterpret_cast(&(sa->sin_addr)), 4); } else if (a->ifa_addr->sa_family == PF_INET6) { sockaddr_in6* sa = reinterpret_cast(a->ifa_addr); address = HostAddress(reinterpret_cast(&(sa->sin6_addr)), 16); } if (address) { std::map::iterator i = interfaces.insert(std::make_pair(name, NetworkInterface(name, a->ifa_flags & IFF_LOOPBACK))).first; i->second.addAddress(*address); } } freeifaddrs(addrs); std::vector result; for (std::map::const_iterator i = interfaces.begin(); i != interfaces.end(); ++i) { result.push_back(i->second); } return result; } } swift-im-2.0+dev6/Swiften/Network/BoostIOServiceThread.h0000644000175000017500000000123112227051774023070 0ustar kismithkismith/* * 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 #include #include #include namespace Swift { class SWIFTEN_API BoostIOServiceThread { public: BoostIOServiceThread(); ~BoostIOServiceThread(); boost::shared_ptr getIOService() const { return ioService_; } private: void doRun(); private: boost::shared_ptr ioService_; boost::thread* thread_; }; } swift-im-2.0+dev6/Swiften/Network/PlatformDomainNameServiceQuery.h0000644000175000017500000000160112227051774025166 0ustar kismithkismith/* * 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 #include #include #include #include namespace Swift { class EventLoop; class PlatformDomainNameServiceQuery : public DomainNameServiceQuery, public PlatformDomainNameQuery, public boost::enable_shared_from_this, public EventOwner { public: PlatformDomainNameServiceQuery(const std::string& service, EventLoop* eventLoop, PlatformDomainNameResolver* resolver); virtual void run(); private: void runBlocking(); void emitError(); private: EventLoop* eventLoop; std::string service; }; } swift-im-2.0+dev6/Swiften/Network/MacOSXProxyProvider.cpp0000644000175000017500000000555312227051774023316 0ustar kismithkismith/* * Copyright (c) 2010-2011 Thilo Cestonaro * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include #include #include #include #include #include #ifndef SWIFTEN_PLATFORM_IPHONE #include #endif using namespace Swift; #ifndef SWIFTEN_PLATFORM_IPHONE static HostAddressPort getFromDictionary(CFDictionaryRef dict, CFStringRef enabledKey, CFStringRef hostKey, CFStringRef portKey) { CFNumberRef numberValue = NULL; HostAddressPort ret = HostAddressPort(HostAddress(), 0); if(CFDictionaryGetValueIfPresent(dict, reinterpret_cast (enabledKey), reinterpret_cast (&numberValue)) == true) { const int i = 0; CFNumberRef zero = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &i); CFComparisonResult result = CFNumberCompare(numberValue, zero, NULL); if(result != kCFCompareEqualTo) { int port = 0; std::string host = ""; try { CFNumberRef numberValue = reinterpret_cast (CFDictionaryGetValue(dict, portKey)); if(numberValue != NULL) { CFNumberGetValue(numberValue, kCFNumberIntType, &port); } CFStringRef stringValue = reinterpret_cast (CFDictionaryGetValue(dict, hostKey)); if(stringValue != NULL) { std::vector buffer; // length must be +1 for the ending zero; and the Docu of CFStringGetCString tells it like // if the string is toby the length must be at least 5. CFIndex length = CFStringGetLength(stringValue) + 1; buffer.resize(length); if(CFStringGetCString(stringValue, &buffer[0], length, kCFStringEncodingMacRoman)) { for(std::vector::iterator iter = buffer.begin(); iter != buffer.end(); ++iter) { host += *iter; } } } } catch(...) { std::cerr << "Exception caught ... " << std::endl; } if(host != "" && port != 0) { ret = HostAddressPort(HostAddress(host), port); } } } return ret; } #endif namespace Swift { MacOSXProxyProvider::MacOSXProxyProvider() { } HostAddressPort MacOSXProxyProvider::getHTTPConnectProxy() const { HostAddressPort result; #ifndef SWIFTEN_PLATFORM_IPHONE CFDictionaryRef proxies = SCDynamicStoreCopyProxies(NULL); if(proxies != NULL) { result = getFromDictionary(proxies, kSCPropNetProxiesHTTPEnable, kSCPropNetProxiesHTTPProxy, kSCPropNetProxiesHTTPPort); } #endif return result; } HostAddressPort MacOSXProxyProvider::getSOCKS5Proxy() const { HostAddressPort result; #ifndef SWIFTEN_PLATFORM_IPHONE CFDictionaryRef proxies = SCDynamicStoreCopyProxies(NULL); if(proxies != NULL) { result = getFromDictionary(proxies, kSCPropNetProxiesSOCKSEnable, kSCPropNetProxiesSOCKSProxy, kSCPropNetProxiesSOCKSPort); } #endif return result; } } swift-im-2.0+dev6/Swiften/Network/UnitTest/0000755000175000017500000000000012227051774020512 5ustar kismithkismithswift-im-2.0+dev6/Swiften/Network/UnitTest/BOSHConnectionTest.cpp0000644000175000017500000002501112227051774024630 0ustar kismithkismith/* * Copyright (c) 2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Swift; class BOSHConnectionTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(BOSHConnectionTest); CPPUNIT_TEST(testHeader); CPPUNIT_TEST(testReadiness_ok); CPPUNIT_TEST(testReadiness_pending); CPPUNIT_TEST(testReadiness_disconnect); CPPUNIT_TEST(testReadiness_noSID); CPPUNIT_TEST(testWrite_Receive); CPPUNIT_TEST(testWrite_ReceiveTwice); CPPUNIT_TEST(testRead_Fragment); CPPUNIT_TEST(testHTTPRequest); CPPUNIT_TEST(testHTTPRequest_Empty); CPPUNIT_TEST_SUITE_END(); public: void setUp() { eventLoop = new DummyEventLoop(); connectionFactory = new MockConnectionFactory(eventLoop); resolver = new StaticDomainNameResolver(eventLoop); timerFactory = new DummyTimerFactory(); connectFinished = false; disconnected = false; disconnectedError = false; dataRead.clear(); } void tearDown() { eventLoop->processEvents(); delete connectionFactory; delete resolver; delete timerFactory; delete eventLoop; } void testHeader() { BOSHConnection::ref testling = createTestling(); testling->connect(); eventLoop->processEvents(); testling->startStream("wonderland.lit", 1); std::string initial(""); readResponse(initial, connectionFactory->connections[0]); CPPUNIT_ASSERT_EQUAL(std::string("MyShinySID"), sid); CPPUNIT_ASSERT(testling->isReadyToSend()); } void testReadiness_ok() { BOSHConnection::ref testling = createTestling(); testling->connect(); eventLoop->processEvents(); testling->setSID("blahhhhh"); CPPUNIT_ASSERT(testling->isReadyToSend()); } void testReadiness_pending() { BOSHConnection::ref testling = createTestling(); testling->connect(); eventLoop->processEvents(); testling->setSID("mySID"); CPPUNIT_ASSERT(testling->isReadyToSend()); testling->write(createSafeByteArray("")); CPPUNIT_ASSERT(!testling->isReadyToSend()); readResponse("", connectionFactory->connections[0]); CPPUNIT_ASSERT(testling->isReadyToSend()); } void testReadiness_disconnect() { BOSHConnection::ref testling = createTestling(); testling->connect(); eventLoop->processEvents(); testling->setSID("mySID"); CPPUNIT_ASSERT(testling->isReadyToSend()); connectionFactory->connections[0]->onDisconnected(false); CPPUNIT_ASSERT(!testling->isReadyToSend()); } void testReadiness_noSID() { BOSHConnection::ref testling = createTestling(); testling->connect(); eventLoop->processEvents(); CPPUNIT_ASSERT(!testling->isReadyToSend()); } void testWrite_Receive() { BOSHConnection::ref testling = createTestling(); testling->connect(); eventLoop->processEvents(); testling->setSID("mySID"); testling->write(createSafeByteArray("")); readResponse("", connectionFactory->connections[0]); CPPUNIT_ASSERT_EQUAL(std::string(""), byteArrayToString(dataRead)); } void testWrite_ReceiveTwice() { BOSHConnection::ref testling = createTestling(); testling->connect(); eventLoop->processEvents(); testling->setSID("mySID"); testling->write(createSafeByteArray("")); readResponse("", connectionFactory->connections[0]); CPPUNIT_ASSERT_EQUAL(std::string(""), byteArrayToString(dataRead)); dataRead.clear(); testling->write(createSafeByteArray("")); readResponse("", connectionFactory->connections[0]); CPPUNIT_ASSERT_EQUAL(std::string(""), byteArrayToString(dataRead)); } void testRead_Fragment() { BOSHConnection::ref testling = createTestling(); testling->connect(); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(static_cast(1), connectionFactory->connections.size()); boost::shared_ptr connection = connectionFactory->connections[0]; boost::shared_ptr data1 = boost::make_shared(createSafeByteArray( "HTTP/1.1 200 OK\r\n" "Content-Type: text/xml; charset=utf-8\r\n" "Access-Control-Allow-Origin: *\r\n" "Access-Control-Allow-Headers: Content-Type\r\n" "Content-Length: 64\r\n")); boost::shared_ptr data2 = boost::make_shared(createSafeByteArray( "\r\n" " data3 = boost::make_shared(createSafeByteArray( "ah/>" "")); connection->onDataRead(data1); connection->onDataRead(data2); CPPUNIT_ASSERT(dataRead.empty()); connection->onDataRead(data3); CPPUNIT_ASSERT_EQUAL(std::string(""), byteArrayToString(dataRead)); } void testHTTPRequest() { std::string data = ""; std::string sid = "wigglebloom"; std::string fullBody = "" + data + ""; std::pair http = BOSHConnection::createHTTPRequest(createSafeByteArray(data), false, false, 20, sid, URL()); CPPUNIT_ASSERT_EQUAL(fullBody.size(), http.second); } void testHTTPRequest_Empty() { std::string data = ""; std::string sid = "wigglebloomsickle"; std::string fullBody = "" + data + ""; std::pair http = BOSHConnection::createHTTPRequest(createSafeByteArray(data), false, false, 42, sid, URL()); CPPUNIT_ASSERT_EQUAL(fullBody.size(), http.second); std::string response = safeByteArrayToString(http.first); size_t bodyPosition = response.find("\r\n\r\n"); CPPUNIT_ASSERT_EQUAL(fullBody, response.substr(bodyPosition+4)); } private: BOSHConnection::ref createTestling() { resolver->addAddress("wonderland.lit", HostAddress("127.0.0.1")); Connector::ref connector = Connector::create("wonderland.lit", 5280, false, resolver, connectionFactory, timerFactory); BOSHConnection::ref c = BOSHConnection::create(URL("http", "wonderland.lit", 5280, "/http-bind"), connector, &parserFactory); c->onConnectFinished.connect(boost::bind(&BOSHConnectionTest::handleConnectFinished, this, _1)); c->onDisconnected.connect(boost::bind(&BOSHConnectionTest::handleDisconnected, this, _1)); c->onXMPPDataRead.connect(boost::bind(&BOSHConnectionTest::handleDataRead, this, _1)); c->onSessionStarted.connect(boost::bind(&BOSHConnectionTest::handleSID, this, _1)); c->setRID(42); return c; } void handleConnectFinished(bool error) { connectFinished = true; connectFinishedWithError = error; } void handleDisconnected(bool e) { disconnected = true; disconnectedError = e; } void handleDataRead(const SafeByteArray& d) { append(dataRead, d); } void handleSID(const std::string& s) { sid = s; } struct MockConnection : public Connection { public: MockConnection(const std::vector& failingPorts, EventLoop* eventLoop) : eventLoop(eventLoop), failingPorts(failingPorts), disconnected(false) { } void listen() { assert(false); } void connect(const HostAddressPort& address) { hostAddressPort = address; bool fail = std::find(failingPorts.begin(), failingPorts.end(), address) != failingPorts.end(); eventLoop->postEvent(boost::bind(boost::ref(onConnectFinished), fail)); } HostAddressPort getLocalAddress() const { return HostAddressPort(); } void disconnect() { disconnected = true; onDisconnected(boost::optional()); } void write(const SafeByteArray& d) { append(dataWritten, d); } EventLoop* eventLoop; boost::optional hostAddressPort; std::vector failingPorts; ByteArray dataWritten; bool disconnected; }; struct MockConnectionFactory : public ConnectionFactory { MockConnectionFactory(EventLoop* eventLoop) : eventLoop(eventLoop) { } boost::shared_ptr createConnection() { boost::shared_ptr connection = boost::make_shared(failingPorts, eventLoop); connections.push_back(connection); return connection; } EventLoop* eventLoop; std::vector< boost::shared_ptr > connections; std::vector failingPorts; }; void readResponse(const std::string& response, boost::shared_ptr connection) { boost::shared_ptr data1 = boost::make_shared(createSafeByteArray( "HTTP/1.1 200 OK\r\n" "Content-Type: text/xml; charset=utf-8\r\n" "Access-Control-Allow-Origin: *\r\n" "Access-Control-Allow-Headers: Content-Type\r\n" "Content-Length: ")); connection->onDataRead(data1); boost::shared_ptr data2 = boost::make_shared(createSafeByteArray(boost::lexical_cast(response.size()))); connection->onDataRead(data2); boost::shared_ptr data3 = boost::make_shared(createSafeByteArray("\r\n\r\n")); connection->onDataRead(data3); boost::shared_ptr data4 = boost::make_shared(createSafeByteArray(response)); connection->onDataRead(data4); } private: DummyEventLoop* eventLoop; MockConnectionFactory* connectionFactory; bool connectFinished; bool connectFinishedWithError; bool disconnected; bool disconnectedError; ByteArray dataRead; PlatformXMLParserFactory parserFactory; StaticDomainNameResolver* resolver; TimerFactory* timerFactory; std::string sid; }; CPPUNIT_TEST_SUITE_REGISTRATION(BOSHConnectionTest); swift-im-2.0+dev6/Swiften/Network/UnitTest/ChainedConnectorTest.cpp0000644000175000017500000001405312227051774025267 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include #include #include using namespace Swift; class ChainedConnectorTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(ChainedConnectorTest); CPPUNIT_TEST(testConnect_FirstConnectorSucceeds); CPPUNIT_TEST(testConnect_SecondConnectorSucceeds); CPPUNIT_TEST(testConnect_NoConnectorSucceeds); CPPUNIT_TEST(testConnect_NoDNS); CPPUNIT_TEST(testStop); CPPUNIT_TEST_SUITE_END(); public: void setUp() { error.reset(); host = HostAddressPort(HostAddress("1.1.1.1"), 1234); eventLoop = new DummyEventLoop(); resolver = new StaticDomainNameResolver(eventLoop); resolver->addXMPPClientService("foo.com", host); connectionFactory1 = new MockConnectionFactory(eventLoop, 1); connectionFactory2 = new MockConnectionFactory(eventLoop, 2); timerFactory = new DummyTimerFactory(); } void tearDown() { delete timerFactory; delete connectionFactory2; delete connectionFactory1; delete resolver; delete eventLoop; } void testConnect_FirstConnectorSucceeds() { boost::shared_ptr testling(createConnector()); connectionFactory1->connects = true; connectionFactory2->connects = false; testling->start(); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(1, static_cast(connections.size())); CPPUNIT_ASSERT(connections[0]); CPPUNIT_ASSERT_EQUAL(1, boost::dynamic_pointer_cast(connections[0])->id); CPPUNIT_ASSERT(!boost::dynamic_pointer_cast(error)); } void testConnect_SecondConnectorSucceeds() { boost::shared_ptr testling(createConnector()); connectionFactory1->connects = false; connectionFactory2->connects = true; testling->start(); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(1, static_cast(connections.size())); CPPUNIT_ASSERT(connections[0]); CPPUNIT_ASSERT_EQUAL(2, boost::dynamic_pointer_cast(connections[0])->id); CPPUNIT_ASSERT(!boost::dynamic_pointer_cast(error)); } void testConnect_NoConnectorSucceeds() { boost::shared_ptr testling(createConnector()); connectionFactory1->connects = false; connectionFactory2->connects = false; testling->start(); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(1, static_cast(connections.size())); CPPUNIT_ASSERT(!connections[0]); CPPUNIT_ASSERT(!boost::dynamic_pointer_cast(error)); } void testConnect_NoDNS() { /* Reset resolver so there's no record */ delete resolver; resolver = new StaticDomainNameResolver(eventLoop); boost::shared_ptr testling(createConnector()); connectionFactory1->connects = false; connectionFactory2->connects = false; testling->start(); //testling->stop(); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(1, static_cast(connections.size())); CPPUNIT_ASSERT(!connections[0]); CPPUNIT_ASSERT(boost::dynamic_pointer_cast(error)); } void testStop() { boost::shared_ptr testling(createConnector()); connectionFactory1->connects = true; connectionFactory2->connects = false; testling->start(); testling->stop(); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(1, static_cast(connections.size())); CPPUNIT_ASSERT(!connections[0]); } private: boost::shared_ptr createConnector() { std::vector factories; factories.push_back(connectionFactory1); factories.push_back(connectionFactory2); boost::shared_ptr connector = boost::make_shared("foo.com", -1, true, resolver, factories, timerFactory); connector->onConnectFinished.connect(boost::bind(&ChainedConnectorTest::handleConnectorFinished, this, _1, _2)); return connector; } void handleConnectorFinished(boost::shared_ptr connection, boost::shared_ptr resultError) { error = resultError; boost::shared_ptr c(boost::dynamic_pointer_cast(connection)); if (connection) { assert(c); } connections.push_back(c); } struct MockConnection : public Connection { public: MockConnection(bool connects, int id, EventLoop* eventLoop) : connects(connects), id(id), eventLoop(eventLoop) { } void listen() { assert(false); } void connect(const HostAddressPort&) { eventLoop->postEvent(boost::bind(boost::ref(onConnectFinished), !connects)); } HostAddressPort getLocalAddress() const { return HostAddressPort(); } void disconnect() { assert(false); } void write(const SafeByteArray&) { assert(false); } bool connects; int id; EventLoop* eventLoop; }; struct MockConnectionFactory : public ConnectionFactory { MockConnectionFactory(EventLoop* eventLoop, int id) : eventLoop(eventLoop), connects(true), id(id) { } boost::shared_ptr createConnection() { return boost::make_shared(connects, id, eventLoop); } EventLoop* eventLoop; bool connects; int id; }; private: HostAddressPort host; DummyEventLoop* eventLoop; StaticDomainNameResolver* resolver; MockConnectionFactory* connectionFactory1; MockConnectionFactory* connectionFactory2; DummyTimerFactory* timerFactory; std::vector< boost::shared_ptr > connections; boost::shared_ptr error; }; CPPUNIT_TEST_SUITE_REGISTRATION(ChainedConnectorTest); swift-im-2.0+dev6/Swiften/Network/UnitTest/HTTPConnectProxiedConnectionTest.cpp0000644000175000017500000002206412227051774027526 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Swift; class HTTPConnectProxiedConnectionTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(HTTPConnectProxiedConnectionTest); CPPUNIT_TEST(testConnect_CreatesConnectionToProxy); CPPUNIT_TEST(testConnect_SendsConnectRequest); CPPUNIT_TEST(testConnect_ReceiveConnectResponse); CPPUNIT_TEST(testConnect_ReceiveMalformedConnectResponse); CPPUNIT_TEST(testConnect_ReceiveErrorConnectResponse); CPPUNIT_TEST(testConnect_ReceiveDataAfterConnect); CPPUNIT_TEST(testWrite_AfterConnect); CPPUNIT_TEST(testDisconnect_AfterConnectRequest); CPPUNIT_TEST(testDisconnect_AfterConnect); CPPUNIT_TEST_SUITE_END(); public: void setUp() { proxyHost = "doo.bah"; proxyPort = 1234; proxyHostAddress = HostAddressPort(HostAddress("1.1.1.1"), proxyPort); host = HostAddressPort(HostAddress("2.2.2.2"), 2345); eventLoop = new DummyEventLoop(); resolver = new StaticDomainNameResolver(eventLoop); resolver->addAddress(proxyHost, proxyHostAddress.getAddress()); timerFactory = new DummyTimerFactory(); connectionFactory = new MockConnectionFactory(eventLoop); connectFinished = false; disconnected = false; } void tearDown() { delete timerFactory; delete connectionFactory; delete resolver; delete eventLoop; } void connect(HTTPConnectProxiedConnection::ref connection, const HostAddressPort& to) { connection->connect(to); eventLoop->processEvents(); eventLoop->processEvents(); eventLoop->processEvents(); } void testConnect_CreatesConnectionToProxy() { HTTPConnectProxiedConnection::ref testling(createTestling()); connect(testling, host); CPPUNIT_ASSERT_EQUAL(1, static_cast(connectionFactory->connections.size())); CPPUNIT_ASSERT(connectionFactory->connections[0]->hostAddressPort); CPPUNIT_ASSERT(proxyHostAddress == *connectionFactory->connections[0]->hostAddressPort); CPPUNIT_ASSERT(!connectFinished); } void testConnect_SendsConnectRequest() { HTTPConnectProxiedConnection::ref testling(createTestling()); connect(testling, HostAddressPort(HostAddress("2.2.2.2"), 2345)); CPPUNIT_ASSERT_EQUAL(createByteArray("CONNECT 2.2.2.2:2345 HTTP/1.1\r\n\r\n"), connectionFactory->connections[0]->dataWritten); } void testConnect_ReceiveConnectResponse() { HTTPConnectProxiedConnection::ref testling(createTestling()); connect(testling, HostAddressPort(HostAddress("2.2.2.2"), 2345)); connectionFactory->connections[0]->onDataRead(createSafeByteArrayRef("HTTP/1.0 200 Connection established\r\n\r\n")); eventLoop->processEvents(); CPPUNIT_ASSERT(connectFinished); CPPUNIT_ASSERT(!connectFinishedWithError); CPPUNIT_ASSERT(dataRead.empty()); } void testConnect_ReceiveMalformedConnectResponse() { HTTPConnectProxiedConnection::ref testling(createTestling()); connect(testling, HostAddressPort(HostAddress("2.2.2.2"), 2345)); connectionFactory->connections[0]->onDataRead(createSafeByteArrayRef("FLOOP")); eventLoop->processEvents(); CPPUNIT_ASSERT(connectFinished); CPPUNIT_ASSERT(connectFinishedWithError); CPPUNIT_ASSERT(connectionFactory->connections[0]->disconnected); } void testConnect_ReceiveErrorConnectResponse() { HTTPConnectProxiedConnection::ref testling(createTestling()); connect(testling, HostAddressPort(HostAddress("2.2.2.2"), 2345)); connectionFactory->connections[0]->onDataRead(createSafeByteArrayRef("HTTP/1.0 401 Unauthorized\r\n\r\n")); eventLoop->processEvents(); CPPUNIT_ASSERT(connectFinished); CPPUNIT_ASSERT(connectFinishedWithError); CPPUNIT_ASSERT(connectionFactory->connections[0]->disconnected); } void testConnect_ReceiveDataAfterConnect() { HTTPConnectProxiedConnection::ref testling(createTestling()); connect(testling, HostAddressPort(HostAddress("2.2.2.2"), 2345)); connectionFactory->connections[0]->onDataRead(createSafeByteArrayRef("HTTP/1.0 200 Connection established\r\n\r\n")); eventLoop->processEvents(); connectionFactory->connections[0]->onDataRead(createSafeByteArrayRef("abcdef")); CPPUNIT_ASSERT_EQUAL(createByteArray("abcdef"), dataRead); } void testWrite_AfterConnect() { HTTPConnectProxiedConnection::ref testling(createTestling()); connect(testling, HostAddressPort(HostAddress("2.2.2.2"), 2345)); connectionFactory->connections[0]->onDataRead(createSafeByteArrayRef("HTTP/1.0 200 Connection established\r\n\r\n")); eventLoop->processEvents(); connectionFactory->connections[0]->dataWritten.clear(); testling->write(createSafeByteArray("abcdef")); CPPUNIT_ASSERT_EQUAL(createByteArray("abcdef"), connectionFactory->connections[0]->dataWritten); } void testDisconnect_AfterConnectRequest() { HTTPConnectProxiedConnection::ref testling(createTestling()); connect(testling, HostAddressPort(HostAddress("2.2.2.2"), 2345)); testling->disconnect(); CPPUNIT_ASSERT(connectionFactory->connections[0]->disconnected); CPPUNIT_ASSERT(disconnected); CPPUNIT_ASSERT(!disconnectedError); } void testDisconnect_AfterConnect() { HTTPConnectProxiedConnection::ref testling(createTestling()); connect(testling, HostAddressPort(HostAddress("2.2.2.2"), 2345)); connectionFactory->connections[0]->onDataRead(createSafeByteArrayRef("HTTP/1.0 200 Connection established\r\n\r\n")); eventLoop->processEvents(); testling->disconnect(); CPPUNIT_ASSERT(connectionFactory->connections[0]->disconnected); CPPUNIT_ASSERT(disconnected); CPPUNIT_ASSERT(!disconnectedError); } private: HTTPConnectProxiedConnection::ref createTestling() { boost::shared_ptr c = HTTPConnectProxiedConnection::create(resolver, connectionFactory, timerFactory, proxyHost, proxyPort, "", ""); c->onConnectFinished.connect(boost::bind(&HTTPConnectProxiedConnectionTest::handleConnectFinished, this, _1)); c->onDisconnected.connect(boost::bind(&HTTPConnectProxiedConnectionTest::handleDisconnected, this, _1)); c->onDataRead.connect(boost::bind(&HTTPConnectProxiedConnectionTest::handleDataRead, this, _1)); return c; } void handleConnectFinished(bool error) { connectFinished = true; connectFinishedWithError = error; } void handleDisconnected(const boost::optional& e) { disconnected = true; disconnectedError = e; } void handleDataRead(boost::shared_ptr d) { append(dataRead, *d); } struct MockConnection : public Connection { public: MockConnection(const std::vector& failingPorts, EventLoop* eventLoop) : eventLoop(eventLoop), failingPorts(failingPorts), disconnected(false) { } void listen() { assert(false); } void connect(const HostAddressPort& address) { hostAddressPort = address; bool fail = std::find(failingPorts.begin(), failingPorts.end(), address) != failingPorts.end(); eventLoop->postEvent(boost::bind(boost::ref(onConnectFinished), fail)); } HostAddressPort getLocalAddress() const { return HostAddressPort(); } void disconnect() { disconnected = true; onDisconnected(boost::optional()); } void write(const SafeByteArray& d) { append(dataWritten, d); } EventLoop* eventLoop; boost::optional hostAddressPort; std::vector failingPorts; ByteArray dataWritten; bool disconnected; }; struct MockConnectionFactory : public ConnectionFactory { MockConnectionFactory(EventLoop* eventLoop) : eventLoop(eventLoop) { } boost::shared_ptr createConnection() { boost::shared_ptr connection = boost::make_shared(failingPorts, eventLoop); connections.push_back(connection); return connection; } EventLoop* eventLoop; std::vector< boost::shared_ptr > connections; std::vector failingPorts; }; private: std::string proxyHost; HostAddressPort proxyHostAddress; int proxyPort; HostAddressPort host; DummyEventLoop* eventLoop; StaticDomainNameResolver* resolver; MockConnectionFactory* connectionFactory; TimerFactory* timerFactory; std::vector< boost::shared_ptr > connections; bool connectFinished; bool connectFinishedWithError; bool disconnected; boost::optional disconnectedError; ByteArray dataRead; }; CPPUNIT_TEST_SUITE_REGISTRATION(HTTPConnectProxiedConnectionTest); swift-im-2.0+dev6/Swiften/Network/UnitTest/BOSHConnectionPoolTest.cpp0000644000175000017500000004763112227051774025476 0ustar kismithkismith/* * Copyright (c) 2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Swift; typedef boost::shared_ptr PoolRef; class BOSHConnectionPoolTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(BOSHConnectionPoolTest); CPPUNIT_TEST(testConnectionCount_OneWrite); CPPUNIT_TEST(testConnectionCount_TwoWrites); CPPUNIT_TEST(testConnectionCount_ThreeWrites); CPPUNIT_TEST(testConnectionCount_ThreeWrites_ManualConnect); CPPUNIT_TEST(testConnectionCount_ThreeWritesTwoReads); CPPUNIT_TEST(testSession); CPPUNIT_TEST(testWrite_Empty); CPPUNIT_TEST_SUITE_END(); public: void setUp() { to = "wonderland.lit"; path = "/http-bind"; port = "5280"; sid = "MyShinySID"; initial = ""; eventLoop = new DummyEventLoop(); connectionFactory = new MockConnectionFactory(eventLoop); boshURL = URL("http", to, 5280, path); sessionTerminated = 0; sessionStarted = 0; initialRID = 2349876; xmppDataRead.clear(); boshDataRead.clear(); boshDataWritten.clear(); resolver = new StaticDomainNameResolver(eventLoop); resolver->addAddress(to, HostAddress("127.0.0.1")); timerFactory = new DummyTimerFactory(); } void tearDown() { eventLoop->processEvents(); delete connectionFactory; delete resolver; delete timerFactory; delete eventLoop; } void testConnectionCount_OneWrite() { PoolRef testling = createTestling(); CPPUNIT_ASSERT_EQUAL(st(1), connectionFactory->connections.size()); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(0, sessionStarted); readResponse(initial, connectionFactory->connections[0]); CPPUNIT_ASSERT_EQUAL(1, sessionStarted); CPPUNIT_ASSERT_EQUAL(st(1), connectionFactory->connections.size()); testling->write(createSafeByteArray("")); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(st(1), connectionFactory->connections.size()); CPPUNIT_ASSERT_EQUAL(1, sessionStarted); } void testConnectionCount_TwoWrites() { PoolRef testling = createTestling(); CPPUNIT_ASSERT_EQUAL(st(1), connectionFactory->connections.size()); eventLoop->processEvents(); readResponse(initial, connectionFactory->connections[0]); eventLoop->processEvents(); testling->write(createSafeByteArray("")); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(st(1), connectionFactory->connections.size()); testling->write(createSafeByteArray("")); eventLoop->processEvents(); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(st(2), connectionFactory->connections.size()); } void testConnectionCount_ThreeWrites() { PoolRef testling = createTestling(); CPPUNIT_ASSERT_EQUAL(st(1), connectionFactory->connections.size()); eventLoop->processEvents(); readResponse(initial, connectionFactory->connections[0]); testling->restartStream(); readResponse("", connectionFactory->connections[0]); testling->restartStream(); readResponse("", connectionFactory->connections[0]); testling->write(createSafeByteArray("")); testling->write(createSafeByteArray("")); testling->write(createSafeByteArray("")); eventLoop->processEvents(); CPPUNIT_ASSERT(st(2) >= connectionFactory->connections.size()); } void testConnectionCount_ThreeWrites_ManualConnect() { connectionFactory->autoFinishConnect = false; PoolRef testling = createTestling(); CPPUNIT_ASSERT_EQUAL(st(1), connectionFactory->connections.size()); CPPUNIT_ASSERT_EQUAL(st(0), boshDataWritten.size()); /* Connection not connected yet, can't send data */ connectionFactory->connections[0]->onConnectFinished(false); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(st(1), boshDataWritten.size()); /* Connection finished, stream header sent */ readResponse(initial, connectionFactory->connections[0]); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(st(1), connectionFactory->connections.size()); CPPUNIT_ASSERT_EQUAL(st(1), boshDataWritten.size()); /* Don't respond to initial data with a holding call */ testling->restartStream(); eventLoop->processEvents(); readResponse("", connectionFactory->connections[0]); eventLoop->processEvents(); testling->restartStream(); eventLoop->processEvents(); testling->write(createSafeByteArray("")); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(st(2), connectionFactory->connections.size()); CPPUNIT_ASSERT_EQUAL(st(3), boshDataWritten.size()); /* New connection isn't up yet. */ connectionFactory->connections[1]->onConnectFinished(false); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(st(4), boshDataWritten.size()); /* New connection ready. */ testling->write(createSafeByteArray("")); eventLoop->processEvents(); testling->write(createSafeByteArray("")); CPPUNIT_ASSERT_EQUAL(st(4), boshDataWritten.size()); /* New data can't be sent, no free connections. */ eventLoop->processEvents(); CPPUNIT_ASSERT(st(2) >= connectionFactory->connections.size()); } void testConnectionCount_ThreeWritesTwoReads() { boost::shared_ptr c0; boost::shared_ptr c1; long rid = initialRID; PoolRef testling = createTestling(); CPPUNIT_ASSERT_EQUAL(st(1), connectionFactory->connections.size()); c0 = connectionFactory->connections[0]; eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(st(1), boshDataWritten.size()); /* header*/ rid++; readResponse(initial, c0); CPPUNIT_ASSERT_EQUAL(st(1), boshDataWritten.size()); CPPUNIT_ASSERT_EQUAL(st(1), connectionFactory->connections.size()); CPPUNIT_ASSERT(!c0->pending); rid++; testling->restartStream(); eventLoop->processEvents(); readResponse("", connectionFactory->connections[0]); rid++; testling->write(createSafeByteArray("")); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(st(2), connectionFactory->connections.size()); /* 0 was waiting for response, open and send on 1 */ CPPUNIT_ASSERT_EQUAL(st(4), boshDataWritten.size()); /* data */ c1 = connectionFactory->connections[1]; std::string fullBody = ""; /* check empty write */ CPPUNIT_ASSERT_EQUAL(fullBody, lastBody()); CPPUNIT_ASSERT(c0->pending); CPPUNIT_ASSERT(c1->pending); rid++; readResponse("", c0); /* Doesn't include necessary attributes - as the support is improved this'll start to fail */ eventLoop->processEvents(); CPPUNIT_ASSERT(!c0->pending); CPPUNIT_ASSERT(c1->pending); CPPUNIT_ASSERT_EQUAL(st(4), boshDataWritten.size()); /* don't send empty in [0], still have [1] waiting */ CPPUNIT_ASSERT_EQUAL(st(2), connectionFactory->connections.size()); rid++; readResponse("", c1); eventLoop->processEvents(); CPPUNIT_ASSERT(!c1->pending); CPPUNIT_ASSERT(c0->pending); CPPUNIT_ASSERT_EQUAL(st(5), boshDataWritten.size()); /* empty to make room */ CPPUNIT_ASSERT_EQUAL(st(2), connectionFactory->connections.size()); rid++; testling->write(createSafeByteArray("")); eventLoop->processEvents(); CPPUNIT_ASSERT(c0->pending); CPPUNIT_ASSERT(c1->pending); CPPUNIT_ASSERT_EQUAL(st(6), boshDataWritten.size()); /* data */ rid++; testling->write(createSafeByteArray("")); CPPUNIT_ASSERT(c0->pending); CPPUNIT_ASSERT(c1->pending); CPPUNIT_ASSERT_EQUAL(st(6), boshDataWritten.size()); /* Don't send data, no room */ eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(st(2), connectionFactory->connections.size()); } void testSession() { to = "prosody.doomsong.co.uk"; resolver->addAddress("prosody.doomsong.co.uk", HostAddress("127.0.0.1")); path = "/http-bind/"; boshURL = URL("http", to, 5280, path); PoolRef testling = createTestling(); CPPUNIT_ASSERT_EQUAL(st(1), connectionFactory->connections.size()); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(st(1), boshDataWritten.size()); /* header*/ CPPUNIT_ASSERT_EQUAL(st(1), connectionFactory->connections.size()); std::string response = "SCRAM-SHA-1DIGEST-MD5"; readResponse(response, connectionFactory->connections[0]); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(st(1), boshDataWritten.size()); CPPUNIT_ASSERT_EQUAL(st(1), connectionFactory->connections.size()); std::string send = "biwsbj1hZG1pbixyPWZhOWE5ZDhiLWZmMDctNGE4Yy04N2E3LTg4YWRiNDQxZGUwYg=="; testling->write(createSafeByteArray(send)); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(st(2), boshDataWritten.size()); CPPUNIT_ASSERT_EQUAL(st(1), connectionFactory->connections.size()); response = "cj1mYTlhOWQ4Yi1mZjA3LTRhOGMtODdhNy04OGFkYjQ0MWRlMGJhZmZlMWNhMy1mMDJkLTQ5NzEtYjkyNS0yM2NlNWQ2MDQyMjYscz1OVGd5WkdWaFptTXRaVE15WXkwMFpXUmhMV0ZqTURRdFpqYzRNbUppWmpGa1pqWXgsaT00MDk2"; readResponse(response, connectionFactory->connections[0]); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(st(2), boshDataWritten.size()); CPPUNIT_ASSERT_EQUAL(st(1), connectionFactory->connections.size()); send = "Yz1iaXdzLHI9ZmE5YTlkOGItZmYwNy00YThjLTg3YTctODhhZGI0NDFkZTBiYWZmZTFjYTMtZjAyZC00OTcxLWI5MjUtMjNjZTVkNjA0MjI2LHA9aU11NWt3dDN2VWplU2RqL01Jb3VIRldkZjBnPQ=="; testling->write(createSafeByteArray(send)); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(st(3), boshDataWritten.size()); CPPUNIT_ASSERT_EQUAL(st(1), connectionFactory->connections.size()); response = "dj1YNmNBY3BBOWxHNjNOOXF2bVQ5S0FacERrVm89"; readResponse(response, connectionFactory->connections[0]); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(st(3), boshDataWritten.size()); CPPUNIT_ASSERT_EQUAL(st(1), connectionFactory->connections.size()); testling->restartStream(); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(st(4), boshDataWritten.size()); CPPUNIT_ASSERT_EQUAL(st(1), connectionFactory->connections.size()); response = ""; readResponse(response, connectionFactory->connections[0]); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(st(5), boshDataWritten.size()); /* Now we've authed (restarted) we should be keeping one query in flight so the server can reply to us at any time it wants. */ CPPUNIT_ASSERT_EQUAL(st(1), connectionFactory->connections.size()); send = "d5a9744036cd20a0"; testling->write(createSafeByteArray(send)); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(st(6), boshDataWritten.size()); CPPUNIT_ASSERT_EQUAL(st(2), connectionFactory->connections.size()); /* and as it keeps one in flight, it's needed to open a second to send these data */ } void testWrite_Empty() { boost::shared_ptr c0; PoolRef testling = createTestling(); CPPUNIT_ASSERT_EQUAL(st(1), connectionFactory->connections.size()); c0 = connectionFactory->connections[0]; readResponse(initial, c0); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(st(1), boshDataWritten.size()); /* Shouldn't have sent anything extra */ eventLoop->processEvents(); testling->restartStream(); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(st(2), boshDataWritten.size()); readResponse("", c0); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(st(3), boshDataWritten.size()); std::string fullBody = ""; std::string response = boshDataWritten[2]; size_t bodyPosition = response.find("\r\n\r\n"); CPPUNIT_ASSERT_EQUAL(fullBody, response.substr(bodyPosition+4)); } private: PoolRef createTestling() { BOSHConnectionPool* a = new BOSHConnectionPool(boshURL, resolver, connectionFactory, &parserFactory, static_cast(NULL), timerFactory, eventLoop, to, initialRID, URL(), SafeString(""), SafeString("")); PoolRef pool(a); //FIXME: Remko - why does the above work, but the below fail? //PoolRef pool = boost::make_shared(boshURL, resolver, connectionFactory, &parserFactory, static_cast(NULL), timerFactory, eventLoop, to, initialRID, URL(), SafeString(""), SafeString("")); pool->onXMPPDataRead.connect(boost::bind(&BOSHConnectionPoolTest::handleXMPPDataRead, this, _1)); pool->onBOSHDataRead.connect(boost::bind(&BOSHConnectionPoolTest::handleBOSHDataRead, this, _1)); pool->onBOSHDataWritten.connect(boost::bind(&BOSHConnectionPoolTest::handleBOSHDataWritten, this, _1)); pool->onSessionStarted.connect(boost::bind(&BOSHConnectionPoolTest::handleSessionStarted, this)); pool->onSessionTerminated.connect(boost::bind(&BOSHConnectionPoolTest::handleSessionTerminated, this)); eventLoop->processEvents(); eventLoop->processEvents(); return pool; } std::string lastBody() { std::string response = boshDataWritten[boshDataWritten.size() - 1]; size_t bodyPosition = response.find("\r\n\r\n"); return response.substr(bodyPosition+4); } size_t st(int val) { return static_cast(val); } void handleXMPPDataRead(const SafeByteArray& d) { xmppDataRead.push_back(safeByteArrayToString(d)); } void handleBOSHDataRead(const SafeByteArray& d) { boshDataRead.push_back(safeByteArrayToString(d)); } void handleBOSHDataWritten(const SafeByteArray& d) { boshDataWritten.push_back(safeByteArrayToString(d)); } void handleSessionStarted() { sessionStarted++; } void handleSessionTerminated() { sessionTerminated++; } struct MockConnection : public Connection { public: MockConnection(const std::vector& failingPorts, EventLoop* eventLoop, bool autoFinishConnect) : eventLoop(eventLoop), failingPorts(failingPorts), disconnected(false), pending(false), autoFinishConnect(autoFinishConnect) { } void listen() { assert(false); } void connect(const HostAddressPort& address) { hostAddressPort = address; bool fail = std::find(failingPorts.begin(), failingPorts.end(), address) != failingPorts.end(); if (autoFinishConnect) { eventLoop->postEvent(boost::bind(boost::ref(onConnectFinished), fail)); } } HostAddressPort getLocalAddress() const { return HostAddressPort(); } void disconnect() { disconnected = true; onDisconnected(boost::optional()); } void write(const SafeByteArray& d) { append(dataWritten, d); pending = true; } EventLoop* eventLoop; boost::optional hostAddressPort; std::vector failingPorts; ByteArray dataWritten; bool disconnected; bool pending; bool autoFinishConnect; }; struct MockConnectionFactory : public ConnectionFactory { MockConnectionFactory(EventLoop* eventLoop, bool autoFinishConnect = true) : eventLoop(eventLoop), autoFinishConnect(autoFinishConnect) { } boost::shared_ptr createConnection() { boost::shared_ptr connection = boost::make_shared(failingPorts, eventLoop, autoFinishConnect); connections.push_back(connection); return connection; } EventLoop* eventLoop; std::vector< boost::shared_ptr > connections; std::vector failingPorts; bool autoFinishConnect; }; void readResponse(const std::string& response, boost::shared_ptr connection) { connection->pending = false; boost::shared_ptr data1 = boost::make_shared(createSafeByteArray( "HTTP/1.1 200 OK\r\n" "Content-Type: text/xml; charset=utf-8\r\n" "Access-Control-Allow-Origin: *\r\n" "Access-Control-Allow-Headers: Content-Type\r\n" "Content-Length: ")); connection->onDataRead(data1); boost::shared_ptr data2 = boost::make_shared(createSafeByteArray(boost::lexical_cast(response.size()))); connection->onDataRead(data2); boost::shared_ptr data3 = boost::make_shared(createSafeByteArray("\r\n\r\n")); connection->onDataRead(data3); boost::shared_ptr data4 = boost::make_shared(createSafeByteArray(response)); connection->onDataRead(data4); } std::string fullRequestFor(const std::string& data) { std::string body = data; std::string result = "POST /" + path + " HTTP/1.1\r\n" + "Host: " + to + ":" + port + "\r\n" + "Content-Type: text/xml; charset=utf-8\r\n" + "Content-Length: " + boost::lexical_cast(body.size()) + "\r\n\r\n" + body; return result; } private: URL boshURL; DummyEventLoop* eventLoop; MockConnectionFactory* connectionFactory; std::vector xmppDataRead; std::vector boshDataRead; std::vector boshDataWritten; PlatformXMLParserFactory parserFactory; StaticDomainNameResolver* resolver; TimerFactory* timerFactory; std::string to; std::string path; std::string port; std::string sid; std::string initial; long initialRID; int sessionStarted; int sessionTerminated; }; CPPUNIT_TEST_SUITE_REGISTRATION(BOSHConnectionPoolTest); swift-im-2.0+dev6/Swiften/Network/UnitTest/HostAddressTest.cpp0000644000175000017500000000332212227051774024301 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include using namespace Swift; class HostAddressTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(HostAddressTest); CPPUNIT_TEST(testConstructor); CPPUNIT_TEST(testConstructor_Invalid); CPPUNIT_TEST(testConstructor_InvalidString); CPPUNIT_TEST(testToString); CPPUNIT_TEST(testToString_IPv6); CPPUNIT_TEST(testToString_Invalid); CPPUNIT_TEST_SUITE_END(); public: void testConstructor() { HostAddress testling("192.168.1.254"); CPPUNIT_ASSERT_EQUAL(std::string("192.168.1.254"), testling.toString()); CPPUNIT_ASSERT(testling.isValid()); } void testConstructor_Invalid() { HostAddress testling; CPPUNIT_ASSERT(!testling.isValid()); } void testConstructor_InvalidString() { HostAddress testling("invalid"); CPPUNIT_ASSERT(!testling.isValid()); } void testToString() { unsigned char address[4] = {10, 0, 1, 253}; HostAddress testling(address, 4); CPPUNIT_ASSERT_EQUAL(std::string("10.0.1.253"), testling.toString()); } void testToString_IPv6() { unsigned char address[16] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17}; HostAddress testling(address, 16); CPPUNIT_ASSERT_EQUAL(std::string("102:304:506:708:90a:b0c:d0e:f11"), testling.toString()); } void testToString_Invalid() { HostAddress testling; CPPUNIT_ASSERT_EQUAL(std::string("0.0.0.0"), testling.toString()); } }; CPPUNIT_TEST_SUITE_REGISTRATION(HostAddressTest); swift-im-2.0+dev6/Swiften/Network/UnitTest/DomainNameServiceQueryTest.cpp0000644000175000017500000000643312227051774026443 0ustar kismithkismith/* * Copyright (c) 2012 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include using namespace Swift; namespace { struct RandomGenerator1 : public RandomGenerator { virtual int generateRandomInteger(int) { return 0; } }; struct RandomGenerator2 : public RandomGenerator { virtual int generateRandomInteger(int i) { return i; } }; } class DomainNameServiceQueryTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(DomainNameServiceQueryTest); CPPUNIT_TEST(testSortResults_Random1); CPPUNIT_TEST(testSortResults_Random2); CPPUNIT_TEST_SUITE_END(); public: void testSortResults_Random1() { std::vector results; results.push_back(DomainNameServiceQuery::Result("server1.com", 5222, 5, 1)); results.push_back(DomainNameServiceQuery::Result("server2.com", 5222, 3, 10)); results.push_back(DomainNameServiceQuery::Result("server3.com", 5222, 6, 1)); results.push_back(DomainNameServiceQuery::Result("server4.com", 5222, 3, 20)); results.push_back(DomainNameServiceQuery::Result("server5.com", 5222, 2, 1)); results.push_back(DomainNameServiceQuery::Result("server6.com", 5222, 3, 10)); RandomGenerator1 generator; DomainNameServiceQuery::sortResults(results, generator); CPPUNIT_ASSERT_EQUAL(std::string("server5.com"), results[0].hostname); CPPUNIT_ASSERT_EQUAL(std::string("server2.com"), results[1].hostname); CPPUNIT_ASSERT_EQUAL(std::string("server4.com"), results[2].hostname); CPPUNIT_ASSERT_EQUAL(std::string("server6.com"), results[3].hostname); CPPUNIT_ASSERT_EQUAL(std::string("server1.com"), results[4].hostname); CPPUNIT_ASSERT_EQUAL(std::string("server3.com"), results[5].hostname); } void testSortResults_Random2() { std::vector results; results.push_back(DomainNameServiceQuery::Result("server1.com", 5222, 5, 1)); results.push_back(DomainNameServiceQuery::Result("server2.com", 5222, 3, 10)); results.push_back(DomainNameServiceQuery::Result("server3.com", 5222, 6, 1)); results.push_back(DomainNameServiceQuery::Result("server4.com", 5222, 3, 20)); results.push_back(DomainNameServiceQuery::Result("server5.com", 5222, 2, 1)); results.push_back(DomainNameServiceQuery::Result("server6.com", 5222, 3, 10)); results.push_back(DomainNameServiceQuery::Result("server7.com", 5222, 3, 40)); RandomGenerator2 generator; DomainNameServiceQuery::sortResults(results, generator); CPPUNIT_ASSERT_EQUAL(std::string("server5.com"), results[0].hostname); CPPUNIT_ASSERT_EQUAL(std::string("server7.com"), results[1].hostname); CPPUNIT_ASSERT_EQUAL(std::string("server2.com"), results[2].hostname); CPPUNIT_ASSERT_EQUAL(std::string("server4.com"), results[3].hostname); CPPUNIT_ASSERT_EQUAL(std::string("server6.com"), results[4].hostname); CPPUNIT_ASSERT_EQUAL(std::string("server1.com"), results[5].hostname); CPPUNIT_ASSERT_EQUAL(std::string("server3.com"), results[6].hostname); } }; CPPUNIT_TEST_SUITE_REGISTRATION(DomainNameServiceQueryTest); swift-im-2.0+dev6/Swiften/Network/UnitTest/ConnectorTest.cpp0000644000175000017500000003212012227051774024006 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include #include #include using namespace Swift; class ConnectorTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(ConnectorTest); CPPUNIT_TEST(testConnect); CPPUNIT_TEST(testConnect_NoServiceLookups); CPPUNIT_TEST(testConnect_NoServiceLookups_DefaultPort); CPPUNIT_TEST(testConnect_FirstAddressHostFails); CPPUNIT_TEST(testConnect_NoSRVHost); CPPUNIT_TEST(testConnect_NoHosts); CPPUNIT_TEST(testConnect_FirstSRVHostFails); CPPUNIT_TEST(testConnect_AllSRVHostsFailWithoutFallbackHost); CPPUNIT_TEST(testConnect_AllSRVHostsFailWithFallbackHost); CPPUNIT_TEST(testConnect_SRVAndFallbackHostsFail); //CPPUNIT_TEST(testConnect_TimeoutDuringResolve); CPPUNIT_TEST(testConnect_TimeoutDuringConnectToOnlyCandidate); CPPUNIT_TEST(testConnect_TimeoutDuringConnectToCandidateFallsBack); CPPUNIT_TEST(testConnect_NoTimeout); CPPUNIT_TEST(testStop_DuringSRVQuery); CPPUNIT_TEST(testStop_Timeout); CPPUNIT_TEST_SUITE_END(); public: void setUp() { host1 = HostAddressPort(HostAddress("1.1.1.1"), 1234); host2 = HostAddressPort(HostAddress("2.2.2.2"), 2345); host3 = HostAddressPort(HostAddress("3.3.3.3"), 5222); eventLoop = new DummyEventLoop(); resolver = new StaticDomainNameResolver(eventLoop); connectionFactory = new MockConnectionFactory(eventLoop); timerFactory = new DummyTimerFactory(); } void tearDown() { delete timerFactory; delete connectionFactory; delete resolver; delete eventLoop; } void testConnect() { Connector::ref testling(createConnector()); resolver->addXMPPClientService("foo.com", host1); resolver->addXMPPClientService("foo.com", host2); resolver->addAddress("foo.com", host3.getAddress()); testling->start(); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(1, static_cast(connections.size())); CPPUNIT_ASSERT(connections[0]); CPPUNIT_ASSERT(host1 == *(connections[0]->hostAddressPort)); CPPUNIT_ASSERT(!boost::dynamic_pointer_cast(error)); } void testConnect_NoServiceLookups() { Connector::ref testling(createConnector(4321, false)); resolver->addXMPPClientService("foo.com", host1); resolver->addXMPPClientService("foo.com", host2); resolver->addAddress("foo.com", host3.getAddress()); testling->start(); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(1, static_cast(connections.size())); CPPUNIT_ASSERT(connections[0]); CPPUNIT_ASSERT(host3.getAddress() == (*(connections[0]->hostAddressPort)).getAddress()); CPPUNIT_ASSERT(4321 == (*(connections[0]->hostAddressPort)).getPort()); CPPUNIT_ASSERT(!boost::dynamic_pointer_cast(error)); } void testConnect_NoServiceLookups_DefaultPort() { Connector::ref testling(createConnector(-1, false)); resolver->addXMPPClientService("foo.com", host1); resolver->addXMPPClientService("foo.com", host2); resolver->addAddress("foo.com", host3.getAddress()); testling->start(); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(1, static_cast(connections.size())); CPPUNIT_ASSERT(connections[0]); CPPUNIT_ASSERT(host3.getAddress() == (*(connections[0]->hostAddressPort)).getAddress()); CPPUNIT_ASSERT_EQUAL(5222, (*(connections[0]->hostAddressPort)).getPort()); CPPUNIT_ASSERT(!boost::dynamic_pointer_cast(error)); } void testConnect_NoSRVHost() { Connector::ref testling(createConnector()); resolver->addAddress("foo.com", host3.getAddress()); testling->start(); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(1, static_cast(connections.size())); CPPUNIT_ASSERT(connections[0]); CPPUNIT_ASSERT(host3 == *(connections[0]->hostAddressPort)); CPPUNIT_ASSERT(!boost::dynamic_pointer_cast(error)); } void testConnect_FirstAddressHostFails() { Connector::ref testling(createConnector()); HostAddress address1("1.1.1.1"); HostAddress address2("2.2.2.2"); resolver->addXMPPClientService("foo.com", "host-foo.com", 1234); resolver->addAddress("host-foo.com", address1); resolver->addAddress("host-foo.com", address2); connectionFactory->failingPorts.push_back(HostAddressPort(address1, 1234)); testling->start(); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(1, static_cast(connections.size())); CPPUNIT_ASSERT(connections[0]); CPPUNIT_ASSERT(HostAddressPort(address2, 1234) == *(connections[0]->hostAddressPort)); CPPUNIT_ASSERT(!boost::dynamic_pointer_cast(error)); } void testConnect_NoHosts() { Connector::ref testling(createConnector()); testling->start(); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(1, static_cast(connections.size())); CPPUNIT_ASSERT(!connections[0]); CPPUNIT_ASSERT(boost::dynamic_pointer_cast(error)); } void testConnect_FirstSRVHostFails() { Connector::ref testling(createConnector()); resolver->addXMPPClientService("foo.com", host1); resolver->addXMPPClientService("foo.com", host2); connectionFactory->failingPorts.push_back(host1); testling->start(); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(1, static_cast(connections.size())); CPPUNIT_ASSERT(host2 == *(connections[0]->hostAddressPort)); CPPUNIT_ASSERT(!boost::dynamic_pointer_cast(error)); } void testConnect_AllSRVHostsFailWithoutFallbackHost() { Connector::ref testling(createConnector()); resolver->addXMPPClientService("foo.com", host1); resolver->addXMPPClientService("foo.com", host2); connectionFactory->failingPorts.push_back(host1); connectionFactory->failingPorts.push_back(host2); testling->start(); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(1, static_cast(connections.size())); CPPUNIT_ASSERT(!connections[0]); CPPUNIT_ASSERT(!boost::dynamic_pointer_cast(error)); } void testConnect_AllSRVHostsFailWithFallbackHost() { Connector::ref testling(createConnector()); resolver->addXMPPClientService("foo.com", host1); resolver->addXMPPClientService("foo.com", host2); resolver->addAddress("foo.com", host3.getAddress()); connectionFactory->failingPorts.push_back(host1); connectionFactory->failingPorts.push_back(host2); testling->start(); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(1, static_cast(connections.size())); CPPUNIT_ASSERT(connections[0]); CPPUNIT_ASSERT(host3 == *(connections[0]->hostAddressPort)); CPPUNIT_ASSERT(!boost::dynamic_pointer_cast(error)); } void testConnect_SRVAndFallbackHostsFail() { Connector::ref testling(createConnector()); resolver->addXMPPClientService("foo.com", host1); resolver->addAddress("foo.com", host3.getAddress()); connectionFactory->failingPorts.push_back(host1); connectionFactory->failingPorts.push_back(host3); testling->start(); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(1, static_cast(connections.size())); CPPUNIT_ASSERT(!connections[0]); CPPUNIT_ASSERT(!boost::dynamic_pointer_cast(error)); } /*void testConnect_TimeoutDuringResolve() { Connector::ref testling(createConnector()); testling->setTimeoutMilliseconds(10); resolver->setIsResponsive(false); testling->start(); eventLoop->processEvents(); timerFactory->setTime(10); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(1, static_cast(connections.size())); CPPUNIT_ASSERT(boost::dynamic_pointer_cast(error)); CPPUNIT_ASSERT(!connections[0]); }*/ void testConnect_TimeoutDuringConnectToOnlyCandidate() { Connector::ref testling(createConnector()); testling->setTimeoutMilliseconds(10); resolver->addXMPPClientService("foo.com", host1); connectionFactory->isResponsive = false; testling->start(); eventLoop->processEvents(); timerFactory->setTime(10); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(1, static_cast(connections.size())); CPPUNIT_ASSERT(!connections[0]); CPPUNIT_ASSERT(!boost::dynamic_pointer_cast(error)); } void testConnect_TimeoutDuringConnectToCandidateFallsBack() { Connector::ref testling(createConnector()); testling->setTimeoutMilliseconds(10); resolver->addXMPPClientService("foo.com", "host-foo.com", 1234); HostAddress address1("1.1.1.1"); resolver->addAddress("host-foo.com", address1); HostAddress address2("2.2.2.2"); resolver->addAddress("host-foo.com", address2); connectionFactory->isResponsive = false; testling->start(); eventLoop->processEvents(); connectionFactory->isResponsive = true; timerFactory->setTime(10); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(1, static_cast(connections.size())); CPPUNIT_ASSERT(connections[0]); CPPUNIT_ASSERT(HostAddressPort(address2, 1234) == *(connections[0]->hostAddressPort)); CPPUNIT_ASSERT(!boost::dynamic_pointer_cast(error)); } void testConnect_NoTimeout() { Connector::ref testling(createConnector()); testling->setTimeoutMilliseconds(10); resolver->addXMPPClientService("foo.com", host1); testling->start(); eventLoop->processEvents(); timerFactory->setTime(10); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(1, static_cast(connections.size())); CPPUNIT_ASSERT(connections[0]); CPPUNIT_ASSERT(!boost::dynamic_pointer_cast(error)); } void testStop_DuringSRVQuery() { Connector::ref testling(createConnector()); resolver->addXMPPClientService("foo.com", host1); testling->start(); testling->stop(); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(1, static_cast(connections.size())); CPPUNIT_ASSERT(!connections[0]); CPPUNIT_ASSERT(boost::dynamic_pointer_cast(error)); } void testStop_Timeout() { Connector::ref testling(createConnector()); testling->setTimeoutMilliseconds(10); resolver->addXMPPClientService("foo.com", host1); testling->start(); testling->stop(); eventLoop->processEvents(); timerFactory->setTime(10); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(1, static_cast(connections.size())); CPPUNIT_ASSERT(!connections[0]); } private: Connector::ref createConnector(int port = -1, bool doServiceLookups = true) { Connector::ref connector = Connector::create("foo.com", port, doServiceLookups, resolver, connectionFactory, timerFactory); connector->onConnectFinished.connect(boost::bind(&ConnectorTest::handleConnectorFinished, this, _1, _2)); return connector; } void handleConnectorFinished(boost::shared_ptr connection, boost::shared_ptr resultError) { boost::shared_ptr c(boost::dynamic_pointer_cast(connection)); if (connection) { assert(c); } connections.push_back(c); error = resultError; } struct MockConnection : public Connection { public: MockConnection(const std::vector& failingPorts, bool isResponsive, EventLoop* eventLoop) : eventLoop(eventLoop), failingPorts(failingPorts), isResponsive(isResponsive) {} void listen() { assert(false); } void connect(const HostAddressPort& address) { hostAddressPort = address; if (isResponsive) { bool fail = std::find(failingPorts.begin(), failingPorts.end(), address) != failingPorts.end(); eventLoop->postEvent(boost::bind(boost::ref(onConnectFinished), fail)); } } HostAddressPort getLocalAddress() const { return HostAddressPort(); } void disconnect() { assert(false); } void write(const SafeByteArray&) { assert(false); } EventLoop* eventLoop; boost::optional hostAddressPort; std::vector failingPorts; bool isResponsive; }; struct MockConnectionFactory : public ConnectionFactory { MockConnectionFactory(EventLoop* eventLoop) : eventLoop(eventLoop), isResponsive(true) { } boost::shared_ptr createConnection() { return boost::shared_ptr(new MockConnection(failingPorts, isResponsive, eventLoop)); } EventLoop* eventLoop; bool isResponsive; std::vector failingPorts; }; private: HostAddressPort host1; HostAddressPort host2; HostAddressPort host3; DummyEventLoop* eventLoop; StaticDomainNameResolver* resolver; MockConnectionFactory* connectionFactory; DummyTimerFactory* timerFactory; std::vector< boost::shared_ptr > connections; boost::shared_ptr error; }; CPPUNIT_TEST_SUITE_REGISTRATION(ConnectorTest); swift-im-2.0+dev6/Swiften/Network/UnixProxyProvider.h0000644000175000017500000000114712227051774022607 0ustar kismithkismith/* * Copyright (c) 2010-2011 Thilo Cestonaro * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include namespace Swift { class GConfProxyProvider; class UnixProxyProvider : public ProxyProvider { public: UnixProxyProvider(); ~UnixProxyProvider(); virtual HostAddressPort getHTTPConnectProxy() const; virtual HostAddressPort getSOCKS5Proxy() const; private: GConfProxyProvider* gconfProxyProvider; EnvironmentProxyProvider environmentProxyProvider; }; } swift-im-2.0+dev6/Swiften/Network/HTTPConnectProxiedConnection.cpp0000644000175000017500000000553112227051774025107 0ustar kismithkismith/* * Copyright (c) 2010-2011 Thilo Cestonaro * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ /* * Copyright (c) 2011-2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include #include using namespace Swift; HTTPConnectProxiedConnection::HTTPConnectProxiedConnection( DomainNameResolver* resolver, ConnectionFactory* connectionFactory, TimerFactory* timerFactory, const std::string& proxyHost, int proxyPort, const SafeString& authID, const SafeString& authPassword) : ProxiedConnection(resolver, connectionFactory, timerFactory, proxyHost, proxyPort), authID_(authID), authPassword_(authPassword) { } void HTTPConnectProxiedConnection::initializeProxy() { std::stringstream connect; connect << "CONNECT " << getServer().getAddress().toString() << ":" << getServer().getPort() << " HTTP/1.1\r\n"; SafeByteArray data = createSafeByteArray(connect.str()); if (!authID_.empty() && !authPassword_.empty()) { append(data, createSafeByteArray("Proxy-Authorization: Basic ")); SafeByteArray credentials = authID_; append(credentials, createSafeByteArray(":")); append(credentials, authPassword_); append(data, Base64::encode(credentials)); append(data, createSafeByteArray("\r\n")); } append(data, createSafeByteArray("\r\n")); SWIFT_LOG(debug) << "HTTP Proxy send headers: " << byteArrayToString(ByteArray(data.begin(), data.end())) << std::endl; write(data); } void HTTPConnectProxiedConnection::handleProxyInitializeData(boost::shared_ptr data) { SWIFT_LOG(debug) << byteArrayToString(ByteArray(data->begin(), data->end())) << std::endl; std::vector tmp = String::split(byteArrayToString(ByteArray(data->begin(), data->end())), ' '); if (tmp.size() > 1) { try { int status = boost::lexical_cast(tmp[1]); SWIFT_LOG(debug) << "Proxy Status: " << status << std::endl; if (status / 100 == 2) { // all 2XX states are OK setProxyInitializeFinished(true); } else { SWIFT_LOG(debug) << "HTTP Proxy returned an error: " << byteArrayToString(ByteArray(data->begin(), data->end())) << std::endl; setProxyInitializeFinished(false); } } catch (boost::bad_lexical_cast&) { SWIFT_LOG(warning) << "Unexpected response: " << tmp[1] << std::endl; setProxyInitializeFinished(false); } } else { setProxyInitializeFinished(false); } } swift-im-2.0+dev6/Swiften/Network/NullProxyProvider.h0000644000175000017500000000066412227051774022601 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include namespace Swift { class NullProxyProvider : public ProxyProvider { public: NullProxyProvider(); virtual HostAddressPort getHTTPConnectProxy() const; virtual HostAddressPort getSOCKS5Proxy() const; }; } swift-im-2.0+dev6/Swiften/Network/HostNameOrAddress.cpp0000644000175000017500000000120212227051774022757 0ustar kismithkismith/* * Copyright (c) 2012 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include using namespace Swift; namespace { struct ToStringVisitor : public boost::static_visitor { std::string operator()(const HostAddress& address) const { return address.toString(); } std::string operator()(const std::string & str) const { return str; } }; } namespace Swift { std::string toString(const HostNameOrAddress& address) { return boost::apply_visitor(ToStringVisitor(), address); } } swift-im-2.0+dev6/Swiften/Network/Timer.h0000644000175000017500000000143312227051774020165 0ustar kismithkismith/* * 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 #include namespace Swift { /** * A class for triggering an event after a given period. */ class SWIFTEN_API Timer { public: typedef boost::shared_ptr ref; virtual ~Timer(); /** * Starts the timer. * * After the given period, onTick() will be called. */ virtual void start() = 0; /** * Cancels the timer. * * If the timer was started, onTick() will no longer be called. */ virtual void stop() = 0; /** * Emitted when the timer expires. */ boost::signal onTick; }; } swift-im-2.0+dev6/Swiften/Network/TLSConnectionFactory.h0000644000175000017500000000126412227051774023121 0ustar kismithkismith/* * Copyright (c) 2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include namespace Swift { class Connection; class TLSConnectionFactory : public ConnectionFactory { public: TLSConnectionFactory(TLSContextFactory* contextFactory, ConnectionFactory* connectionFactory); virtual ~TLSConnectionFactory(); virtual boost::shared_ptr createConnection(); private: TLSContextFactory* contextFactory; ConnectionFactory* connectionFactory; }; } swift-im-2.0+dev6/Swiften/Network/BoostConnectionFactory.h0000644000175000017500000000124012227051774023537 0ustar kismithkismith/* * 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 #include #include namespace Swift { class BoostConnection; class BoostConnectionFactory : public ConnectionFactory { public: BoostConnectionFactory(boost::shared_ptr, EventLoop* eventLoop); virtual boost::shared_ptr createConnection(); private: boost::shared_ptr ioService; EventLoop* eventLoop; }; } swift-im-2.0+dev6/Swiften/Network/PlatformDomainNameAddressQuery.h0000644000175000017500000000173612227051774025164 0ustar kismithkismith/* * 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 #include #include #include #include #include namespace Swift { class PlatformDomainNameResolver; class EventLoop; class PlatformDomainNameAddressQuery : public DomainNameAddressQuery, public PlatformDomainNameQuery, public boost::enable_shared_from_this, public EventOwner { public: PlatformDomainNameAddressQuery(const std::string& host, EventLoop* eventLoop, PlatformDomainNameResolver*); void run(); private: void runBlocking(); void emitError(); private: boost::asio::io_service ioService; std::string hostname; EventLoop* eventLoop; }; } swift-im-2.0+dev6/Swiften/Network/BoostConnection.h0000644000175000017500000000367612227051774022226 0ustar kismithkismith/* * 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 #include #include #include #include #include #include #include namespace boost { class thread; namespace system { class error_code; } } namespace Swift { class EventLoop; class SWIFTEN_API BoostConnection : public Connection, public EventOwner, public boost::enable_shared_from_this { public: typedef boost::shared_ptr ref; ~BoostConnection(); static ref create(boost::shared_ptr ioService, EventLoop* eventLoop) { return ref(new BoostConnection(ioService, eventLoop)); } virtual void listen(); virtual void connect(const HostAddressPort& address); virtual void disconnect(); virtual void write(const SafeByteArray& data); boost::asio::ip::tcp::socket& getSocket() { return socket_; } HostAddressPort getLocalAddress() const; private: BoostConnection(boost::shared_ptr ioService, EventLoop* eventLoop); void handleConnectFinished(const boost::system::error_code& error); void handleSocketRead(const boost::system::error_code& error, size_t bytesTransferred); void handleDataWritten(const boost::system::error_code& error); void doRead(); void doWrite(const SafeByteArray& data); void closeSocket(); private: EventLoop* eventLoop; boost::shared_ptr ioService; boost::asio::ip::tcp::socket socket_; boost::shared_ptr readBuffer_; boost::mutex writeMutex_; bool writing_; SafeByteArray writeQueue_; bool closeSocketAfterNextWrite_; }; } swift-im-2.0+dev6/Swiften/Network/NetworkFactories.h0000644000175000017500000000213212227051774022373 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once namespace Swift { class TimerFactory; class ConnectionFactory; class DomainNameResolver; class ConnectionServerFactory; class NATTraverser; class XMLParserFactory; class TLSContextFactory; class CertificateFactory; class ProxyProvider; class EventLoop; /** * An interface collecting network factories. */ class NetworkFactories { public: virtual ~NetworkFactories(); virtual TimerFactory* getTimerFactory() const = 0; virtual ConnectionFactory* getConnectionFactory() const = 0; virtual DomainNameResolver* getDomainNameResolver() const = 0; virtual ConnectionServerFactory* getConnectionServerFactory() const = 0; virtual NATTraverser* getNATTraverser() const = 0; virtual XMLParserFactory* getXMLParserFactory() const = 0; virtual TLSContextFactory* getTLSContextFactory() const = 0; virtual ProxyProvider* getProxyProvider() const = 0; virtual EventLoop* getEventLoop() const = 0; }; } swift-im-2.0+dev6/Swiften/Network/NATPMPInterface.h0000644000175000017500000000146612227051774021733 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include #include #include #include namespace Swift { class NATPMPInterface : public NATTraversalInterface, boost::noncopyable { public: NATPMPInterface(); ~NATPMPInterface(); virtual bool isAvailable(); virtual boost::optional getPublicIP(); virtual boost::optional addPortForward(int localPort, int publicPort); virtual bool removePortForward(const NATPortMapping&); private: struct Private; boost::shared_ptr p; }; } swift-im-2.0+dev6/Swiften/Network/PlatformDomainNameResolver.h0000644000175000017500000000244612227051774024351 0ustar kismithkismith/* * 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 #include #include #include #include #include #include #include #include namespace Swift { class EventLoop; class SWIFTEN_API PlatformDomainNameResolver : public DomainNameResolver { public: PlatformDomainNameResolver(EventLoop* eventLoop); ~PlatformDomainNameResolver(); virtual DomainNameServiceQuery::ref createServiceQuery(const std::string& name); virtual DomainNameAddressQuery::ref createAddressQuery(const std::string& name); private: void run(); void addQueryToQueue(PlatformDomainNameQuery::ref); private: friend class PlatformDomainNameServiceQuery; friend class PlatformDomainNameAddressQuery; EventLoop* eventLoop; bool stopRequested; boost::thread* thread; std::deque queue; boost::mutex queueMutex; boost::condition_variable queueNonEmpty; }; } swift-im-2.0+dev6/Swiften/Network/DomainNameAddressQuery.cpp0000644000175000017500000000044112227051774024002 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { DomainNameAddressQuery::~DomainNameAddressQuery() { } } swift-im-2.0+dev6/Swiften/Network/DomainNameServiceQuery.cpp0000644000175000017500000000375212227051774024025 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include using namespace Swift; namespace { struct ResultPriorityComparator { bool operator()(const DomainNameServiceQuery::Result& a, const DomainNameServiceQuery::Result& b) const { return a.priority < b.priority; } }; struct GetWeight { GetWeight() {} int operator()(const DomainNameServiceQuery::Result& result) { return result.weight + 1 /* easy hack to account for '0' weights getting at least some weight */; } }; } namespace Swift { DomainNameServiceQuery::~DomainNameServiceQuery() { } void DomainNameServiceQuery::sortResults(std::vector& queries, RandomGenerator& generator) { ResultPriorityComparator comparator; std::sort(queries.begin(), queries.end(), comparator); std::vector::iterator i = queries.begin(); while (i != queries.end()) { std::vector::iterator next = std::upper_bound(i, queries.end(), *i, comparator); if (std::distance(i, next) > 1) { std::vector weights; std::transform(i, next, std::back_inserter(weights), GetWeight()); for (size_t j = 0; j < weights.size() - 1; ++j) { std::vector cumulativeWeights; std::partial_sum(weights.begin() + j, weights.end(), std::back_inserter(cumulativeWeights)); int randomNumber = generator.generateRandomInteger(cumulativeWeights.back()); int selectedIndex = std::lower_bound(cumulativeWeights.begin(), cumulativeWeights.end(), randomNumber) - cumulativeWeights.begin(); std::swap(i[j], i[j + selectedIndex]); std::swap(weights.begin()[j], weights.begin()[j + selectedIndex]); } } i = next; } } } swift-im-2.0+dev6/Swiften/Network/BoostConnectionFactory.cpp0000644000175000017500000000111112227051774024067 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include namespace Swift { BoostConnectionFactory::BoostConnectionFactory(boost::shared_ptr ioService, EventLoop* eventLoop) : ioService(ioService), eventLoop(eventLoop) { } boost::shared_ptr BoostConnectionFactory::createConnection() { return BoostConnection::create(ioService, eventLoop); } } swift-im-2.0+dev6/Swiften/Network/BoostNetworkFactories.h0000644000175000017500000000337212227051774023411 0ustar kismithkismith/* * 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 #include #include namespace Swift { class EventLoop; class NATTraverser; class PlatformTLSFactories; class SWIFTEN_API BoostNetworkFactories : public NetworkFactories { public: BoostNetworkFactories(EventLoop* eventLoop); ~BoostNetworkFactories(); virtual TimerFactory* getTimerFactory() const { return timerFactory; } virtual ConnectionFactory* getConnectionFactory() const { return connectionFactory; } BoostIOServiceThread* getIOServiceThread() { return &ioServiceThread; } DomainNameResolver* getDomainNameResolver() const { return domainNameResolver; } ConnectionServerFactory* getConnectionServerFactory() const { return connectionServerFactory; } NATTraverser* getNATTraverser() const { return natTraverser; } virtual XMLParserFactory* getXMLParserFactory() const { return xmlParserFactory; } virtual TLSContextFactory* getTLSContextFactory() const; virtual ProxyProvider* getProxyProvider() const { return proxyProvider; } virtual EventLoop* getEventLoop() const { return eventLoop; } private: BoostIOServiceThread ioServiceThread; TimerFactory* timerFactory; ConnectionFactory* connectionFactory; DomainNameResolver* domainNameResolver; ConnectionServerFactory* connectionServerFactory; NATTraverser* natTraverser; XMLParserFactory* xmlParserFactory; PlatformTLSFactories* tlsFactories; ProxyProvider* proxyProvider; EventLoop* eventLoop; }; } swift-im-2.0+dev6/Swiften/Network/TimerFactory.cpp0000644000175000017500000000040312227051774022044 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { TimerFactory::~TimerFactory() { } } swift-im-2.0+dev6/Swiften/Network/TimerFactory.h0000644000175000017500000000065312227051774021520 0ustar kismithkismith/* * 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 #include #include namespace Swift { class SWIFTEN_API TimerFactory { public: virtual ~TimerFactory(); virtual Timer::ref createTimer(int milliseconds) = 0; }; } swift-im-2.0+dev6/Swiften/Network/PlatformNetworkEnvironment.h0000644000175000017500000000127712227051774024476 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #if defined(SWIFTEN_PLATFORM_MACOSX) #include namespace Swift { typedef UnixNetworkEnvironment PlatformNetworkEnvironment; } #elif defined(SWIFTEN_PLATFORM_WIN32) #include namespace Swift { typedef WindowsNetworkEnvironment PlatformNetworkEnvironment; } #else #include namespace Swift { typedef UnixNetworkEnvironment PlatformNetworkEnvironment; } #endif swift-im-2.0+dev6/Swiften/Network/DummyTimerFactory.h0000644000175000017500000000114512227051774022531 0ustar kismithkismith/* * 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 #include #include namespace Swift { class SWIFTEN_API DummyTimerFactory : public TimerFactory { public: class DummyTimer; DummyTimerFactory(); virtual boost::shared_ptr createTimer(int milliseconds); void setTime(int time); private: friend class DummyTimer; int currentTime; std::list > timers; }; } swift-im-2.0+dev6/Swiften/Network/BoostNetworkFactories.cpp0000644000175000017500000000341512227051774023742 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include namespace Swift { BoostNetworkFactories::BoostNetworkFactories(EventLoop* eventLoop) : eventLoop(eventLoop){ timerFactory = new BoostTimerFactory(ioServiceThread.getIOService(), eventLoop); connectionFactory = new BoostConnectionFactory(ioServiceThread.getIOService(), eventLoop); domainNameResolver = new PlatformDomainNameResolver(eventLoop); connectionServerFactory = new BoostConnectionServerFactory(ioServiceThread.getIOService(), eventLoop); #ifdef SWIFT_EXPERIMENTAL_FT natTraverser = new PlatformNATTraversalWorker(eventLoop); #else natTraverser = new NullNATTraverser(eventLoop); #endif xmlParserFactory = new PlatformXMLParserFactory(); tlsFactories = new PlatformTLSFactories(); proxyProvider = new PlatformProxyProvider(); } BoostNetworkFactories::~BoostNetworkFactories() { delete proxyProvider; delete tlsFactories; delete xmlParserFactory; delete natTraverser; delete connectionServerFactory; delete domainNameResolver; delete connectionFactory; delete timerFactory; } TLSContextFactory* BoostNetworkFactories::getTLSContextFactory() const { return tlsFactories->getTLSContextFactory(); } } swift-im-2.0+dev6/Swiften/Network/NATTraversalForwardPortRequest.h0000644000175000017500000000101212227051774025147 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include #include namespace Swift { class SWIFTEN_API NATTraversalForwardPortRequest { public: virtual ~NATTraversalForwardPortRequest(); virtual void run() = 0; boost::signal)> onResult; }; } swift-im-2.0+dev6/Swiften/Network/DomainNameResolveError.h0000644000175000017500000000047712227051774023476 0ustar kismithkismith/* * 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 namespace Swift { class DomainNameResolveError : public Error { public: DomainNameResolveError() {} }; } swift-im-2.0+dev6/Swiften/Network/NullNATTraverser.h0000644000175000017500000000131612227051774022260 0ustar kismithkismith/* * 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 namespace Swift { class EventLoop; class NullNATTraverser : public NATTraverser { public: NullNATTraverser(EventLoop* eventLoop); boost::shared_ptr createGetPublicIPRequest(); boost::shared_ptr createForwardPortRequest(int localPort, int publicPort); boost::shared_ptr createRemovePortForwardingRequest(int localPort, int publicPort); private: EventLoop* eventLoop; }; } swift-im-2.0+dev6/Swiften/Network/GConfProxyProvider.h0000644000175000017500000000111612227051774022654 0ustar kismithkismith/* * Copyright (c) 2010-2011 Thilo Cestonaro * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include namespace Swift { class GConfProxyProvider : public ProxyProvider { public: GConfProxyProvider(); virtual HostAddressPort getHTTPConnectProxy() const; virtual HostAddressPort getSOCKS5Proxy() const; private: HostAddressPort getFromGConf(const char* gcHost, const char* gcPort); HostAddressPort socksProxy; HostAddressPort httpProxy; }; } swift-im-2.0+dev6/Swiften/Network/NetworkFactories.cpp0000644000175000017500000000041712227051774022732 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { NetworkFactories::~NetworkFactories() { } } swift-im-2.0+dev6/Swiften/Network/PlatformDomainNameResolver.cpp0000644000175000017500000000436712227051774024710 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include // Putting this early on, because some system types conflict with thread #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Swift; namespace Swift { PlatformDomainNameResolver::PlatformDomainNameResolver(EventLoop* eventLoop) : eventLoop(eventLoop), stopRequested(false) { thread = new boost::thread(boost::bind(&PlatformDomainNameResolver::run, this)); } PlatformDomainNameResolver::~PlatformDomainNameResolver() { stopRequested = true; addQueryToQueue(boost::shared_ptr()); thread->join(); delete thread; } boost::shared_ptr PlatformDomainNameResolver::createServiceQuery(const std::string& name) { return boost::shared_ptr(new PlatformDomainNameServiceQuery(IDNA::getEncoded(name), eventLoop, this)); } boost::shared_ptr PlatformDomainNameResolver::createAddressQuery(const std::string& name) { return boost::shared_ptr(new PlatformDomainNameAddressQuery(IDNA::getEncoded(name), eventLoop, this)); } void PlatformDomainNameResolver::run() { while (!stopRequested) { PlatformDomainNameQuery::ref query; { boost::unique_lock lock(queueMutex); while (queue.empty()) { queueNonEmpty.wait(lock); } query = queue.front(); queue.pop_front(); } // Check whether we don't have a non-null query (used to stop the // resolver) if (query) { query->runBlocking(); } } } void PlatformDomainNameResolver::addQueryToQueue(PlatformDomainNameQuery::ref query) { { boost::lock_guard lock(queueMutex); queue.push_back(query); } queueNonEmpty.notify_one(); } } swift-im-2.0+dev6/Swiften/Network/NullNATTraversalInterface.h0000644000175000017500000000132312227051774024065 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include namespace Swift { class NullNATTraversalInterface : public NATTraversalInterface { public: virtual bool isAvailable() { return true; } virtual boost::optional getPublicIP() { return boost::optional(); } virtual boost::optional addPortForward(int, int) { return boost::optional(); } virtual bool removePortForward(const NATPortMapping&) { return false; } }; } swift-im-2.0+dev6/Swiften/Network/NATTraversalGetPublicIPRequest.h0000644000175000017500000000073112227051774025014 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include namespace Swift { class NATTraversalGetPublicIPRequest { public: virtual ~NATTraversalGetPublicIPRequest(); virtual void run() = 0; boost::signal)> onResult; }; } swift-im-2.0+dev6/Swiften/Network/TLSConnectionFactory.cpp0000644000175000017500000000132512227051774023452 0ustar kismithkismith/* * Copyright (c) 2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include namespace Swift { TLSConnectionFactory::TLSConnectionFactory(TLSContextFactory* contextFactory, ConnectionFactory* connectionFactory) : contextFactory(contextFactory), connectionFactory(connectionFactory){ } TLSConnectionFactory::~TLSConnectionFactory() { } boost::shared_ptr TLSConnectionFactory::createConnection() { return boost::make_shared(connectionFactory->createConnection(), contextFactory); } } swift-im-2.0+dev6/Swiften/Network/ConnectionFactory.h0000644000175000017500000000065612227051774022542 0ustar kismithkismith/* * 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 #include namespace Swift { class Connection; class SWIFTEN_API ConnectionFactory { public: virtual ~ConnectionFactory(); virtual boost::shared_ptr createConnection() = 0; }; } swift-im-2.0+dev6/Swiften/Network/ChainedConnector.h0000644000175000017500000000271612227051774022320 0ustar kismithkismith/* * 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 #include #include #include #include #include #include namespace Swift { class Connection; class Connector; class ConnectionFactory; class TimerFactory; class DomainNameResolver; class SWIFTEN_API ChainedConnector { public: ChainedConnector(const std::string& hostname, int port, bool doServiceLookups, DomainNameResolver*, const std::vector&, TimerFactory*); void setTimeoutMilliseconds(int milliseconds); void start(); void stop(); boost::signal, boost::shared_ptr)> onConnectFinished; private: void finish(boost::shared_ptr connection, boost::shared_ptr); void tryNextConnectionFactory(); void handleConnectorFinished(boost::shared_ptr, boost::shared_ptr); private: std::string hostname; int port; bool doServiceLookups; DomainNameResolver* resolver; std::vector connectionFactories; TimerFactory* timerFactory; int timeoutMilliseconds; std::deque connectionFactoryQueue; boost::shared_ptr currentConnector; boost::shared_ptr lastError; }; }; swift-im-2.0+dev6/Swiften/Network/DummyConnection.cpp0000644000175000017500000000130612227051774022552 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include namespace Swift { DummyConnection::DummyConnection(EventLoop* eventLoop) : eventLoop(eventLoop) { } void DummyConnection::receive(const SafeByteArray& data) { eventLoop->postEvent(boost::bind(boost::ref(onDataRead), boost::make_shared(data)), shared_from_this()); } void DummyConnection::listen() { assert(false); } void DummyConnection::connect(const HostAddressPort&) { assert(false); } } swift-im-2.0+dev6/Swiften/Network/HostAddress.h0000644000175000017500000000135312227051774021331 0ustar kismithkismith/* * 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 #include #include namespace Swift { class SWIFTEN_API HostAddress { public: HostAddress(); HostAddress(const std::string&); HostAddress(const unsigned char* address, int length); HostAddress(const boost::asio::ip::address& address); std::string toString() const; boost::asio::ip::address getRawAddress() const; bool operator==(const HostAddress& o) const { return address_ == o.address_; } bool isValid() const; private: boost::asio::ip::address address_; }; } swift-im-2.0+dev6/Swiften/Network/Connector.cpp0000644000175000017500000001407512227051774021400 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include namespace Swift { Connector::Connector(const std::string& hostname, int port, bool doServiceLookups, DomainNameResolver* resolver, ConnectionFactory* connectionFactory, TimerFactory* timerFactory) : hostname(hostname), port(port), doServiceLookups(doServiceLookups), resolver(resolver), connectionFactory(connectionFactory), timerFactory(timerFactory), timeoutMilliseconds(0), queriedAllServices(true), foundSomeDNS(false) { } void Connector::setTimeoutMilliseconds(int milliseconds) { timeoutMilliseconds = milliseconds; } void Connector::start() { SWIFT_LOG(debug) << "Starting connector for " << hostname << std::endl; //std::cout << "Connector::start()" << std::endl; assert(!currentConnection); assert(!serviceQuery); assert(!timer); queriedAllServices = false; if (doServiceLookups) { serviceQuery = resolver->createServiceQuery("_xmpp-client._tcp." + hostname); serviceQuery->onResult.connect(boost::bind(&Connector::handleServiceQueryResult, shared_from_this(), _1)); if (timeoutMilliseconds > 0) { timer = timerFactory->createTimer(timeoutMilliseconds); timer->onTick.connect(boost::bind(&Connector::handleTimeout, shared_from_this())); } serviceQuery->run(); } else { queryAddress(hostname); } } void Connector::stop() { finish(boost::shared_ptr()); } void Connector::queryAddress(const std::string& hostname) { assert(!addressQuery); addressQuery = resolver->createAddressQuery(hostname); addressQuery->onResult.connect(boost::bind(&Connector::handleAddressQueryResult, shared_from_this(), _1, _2)); addressQuery->run(); } void Connector::handleServiceQueryResult(const std::vector& result) { SWIFT_LOG(debug) << result.size() << " SRV result(s)" << std::endl; serviceQueryResults = std::deque(result.begin(), result.end()); serviceQuery.reset(); if (!serviceQueryResults.empty()) { foundSomeDNS = true; } tryNextServiceOrFallback(); } void Connector::tryNextServiceOrFallback() { if (queriedAllServices) { SWIFT_LOG(debug) << "Queried all services" << std::endl; finish(boost::shared_ptr()); } else if (serviceQueryResults.empty()) { SWIFT_LOG(debug) << "Falling back on A resolution" << std::endl; // Fall back on simple address resolving queriedAllServices = true; queryAddress(hostname); } else { SWIFT_LOG(debug) << "Querying next address" << std::endl; queryAddress(serviceQueryResults.front().hostname); } } void Connector::handleAddressQueryResult(const std::vector& addresses, boost::optional error) { SWIFT_LOG(debug) << addresses.size() << " addresses" << std::endl; addressQuery.reset(); if (error || addresses.empty()) { if (!serviceQueryResults.empty()) { serviceQueryResults.pop_front(); } tryNextServiceOrFallback(); } else { foundSomeDNS = true; addressQueryResults = std::deque(addresses.begin(), addresses.end()); tryNextAddress(); } } void Connector::tryNextAddress() { if (addressQueryResults.empty()) { SWIFT_LOG(debug) << "Done trying addresses. Moving on." << std::endl; // Done trying all addresses. Move on to the next host. if (!serviceQueryResults.empty()) { serviceQueryResults.pop_front(); } tryNextServiceOrFallback(); } else { SWIFT_LOG(debug) << "Trying next address" << std::endl; HostAddress address = addressQueryResults.front(); addressQueryResults.pop_front(); int connectPort = (port == -1 ? 5222 : port); if (!serviceQueryResults.empty()) { connectPort = serviceQueryResults.front().port; } tryConnect(HostAddressPort(address, connectPort)); } } void Connector::tryConnect(const HostAddressPort& target) { assert(!currentConnection); SWIFT_LOG(debug) << "Trying to connect to " << target.getAddress().toString() << ":" << target.getPort() << std::endl; currentConnection = connectionFactory->createConnection(); currentConnection->onConnectFinished.connect(boost::bind(&Connector::handleConnectionConnectFinished, shared_from_this(), _1)); currentConnection->connect(target); if (timer) { timer->start(); } } void Connector::handleConnectionConnectFinished(bool error) { SWIFT_LOG(debug) << "ConnectFinished: " << (error ? "error" : "success") << std::endl; if (timer) { timer->stop(); timer.reset(); } currentConnection->onConnectFinished.disconnect(boost::bind(&Connector::handleConnectionConnectFinished, shared_from_this(), _1)); if (error) { currentConnection.reset(); if (!addressQueryResults.empty()) { tryNextAddress(); } else { if (!serviceQueryResults.empty()) { serviceQueryResults.pop_front(); } tryNextServiceOrFallback(); } } else { finish(currentConnection); } } void Connector::finish(boost::shared_ptr connection) { if (timer) { timer->stop(); timer->onTick.disconnect(boost::bind(&Connector::handleTimeout, shared_from_this())); timer.reset(); } if (serviceQuery) { serviceQuery->onResult.disconnect(boost::bind(&Connector::handleServiceQueryResult, shared_from_this(), _1)); serviceQuery.reset(); } if (addressQuery) { addressQuery->onResult.disconnect(boost::bind(&Connector::handleAddressQueryResult, shared_from_this(), _1, _2)); addressQuery.reset(); } if (currentConnection) { currentConnection->onConnectFinished.disconnect(boost::bind(&Connector::handleConnectionConnectFinished, shared_from_this(), _1)); currentConnection.reset(); } onConnectFinished(connection, (connection || foundSomeDNS) ? boost::shared_ptr() : boost::make_shared()); } void Connector::handleTimeout() { SWIFT_LOG(debug) << "Timeout" << std::endl; handleConnectionConnectFinished(true); } }; swift-im-2.0+dev6/Swiften/Network/DummyTimerFactory.cpp0000644000175000017500000000247112227051774023067 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include namespace Swift { class DummyTimerFactory::DummyTimer : public Timer { public: DummyTimer(int timeout, DummyTimerFactory* factory) : timeout(timeout), factory(factory), isRunning(false), startTime(~0) { } virtual void start() { isRunning = true; startTime = factory->currentTime; } virtual void stop() { isRunning = false; } int getAlarmTime() const { return startTime + timeout; } int timeout; DummyTimerFactory* factory; bool isRunning; int startTime; }; DummyTimerFactory::DummyTimerFactory() : currentTime(0) { } boost::shared_ptr DummyTimerFactory::createTimer(int milliseconds) { boost::shared_ptr timer(new DummyTimer(milliseconds, this)); timers.push_back(timer); return timer; } void DummyTimerFactory::setTime(int time) { assert(time > currentTime); foreach(boost::shared_ptr timer, timers) { if (timer->getAlarmTime() > currentTime && timer->getAlarmTime() <= time && timer->isRunning) { timer->onTick(); } } currentTime = time; } } swift-im-2.0+dev6/Swiften/Network/ConnectionServer.cpp0000644000175000017500000000041712227051774022727 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { ConnectionServer::~ConnectionServer() { } } swift-im-2.0+dev6/Swiften/Network/WindowsNetworkEnvironment.cpp0000644000175000017500000000453412227051774024676 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include #include #include #include #include #include #include #include #include #include #include namespace Swift { std::vector WindowsNetworkEnvironment::getNetworkInterfaces() const { std::vector result; ByteArray adapters; ULONG bufferSize = 0; ULONG ret; ULONG flags = GAA_FLAG_INCLUDE_ALL_INTERFACES | GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER; while ((ret = GetAdaptersAddresses(AF_UNSPEC, flags, NULL, reinterpret_cast(vecptr(adapters)), &bufferSize)) == ERROR_BUFFER_OVERFLOW) { adapters.resize(bufferSize); }; if (ret != ERROR_SUCCESS) { return result; } std::map interfaces; for (IP_ADAPTER_ADDRESSES* adapter = reinterpret_cast(vecptr(adapters)); adapter; adapter = adapter->Next) { std::string name(adapter->AdapterName); if (adapter->OperStatus != IfOperStatusUp) { continue; } for (IP_ADAPTER_UNICAST_ADDRESS* address = adapter->FirstUnicastAddress; address; address = address->Next) { boost::optional hostAddress; if (address->Address.lpSockaddr->sa_family == PF_INET) { sockaddr_in* sa = reinterpret_cast(address->Address.lpSockaddr); hostAddress = HostAddress(reinterpret_cast(&(sa->sin_addr)), 4); } else if (address->Address.lpSockaddr->sa_family == PF_INET6) { sockaddr_in6* sa = reinterpret_cast(address->Address.lpSockaddr); hostAddress = HostAddress(reinterpret_cast(&(sa->sin6_addr)), 16); } if (hostAddress) { std::map::iterator i = interfaces.insert(std::make_pair(name, NetworkInterface(name, false))).first; i->second.addAddress(*hostAddress); } } } for (std::map::const_iterator i = interfaces.begin(); i != interfaces.end(); ++i) { result.push_back(i->second); } return result; } } swift-im-2.0+dev6/Swiften/Network/DummyConnection.h0000644000175000017500000000214012227051774022214 0ustar kismithkismith/* * 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 #include #include #include #include #include namespace Swift { class SWIFTEN_API DummyConnection : public Connection, public EventOwner, public boost::enable_shared_from_this { public: DummyConnection(EventLoop* eventLoop); void listen(); void connect(const HostAddressPort&); void disconnect() { //assert(false); } void write(const SafeByteArray& data) { eventLoop->postEvent(boost::ref(onDataWritten), shared_from_this()); onDataSent(data); } void receive(const SafeByteArray& data); HostAddressPort getLocalAddress() const { return localAddress; } boost::signal onDataSent; EventLoop* eventLoop; HostAddressPort localAddress; }; } swift-im-2.0+dev6/Swiften/Network/SOCKS5ProxiedConnectionFactory.h0000644000175000017500000000155412227051774024763 0ustar kismithkismith/* * Copyright (c) 2010-2011 Thilo Cestonaro * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include #include namespace Swift { class DomainNameResolver; class TimerFactory; class SOCKS5ProxiedConnectionFactory : public ConnectionFactory { public: SOCKS5ProxiedConnectionFactory(DomainNameResolver* resolver, ConnectionFactory* connectionFactory, TimerFactory* timerFactory, const std::string& proxyHost, int proxyPort); virtual boost::shared_ptr createConnection(); private: DomainNameResolver* resolver_; ConnectionFactory* connectionFactory_; TimerFactory* timerFactory_; std::string proxyHost_; int proxyPort_; }; } swift-im-2.0+dev6/Swiften/Network/ProxyProvider.cpp0000644000175000017500000000044212227051774022273 0ustar kismithkismith/* * Copyright (c) 2010-2011 Thilo Cestonaro * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include "ProxyProvider.h" namespace Swift { ProxyProvider::ProxyProvider() { } ProxyProvider::~ProxyProvider() { } } swift-im-2.0+dev6/Swiften/Network/UnixNetworkEnvironment.h0000644000175000017500000000075012227051774023630 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include #include #include namespace Swift { class UnixNetworkEnvironment : public NetworkEnvironment { public: std::vector getNetworkInterfaces() const; }; } swift-im-2.0+dev6/Swiften/Network/BOSHConnection.h0000644000175000017500000000721312227051774021662 0ustar kismithkismith/* * Copyright (c) 2011 Thilo Cestonaro * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ /* * Copyright (c) 2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include #include #include #include #include #include namespace boost { class thread; namespace system { class error_code; } } class BOSHConnectionTest; namespace Swift { class XMLParserFactory; class TLSContextFactory; class SWIFTEN_API BOSHError : public SessionStream::SessionStreamError { public: enum Type {BadRequest, HostGone, HostUnknown, ImproperAddressing, InternalServerError, ItemNotFound, OtherRequest, PolicyViolation, RemoteConnectionFailed, RemoteStreamError, SeeOtherURI, SystemShutdown, UndefinedCondition, NoError}; BOSHError(Type type) : SessionStream::SessionStreamError(SessionStream::SessionStreamError::ConnectionReadError), type(type) {} Type getType() {return type;} typedef boost::shared_ptr ref; private: Type type; }; class SWIFTEN_API BOSHConnection : public boost::enable_shared_from_this { public: typedef boost::shared_ptr ref; static ref create(const URL& boshURL, Connector::ref connector, XMLParserFactory* parserFactory) { return ref(new BOSHConnection(boshURL, connector, parserFactory)); } virtual ~BOSHConnection(); virtual void connect(); virtual void disconnect(); virtual void write(const SafeByteArray& data); const std::string& getSID(); void setRID(unsigned long long rid); void setSID(const std::string& sid); void startStream(const std::string& to, unsigned long long rid); void terminateStream(); bool isReadyToSend(); void restartStream(); boost::signal onConnectFinished; boost::signal onDisconnected; boost::signal onSessionTerminated; boost::signal onSessionStarted; boost::signal onXMPPDataRead; boost::signal onBOSHDataRead; boost::signal onBOSHDataWritten; boost::signal onHTTPError; private: friend class ::BOSHConnectionTest; BOSHConnection(const URL& boshURL, Connector::ref connector, XMLParserFactory* parserFactory); static std::pair createHTTPRequest(const SafeByteArray& data, bool streamRestart, bool terminate, unsigned long long rid, const std::string& sid, const URL& boshURL); void handleConnectFinished(Connection::ref); void handleDataRead(boost::shared_ptr data); void handleDisconnected(const boost::optional& error); void write(const SafeByteArray& data, bool streamRestart, bool terminate); /* FIXME: refactor */ BOSHError::Type parseTerminationCondition(const std::string& text); void cancelConnector(); URL boshURL_; Connector::ref connector_; XMLParserFactory* parserFactory_; boost::shared_ptr connection_; std::string sid_; bool waitingForStartResponse_; unsigned long long rid_; SafeByteArray buffer_; bool pending_; bool connectionReady_; }; } swift-im-2.0+dev6/Swiften/Network/NATTraversalRemovePortForwardingRequest.h0000644000175000017500000000127512227051774027036 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include namespace Swift { class NATTraversalRemovePortForwardingRequest { public: struct PortMapping { enum Protocol { TCP, UDP, }; unsigned int publicPort; unsigned int localPort; Protocol protocol; unsigned long leaseInSeconds; }; public: virtual ~NATTraversalRemovePortForwardingRequest(); virtual void run() = 0; boost::signal /* failure */)> onResult; }; } swift-im-2.0+dev6/Swiften/Network/BoostConnection.cpp0000644000175000017500000001313212227051774022545 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Swift { static const size_t BUFFER_SIZE = 4096; // ----------------------------------------------------------------------------- // A reference-counted non-modifiable buffer class. class SharedBuffer { public: SharedBuffer(const SafeByteArray& data) : data_(new std::vector >(data.begin(), data.end())), buffer_(boost::asio::buffer(*data_)) { } // ConstBufferSequence requirements. typedef boost::asio::const_buffer value_type; typedef const boost::asio::const_buffer* const_iterator; const boost::asio::const_buffer* begin() const { return &buffer_; } const boost::asio::const_buffer* end() const { return &buffer_ + 1; } private: boost::shared_ptr< std::vector > > data_; boost::asio::const_buffer buffer_; }; // ----------------------------------------------------------------------------- BoostConnection::BoostConnection(boost::shared_ptr ioService, EventLoop* eventLoop) : eventLoop(eventLoop), ioService(ioService), socket_(*ioService), writing_(false), closeSocketAfterNextWrite_(false) { } BoostConnection::~BoostConnection() { } void BoostConnection::listen() { doRead(); } void BoostConnection::connect(const HostAddressPort& addressPort) { boost::asio::ip::tcp::endpoint endpoint( boost::asio::ip::address::from_string(addressPort.getAddress().toString()), addressPort.getPort()); socket_.async_connect( endpoint, boost::bind(&BoostConnection::handleConnectFinished, shared_from_this(), boost::asio::placeholders::error)); } void BoostConnection::disconnect() { //MainEventLoop::removeEventsFromOwner(shared_from_this()); // Mac OS X apparently exhibits a problem where closing a socket during a write could potentially go into uninterruptable sleep. // See e.g. http://bugs.python.org/issue7401 // We therefore wait until any pending write finishes, which hopefully should fix our hang on exit during close(). boost::lock_guard lock(writeMutex_); if (writing_) { closeSocketAfterNextWrite_ = true; } else { closeSocket(); } } void BoostConnection::closeSocket() { boost::system::error_code errorCode; socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both, errorCode); socket_.close(); } void BoostConnection::write(const SafeByteArray& data) { boost::lock_guard lock(writeMutex_); if (!writing_) { writing_ = true; doWrite(data); } else { append(writeQueue_, data); } } void BoostConnection::doWrite(const SafeByteArray& data) { boost::asio::async_write(socket_, SharedBuffer(data), boost::bind(&BoostConnection::handleDataWritten, shared_from_this(), boost::asio::placeholders::error)); } void BoostConnection::handleConnectFinished(const boost::system::error_code& error) { SWIFT_LOG(debug) << "Connect finished: " << error << std::endl; if (!error) { eventLoop->postEvent(boost::bind(boost::ref(onConnectFinished), false), shared_from_this()); doRead(); } else if (error != boost::asio::error::operation_aborted) { eventLoop->postEvent(boost::bind(boost::ref(onConnectFinished), true), shared_from_this()); } } void BoostConnection::doRead() { readBuffer_ = boost::make_shared(BUFFER_SIZE); socket_.async_read_some( boost::asio::buffer(*readBuffer_), boost::bind(&BoostConnection::handleSocketRead, shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); } void BoostConnection::handleSocketRead(const boost::system::error_code& error, size_t bytesTransferred) { SWIFT_LOG(debug) << "Socket read " << error << std::endl; if (!error) { readBuffer_->resize(bytesTransferred); eventLoop->postEvent(boost::bind(boost::ref(onDataRead), readBuffer_), shared_from_this()); doRead(); } else if (/*error == boost::asio::error::eof ||*/ error == boost::asio::error::operation_aborted) { eventLoop->postEvent(boost::bind(boost::ref(onDisconnected), boost::optional()), shared_from_this()); } else { eventLoop->postEvent(boost::bind(boost::ref(onDisconnected), ReadError), shared_from_this()); } } void BoostConnection::handleDataWritten(const boost::system::error_code& error) { SWIFT_LOG(debug) << "Data written " << error << std::endl; if (!error) { eventLoop->postEvent(boost::ref(onDataWritten), shared_from_this()); } else if (/*error == boost::asio::error::eof || */error == boost::asio::error::operation_aborted) { eventLoop->postEvent(boost::bind(boost::ref(onDisconnected), boost::optional()), shared_from_this()); } else { eventLoop->postEvent(boost::bind(boost::ref(onDisconnected), WriteError), shared_from_this()); } { boost::lock_guard lock(writeMutex_); if (writeQueue_.empty()) { writing_ = false; if (closeSocketAfterNextWrite_) { closeSocket(); } } else { doWrite(writeQueue_); writeQueue_.clear(); } } } HostAddressPort BoostConnection::getLocalAddress() const { return HostAddressPort(socket_.local_endpoint()); } } swift-im-2.0+dev6/Swiften/Network/MacOSXProxyProvider.h0000644000175000017500000000074712227051774022763 0ustar kismithkismith/* * Copyright (c) 2010-2011 Thilo Cestonaro * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include namespace Swift { class MacOSXProxyProvider : public ProxyProvider { public: MacOSXProxyProvider(); virtual HostAddressPort getHTTPConnectProxy() const; virtual HostAddressPort getSOCKS5Proxy() const; }; } swift-im-2.0+dev6/Swiften/Network/BoostTimerFactory.h0000644000175000017500000000123212227051774022521 0ustar kismithkismith/* * 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 #include #include namespace Swift { class BoostTimer; class EventLoop; class BoostTimerFactory : public TimerFactory { public: BoostTimerFactory(boost::shared_ptr, EventLoop* eventLoop); virtual boost::shared_ptr createTimer(int milliseconds); private: boost::shared_ptr ioService; EventLoop* eventLoop; }; } swift-im-2.0+dev6/Swiften/Network/NATTraverser.cpp0000644000175000017500000000040312227051774021754 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { NATTraverser::~NATTraverser() { } } swift-im-2.0+dev6/Swiften/Network/HTTPConnectProxiedConnection.h0000644000175000017500000000272012227051774024551 0ustar kismithkismith/* * Copyright (c) 2010-2011 Thilo Cestonaro * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ /* * Copyright (c) 2011-2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include namespace Swift { class DomainNameResolver; class ConnectionFactory; class EventLoop; class TimerFactory; class SWIFTEN_API HTTPConnectProxiedConnection : public ProxiedConnection { public: typedef boost::shared_ptr ref; static ref create(DomainNameResolver* resolver, ConnectionFactory* connectionFactory, TimerFactory* timerFactory, const std::string& proxyHost, int proxyPort, const SafeString& authID, const SafeString& authPassword) { return ref(new HTTPConnectProxiedConnection(resolver, connectionFactory, timerFactory, proxyHost, proxyPort, authID, authPassword)); } private: HTTPConnectProxiedConnection(DomainNameResolver* resolver, ConnectionFactory* connectionFactory, TimerFactory* timerFactory, const std::string& proxyHost, int proxyPort, const SafeString& authID, const SafeString& authPassword); virtual void initializeProxy(); virtual void handleProxyInitializeData(boost::shared_ptr data); private: SafeByteArray authID_; SafeByteArray authPassword_; }; } swift-im-2.0+dev6/Swiften/Network/BoostIOServiceThread.cpp0000644000175000017500000000125312227051774023427 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include namespace Swift { BoostIOServiceThread::BoostIOServiceThread() { ioService_ = boost::make_shared(); thread_ = new boost::thread(boost::bind(&BoostIOServiceThread::doRun, this)); } BoostIOServiceThread::~BoostIOServiceThread() { ioService_->stop(); thread_->join(); delete thread_; } void BoostIOServiceThread::doRun() { boost::asio::io_service::work work(*ioService_); ioService_->run(); } } swift-im-2.0+dev6/Swiften/Network/DomainNameAddressQuery.h0000644000175000017500000000124012227051774023445 0ustar kismithkismith/* * 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 #include #include #include #include namespace Swift { class DomainNameAddressQuery { public: typedef boost::shared_ptr ref; virtual ~DomainNameAddressQuery(); virtual void run() = 0; boost::signal&, boost::optional)> onResult; }; } swift-im-2.0+dev6/Swiften/Network/NATPortMapping.h0000644000175000017500000000152612227051774021713 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include namespace Swift { class NATPortMapping { public: enum Protocol { TCP, UDP, }; NATPortMapping(int localPort, int publicPort, Protocol protocol = TCP, int leaseInSeconds = 60 * 60 * 24) : publicPort(publicPort), localPort(localPort), protocol(protocol), leaseInSeconds(leaseInSeconds) { } int getPublicPort() const { return publicPort; } int getLocalPort() const { return localPort; } Protocol getProtocol() const { return protocol; } int getLeaseInSeconds() const { return leaseInSeconds; } private: int publicPort; int localPort; Protocol protocol; int leaseInSeconds; }; } swift-im-2.0+dev6/Swiften/Network/BoostTimer.cpp0000644000175000017500000000215212227051774021526 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include namespace Swift { BoostTimer::BoostTimer(int milliseconds, boost::shared_ptr service, EventLoop* eventLoop) : timeout(milliseconds), ioService(service), timer(*service), eventLoop(eventLoop) { } void BoostTimer::start() { timer.expires_from_now(boost::posix_time::milliseconds(timeout)); timer.async_wait(boost::bind(&BoostTimer::handleTimerTick, shared_from_this(), boost::asio::placeholders::error)); } void BoostTimer::stop() { timer.cancel(); eventLoop->removeEventsFromOwner(shared_from_this()); } void BoostTimer::handleTimerTick(const boost::system::error_code& error) { if (error) { assert(error == boost::asio::error::operation_aborted); } else { eventLoop->postEvent(boost::bind(boost::ref(onTick)), shared_from_this()); } } } swift-im-2.0+dev6/Swiften/QA/0000755000175000017500000000000012227051774015603 5ustar kismithkismithswift-im-2.0+dev6/Swiften/QA/SConscript0000644000175000017500000000027512227051774017621 0ustar kismithkismithImport("swiften_env") SConscript(dirs = [ "NetworkTest", # "ReconnectTest", "ClientTest", # "DNSSDTest", # "StorageTest", "TLSTest", "ScriptedTests", "ProxyProviderTest", ]) swift-im-2.0+dev6/Swiften/QA/TLSTest/0000755000175000017500000000000012227051774017105 5ustar kismithkismithswift-im-2.0+dev6/Swiften/QA/TLSTest/SConscript0000644000175000017500000000057412227051774021125 0ustar kismithkismithimport os Import("env") if env["TEST"] : myenv = env.Clone() myenv.MergeFlags(myenv["CHECKER_FLAGS"]) myenv.MergeFlags(myenv["SWIFTOOLS_FLAGS"]) myenv.MergeFlags(myenv["SWIFTEN_FLAGS"]) myenv.MergeFlags(myenv["SWIFTEN_DEP_FLAGS"]) myenv.MergeFlags(myenv["CPPUNIT_FLAGS"]) tester = myenv.Program("TLSTest", [ "CertificateTest.cpp", ]) myenv.Test(tester, "system") swift-im-2.0+dev6/Swiften/QA/TLSTest/CertificateTest.cpp0000644000175000017500000000676112227051774022705 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include "SwifTools/Application/PlatformApplicationPathProvider.h" using namespace Swift; template class CertificateTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(CertificateTest); CPPUNIT_TEST(testConstructFromDER); CPPUNIT_TEST(testToDER); //CPPUNIT_TEST(testGetSubjectName); CPPUNIT_TEST(testGetCommonNames); CPPUNIT_TEST(testGetSRVNames); CPPUNIT_TEST(testGetDNSNames); CPPUNIT_TEST(testGetXMPPAddresses); CPPUNIT_TEST_SUITE_END(); public: void setUp() { pathProvider = new PlatformApplicationPathProvider("FileReadBytestreamTest"); readByteArrayFromFile(certificateData, (pathProvider->getExecutableDir() / "jabber_org.crt").string()); certificateFactory = new CERTIFICATE_FACTORY(); } void tearDown() { delete certificateFactory; delete pathProvider; } void testConstructFromDER() { Certificate::ref testling = certificateFactory->createCertificateFromDER(certificateData); CPPUNIT_ASSERT_EQUAL(std::string("*.jabber.org"), testling->getCommonNames()[0]); } void testToDER() { Certificate::ref testling = certificateFactory->createCertificateFromDER(certificateData); CPPUNIT_ASSERT_EQUAL(certificateData, testling->toDER()); } /* void testGetSubjectName() { Certificate::ref testling = certificateFactory->createCertificateFromDER(certificateData); CPPUNIT_ASSERT_EQUAL(std::string("/description=114072-VMk8pdi1aj5kTXxO/C=US/ST=Colorado/L=Denver/O=Peter Saint-Andre/OU=StartCom Trusted Certificate Member/CN=*.jabber.org/emailAddress=hostmaster@jabber.org"), testling->getSubjectName()); } */ void testGetCommonNames() { Certificate::ref testling = certificateFactory->createCertificateFromDER(certificateData); CPPUNIT_ASSERT_EQUAL(1, static_cast(testling->getCommonNames().size())); CPPUNIT_ASSERT_EQUAL(std::string("*.jabber.org"), testling->getCommonNames()[0]); } void testGetSRVNames() { Certificate::ref testling = certificateFactory->createCertificateFromDER(certificateData); CPPUNIT_ASSERT_EQUAL(1, static_cast(testling->getSRVNames().size())); CPPUNIT_ASSERT_EQUAL(std::string("*.jabber.org"), testling->getSRVNames()[0]); } void testGetDNSNames() { Certificate::ref testling = certificateFactory->createCertificateFromDER(certificateData); CPPUNIT_ASSERT_EQUAL(2, static_cast(testling->getDNSNames().size())); CPPUNIT_ASSERT_EQUAL(std::string("*.jabber.org"), testling->getDNSNames()[0]); CPPUNIT_ASSERT_EQUAL(std::string("jabber.org"), testling->getDNSNames()[1]); } void testGetXMPPAddresses() { Certificate::ref testling = certificateFactory->createCertificateFromDER(certificateData); CPPUNIT_ASSERT_EQUAL(1, static_cast(testling->getXMPPAddresses().size())); CPPUNIT_ASSERT_EQUAL(std::string("*.jabber.org"), testling->getXMPPAddresses()[0]); } private: PlatformApplicationPathProvider* pathProvider; ByteArray certificateData; CertificateFactory* certificateFactory; }; #ifdef HAVE_OPENSSL #include CPPUNIT_TEST_SUITE_REGISTRATION(CertificateTest); #endif swift-im-2.0+dev6/Swiften/QA/TLSTest/jabber_org.crt0000644000175000017500000000404112227051774021712 0ustar kismithkismith00j0  *H 01 0 UIL10U  StartCom Ltd.1+0)U "Secure Digital Certificate Signing1806U/StartCom Class 3 Primary Intermediate Server CA0 091215182618Z 101216004311Z01 0U 114072-VMk8pdi1aj5kTXxO1 0 UUS10UColorado10 UDenver10U Peter Saint-Andre1,0*U #StartCom Trusted Certificate Member10U *.jabber.org1$0" *H  hostmaster@jabber.org0"0  *H 0  T*\jFN3.FNa"!o2T$nAb F(U \ }69asaM4ł.TAJ*iZE W/݈ \'R.m+%WyMhǀ^RP.Or-yX9tG";Y]EWpv_YWbCg .@y^,*c%%آח0i,ښ'mI]ҎCq1 L`uTkbQrǢXIXY/e'lܸ-hj6p JLU_Rif-Q|)هFOٶU򂩢_G!@ô9e&1dܸ,%el}ZY :* e[ 1Yg2|;[Qb F!7030 U00 U0U%0++0UC< tDy6B-V0U#0Bb2 +A>{\K0[UT0R *.jabber.org jabber.org+ *.jabber.org+ *.jabber.org0BU 90501 +70 0.+"http://www.startssl.com/policy.pdf04+(http://www.startssl.com/intermediate.pdf0+00 StartCom Ltd.0Limited Liability, see section *Legal Limitations* of the StartCom Certification Authority Policy available at http://www.startssl.com/policy.pdf0aUZ0X0*(&$http://www.startssl.com/crt3-crl.crl0*(&$http://crl.startssl.com/crt3-crl.crl0+009+0-http://ocsp.startssl.com/sub/class3/server/ca0B+06http://www.startssl.com/certs/sub.class3.server.ca.crt0#U0http://www.startssl.com/0  *H iQw~+~! B^f3gzFE8RYOP7;& M| )"-@K}b$ y./oIgYeGx'󽄢;>gN. I9a^? #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_AVAHI #include #endif #define SLEEP_INTERVALS 20 using namespace Swift; template class DNSSDTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(DNSSDTest); CPPUNIT_TEST(testPublish); CPPUNIT_TEST_SUITE_END(); public: void setUp() { eventLoop = new DummyEventLoop(); querier = boost::shared_ptr(new DNSSDQuerierType()); querier->start(); } void tearDown() { querier->stop(); querier.reset(); delete eventLoop; } void testPublish() { boost::shared_ptr browseQuery = querier->createBrowseQuery(); browseQuery->onServiceAdded.connect(boost::bind(&DNSSDTest::handleServiceAdded, this, _1)); browseQuery->onServiceRemoved.connect(boost::bind(&DNSSDTest::handleServiceRemoved, this, _1)); browseQuery->onError.connect(boost::bind(&DNSSDTest::handleBrowseError, this)); browseQuery->startBrowsing(); eventLoop->processEvents(); // Publish the service LinkLocalServiceInfo info; boost::shared_ptr registerQuery = querier->createRegisterQuery("DNSSDTest", 1234, info.toTXTRecord()); registerQuery->onRegisterFinished.connect(boost::bind(&DNSSDTest::handleRegisterFinished, this, _1)); registerQuery->registerService(); // Wait for a while wait(); // Check that our registered queries are correct CPPUNIT_ASSERT_EQUAL(1, static_cast((registered.size()))); CPPUNIT_ASSERT_EQUAL(std::string("DNSSDTest"), registered[0].getName()); CPPUNIT_ASSERT_EQUAL(std::string("local"), registered[0].getDomain()); CPPUNIT_ASSERT_EQUAL(std::string("_presence._tcp"), registered[0].getType()); // Check that our browse query discovered us std::sort(added.begin(), added.end()); CPPUNIT_ASSERT(added.size() >= 1); //for (size_t i = 0; i < added.size(); ++i) { for (size_t i = 0; i < added.size(); ++i) { CPPUNIT_ASSERT_EQUAL(std::string("DNSSDTest"), added[i].getName()); CPPUNIT_ASSERT_EQUAL(std::string("local"), added[i].getDomain()); CPPUNIT_ASSERT_EQUAL(std::string("_presence._tcp"), added[i].getType()); CPPUNIT_ASSERT(added[i].getNetworkInterfaceID() != 0); } // Resolve all added services for (size_t i = 0; i < added.size(); ++i) { resolvedServices.clear(); boost::shared_ptr resolveServiceQuery = querier->createResolveServiceQuery(added[i]); resolveServiceQuery->onServiceResolved.connect(boost::bind(&DNSSDTest::handleResolveFinished, this, _1)); resolveServiceQuery->start(); wait(); CPPUNIT_ASSERT_EQUAL(1, static_cast(resolvedServices.size())); resolveServiceQuery->stop(); } // Unregister the service & check if the browse query picks this up toRemove.clear(); toRemove.insert(toRemove.begin(), added.begin(), added.end()); registerQuery->unregisterService(); while (!toRemove.empty()) { Swift::sleep(100); eventLoop->processEvents(); } browseQuery->stopBrowsing(); eventLoop->processEvents(); } private: void handleServiceAdded(const DNSSDServiceID& id) { std::cout << "Service added: " << id.getNetworkInterfaceID() << std::endl; added.push_back(id); } void handleServiceRemoved(const DNSSDServiceID& id) { CPPUNIT_ASSERT(std::find(toRemove.begin(), toRemove.end(), id) != toRemove.end()); erase(toRemove, id); } void handleRegisterFinished(boost::optional id) { if (id) { registered.push_back(*id); } } void handleBrowseError() { } void wait() { for (int i = 0; i < SLEEP_INTERVALS; ++i) { Swift::sleep(100); eventLoop->processEvents(); } } void handleResolveFinished(const boost::optional& result) { CPPUNIT_ASSERT(result); resolvedServices.push_back(*result); } private: DummyEventLoop* eventLoop; boost::shared_ptr querier; std::vector added; std::vector registered; std::vector toRemove; std::vector resolvedServices; }; #ifdef HAVE_AVAHI CPPUNIT_TEST_SUITE_REGISTRATION(DNSSDTest); #endif swift-im-2.0+dev6/Swiften/QA/NetworkTest/0000755000175000017500000000000012227051774020074 5ustar kismithkismithswift-im-2.0+dev6/Swiften/QA/NetworkTest/SConscript0000644000175000017500000000077112227051774022113 0ustar kismithkismithimport os Import("env") if env["TEST"] : myenv = env.Clone() if "test_ipv6" in ARGUMENTS : myenv.Append(CPPDEFINES = ["TEST_IPV6"]) myenv.MergeFlags(myenv["CHECKER_FLAGS"]) myenv.MergeFlags(myenv["SWIFTEN_FLAGS"]) myenv.MergeFlags(myenv["SWIFTEN_DEP_FLAGS"]) myenv.MergeFlags(myenv["CPPUNIT_FLAGS"]) tester = myenv.Program("NetworkTest", [ "BoostConnectionServerTest.cpp", "BoostConnectionTest.cpp", "DomainNameResolverTest.cpp", ]) myenv.Test(tester, "system", is_checker = True) swift-im-2.0+dev6/Swiften/QA/NetworkTest/DomainNameResolverTest.cpp0000644000175000017500000001675412227051774025207 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include #include using namespace Swift; struct CompareHostAddresses { bool operator()(const HostAddress& h1, const HostAddress& h2) { return h1.toString() < h2.toString(); } }; class DomainNameResolverTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(DomainNameResolverTest); CPPUNIT_TEST(testResolveAddress); CPPUNIT_TEST(testResolveAddress_Error); CPPUNIT_TEST(testResolveAddress_IPv6); CPPUNIT_TEST(testResolveAddress_IPv4and6); CPPUNIT_TEST(testResolveAddress_International); CPPUNIT_TEST(testResolveAddress_Localhost); CPPUNIT_TEST(testResolveAddress_Parallel); CPPUNIT_TEST(testResolveService); CPPUNIT_TEST(testResolveService_Error); CPPUNIT_TEST_SUITE_END(); public: void setUp() { eventLoop = new DummyEventLoop(); resolver = new PlatformDomainNameResolver(eventLoop); resultsAvailable = false; } void tearDown() { delete resolver; delete eventLoop; } void testResolveAddress() { boost::shared_ptr query(createAddressQuery("xmpp.test.swift.im")); query->run(); waitForResults(); CPPUNIT_ASSERT(!addressQueryError); CPPUNIT_ASSERT_EQUAL(1, static_cast(addressQueryResult.size())); CPPUNIT_ASSERT_EQUAL(std::string("10.0.0.0"), addressQueryResult[0].toString()); } void testResolveAddress_Error() { boost::shared_ptr query(createAddressQuery("invalid.test.swift.im")); query->run(); waitForResults(); CPPUNIT_ASSERT(addressQueryError); } void testResolveAddress_IPv6() { boost::shared_ptr query(createAddressQuery("xmpp-ipv6.test.swift.im")); query->run(); waitForResults(); CPPUNIT_ASSERT(!addressQueryError); CPPUNIT_ASSERT_EQUAL(std::string("2001:470:1f0e:852::2"), addressQueryResult[0].toString()); } void testResolveAddress_IPv4and6() { boost::shared_ptr query(createAddressQuery("xmpp-ipv46.test.swift.im")); query->run(); waitForResults(); CPPUNIT_ASSERT(!addressQueryError); CPPUNIT_ASSERT_EQUAL(2, static_cast(addressQueryResult.size())); CPPUNIT_ASSERT_EQUAL(std::string("10.0.0.7"), addressQueryResult[0].toString()); CPPUNIT_ASSERT_EQUAL(std::string("1234:5678:9abc:def0:fed:cba9:8765:4321"), addressQueryResult[1].toString()); } void testResolveAddress_International() { boost::shared_ptr query(createAddressQuery("tron\xc3\xa7on.test.swift.im")); query->run(); waitForResults(); CPPUNIT_ASSERT(!addressQueryError); CPPUNIT_ASSERT_EQUAL(1, static_cast(addressQueryResult.size())); CPPUNIT_ASSERT_EQUAL(std::string("10.0.0.3"), addressQueryResult[0].toString()); } void testResolveAddress_Localhost() { boost::shared_ptr query(createAddressQuery("localhost")); query->run(); waitForResults(); CPPUNIT_ASSERT(!addressQueryError); CPPUNIT_ASSERT(std::find(addressQueryResult.begin(), addressQueryResult.end(), HostAddress("127.0.0.1")) != addressQueryResult.end()); } void testResolveAddress_Parallel() { std::vector queries; static const size_t numQueries = 100; for (size_t i = 0; i < numQueries; ++i) { DomainNameAddressQuery::ref query(createAddressQuery("xmpp.test.swift.im")); queries.push_back(query); query->run(); } eventLoop->processEvents(); int ticks = 0; while (allAddressQueryResults.size() < numQueries) { ticks++; if (ticks > 1000) { CPPUNIT_ASSERT(false); } Swift::sleep(10); eventLoop->processEvents(); } CPPUNIT_ASSERT_EQUAL(numQueries, allAddressQueryResults.size()); for (size_t i = 0; i < numQueries; ++i) { CPPUNIT_ASSERT_EQUAL(std::string("10.0.0.0"), allAddressQueryResults[i].toString()); } } void testResolveService() { boost::shared_ptr query(createServiceQuery("_xmpp-client._tcp.xmpp-srv.test.swift.im")); query->run(); waitForResults(); CPPUNIT_ASSERT_EQUAL(4, static_cast(serviceQueryResult.size())); CPPUNIT_ASSERT_EQUAL(std::string("xmpp1.test.swift.im"), serviceQueryResult[0].hostname); CPPUNIT_ASSERT_EQUAL(5000, serviceQueryResult[0].port); CPPUNIT_ASSERT_EQUAL(0, serviceQueryResult[0].priority); CPPUNIT_ASSERT_EQUAL(1, serviceQueryResult[0].weight); CPPUNIT_ASSERT_EQUAL(std::string("xmpp-invalid.test.swift.im"), serviceQueryResult[1].hostname); CPPUNIT_ASSERT_EQUAL(5000, serviceQueryResult[1].port); CPPUNIT_ASSERT_EQUAL(1, serviceQueryResult[1].priority); CPPUNIT_ASSERT_EQUAL(100, serviceQueryResult[1].weight); CPPUNIT_ASSERT_EQUAL(std::string("xmpp3.test.swift.im"), serviceQueryResult[2].hostname); CPPUNIT_ASSERT_EQUAL(5000, serviceQueryResult[2].port); CPPUNIT_ASSERT_EQUAL(3, serviceQueryResult[2].priority); CPPUNIT_ASSERT_EQUAL(100, serviceQueryResult[2].weight); CPPUNIT_ASSERT_EQUAL(std::string("xmpp2.test.swift.im"), serviceQueryResult[3].hostname); CPPUNIT_ASSERT_EQUAL(5000, serviceQueryResult[3].port); CPPUNIT_ASSERT_EQUAL(5, serviceQueryResult[3].priority); CPPUNIT_ASSERT_EQUAL(100, serviceQueryResult[3].weight); } void testResolveService_Error() { } private: boost::shared_ptr createAddressQuery(const std::string& domain) { boost::shared_ptr result = resolver->createAddressQuery(domain); result->onResult.connect(boost::bind(&DomainNameResolverTest::handleAddressQueryResult, this, _1, _2)); return result; } void handleAddressQueryResult(const std::vector& addresses, boost::optional error) { addressQueryResult = addresses; std::sort(addressQueryResult.begin(), addressQueryResult.end(), CompareHostAddresses()); allAddressQueryResults.insert(allAddressQueryResults.begin(), addresses.begin(), addresses.end()); addressQueryError = error; resultsAvailable = true; } boost::shared_ptr createServiceQuery(const std::string& domain) { boost::shared_ptr result = resolver->createServiceQuery(domain); result->onResult.connect(boost::bind(&DomainNameResolverTest::handleServiceQueryResult, this, _1)); return result; } void handleServiceQueryResult(const std::vector& result) { serviceQueryResult = result; resultsAvailable = true; } void waitForResults() { eventLoop->processEvents(); int ticks = 0; while (!resultsAvailable) { ticks++; if (ticks > 1000) { CPPUNIT_ASSERT(false); } Swift::sleep(10); eventLoop->processEvents(); } } private: DummyEventLoop* eventLoop; bool resultsAvailable; std::vector addressQueryResult; std::vector allAddressQueryResults; boost::optional addressQueryError; std::vector serviceQueryResult; PlatformDomainNameResolver* resolver; }; CPPUNIT_TEST_SUITE_REGISTRATION(DomainNameResolverTest); swift-im-2.0+dev6/Swiften/QA/NetworkTest/BoostConnectionServerTest.cpp0000644000175000017500000000474512227051774025747 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include using namespace Swift; class BoostConnectionServerTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(BoostConnectionServerTest); CPPUNIT_TEST(testConstructor_TwoServersOnSamePort); CPPUNIT_TEST(testStart_Conflict); CPPUNIT_TEST(testStop); CPPUNIT_TEST_SUITE_END(); public: void setUp() { boostIOServiceThread_ = new BoostIOServiceThread(); eventLoop_ = new DummyEventLoop(); stopped = false; stoppedError.reset(); } void tearDown() { while (eventLoop_->hasEvents()) { eventLoop_->processEvents(); } delete eventLoop_; delete boostIOServiceThread_; } void testConstructor_TwoServersOnSamePort() { BoostConnectionServer::ref testling(BoostConnectionServer::create(9999, boostIOServiceThread_->getIOService(), eventLoop_)); BoostConnectionServer::ref testling2(BoostConnectionServer::create(9999, boostIOServiceThread_->getIOService(), eventLoop_)); } void testStart_Conflict() { BoostConnectionServer::ref testling(BoostConnectionServer::create(9999, boostIOServiceThread_->getIOService(), eventLoop_)); testling->start(); BoostConnectionServer::ref testling2(BoostConnectionServer::create(9999, boostIOServiceThread_->getIOService(), eventLoop_)); testling2->onStopped.connect( boost::bind(&BoostConnectionServerTest::handleStopped, this, _1)); testling->stop(); } void testStop() { BoostConnectionServer::ref testling(BoostConnectionServer::create(9999, boostIOServiceThread_->getIOService(), eventLoop_)); testling->start(); testling->stop(); BoostConnectionServer::ref testling2(BoostConnectionServer::create(9999, boostIOServiceThread_->getIOService(), eventLoop_)); testling2->start(); testling2->stop(); } void handleStopped(boost::optional e) { stopped = true; stoppedError = e; } private: BoostIOServiceThread* boostIOServiceThread_; DummyEventLoop* eventLoop_; bool stopped; boost::optional stoppedError; }; CPPUNIT_TEST_SUITE_REGISTRATION(BoostConnectionServerTest); swift-im-2.0+dev6/Swiften/QA/NetworkTest/BoostConnectionTest.cpp0000644000175000017500000001244412227051774024553 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include #include #include const unsigned char* address = reinterpret_cast("\x41\x63\xde\x89"); using namespace Swift; class BoostConnectionTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(BoostConnectionTest); CPPUNIT_TEST(testDestructor); CPPUNIT_TEST(testDestructor_PendingEvents); CPPUNIT_TEST(testWrite); CPPUNIT_TEST(testWriteMultipleSimultaniouslyQueuesWrites); #ifdef TEST_IPV6 CPPUNIT_TEST(testWrite_IPv6); #endif CPPUNIT_TEST_SUITE_END(); public: void setUp() { boostIOServiceThread_ = new BoostIOServiceThread(); boostIOService = boost::make_shared(); eventLoop_ = new DummyEventLoop(); disconnected = false; connectFinished = false; } void tearDown() { delete eventLoop_; delete boostIOServiceThread_; } void testDestructor() { { BoostConnection::ref testling(BoostConnection::create(boostIOServiceThread_->getIOService(), eventLoop_)); testling->connect(HostAddressPort(HostAddress(address, 4), 5222)); } } void testDestructor_PendingEvents() { { BoostConnection::ref testling(BoostConnection::create(boostIOServiceThread_->getIOService(), eventLoop_)); testling->connect(HostAddressPort(HostAddress(address, 4), 5222)); while (!eventLoop_->hasEvents()) { Swift::sleep(10); } } eventLoop_->processEvents(); } void testWrite() { BoostConnection::ref testling(BoostConnection::create(boostIOServiceThread_->getIOService(), eventLoop_)); testling->onConnectFinished.connect(boost::bind(&BoostConnectionTest::doWrite, this, testling.get())); testling->onDataRead.connect(boost::bind(&BoostConnectionTest::handleDataRead, this, _1)); testling->onDisconnected.connect(boost::bind(&BoostConnectionTest::handleDisconnected, this)); testling->connect(HostAddressPort(HostAddress("65.99.222.137"), 5222)); while (receivedData.empty()) { Swift::sleep(10); eventLoop_->processEvents(); } testling->disconnect(); } void testWrite_IPv6() { BoostConnection::ref testling(BoostConnection::create(boostIOServiceThread_->getIOService(), eventLoop_)); testling->onConnectFinished.connect(boost::bind(&BoostConnectionTest::doWrite, this, testling.get())); testling->onDataRead.connect(boost::bind(&BoostConnectionTest::handleDataRead, this, _1)); testling->onDisconnected.connect(boost::bind(&BoostConnectionTest::handleDisconnected, this)); testling->connect(HostAddressPort(HostAddress("2001:470:1f0e:852::2"), 80)); while (receivedData.empty()) { Swift::sleep(10); eventLoop_->processEvents(); } testling->disconnect(); } void testWriteMultipleSimultaniouslyQueuesWrites() { BoostConnection::ref testling(BoostConnection::create(boostIOService, eventLoop_)); testling->onConnectFinished.connect(boost::bind(&BoostConnectionTest::handleConnectFinished, this)); testling->onDataRead.connect(boost::bind(&BoostConnectionTest::handleDataRead, this, _1)); testling->onDisconnected.connect(boost::bind(&BoostConnectionTest::handleDisconnected, this)); testling->connect(HostAddressPort(HostAddress("65.99.222.137"), 5222)); while (!connectFinished) { boostIOService->run_one(); eventLoop_->processEvents(); } testling->write(createSafeByteArray("write(createSafeByteArray("m")); testling->write(createSafeByteArray(">")); // Check that we only did one write event, the others are queued /*int runHandlers = */boostIOService->poll(); // Disabling this test, because poll runns all handlers that are added during poll() as well, so // this test doesn't really work any more. We'll have to trust that things are queued. //CPPUNIT_ASSERT_EQUAL(1, runHandlers); // Process the other events while (receivedData.empty()) { boostIOService->run_one(); eventLoop_->processEvents(); } // Disconnect & clean up testling->disconnect(); while (!disconnected) { boostIOService->run_one(); eventLoop_->processEvents(); } } void doWrite(BoostConnection* connection) { connection->write(createSafeByteArray("")); connection->write(createSafeByteArray("\r\n\r\n")); // Temporarily, while we don't have an xmpp server running on ipv6 } void handleDataRead(boost::shared_ptr data) { append(receivedData, *data); } void handleDisconnected() { disconnected = true; } void handleConnectFinished() { connectFinished = true; } private: BoostIOServiceThread* boostIOServiceThread_; boost::shared_ptr boostIOService; DummyEventLoop* eventLoop_; ByteArray receivedData; bool disconnected; bool connectFinished; }; CPPUNIT_TEST_SUITE_REGISTRATION(BoostConnectionTest); swift-im-2.0+dev6/Swiften/QA/ScriptedTests/0000755000175000017500000000000012227051774020403 5ustar kismithkismithswift-im-2.0+dev6/Swiften/QA/ScriptedTests/SConscript0000644000175000017500000000021612227051774022414 0ustar kismithkismithImport("env") if env["TEST"] : env.ScriptTests([ "SendMessage.lua", "MultipleClients.lua", ], "Swiften.QA.ScriptedTests", "system") swift-im-2.0+dev6/Swiften/QA/ScriptedTests/MultipleClients.lua0000644000175000017500000000137212227051774024226 0ustar kismithkismith-- -- Copyright (c) 2010 Remko Tronçon -- Licensed under the GNU General Public License v3. -- See Documentation/Licenses/GPLv3.txt for more information. -- require "sluift" -- sluift.debug = true num_clients = 10 print("Connecting clients") clients = {} for i = 1, num_clients do jid = os.getenv("SWIFT_CLIENTTEST_JID") .. "/Client" .. i client = sluift.new_client(jid, os.getenv("SWIFT_CLIENTTEST_PASS")) client:set_options({compress = false}) client:async_connect() table.insert(clients, client) end print("Waiting for clients to be connected") for i, client in ipairs(clients) do client:wait_connected() client:send_presence("Hello") end print("Disconnecting clients") for i, client in ipairs(clients) do client:disconnect() end print("Done") swift-im-2.0+dev6/Swiften/QA/ScriptedTests/SendMessage.lua0000644000175000017500000000243312227051774023306 0ustar kismithkismith-- -- Copyright (c) 2010 Remko Tronçon -- Licensed under the GNU General Public License v3. -- See Documentation/Licenses/GPLv3.txt for more information. -- require "sluift" -- sluift.debug = true client1_jid = os.getenv("SWIFT_CLIENTTEST_JID") .. "/Client1" client2_jid = os.getenv("SWIFT_CLIENTTEST_JID") .. "/Client2" password = os.getenv("SWIFT_CLIENTTEST_PASS") print "Connecting client 1" client1 = sluift.new_client(client1_jid, password) client1:connect() client1:send_presence("I'm here") print "Connecting client 2" client2 = sluift.new_client(client2_jid, password) client2:connect() client2:send_presence("I'm here") print "Checking version of client 2 from client 1" client2:set_version({name = "Sluift Test", version = "1.0"}) client2_version = client1:get_version(client2_jid) assert(client2_version["name"] == "Sluift Test") assert(client2_version["version"] == "1.0") print "Sending message from client 1 to client 2" client1:send_message(client2_jid, "Hello") received_message = client2:for_event(function(event) if event["type"] == "message" and event["from"] == client1_jid then return event["body"] end end, 10000) assert(received_message == "Hello") print "Retrieving the roster" roster = client1:get_roster() tprint(roster) client1:disconnect() client2:disconnect() swift-im-2.0+dev6/Swiften/QA/ClientTest/0000755000175000017500000000000012227051774017661 5ustar kismithkismithswift-im-2.0+dev6/Swiften/QA/ClientTest/SConscript0000644000175000017500000000067612227051774021704 0ustar kismithkismithimport os Import("env") if env["TEST"] : myenv = env.Clone() myenv.UseFlags(myenv["SWIFTEN_FLAGS"]) myenv.UseFlags(myenv["SWIFTEN_DEP_FLAGS"]) for i in ["SWIFT_CLIENTTEST_JID", "SWIFT_CLIENTTEST_PASS"]: if ARGUMENTS.get(i.lower(), False) : myenv["ENV"][i] = ARGUMENTS[i.lower()] elif os.environ.get(i, "") : myenv["ENV"][i] = os.environ[i] tester = myenv.Program("ClientTest", ["ClientTest.cpp"]) myenv.Test(tester, "system") swift-im-2.0+dev6/Swiften/QA/ClientTest/ClientTest.cpp0000644000175000017500000000536712227051774022456 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include using namespace Swift; SimpleEventLoop eventLoop; BoostNetworkFactories networkFactories(&eventLoop); Client* client = 0; bool rosterReceived = false; enum TestStage { FirstConnect, Reconnect }; TestStage stage; ClientOptions options; void handleDisconnected(boost::optional e) { std::cout << "Disconnected: " << e << std::endl; if (stage == FirstConnect) { stage = Reconnect; client->connect(options); } else { eventLoop.stop(); } } void handleRosterReceived(boost::shared_ptr) { rosterReceived = true; std::cout << "Disconnecting" << std::endl; client->disconnect(); } void handleConnected() { std::cout << "Connected" << std::endl; rosterReceived = false; GetRosterRequest::ref rosterRequest = GetRosterRequest::create(client->getIQRouter()); rosterRequest->onResponse.connect(boost::bind(&handleRosterReceived, _1)); rosterRequest->send(); } int main(int, char**) { char* jid = getenv("SWIFT_CLIENTTEST_JID"); if (!jid) { std::cerr << "Please set the SWIFT_CLIENTTEST_JID environment variable" << std::endl; return -1; } char* pass = getenv("SWIFT_CLIENTTEST_PASS"); if (!pass) { std::cerr << "Please set the SWIFT_CLIENTTEST_PASS environment variable" << std::endl; return -1; } char* boshHost = getenv("SWIFT_CLIENTTEST_BOSH_HOST"); char* boshPort = getenv("SWIFT_CLIENTTEST_BOSH_PORT"); char* boshPath = getenv("SWIFT_CLIENTTEST_BOSH_PATH"); if (boshHost && boshPort && boshPath) { std::cout << "Using BOSH with URL: http://" << boshHost << ":" << boshPort << "/" << boshPath << std::endl; options.boshURL = URL("http", boshHost, atoi(boshPort), boshPath); } client = new Swift::Client(JID(jid), std::string(pass), &networkFactories); ClientXMLTracer* tracer = new ClientXMLTracer(client, !options.boshURL.isEmpty()); client->onConnected.connect(&handleConnected); client->onDisconnected.connect(boost::bind(&handleDisconnected, _1)); client->setAlwaysTrustCertificates(); stage = FirstConnect; client->connect(options); { Timer::ref timer = networkFactories.getTimerFactory()->createTimer(60000); timer->onTick.connect(boost::bind(&SimpleEventLoop::stop, &eventLoop)); timer->start(); eventLoop.run(); } delete tracer; delete client; return !rosterReceived; } swift-im-2.0+dev6/Swiften/QA/ProxyProviderTest/0000755000175000017500000000000012227051774021277 5ustar kismithkismithswift-im-2.0+dev6/Swiften/QA/ProxyProviderTest/SConscript0000644000175000017500000000031112227051774023304 0ustar kismithkismithimport os Import("env") myenv = env.Clone() myenv.MergeFlags(myenv["SWIFTEN_FLAGS"]) myenv.MergeFlags(myenv["SWIFTEN_DEP_FLAGS"]) myenv.Program("ProxyProviderTest", [ "ProxyProviderTest.cpp", ]) swift-im-2.0+dev6/Swiften/QA/ProxyProviderTest/ProxyProviderTest.cpp0000644000175000017500000000163012227051774025477 0ustar kismithkismith/* * Copyright (c) 2010 Thilo Cestonaro * Licensed under the BSD License. * See Documentation/Licenses/BSD.txt for more information. */ #include #include #include using namespace Swift; int main(void) { int ret = 0; HostAddressPort hap; std::cout << "constructing PlatfromProxyProvider instance ..." << std::endl; PlatformProxyProvider ppp; hap = ppp.getSOCKS5Proxy(); std::cout << "SOCKS5 Proxy configured: " << hap.isValid() << std::endl; if(hap.isValid()) { std::cout << "SOCKS5 Proxy: " << hap.getAddress().toString() << ":" << hap.getPort() << std::endl; } hap = ppp.getHTTPConnectProxy(); std::cout << "HTTPConnect Proxy configured: " << hap.isValid() << std::endl; if(hap.isValid()) { std::cout << "HTTPConnect Proxy: " << hap.getAddress().toString() << ":" << hap.getPort() << std::endl; } return ret; } swift-im-2.0+dev6/Swiften/QA/StorageTest/0000755000175000017500000000000012227051774020047 5ustar kismithkismithswift-im-2.0+dev6/Swiften/QA/StorageTest/SConscript0000644000175000017500000000114712227051774022064 0ustar kismithkismithimport os Import("env") if env["TEST"] : myenv = env.Clone() myenv.MergeFlags(myenv["CHECKER_FLAGS"]) myenv.MergeFlags(myenv["SWIFTOOLS_FLAGS"]) myenv.MergeFlags(myenv["SWIFTEN_FLAGS"]) myenv.MergeFlags(myenv["CPPUNIT_FLAGS"]) myenv.MergeFlags(myenv["BOOST_FLAGS"]) myenv.MergeFlags(myenv["LIBIDN_FLAGS"]) myenv.MergeFlags(myenv.get("EXPAT_FLAGS", {})) myenv.MergeFlags(myenv.get("LIBXML_FLAGS", {})) myenv.MergeFlags(myenv["PLATFORM_FLAGS"]) tester = myenv.Program("StorageTest", [ "VCardFileStorageTest.cpp", "FileReadBytestreamTest.cpp", ]) myenv.Test(tester, "system", is_checker = True) swift-im-2.0+dev6/Swiften/QA/StorageTest/FileReadBytestreamTest.cpp0000644000175000017500000000365312227051774025135 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include "SwifTools/Application/PlatformApplicationPathProvider.h" using namespace Swift; class FileReadBytestreamTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(FileReadBytestreamTest); CPPUNIT_TEST(testRead); CPPUNIT_TEST(testRead_Twice); CPPUNIT_TEST(testIsFinished_NotFinished); CPPUNIT_TEST(testIsFinished_IsFinished); CPPUNIT_TEST_SUITE_END(); public: void setUp() { pathProvider = new PlatformApplicationPathProvider("FileReadBytestreamTest"); } void tearDown() { delete pathProvider; } void testRead() { boost::shared_ptr testling(createTestling()); std::vector result = testling->read(10); CPPUNIT_ASSERT(ByteArray::create("/*\n * Copy") == result); } void testRead_Twice() { boost::shared_ptr testling(createTestling()); testling->read(10); ByteArray result(testling->read(10)); CPPUNIT_ASSERT_EQUAL(std::string("right (c) "), result.toString()); } void testIsFinished_NotFinished() { boost::shared_ptr testling(createTestling()); testling->read(10); CPPUNIT_ASSERT(!testling->isFinished()); } void testIsFinished_IsFinished() { boost::shared_ptr testling(createTestling()); testling->read(4096); CPPUNIT_ASSERT(testling->isFinished()); } private: FileReadBytestream* createTestling() { return new FileReadBytestream(pathProvider->getExecutableDir() / "FileReadBytestreamTest.cpp"); } PlatformApplicationPathProvider* pathProvider; }; CPPUNIT_TEST_SUITE_REGISTRATION(FileReadBytestreamTest); swift-im-2.0+dev6/Swiften/QA/StorageTest/VCardFileStorageTest.cpp0000644000175000017500000000666512227051774024554 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include "SwifTools/Application/PlatformApplicationPathProvider.h" #include using namespace Swift; class VCardFileStorageTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(VCardFileStorageTest); CPPUNIT_TEST(testSetVCard); // Temporarily disabling this, because it generates error messages on console. Need to figure // out something for not showing error messages during tests. //CPPUNIT_TEST(testSetVCard_LargeFilename); CPPUNIT_TEST(testGetVCard); CPPUNIT_TEST(testGetVCard_FileDoesNotExist); //CPPUNIT_TEST(testGetVCard_LargeFilename); CPPUNIT_TEST_SUITE_END(); public: void setUp() { pathProvider = new PlatformApplicationPathProvider("VCardStorageTest"); vcardsPath = pathProvider->getExecutableDir() / "vcards"; boost::filesystem::remove_all(vcardsPath); } void tearDown() { delete pathProvider; boost::filesystem::remove_all(vcardsPath); } void testSetVCard() { boost::shared_ptr testling(createTestling()); VCard::ref vcard(new VCard()); vcard->setFullName("Alice In Wonderland"); testling->setVCard(JID("alice@wonderland.lit/TeaRoom"), vcard); boost::filesystem::path vcardFile(vcardsPath / "alice@wonderland.lit%2fTeaRoom.xml"); CPPUNIT_ASSERT(boost::filesystem::exists(vcardFile)); ByteArray data; data.readFromFile(vcardFile.string()); CPPUNIT_ASSERT(boost::starts_with(data.toString(), "")); } void testSetVCard_LargeFilename() { std::auto_ptr testling(createTestling()); VCard::ref vcard(new VCard()); vcard->setFullName("Alice In Wonderland"); std::ostringstream s; for (int i = 0; i < 1000; ++i) { s << "_"; } JID jid("alice@wonderland.lit/" + s.str()); testling->setVCard(jid, vcard); // Just check whether we don't crash } void testGetVCard() { boost::shared_ptr testling(createTestling()); VCard::ref vcard(new VCard()); vcard->setFullName("Alice In Wonderland"); testling->setVCard(JID("alice@wonderland.lit"), vcard); VCard::ref result = testling->getVCard(JID("alice@wonderland.lit")); CPPUNIT_ASSERT_EQUAL(std::string("Alice In Wonderland"), result->getFullName()); } void testGetVCard_LargeFilename() { std::auto_ptr testling(createTestling()); VCard::ref vcard(new VCard()); vcard->setFullName("Alice In Wonderland"); std::ostringstream s; for (int i = 0; i < 1000; ++i) { s << "_"; } JID jid("alice@wonderland.lit/" + s.str()); VCard::ref result = testling->getVCard(jid); // Just check that we don't have an exception } void testGetVCard_FileDoesNotExist() { boost::shared_ptr testling(createTestling()); VCard::ref result = testling->getVCard(JID("alice@wonderland.lit")); CPPUNIT_ASSERT(!result); } private: VCardFileStorage* createTestling() { return new VCardFileStorage(vcardsPath); } PlatformApplicationPathProvider* pathProvider; boost::filesystem::path vcardsPath; }; CPPUNIT_TEST_SUITE_REGISTRATION(VCardFileStorageTest); swift-im-2.0+dev6/Swiften/QA/ReconnectTest/0000755000175000017500000000000012227051774020363 5ustar kismithkismithswift-im-2.0+dev6/Swiften/QA/ReconnectTest/SConscript0000644000175000017500000000146212227051774022400 0ustar kismithkismithimport os Import("env") if env["TEST"] : myenv = env.Clone() myenv.MergeFlags(myenv["SWIFTEN_FLAGS"]) myenv.MergeFlags(myenv["CPPUNIT_FLAGS"]) myenv.MergeFlags(myenv["LIBIDN_FLAGS"]) myenv.MergeFlags(myenv["BOOST_FLAGS"]) myenv.MergeFlags(myenv.get("SQLITE_FLAGS", "")) myenv.MergeFlags(myenv["ZLIB_FLAGS"]) myenv.MergeFlags(myenv["OPENSSL_FLAGS"]) myenv.MergeFlags(myenv.get("LIBXML_FLAGS", "")) myenv.MergeFlags(myenv.get("EXPAT_FLAGS", "")) myenv.MergeFlags(myenv["PLATFORM_FLAGS"]) # myenv.Append(LIBPATH = ["/opt/local/lib"]) # myenv.Append(LIBS = ["efence"]) for i in ["SWIFT_CLIENTTEST_JID", "SWIFT_CLIENTTEST_PASS"]: if os.environ.get(i, "") : myenv["ENV"][i] = os.environ[i] tester = myenv.Program("ReconnectTest", ["ReconnectTest.cpp"]) myenv.Test(tester, "system", is_checker = True) swift-im-2.0+dev6/Swiften/QA/ReconnectTest/ReconnectTest.cpp0000644000175000017500000000343112227051774023650 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include using namespace Swift; using namespace Swift; bool connecting_ = false; Client* client_; SimpleEventLoop eventLoop_; int count = 0; void handleTick(boost::shared_ptr timer) { std::cout << "Count " << count++ << std::endl; if (timer) { timer->stop(); } if (connecting_) { client_->disconnect(); } else { if (count > 60) { eventLoop_.stop(); return; } client_->connect(); } connecting_ = !connecting_; int delay = 500; // int delay = 0; boost::shared_ptr newTimer(BoostTimer::create(delay, &MainBoostIOServiceThread::getInstance().getIOService())); newTimer->onTick.connect(boost::bind(&handleTick, timer)); newTimer->start(); } int main(int, char**) { char* jidChars = getenv("SWIFT_CLIENTTEST_JID"); if (!jidChars) { std::cerr << "Please set the SWIFT_CLIENTTEST_JID environment variable" << std::endl; return -1; } char* passChars = getenv("SWIFT_CLIENTTEST_PASS"); if (!passChars) { std::cerr << "Please set the SWIFT_CLIENTTEST_PASS environment variable" << std::endl; return -1; } JID jid(jidChars); std::string pass(passChars); client_ = new Swift::Client(jid, pass); handleTick(boost::shared_ptr()); eventLoop_.run(); delete client_; return 0; } swift-im-2.0+dev6/Swiften/Swiften.rc0000644000175000017500000000170712227051774017254 0ustar kismithkismith#include "Swiften/Version.h" 1 VERSIONINFO FILEVERSION SWIFTEN_VERSION_MAJOR, SWIFTEN_VERSION_MINOR, SWIFTEN_VERSION_PATCH PRODUCTVERSION 1,2,3 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L #else FILEFLAGS 0x0L #endif FILEOS 0x4L FILETYPE 0x2L FILESUBTYPE 0x0L BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904b0" BEGIN VALUE "Comments", "Swiften XMPP Library\0" VALUE "CompanyName", "Swift\0" VALUE "FileDescription", "Swiften\0" VALUE "FileVersion", SWIFTEN_VERSION_STRING VALUE "InternalName", "Swiften\0" VALUE "LegalCopyright", "Copyright \251 " SWIFTEN_COPYRIGHT_YEAR " Swift Team\0" VALUE "OriginalFilename", SWIFTEN_LIBRARY_FILE VALUE "ProductName", "Swiften\0" VALUE "ProductVersion", SWIFTEN_VERSION_STRING END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x409, 1200 END END swift-im-2.0+dev6/Swiften/Whiteboard/0000755000175000017500000000000012227051774017372 5ustar kismithkismithswift-im-2.0+dev6/Swiften/Whiteboard/IncomingWhiteboardSession.cpp0000644000175000017500000000403612227051774025221 0ustar kismithkismith/* * Copyright (c) 2012 Mateusz Piękos * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include #include #include #include #include #include namespace Swift { IncomingWhiteboardSession::IncomingWhiteboardSession(const JID& jid, IQRouter* router) : WhiteboardSession(jid, router) { } IncomingWhiteboardSession::~IncomingWhiteboardSession() { } void IncomingWhiteboardSession::accept() { boost::shared_ptr payload = boost::make_shared(WhiteboardPayload::SessionAccept); boost::shared_ptr > request = boost::make_shared >(IQ::Set, toJID_, payload, router_); request->send(); onRequestAccepted(toJID_); } void IncomingWhiteboardSession::handleIncomingOperation(WhiteboardOperation::ref operation) { WhiteboardClient::Result pairResult = client.handleServerOperationReceived(operation); if (pairResult.client) { if (pairResult.client->getPos() != -1) { onOperationReceived(pairResult.client); } lastOpID = pairResult.client->getID(); } if (pairResult.server) { WhiteboardPayload::ref payload = boost::make_shared(); payload->setOperation(pairResult.server); sendPayload(payload); } } void IncomingWhiteboardSession::sendOperation(WhiteboardOperation::ref operation) { operation->setID(idGenerator.generateID()); operation->setParentID(lastOpID); lastOpID = operation->getID(); WhiteboardOperation::ref result = client.handleLocalOperationReceived(operation); if (result) { WhiteboardPayload::ref payload = boost::make_shared(); payload->setOperation(result); sendPayload(payload); } } } swift-im-2.0+dev6/Swiften/Whiteboard/IncomingWhiteboardSession.h0000644000175000017500000000143212227051774024663 0ustar kismithkismith/* * Copyright (c) 2012 Mateusz Piękos * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include #include #include namespace Swift { class SWIFTEN_API IncomingWhiteboardSession : public WhiteboardSession { public: typedef boost::shared_ptr ref; public: IncomingWhiteboardSession(const JID& jid, IQRouter* router); ~IncomingWhiteboardSession(); void accept(); private: void handleIncomingOperation(WhiteboardOperation::ref operation); void sendOperation(WhiteboardOperation::ref operation); WhiteboardClient client; }; } swift-im-2.0+dev6/Swiften/Whiteboard/WhiteboardSessionManager.h0000644000175000017500000000323512227051774024475 0ustar kismithkismith/* * Copyright (c) 2012 Mateusz Piękos * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include #include #include #include #include #include #include #include namespace Swift { class IQRouter; class WhiteboardResponder; class PresenceOracle; class EntityCapsProvider; class SWIFTEN_API WhiteboardSessionManager { friend class WhiteboardResponder; public: WhiteboardSessionManager(IQRouter* router, StanzaChannel* stanzaChannel, PresenceOracle* presenceOracle, EntityCapsProvider* capsProvider); ~WhiteboardSessionManager(); WhiteboardSession::ref getSession(const JID& to); WhiteboardSession::ref requestSession(const JID& to); public: boost::signal< void (IncomingWhiteboardSession::ref)> onSessionRequest; private: JID getFullJID(const JID& bareJID); OutgoingWhiteboardSession::ref createOutgoingSession(const JID& to); void handleIncomingSession(IncomingWhiteboardSession::ref session); void handlePresenceReceived(Presence::ref presence); void handleAvailableChanged(bool available); void deleteSessionEntry(const JID& contact); private: std::map > sessions_; IQRouter* router_; StanzaChannel* stanzaChannel_; PresenceOracle* presenceOracle_; EntityCapsProvider* capsProvider_; WhiteboardResponder* responder; }; } swift-im-2.0+dev6/Swiften/Whiteboard/WhiteboardSessionManager.cpp0000644000175000017500000000762112227051774025033 0ustar kismithkismith/* * Copyright (c) 2012 Mateusz Piękos * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include #include #include #include #include #include #include #include "Swiften/Disco/EntityCapsProvider.h" namespace Swift { WhiteboardSessionManager::WhiteboardSessionManager(IQRouter* router, StanzaChannel* stanzaChannel, PresenceOracle* presenceOracle, EntityCapsProvider* capsProvider) : router_(router), stanzaChannel_(stanzaChannel), presenceOracle_(presenceOracle), capsProvider_(capsProvider) { responder = new WhiteboardResponder(this, router); responder->start(); stanzaChannel_->onPresenceReceived.connect(boost::bind(&WhiteboardSessionManager::handlePresenceReceived, this, _1)); stanzaChannel_->onAvailableChanged.connect(boost::bind(&WhiteboardSessionManager::handleAvailableChanged, this, _1)); } WhiteboardSessionManager::~WhiteboardSessionManager() { responder->stop(); delete responder; } WhiteboardSession::ref WhiteboardSessionManager::getSession(const JID& to) { if (sessions_.find(to) == sessions_.end()) { return boost::shared_ptr(); } return sessions_[to]; } OutgoingWhiteboardSession::ref WhiteboardSessionManager::createOutgoingSession(const JID& to) { JID fullJID = to; if (fullJID.isBare()) { fullJID = getFullJID(fullJID); } OutgoingWhiteboardSession::ref session = boost::make_shared(fullJID, router_); sessions_[fullJID] = session; session->onSessionTerminated.connect(boost::bind(&WhiteboardSessionManager::deleteSessionEntry, this, _1)); session->onRequestRejected.connect(boost::bind(&WhiteboardSessionManager::deleteSessionEntry, this, _1)); return session; } WhiteboardSession::ref WhiteboardSessionManager::requestSession(const JID& to) { WhiteboardSession::ref session = getSession(to); if (!session) { OutgoingWhiteboardSession::ref outgoingSession = createOutgoingSession(to); outgoingSession->startSession(); return outgoingSession; } else { return session; } } void WhiteboardSessionManager::handleIncomingSession(IncomingWhiteboardSession::ref session) { sessions_[session->getTo()] = session; session->onSessionTerminated.connect(boost::bind(&WhiteboardSessionManager::deleteSessionEntry, this, _1)); onSessionRequest(session); } JID WhiteboardSessionManager::getFullJID(const JID& bareJID) { JID fullReceipientJID; int priority = INT_MIN; //getAllPresence(bareJID) gives you all presences for the bare JID (i.e. all resources) Remko Tronçon @ 11:11 std::vector presences = presenceOracle_->getAllPresence(bareJID); //iterate over them foreach(Presence::ref pres, presences) { if (pres->getPriority() > priority) { // look up caps from the jid DiscoInfo::ref info = capsProvider_->getCaps(pres->getFrom()); if (info && info->hasFeature(DiscoInfo::WhiteboardFeature)) { priority = pres->getPriority(); fullReceipientJID = pres->getFrom(); } } } return fullReceipientJID; } void WhiteboardSessionManager::deleteSessionEntry(const JID& contact) { sessions_.erase(contact); } void WhiteboardSessionManager::handlePresenceReceived(Presence::ref presence) { if (!presence->isAvailable()) { WhiteboardSession::ref session = getSession(presence->getFrom()); if (session) { session->cancel(); } } } void WhiteboardSessionManager::handleAvailableChanged(bool available) { if (!available) { std::map sessionsCopy = sessions_; std::map::iterator it; for (it = sessionsCopy.begin(); it != sessionsCopy.end(); ++it) { it->second->cancel(); } } } } swift-im-2.0+dev6/Swiften/Whiteboard/WhiteboardSession.cpp0000644000175000017500000000427212227051774023537 0ustar kismithkismith/* * Copyright (c) 2012 Mateusz Piękos * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include #include #include #include #include #include namespace Swift { WhiteboardSession::WhiteboardSession(const JID& jid, IQRouter* router) : toJID_(jid), router_(router) { } WhiteboardSession::~WhiteboardSession() { } void WhiteboardSession::handleIncomingAction(boost::shared_ptr payload) { switch (payload->getType()) { case WhiteboardPayload::Data: handleIncomingOperation(payload->getOperation()); return; case WhiteboardPayload::SessionAccept: onRequestAccepted(toJID_); return; case WhiteboardPayload::SessionTerminate: onSessionTerminated(toJID_); return; //handled elsewhere case WhiteboardPayload::SessionRequest: case WhiteboardPayload::UnknownType: return; } } void WhiteboardSession::sendElement(const WhiteboardElement::ref element) { boost::shared_ptr payload = boost::make_shared(); payload->setElement(element); boost::shared_ptr > request = boost::make_shared >(IQ::Set, toJID_, payload, router_); request->send(); } void WhiteboardSession::sendPayload(boost::shared_ptr payload) { boost::shared_ptr > request = boost::make_shared >(IQ::Set, toJID_, payload, router_); request->send(); } void WhiteboardSession::cancel() { if (router_->isAvailable()) { boost::shared_ptr payload = boost::make_shared(WhiteboardPayload::SessionTerminate); boost::shared_ptr > request = boost::make_shared >(IQ::Set, toJID_, payload, router_); request->send(); } onSessionTerminated(toJID_); } const JID& WhiteboardSession::getTo() const { return toJID_; } } swift-im-2.0+dev6/Swiften/Whiteboard/WhiteboardSession.h0000644000175000017500000000320512227051774023177 0ustar kismithkismith/* * Copyright (c) 2012 Mateusz Piękos * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include #include #include #include #include #include #include namespace Swift { class IQRouter; class ErrorPayload; class WhiteboardPayload; class SWIFTEN_API WhiteboardSession { public: typedef boost::shared_ptr ref; public: WhiteboardSession(const JID& jid, IQRouter* router); virtual ~WhiteboardSession(); void handleIncomingAction(boost::shared_ptr payload); void sendElement(const WhiteboardElement::ref element); virtual void sendOperation(WhiteboardOperation::ref operation) = 0; void cancel(); const JID& getTo() const; public: boost::signal< void(const WhiteboardElement::ref element)> onElementReceived; boost::signal< void(const WhiteboardOperation::ref operation)> onOperationReceived; boost::signal< void(const JID& contact)> onSessionTerminated; boost::signal< void(const JID& contact)> onRequestAccepted; boost::signal< void(const JID& contact)> onRequestRejected; private: virtual void handleIncomingOperation(WhiteboardOperation::ref operation) = 0; protected: void sendPayload(boost::shared_ptr payload); JID toJID_; IQRouter* router_; std::string lastOpID; IDGenerator idGenerator; }; } swift-im-2.0+dev6/Swiften/Whiteboard/WhiteboardClient.h0000644000175000017500000000176212227051774023000 0ustar kismithkismith/* * Copyright (c) 2012 Mateusz Piękos * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include #include #include namespace Swift { class SWIFTEN_API WhiteboardClient { public: struct Result { WhiteboardOperation::ref client; WhiteboardOperation::ref server; }; /*! * @return Operation to send */ WhiteboardOperation::ref handleLocalOperationReceived(WhiteboardOperation::ref operation); /*! * @return pair.first-element to handle locally, pair.second-element to send to server */ Result handleServerOperationReceived(WhiteboardOperation::ref operation); void print(); private: std::list localOperations_; std::list serverOperations_; std::list bridge_; std::string lastSentOperationID_; }; } swift-im-2.0+dev6/Swiften/Whiteboard/WhiteboardServer.h0000644000175000017500000000114012227051774023016 0ustar kismithkismith/* * Copyright (c) 2012 Mateusz Piękos * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include #include namespace Swift { class SWIFTEN_API WhiteboardServer { public: void handleLocalOperationReceived(WhiteboardOperation::ref operation); WhiteboardOperation::ref handleClientOperationReceived(WhiteboardOperation::ref operation); void print(); private: std::list operations_; }; } swift-im-2.0+dev6/Swiften/Whiteboard/WhiteboardClient.cpp0000644000175000017500000001030612227051774023325 0ustar kismithkismith/* * Copyright (c) 2012 Mateusz Piękos * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include #include #include #include namespace Swift { WhiteboardOperation::ref WhiteboardClient::handleLocalOperationReceived(WhiteboardOperation::ref operation) { localOperations_.push_back(operation); WhiteboardOperation::ref op; WhiteboardInsertOperation::ref insertOp = boost::dynamic_pointer_cast(operation); if (insertOp) { op = boost::make_shared(*insertOp); } WhiteboardUpdateOperation::ref updateOp = boost::dynamic_pointer_cast(operation); if (updateOp) { op = boost::make_shared(*updateOp); } WhiteboardDeleteOperation::ref deleteOp = boost::dynamic_pointer_cast(operation); if (deleteOp) { op = boost::make_shared(*deleteOp); } if (!bridge_.empty()) { op->setParentID(bridge_.back()->getID()); } bridge_.push_back(op); if (lastSentOperationID_.empty()) { WhiteboardInsertOperation::ref insertOp = boost::dynamic_pointer_cast(operation); if (insertOp) { op = boost::make_shared(*insertOp); } WhiteboardUpdateOperation::ref updateOp = boost::dynamic_pointer_cast(operation); if (updateOp) { op = boost::make_shared(*updateOp); } WhiteboardDeleteOperation::ref deleteOp = boost::dynamic_pointer_cast(operation); if (deleteOp) { op = boost::make_shared(*deleteOp); } if (!serverOperations_.empty()) { op->setParentID(serverOperations_.back()->getID()); } lastSentOperationID_ = operation->getID(); return op; } else { return WhiteboardOperation::ref(); } } WhiteboardClient::Result WhiteboardClient::handleServerOperationReceived(WhiteboardOperation::ref operation) { serverOperations_.push_back(operation); Result result; // if (localOperations_.empty()) {// || localOperations_.back()->getID() == operation->getParentID()) { //Situation where client and server are in sync if (localOperations_.size() == serverOperations_.size()-1) { localOperations_.push_back(operation); // clientOp = operation; result.client = operation; } else if (lastSentOperationID_ == operation->getID()) { //Client received confirmation about own operation and it sends next operation to server if (!bridge_.empty() && lastSentOperationID_ == bridge_.front()->getID()) { bridge_.erase(bridge_.begin()); } if (!bridge_.empty() && (bridge_.front())->getParentID() == lastSentOperationID_) { lastSentOperationID_ = (bridge_.front())->getID(); result.server = bridge_.front(); } if (!result.server) { lastSentOperationID_.clear(); } } else { std::list::iterator it = bridge_.begin(); std::pair opPair; WhiteboardOperation::ref temp; opPair = WhiteboardTransformer::transform(*it, operation); temp = opPair.first; *it = opPair.second; std::string previousID = (*it)->getID(); ++it; for (; it != bridge_.end(); ++it) { opPair = WhiteboardTransformer::transform(*it, temp); temp = opPair.first; *it = opPair.second; (*it)->setParentID(previousID); previousID = (*it)->getID(); } temp->setParentID(localOperations_.back()->getID()); localOperations_.push_back(temp); result.client = temp; } return result; } void WhiteboardClient::print() { std::list::iterator it; std::cout << "Client" << std::endl; for(it = localOperations_.begin(); it != localOperations_.end(); ++it) { std::cout << (*it)->getID() << " " << (*it)->getPos() << std::endl; } std::cout << "Server" << std::endl; for(it = serverOperations_.begin(); it != serverOperations_.end(); ++it) { std::cout << (*it)->getID() << " " << (*it)->getPos() << std::endl; } } } swift-im-2.0+dev6/Swiften/Whiteboard/WhiteboardResponder.cpp0000644000175000017500000000274112227051774024054 0ustar kismithkismith/* * Copyright (c) 2012 Mateusz Piękos * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include #include #include #include #include #include namespace Swift { WhiteboardResponder::WhiteboardResponder(WhiteboardSessionManager* sessionManager, IQRouter* router) : SetResponder(router), sessionManager_(sessionManager), router_(router) { } bool WhiteboardResponder::handleSetRequest(const JID& from, const JID& /*to*/, const std::string& id, boost::shared_ptr payload) { if (payload->getType() == WhiteboardPayload::SessionRequest) { if (sessionManager_->getSession(from)) { sendError(from, id, ErrorPayload::Conflict, ErrorPayload::Cancel); } else { sendResponse(from, id, boost::shared_ptr()); IncomingWhiteboardSession::ref session = boost::make_shared(from, router_); sessionManager_->handleIncomingSession(session); } } else { sendResponse(from, id, boost::shared_ptr()); WhiteboardSession::ref session = sessionManager_->getSession(from); if (session != NULL) { session->handleIncomingAction(payload); } } return true; } } swift-im-2.0+dev6/Swiften/Whiteboard/WhiteboardServer.cpp0000644000175000017500000000311012227051774023350 0ustar kismithkismith/* * Copyright (c) 2012 Mateusz Piękos * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include #include #include namespace Swift { void WhiteboardServer::handleLocalOperationReceived(WhiteboardOperation::ref operation) { operations_.push_back(operation); } WhiteboardOperation::ref WhiteboardServer::handleClientOperationReceived(WhiteboardOperation::ref newOperation) { std::list::reverse_iterator it; if (operations_.empty() || newOperation->getParentID() == operations_.back()->getID()) { operations_.push_back(newOperation); return newOperation; } for (it = operations_.rbegin(); it != operations_.rend(); ++it) { WhiteboardOperation::ref operation = *it; while (newOperation->getParentID() == operation->getParentID()) { std::pair tResult = WhiteboardTransformer::transform(newOperation, operation); if (it == operations_.rbegin()) { operations_.push_back(tResult.second); return tResult.second; } else { newOperation = tResult.second; --it; operation = *it; } } } return WhiteboardOperation::ref(); } void WhiteboardServer::print() { std::list::iterator it; std::cout << "Server:" << std::endl; for(it = operations_.begin(); it != operations_.end(); ++it) { std::cout << (*it)->getID() << " " << (*it)->getPos() << std::endl; } } } swift-im-2.0+dev6/Swiften/Whiteboard/UnitTest/0000755000175000017500000000000012227051774021151 5ustar kismithkismithswift-im-2.0+dev6/Swiften/Whiteboard/UnitTest/WhiteboardServerTest.cpp0000644000175000017500000001276112227051774026003 0ustar kismithkismith/* * Copyright (c) 2012 Mateusz Piękos * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include #include #include #include #include #include #include #include using namespace Swift; class WhiteboardServerTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(WhiteboardServerTest); CPPUNIT_TEST(testSimpleOp); CPPUNIT_TEST(testSimpleOp1); CPPUNIT_TEST(testSimpleOp2); CPPUNIT_TEST(testFewSimpleOps); CPPUNIT_TEST_SUITE_END(); public: void testSimpleOp() { WhiteboardServer server; WhiteboardInsertOperation::ref firstOp = boost::make_shared(); firstOp->setID("0"); server.handleLocalOperationReceived(firstOp); WhiteboardInsertOperation::ref serverOp = boost::make_shared(); serverOp->setID("b"); serverOp->setParentID("0"); serverOp->setPos(1); server.handleLocalOperationReceived(serverOp); WhiteboardInsertOperation::ref clientOp = boost::make_shared(); WhiteboardEllipseElement::ref clientElement = boost::make_shared(0,0,0,0); clientOp->setID("a"); clientOp->setParentID("0"); clientOp->setPos(1); clientOp->setElement(clientElement); WhiteboardInsertOperation::ref op = boost::dynamic_pointer_cast(server.handleClientOperationReceived(clientOp)); CPPUNIT_ASSERT_EQUAL(std::string("b"), op->getParentID()); CPPUNIT_ASSERT_EQUAL(std::string("a"), op->getID()); CPPUNIT_ASSERT_EQUAL(1, op->getPos()); CPPUNIT_ASSERT_EQUAL(clientElement, boost::dynamic_pointer_cast(op->getElement())); } void testSimpleOp1() { WhiteboardServer server; WhiteboardInsertOperation::ref firstOp = boost::make_shared(); firstOp->setID("0"); server.handleLocalOperationReceived(firstOp); WhiteboardDeleteOperation::ref serverOp = boost::make_shared(); serverOp->setID("b"); serverOp->setParentID("0"); serverOp->setPos(1); server.handleLocalOperationReceived(serverOp); WhiteboardUpdateOperation::ref clientOp = boost::make_shared(); WhiteboardEllipseElement::ref clientElement = boost::make_shared(0,0,0,0); clientOp->setID("a"); clientOp->setParentID("0"); clientOp->setPos(1); clientOp->setElement(clientElement); WhiteboardDeleteOperation::ref op = boost::dynamic_pointer_cast(server.handleClientOperationReceived(clientOp)); CPPUNIT_ASSERT_EQUAL(std::string("b"), op->getParentID()); CPPUNIT_ASSERT_EQUAL(std::string("a"), op->getID()); CPPUNIT_ASSERT_EQUAL(-1, op->getPos()); } void testSimpleOp2() { WhiteboardServer server; WhiteboardInsertOperation::ref firstOp = boost::make_shared(); firstOp->setID("0"); server.handleLocalOperationReceived(firstOp); WhiteboardUpdateOperation::ref serverOp = boost::make_shared(); serverOp->setID("b"); serverOp->setParentID("0"); serverOp->setPos(1); server.handleLocalOperationReceived(serverOp); WhiteboardDeleteOperation::ref clientOp = boost::make_shared(); clientOp->setID("a"); clientOp->setParentID("0"); clientOp->setPos(1); WhiteboardDeleteOperation::ref op = boost::dynamic_pointer_cast(server.handleClientOperationReceived(clientOp)); CPPUNIT_ASSERT_EQUAL(std::string("b"), op->getParentID()); CPPUNIT_ASSERT_EQUAL(std::string("a"), op->getID()); CPPUNIT_ASSERT_EQUAL(1, op->getPos()); } void testFewSimpleOps() { WhiteboardServer server; WhiteboardInsertOperation::ref firstOp = boost::make_shared(); firstOp->setID("0"); server.handleLocalOperationReceived(firstOp); WhiteboardInsertOperation::ref serverOp = boost::make_shared(); serverOp->setID("a"); serverOp->setParentID("0"); serverOp->setPos(1); server.handleLocalOperationReceived(serverOp); serverOp = boost::make_shared(); serverOp->setID("b"); serverOp->setParentID("a"); serverOp->setPos(2); server.handleLocalOperationReceived(serverOp); serverOp = boost::make_shared(); serverOp->setID("c"); serverOp->setParentID("b"); serverOp->setPos(3); server.handleLocalOperationReceived(serverOp); WhiteboardInsertOperation::ref clientOp = boost::make_shared(); WhiteboardEllipseElement::ref clientElement = boost::make_shared(0,0,0,0); clientOp->setID("d"); clientOp->setParentID("0"); clientOp->setPos(1); clientOp->setElement(clientElement); WhiteboardInsertOperation::ref op = boost::dynamic_pointer_cast(server.handleClientOperationReceived(clientOp)); CPPUNIT_ASSERT_EQUAL(std::string("c"), op->getParentID()); CPPUNIT_ASSERT_EQUAL(std::string("d"), op->getID()); CPPUNIT_ASSERT_EQUAL(1, op->getPos()); CPPUNIT_ASSERT_EQUAL(clientElement, boost::dynamic_pointer_cast(op->getElement())); } }; CPPUNIT_TEST_SUITE_REGISTRATION(WhiteboardServerTest); swift-im-2.0+dev6/Swiften/Whiteboard/UnitTest/WhiteboardClientTest.cpp0000644000175000017500000007171112227051774025753 0ustar kismithkismith/* * Copyright (c) 2012 Mateusz Piękos * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include #include #include #include #include #include #include #include using namespace Swift; class WhiteboardClientTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(WhiteboardClientTest); CPPUNIT_TEST(testSynchronize_simplestSync); CPPUNIT_TEST(testSynchronize_withoutTranslation); CPPUNIT_TEST(testSynchronize_nonInterrupted); CPPUNIT_TEST(testSynchronize_clientInterruption); CPPUNIT_TEST(testSynchronize_serverInterruption); CPPUNIT_TEST(testSynchronize_nonInterruptedMixOperations); CPPUNIT_TEST(testSynchronize_nonInterruptedMixOperations2); CPPUNIT_TEST_SUITE_END(); public: /*! * /\ * \/ * \ */ void testSynchronize_simplestSync() { WhiteboardClient client; WhiteboardInsertOperation::ref serverOp; serverOp = createInsertOperation("0", "", 0); WhiteboardClient::Result pairResult = client.handleServerOperationReceived(serverOp); CPPUNIT_ASSERT_EQUAL(serverOp, boost::dynamic_pointer_cast(pairResult.client)); CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.server); //Client receives first local operation, because it's parented off "0" which exists //in server history and client doesn't wait for any operation ack from server, //so this operation could be send WhiteboardInsertOperation::ref clientOp; clientOp = createInsertOperation("a", "0", 1); WhiteboardEllipseElement::ref aElement = boost::make_shared(0,0,0,0); clientOp->setElement(aElement); WhiteboardInsertOperation::ref result; checkOperation(client.handleLocalOperationReceived(clientOp), "a", "0", 1, aElement); //Client receives server operation parented off "0", which isn't last client operation //so it have to be transformed against local operations and then transformed value can //be returned to draw serverOp = createInsertOperation("b", "0", 1); WhiteboardEllipseElement::ref bElement = boost::make_shared(0,0,0,0); serverOp->setElement(bElement); pairResult = client.handleServerOperationReceived(serverOp); checkOperation(pairResult.client, "b", "a", 2, bElement); CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.server); //Client receives confirmation from the server about processed "a" operation, it had to //be transformed against "b" on the server side to receive operation parented off "b". //There aren't any waiting operations to send to server, this operation should return //nothing serverOp = createInsertOperation("a", "b", 1); pairResult = client.handleServerOperationReceived(serverOp); CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.client); CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.server); //Client receives local operation, it doesn't have to be transformed against anything //but operation returned to send to the server should be parented off last server //operation, which is "b" clientOp = createInsertOperation("c", "b", 3); WhiteboardEllipseElement::ref cElement = boost::make_shared(0,0,0,0); clientOp->setElement(cElement); checkOperation(client.handleLocalOperationReceived(clientOp), "c", "a", 3, cElement); //Client receives confirmation from the server about processed "a" operation, it //should be the same operation as it was sent because server didn't have to //transform it clientOp = createInsertOperation("c", "a", 3); clientOp->setElement(cElement); pairResult = client.handleServerOperationReceived(clientOp); CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.client); CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.server); //Results: //Client operations: //ID pos //0 0 //a 1 //b 2 //c 3 // //Server operations: //ID pos //0 0 //b 1 //a 1 //c 3 // //what gives 0abc on both sides } /*! * / * / * \ */ void testSynchronize_withoutTranslation() { WhiteboardClient client; WhiteboardInsertOperation::ref serverOp; serverOp = createInsertOperation("0", "", 0); WhiteboardClient::Result pairResult = client.handleServerOperationReceived(serverOp); CPPUNIT_ASSERT_EQUAL(serverOp, boost::dynamic_pointer_cast(pairResult.client)); CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.server); //Client receives first local operation, because it's parented off "0" which exists //in server history and client doesn't wait for any operation ack from server, //so this operation could be send WhiteboardInsertOperation::ref clientOp = createInsertOperation("c", "0", 1); WhiteboardEllipseElement::ref cElement = boost::make_shared(0,0,0,0); clientOp->setElement(cElement); checkOperation(client.handleLocalOperationReceived(clientOp), "c", "0", 1, cElement); //Client receives second local operation, client didn't receive ack about previous //operation from the server so it can't be send. clientOp = createInsertOperation("d", "c", 2); WhiteboardEllipseElement::ref dElement = boost::make_shared(0,0,0,0); clientOp->setElement(dElement); CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), client.handleLocalOperationReceived(clientOp)); //Client receives confirmation about processing "c" operation, it should be the //same as sent operation because it wasn't transformed, client could send now //operation "d" clientOp = createInsertOperation("c", "0", 1); clientOp->setElement(cElement); pairResult = client.handleServerOperationReceived(clientOp); checkOperation(pairResult.server, "d", "c", 2, dElement); CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.client); //Client receives confirmation about processing "d", it should be the same as //sent operation. There aren't any operations in queue to send. clientOp = createInsertOperation("d", "c", 2); clientOp->setElement(dElement); pairResult = client.handleServerOperationReceived(clientOp); CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.client); CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.server); //Client receives new operation from server, it's parented off "d" which is at //the end of local history so it doesn't have to be transformed, so operation //to pass to window should be the same serverOp = createInsertOperation("e", "d", 3); WhiteboardEllipseElement::ref eElement = boost::make_shared(0,0,0,0); serverOp->setElement(eElement); pairResult = client.handleServerOperationReceived(serverOp); WhiteboardInsertOperation::ref result = boost::dynamic_pointer_cast(pairResult.client); CPPUNIT_ASSERT_EQUAL(serverOp, boost::dynamic_pointer_cast(pairResult.client)); CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.server); //Client operations: //ID pos //0 0 //c 1 //d 2 //e 3 // //Server operations: //ID pos //0 0 //c 1 //d 2 //e 3 } /*! * /\ * / \ * \ / * \/ */ void testSynchronize_nonInterrupted() { WhiteboardClient client; WhiteboardInsertOperation::ref serverOp; serverOp = createInsertOperation("0", "", 0); WhiteboardClient::Result pairResult = client.handleServerOperationReceived(serverOp); CPPUNIT_ASSERT_EQUAL(serverOp, boost::dynamic_pointer_cast(pairResult.client)); CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.server); //Client receives first local operation, because it's parented off "0" which exists //in server history and client doesn't wait for any operation ack from server, //so this operation could be send WhiteboardInsertOperation::ref clientOp; clientOp = createInsertOperation("a", "0", 1); WhiteboardEllipseElement::ref aElement = boost::make_shared(0,0,0,0); clientOp->setElement(aElement); checkOperation(client.handleLocalOperationReceived(clientOp), "a", "0", 1, aElement); //Client receives second local operation, client didn't receive ack about previous //operation from the server so it can't be send. clientOp = createInsertOperation("b", "a", 2); WhiteboardEllipseElement::ref bElement = boost::make_shared(0,0,0,0); clientOp->setElement(bElement); CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), client.handleLocalOperationReceived(clientOp)); //Client receives new operation from server, it should be transformed against //"a" and "b" before adding to local operations history because it's parented off "0". //Because client is waiting for ack of "a", there is no operation to send to server serverOp = createInsertOperation("c", "0", 1); WhiteboardEllipseElement::ref cElement = boost::make_shared(0,0,0,0); serverOp->setElement(cElement); pairResult = client.handleServerOperationReceived(serverOp); checkOperation(pairResult.client, "c", "b", 3, cElement); CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.server); //Client receives new operation from server, it should be transformed against //results of previous transformations, returned operation should be parented off //"c" existing in local history. //Because client is waiting for ack of "a", there is no operation to send to server serverOp = createInsertOperation("d", "c", 2); WhiteboardEllipseElement::ref dElement = boost::make_shared(0,0,0,0); serverOp->setElement(dElement); pairResult = client.handleServerOperationReceived(serverOp); checkOperation(pairResult.client, "d", "c", 4, dElement); CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.server); //Client receives confirmation about processing "a", it should send next operation //to server which is "b", but it should be version parented of transformed "a" serverOp = createInsertOperation("a", "d", 1); pairResult = client.handleServerOperationReceived(serverOp); checkOperation(pairResult.server, "b", "a", 2, bElement); CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.client); //Client receives confirmation about processing "b", there aren't any operations //waiting so it should return nothing. serverOp = createInsertOperation("b", "a", 2); pairResult = client.handleServerOperationReceived(serverOp); CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.client); CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.server); //Client operations: //ID pos //0 0 //a 1 //b 2 //c 3 //d 4 // //Server operations: //ID pos //0 0 //c 1 //d 2 //a 1 //b 2 // //what gives 0abcd on both sides. } /*! * /\ * / \ * \ / * / / * \/ */ void testSynchronize_clientInterruption() { WhiteboardClient client; WhiteboardInsertOperation::ref serverOp; serverOp = createInsertOperation("0", "", 0); WhiteboardClient::Result pairResult = client.handleServerOperationReceived(serverOp); CPPUNIT_ASSERT_EQUAL(serverOp, boost::dynamic_pointer_cast(pairResult.client)); CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.server); //Client receives first local operation, because it's parented off "0" which exists //in server history and client doesn't wait for any operation ack from server, //so this operation could be send WhiteboardInsertOperation::ref clientOp; clientOp = createInsertOperation("a", "0", 1); WhiteboardEllipseElement::ref aElement = boost::make_shared(0,0,0,0); clientOp->setElement(aElement); checkOperation(client.handleLocalOperationReceived(clientOp), "a", "0", 1, aElement); //Client receives second local operation, client didn't receive ack about previous //operation from the server so it can't be send. clientOp = createInsertOperation("b", "a", 2); WhiteboardEllipseElement::ref bElement = boost::make_shared(0,0,0,0); clientOp->setElement(bElement); CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), client.handleLocalOperationReceived(clientOp)); //Client receives new operation from server, it should be transformed against //"a" and "b" before adding to local operations history because it's parented off "0". //Because client is waiting for ack of "a", there is no operation to send to server serverOp = createInsertOperation("c", "0", 1); WhiteboardEllipseElement::ref cElement = boost::make_shared(0,0,0,0); serverOp->setElement(cElement); pairResult = client.handleServerOperationReceived(serverOp); checkOperation(pairResult.client, "c", "b", 3, cElement); CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.server); //Client receives new local operation, client is still waiting for ack so, it //should return nothing clientOp = createInsertOperation("e", "a", 4); WhiteboardEllipseElement::ref eElement = boost::make_shared(0,0,0,0); clientOp->setElement(eElement); CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), client.handleLocalOperationReceived(clientOp)); //Client receives new server operation, to add it to local history it should be transformed //against result of previous transformations and operation "e", returned operation should //be parented off "e", which was last local operation serverOp = createInsertOperation("d", "c", 2); WhiteboardEllipseElement::ref dElement = boost::make_shared(0,0,0,0); serverOp->setElement(dElement); pairResult = client.handleServerOperationReceived(serverOp); checkOperation(pairResult.client, "d", "e", 5, dElement); CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.server); //Client receives confirmation about processing "a", it had to be transformed against //"c" and "d" and it is now parented off "d", returned value should be next operation //which have to be send to server("b" parented off server version of "a"). serverOp = createInsertOperation("a", "d", 1); pairResult = client.handleServerOperationReceived(serverOp); checkOperation(pairResult.server, "b", "a", 2, bElement); CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.client); //Client receives confirmation about processing "b", it is the same operation as sent because //it didn't have to be transformed, returned value should be next operation //which have to be send to server("e" parented off server version of "b"). serverOp = createInsertOperation("b", "a", 2); pairResult = client.handleServerOperationReceived(serverOp); checkOperation(pairResult.server, "e", "b", 4, eElement); CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.client); //Client receives confirmation about processing "b", it is the same operation as sent because //it didn't have to be transformed, there aren't any operations to send so this function returns //nothing serverOp = createInsertOperation("e", "b", 4); pairResult = client.handleServerOperationReceived(serverOp); CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.client); CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.server); //Result: //Client operations: //ID pos //0 0 //a 1 //b 2 //c 3 //e 4 //d 5 // //Server operations: //0 0 //c 1 //d 2 //a 1 //b 2 //e 4 //what gives 0abced on both sides } /*! * /\ * / / * \ \ * \/ */ void testSynchronize_serverInterruption() { WhiteboardClient client; WhiteboardInsertOperation::ref serverOp; serverOp = createInsertOperation("0", "", 0); WhiteboardClient::Result pairResult = client.handleServerOperationReceived(serverOp); CPPUNIT_ASSERT_EQUAL(serverOp, boost::dynamic_pointer_cast(pairResult.client)); CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.server); //Client receives first local operation, because it's parented off "0" which exists //in server history and client doesn't wait for any operation ack from server, //so this operation could be send WhiteboardInsertOperation::ref clientOp; clientOp = createInsertOperation("a", "0", 1); WhiteboardEllipseElement::ref aElement = boost::make_shared(0,0,0,0); clientOp->setElement(aElement); checkOperation(client.handleLocalOperationReceived(clientOp), "a", "0", 1, aElement); //Client receives second local operation, client didn't receive ack about previous //operation from the server so it can't be send. clientOp = createInsertOperation("b", "a", 2); WhiteboardEllipseElement::ref bElement = boost::make_shared(0,0,0,0); clientOp->setElement(bElement); CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), client.handleLocalOperationReceived(clientOp)); //Client receives new operation from server, it should be transformed against //"a" and "b" before adding to local operations history because it's parented off "0". //Because client is waiting for ack of "a", there is no operation to send to server serverOp = createInsertOperation("c", "0", 1); WhiteboardEllipseElement::ref cElement = boost::make_shared(0,0,0,0); serverOp->setElement(cElement); pairResult = client.handleServerOperationReceived(serverOp); checkOperation(pairResult.client, "c", "b", 3, cElement); CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.server); //Client receives confirmation about processing "a", it had to be transformed against //"c" and it is now parented off "c", returned value should be next operation //which have to be send to server("b" parented off server version of "a"). serverOp = createInsertOperation("a", "c", 1); serverOp->setElement(aElement); pairResult = client.handleServerOperationReceived(serverOp); checkOperation(pairResult.server, "b", "a", 2, bElement); CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.client); //Client receives new server operation, to add it to local history it should be transformed //against result of previous transformation(but only with transformation of "b"), returned //operation should be parented off "c", which was last local operation serverOp = createInsertOperation("d", "a", 3); WhiteboardEllipseElement::ref dElement = boost::make_shared(0,0,0,0); serverOp->setElement(dElement); pairResult = client.handleServerOperationReceived(serverOp); checkOperation(pairResult.client, "d", "c", 4, dElement); CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.server); //Client receives confirmation about processing "b", it had to be transformed against //"d" because both operations was parented off server version of "a". //there aren't any operations to send so this function returns nothing. serverOp = createInsertOperation("b", "d", 2); serverOp->setElement(bElement); pairResult = client.handleServerOperationReceived(serverOp); CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.client); CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.server); //Client operations: //ID pos //0 0 //a 1 //b 2 //c 3 //d 4 // //Server operations: //ID pos //0 0 //c 1 //a 1 //d 3 //b 2 // //what gives 0abcd on both sides } /*! * /\ * / \ * \ / * \/ */ void testSynchronize_nonInterruptedMixOperations() { WhiteboardClient client; WhiteboardInsertOperation::ref serverOp; serverOp = createInsertOperation("0", "", 0); WhiteboardClient::Result pairResult = client.handleServerOperationReceived(serverOp); CPPUNIT_ASSERT_EQUAL(serverOp, boost::dynamic_pointer_cast(pairResult.client)); CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.server); //Client receives first local operation, because it's parented off "0" which exists //in server history and client doesn't wait for any operation ack from server, //so this operation could be send WhiteboardInsertOperation::ref clientOp; clientOp = createInsertOperation("a", "0", 1); WhiteboardEllipseElement::ref aElement = boost::make_shared(0,0,0,0); clientOp->setElement(aElement); checkOperation(client.handleLocalOperationReceived(clientOp), "a", "0", 1, aElement); //Client receives second local operation, client didn't receive ack about previous //operation from the server so it can't be send. WhiteboardUpdateOperation::ref clientUpdateOp = createUpdateOperation("b", "a", 0); WhiteboardEllipseElement::ref bElement = boost::make_shared(0,0,0,0); clientUpdateOp->setElement(bElement); CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), client.handleLocalOperationReceived(clientUpdateOp)); //Client receives new operation from server, it should be transformed against //"a" and "b" before adding to local operations history because it's parented off "0". //Because client is waiting for ack of "a", there is no operation to send to server WhiteboardUpdateOperation::ref serverUpdateOp = createUpdateOperation("c", "0", 0); WhiteboardEllipseElement::ref cElement = boost::make_shared(0,0,0,0); serverUpdateOp->setElement(cElement); pairResult = client.handleServerOperationReceived(serverUpdateOp); checkOperation(pairResult.client, "c", "b", 0, cElement); CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.server); //Client receives new operation from server, it should be transformed against //results of previous transformations, returned operation should be parented off //"c" existing in local history. //Because client is waiting for ack of "a", there is no operation to send to server serverOp = createInsertOperation("d", "c", 1); WhiteboardEllipseElement::ref dElement = boost::make_shared(0,0,0,0); serverOp->setElement(dElement); pairResult = client.handleServerOperationReceived(serverOp); checkOperation(pairResult.client, "d", "c", 2, dElement); CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.server); //Client receives confirmation about processing "a", it should send next operation //to server which is "b", but it should be version parented of transformed "a" serverOp = createInsertOperation("a", "d", 1); pairResult = client.handleServerOperationReceived(serverOp); checkOperation(pairResult.server, "b", "a", 0, cElement); CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.client); //Client receives confirmation about processing "b", there aren't any operations //waiting so it should return nothing. serverUpdateOp = createUpdateOperation("b", "a", 0); pairResult = client.handleServerOperationReceived(serverUpdateOp); CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.client); CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.server); //Client operations: //ID pos //0 0 //a 1 //b 2 //c 3 //d 4 // //Server operations: //ID pos //0 0 //c 1 //d 2 //a 1 //b 2 // //what gives 0abcd on both sides. } /*! * /\ * / \ * \ / * \/ */ void testSynchronize_nonInterruptedMixOperations2() { WhiteboardClient client; WhiteboardInsertOperation::ref serverOp; serverOp = createInsertOperation("0", "", 0); WhiteboardClient::Result pairResult = client.handleServerOperationReceived(serverOp); CPPUNIT_ASSERT_EQUAL(serverOp, boost::dynamic_pointer_cast(pairResult.client)); CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.server); serverOp = createInsertOperation("1", "0", 1); pairResult = client.handleServerOperationReceived(serverOp); CPPUNIT_ASSERT_EQUAL(serverOp, boost::dynamic_pointer_cast(pairResult.client)); CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.server); //Client receives first local operation, because it's parented off "0" which exists //in server history and client doesn't wait for any operation ack from server, //so this operation could be send WhiteboardInsertOperation::ref clientOp; WhiteboardUpdateOperation::ref clientUpdateOp; WhiteboardDeleteOperation::ref clientDeleteOp; clientUpdateOp = createUpdateOperation("a", "1", 0); WhiteboardEllipseElement::ref aElement = boost::make_shared(0,0,0,0); clientUpdateOp->setElement(aElement); checkOperation(client.handleLocalOperationReceived(clientUpdateOp), "a", "1", 0, aElement); //Client receives second local operation, client didn't receive ack about previous //operation from the server so it can't be send. clientDeleteOp = createDeleteOperation("b", "a", 1); CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), client.handleLocalOperationReceived(clientDeleteOp)); //Client receives new operation from server, it should be transformed against //"a" and "b" before adding to local operations history because it's parented off "0". //Because client is waiting for ack of "a", there is no operation to send to server serverOp = createInsertOperation("c", "1", 2); WhiteboardEllipseElement::ref cElement = boost::make_shared(0,0,0,0); serverOp->setElement(cElement); pairResult = client.handleServerOperationReceived(serverOp); checkOperation(pairResult.client, "c", "b", 1, cElement); CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.server); //Client receives new operation from server, it should be transformed against //results of previous transformations, returned operation should be parented off //"c" existing in local history. //Because client is waiting for ack of "a", there is no operation to send to server WhiteboardUpdateOperation::ref serverUpdateOp = createUpdateOperation("d", "c", 0); WhiteboardEllipseElement::ref dElement = boost::make_shared(0,0,0,0); serverUpdateOp->setElement(dElement); pairResult = client.handleServerOperationReceived(serverUpdateOp); checkOperation(pairResult.client, "d", "c", 0, dElement); CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.server); //Client receives confirmation about processing "a", it should send next operation //to server which is "b", but it should be version parented of transformed "a" serverUpdateOp = createUpdateOperation("a", "d", 0); pairResult = client.handleServerOperationReceived(serverUpdateOp); checkOperation(pairResult.server, "b", "a", 1); CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.client); //Client receives confirmation about processing "b", there aren't any operations //waiting so it should return nothing. WhiteboardDeleteOperation::ref serverDeleteOp = createDeleteOperation("b", "a", 0); pairResult = client.handleServerOperationReceived(serverDeleteOp); CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.client); CPPUNIT_ASSERT_EQUAL(WhiteboardOperation::ref(), pairResult.server); //Client operations: //ID pos //0 0 //a 1 //b 2 //c 3 //d 4 // //Server operations: //ID pos //0 0 //c 1 //d 2 //a 1 //b 2 // //what gives 0abcd on both sides. } WhiteboardInsertOperation::ref createInsertOperation(std::string id, std::string parent, int pos) { WhiteboardInsertOperation::ref operation = boost::make_shared(); operation->setParentID(parent); operation->setID(id); operation->setPos(pos); return operation; } WhiteboardUpdateOperation::ref createUpdateOperation(std::string id, std::string parent, int pos) { WhiteboardUpdateOperation::ref operation = boost::make_shared(); operation->setParentID(parent); operation->setID(id); operation->setPos(pos); return operation; } WhiteboardDeleteOperation::ref createDeleteOperation(std::string id, std::string parent, int pos) { WhiteboardDeleteOperation::ref operation = boost::make_shared(); operation->setParentID(parent); operation->setID(id); operation->setPos(pos); return operation; } void checkOperation(WhiteboardOperation::ref operation, std::string id, std::string parent, int pos = -1, WhiteboardElement::ref element = WhiteboardElement::ref()) { CPPUNIT_ASSERT_EQUAL(id, operation->getID()); CPPUNIT_ASSERT_EQUAL(parent, operation->getParentID()); if (pos != -1) { CPPUNIT_ASSERT_EQUAL(pos, operation->getPos()); } if (element) { WhiteboardInsertOperation::ref insertOp = boost::dynamic_pointer_cast(operation); if (insertOp) { CPPUNIT_ASSERT_EQUAL(element, insertOp->getElement()); } WhiteboardUpdateOperation::ref updateOp = boost::dynamic_pointer_cast(operation); if (updateOp) { CPPUNIT_ASSERT_EQUAL(element, updateOp->getElement()); } } } }; CPPUNIT_TEST_SUITE_REGISTRATION(WhiteboardClientTest); swift-im-2.0+dev6/Swiften/Whiteboard/WhiteboardTransformer.cpp0000644000175000017500000002507712227051774024424 0ustar kismithkismith/* * Copyright (c) 2012 Mateusz Piękos * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include #include namespace Swift { std::pair WhiteboardTransformer::transform(WhiteboardOperation::ref clientOp, WhiteboardOperation::ref serverOp) { WhiteboardInsertOperation::ref clientInsert = boost::dynamic_pointer_cast(clientOp); WhiteboardInsertOperation::ref serverInsert = boost::dynamic_pointer_cast(serverOp); WhiteboardUpdateOperation::ref clientUpdate = boost::dynamic_pointer_cast(clientOp); WhiteboardUpdateOperation::ref serverUpdate = boost::dynamic_pointer_cast(serverOp); WhiteboardDeleteOperation::ref clientDelete = boost::dynamic_pointer_cast(clientOp); WhiteboardDeleteOperation::ref serverDelete = boost::dynamic_pointer_cast(serverOp); if (clientInsert && serverInsert) { return transform(clientInsert, serverInsert); } else if (clientUpdate && serverUpdate) { return transform(clientUpdate, serverUpdate); } else if (clientInsert && serverUpdate) { return transform(clientInsert, serverUpdate); } else if (clientUpdate && serverInsert) { return transform(clientUpdate, serverInsert); } else if (clientDelete && serverDelete) { return transform(clientDelete, serverDelete); } else if (clientInsert && serverDelete) { return transform(clientInsert, serverDelete); } else if (clientDelete && serverInsert) { return transform(clientDelete, serverInsert); } else if (clientUpdate && serverDelete) { return transform(clientUpdate, serverDelete); } else if (clientDelete && serverUpdate) { return transform(clientDelete, serverUpdate); } else { return std::pair(); } } std::pair WhiteboardTransformer::transform(WhiteboardInsertOperation::ref clientOp, WhiteboardInsertOperation::ref serverOp) { std::pair result; result.first = boost::make_shared(*serverOp); result.first->setParentID(clientOp->getID()); result.second = boost::make_shared(*clientOp); result.second->setParentID(serverOp->getID()); if (clientOp->getPos() <= serverOp->getPos()) { result.first->setPos(result.first->getPos()+1); } else { result.second->setPos(result.second->getPos()+1); } return result; } std::pair WhiteboardTransformer::transform(WhiteboardUpdateOperation::ref clientOp, WhiteboardUpdateOperation::ref serverOp) { std::pair result; result.first = boost::make_shared(*serverOp); result.first->setParentID(clientOp->getID()); if (clientOp->getPos() == serverOp->getPos()) { result.second = boost::make_shared(*serverOp); result.second->setID(clientOp->getID()); result.second->setParentID(serverOp->getID()); } else { result.second = boost::make_shared(*clientOp); result.second->setParentID(serverOp->getID()); } if (clientOp->getPos() < serverOp->getPos() && clientOp->getNewPos() > serverOp->getPos()) { result.first->setPos(result.first->getPos()-1); if (clientOp->getNewPos() >= serverOp->getNewPos()) { result.first->setNewPos(result.first->getNewPos()-1); } } else if (clientOp->getNewPos() >= serverOp->getNewPos()) { result.first->setNewPos(result.first->getNewPos()-1); } if (serverOp->getPos() < clientOp->getPos() && serverOp->getNewPos() > clientOp->getPos()) { result.second->setPos(result.second->getPos()-1); if (serverOp->getNewPos() >= clientOp->getNewPos()) { result.second->setNewPos(result.second->getNewPos()-1); } } else if (serverOp->getNewPos() >= clientOp->getNewPos()) { result.second->setNewPos(result.second->getNewPos()-1); } return result; } std::pair WhiteboardTransformer::transform(WhiteboardUpdateOperation::ref clientOp, WhiteboardInsertOperation::ref serverOp) { std::pair result; result.first = boost::make_shared(*serverOp); result.first->setParentID(clientOp->getID()); result.second = boost::make_shared(*clientOp); result.second->setParentID(serverOp->getID()); if (serverOp->getPos() <= clientOp->getPos()) { result.second->setPos(result.second->getPos()+1); } return result; } std::pair WhiteboardTransformer::transform(WhiteboardInsertOperation::ref clientOp, WhiteboardUpdateOperation::ref serverOp) { std::pair result; result.first = boost::make_shared(*serverOp); result.first->setParentID(clientOp->getID()); result.second = boost::make_shared(*clientOp); result.second->setParentID(serverOp->getID()); if (serverOp->getPos() >= clientOp->getPos()) { result.first->setPos(result.first->getPos()+1); } return result; } std::pair WhiteboardTransformer::transform(WhiteboardDeleteOperation::ref clientOp, WhiteboardDeleteOperation::ref serverOp) { std::pair result; result.first = boost::make_shared(*serverOp); result.first->setParentID(clientOp->getID()); result.second = boost::make_shared(*clientOp); result.second->setParentID(serverOp->getID()); if (clientOp->getPos() == -1) { result.second->setPos(-1); } if (serverOp->getPos() == -1) { result.first->setPos(-1); } if (clientOp->getPos() < serverOp->getPos()) { result.first->setPos(result.first->getPos()-1); } else if (clientOp->getPos() > serverOp->getPos()) { result.second->setPos(result.second->getPos()-1); } else { result.first->setPos(-1); result.second->setPos(-1); } return result; } std::pair WhiteboardTransformer::transform(WhiteboardInsertOperation::ref clientOp, WhiteboardDeleteOperation::ref serverOp) { std::pair result; result.first = boost::make_shared(*serverOp); result.first->setParentID(clientOp->getID()); result.second = boost::make_shared(*clientOp); result.second->setParentID(serverOp->getID()); if (clientOp->getPos() <= serverOp->getPos()) { result.first->setPos(result.first->getPos()+1); } else if (serverOp->getPos() != -1) { result.second->setPos(result.second->getPos()-1); } return result; } std::pair WhiteboardTransformer::transform(WhiteboardDeleteOperation::ref clientOp, WhiteboardInsertOperation::ref serverOp) { std::pair result; result.first = boost::make_shared(*serverOp); result.first->setParentID(clientOp->getID()); result.second = boost::make_shared(*clientOp); result.second->setParentID(serverOp->getID()); if (serverOp->getPos() <= clientOp->getPos()) { result.second->setPos(result.second->getPos()+1); } else if (clientOp->getPos() != -1) { result.first->setPos(result.first->getPos()-1); } return result; } std::pair WhiteboardTransformer::transform(WhiteboardUpdateOperation::ref clientOp, WhiteboardDeleteOperation::ref serverOp) { std::pair result; result.first = boost::make_shared(*serverOp); result.first->setParentID(clientOp->getID()); WhiteboardUpdateOperation::ref updateOp = boost::make_shared(*clientOp); result.second = updateOp; result.second->setParentID(serverOp->getID()); if (clientOp->getPos() == serverOp->getPos()) { WhiteboardDeleteOperation::ref deleteOp = boost::make_shared(); result.second = deleteOp; result.second->setPos(-1); result.second->setID(clientOp->getID()); result.second->setParentID(serverOp->getID()); deleteOp->setElementID(serverOp->getElementID()); } else if (clientOp->getPos() > serverOp->getPos() && clientOp->getNewPos() <= serverOp->getPos()) { result.second->setPos(result.second->getPos()-1); } else if (clientOp->getPos() < serverOp->getPos() && clientOp->getNewPos() >= serverOp->getPos()) { updateOp->setNewPos(updateOp->getNewPos()-1); } else if (clientOp->getPos() > serverOp->getPos()) { result.second->setPos(result.second->getPos()-1); updateOp->setNewPos(updateOp->getNewPos()-1); } return result; } std::pair WhiteboardTransformer::transform(WhiteboardDeleteOperation::ref clientOp, WhiteboardUpdateOperation::ref serverOp) { std::pair result; WhiteboardUpdateOperation::ref updateOp = boost::make_shared(*serverOp); result.first = updateOp; result.first->setParentID(clientOp->getID()); result.second = boost::make_shared(*clientOp); result.second->setParentID(serverOp->getID()); if (clientOp->getPos() == serverOp->getPos()) { WhiteboardDeleteOperation::ref deleteOp = boost::make_shared(); result.first = deleteOp; result.first->setPos(-1); result.first->setID(serverOp->getID()); result.first->setParentID(clientOp->getID()); deleteOp->setElementID(clientOp->getElementID()); } else if (clientOp->getPos() < serverOp->getPos() && clientOp->getPos() >= serverOp->getNewPos()) { result.first->setPos(result.first->getPos()-1); } else if (clientOp->getPos() > serverOp->getPos() && clientOp->getPos() <= serverOp->getNewPos()) { updateOp->setNewPos(updateOp->getNewPos()-1); } else if (clientOp->getPos() < serverOp->getPos()) { result.first->setPos(result.first->getPos()-1); updateOp->setNewPos(updateOp->getNewPos()-1); } return result; } } swift-im-2.0+dev6/Swiften/Whiteboard/OutgoingWhiteboardSession.cpp0000644000175000017500000000421112227051774025244 0ustar kismithkismith/* * Copyright (c) 2012 Mateusz Piękos * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include #include #include #include #include #include #include namespace Swift { OutgoingWhiteboardSession::OutgoingWhiteboardSession(const JID& jid, IQRouter* router) : WhiteboardSession(jid, router) { } OutgoingWhiteboardSession::~OutgoingWhiteboardSession() { } void OutgoingWhiteboardSession::startSession() { boost::shared_ptr payload = boost::make_shared(WhiteboardPayload::SessionRequest); boost::shared_ptr > request = boost::make_shared >(IQ::Set, toJID_, payload, router_); request->onResponse.connect(boost::bind(&OutgoingWhiteboardSession::handleRequestResponse, this, _1, _2)); request->send(); } void OutgoingWhiteboardSession::handleRequestResponse(boost::shared_ptr /*payload*/, ErrorPayload::ref error) { if (error) { onRequestRejected(toJID_); } } void OutgoingWhiteboardSession::handleIncomingOperation(WhiteboardOperation::ref operation) { WhiteboardOperation::ref op = server.handleClientOperationReceived(operation); if (op->getPos() != -1) { onOperationReceived(op); } lastOpID = op->getID(); WhiteboardPayload::ref payload = boost::make_shared(); payload->setOperation(op); sendPayload(payload); } void OutgoingWhiteboardSession::sendOperation(WhiteboardOperation::ref operation) { operation->setID(idGenerator.generateID()); operation->setParentID(lastOpID); lastOpID = operation->getID(); server.handleLocalOperationReceived(operation); WhiteboardPayload::ref payload = boost::make_shared(); payload->setOperation(operation); sendPayload(payload); } } swift-im-2.0+dev6/Swiften/Whiteboard/OutgoingWhiteboardSession.h0000644000175000017500000000167412227051774024723 0ustar kismithkismith/* * Copyright (c) 2012 Mateusz Piękos * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include #include #include #include namespace Swift { class SWIFTEN_API OutgoingWhiteboardSession : public WhiteboardSession { public: typedef boost::shared_ptr ref; public: OutgoingWhiteboardSession(const JID& jid, IQRouter* router); virtual ~OutgoingWhiteboardSession(); void startSession(); private: void handleRequestResponse(boost::shared_ptr /*payload*/, ErrorPayload::ref error); void handleIncomingOperation(WhiteboardOperation::ref operation); void sendOperation(WhiteboardOperation::ref operation); WhiteboardServer server; }; } swift-im-2.0+dev6/Swiften/Whiteboard/WhiteboardTransformer.h0000644000175000017500000000405212227051774024057 0ustar kismithkismith/* * Copyright (c) 2012 Mateusz Piękos * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include #include #include namespace Swift { class WhiteboardTransformer { public: static std::pair transform(WhiteboardOperation::ref clientOp, WhiteboardOperation::ref serverOp); static std::pair transform(WhiteboardInsertOperation::ref clientOp, WhiteboardInsertOperation::ref serverOp); static std::pair transform(WhiteboardUpdateOperation::ref clientOp, WhiteboardUpdateOperation::ref serverOp); static std::pair transform(WhiteboardUpdateOperation::ref clientOp, WhiteboardInsertOperation::ref serverOp); static std::pair transform(WhiteboardInsertOperation::ref clientOp, WhiteboardUpdateOperation::ref serverOp); static std::pair transform(WhiteboardDeleteOperation::ref clientOp, WhiteboardDeleteOperation::ref serverOp); static std::pair transform(WhiteboardInsertOperation::ref clientOp, WhiteboardDeleteOperation::ref serverOp); static std::pair transform(WhiteboardDeleteOperation::ref clientOp, WhiteboardInsertOperation::ref serverOp); static std::pair transform(WhiteboardUpdateOperation::ref clientOp, WhiteboardDeleteOperation::ref serverOp); static std::pair transform(WhiteboardDeleteOperation::ref clientOp, WhiteboardUpdateOperation::ref serverOp); }; } swift-im-2.0+dev6/Swiften/Whiteboard/WhiteboardResponder.h0000644000175000017500000000131012227051774023510 0ustar kismithkismith/* * Copyright (c) 2012 Mateusz Piękos * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include namespace Swift { class IQRouter; class WhiteboardSessionManager; class WhiteboardResponder : public SetResponder { public: WhiteboardResponder(WhiteboardSessionManager* sessionManager, IQRouter* router); bool handleSetRequest(const JID& from, const JID& /*to*/, const std::string& id, boost::shared_ptr payload); private: WhiteboardSessionManager* sessionManager_; IQRouter* router_; }; } swift-im-2.0+dev6/Swiften/AdHoc/0000755000175000017500000000000012227051774016260 5ustar kismithkismithswift-im-2.0+dev6/Swiften/AdHoc/SConscript0000644000175000017500000000023012227051774020265 0ustar kismithkismithImport("swiften_env") objects = swiften_env.SwiftenObject([ "OutgoingAdHocCommandSession.cpp", ]) swiften_env.Append(SWIFTEN_OBJECTS = [objects]) swift-im-2.0+dev6/Swiften/AdHoc/OutgoingAdHocCommandSession.cpp0000644000175000017500000000666212227051774024333 0ustar kismithkismith/* * Copyright (c) 2010-2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include namespace Swift { OutgoingAdHocCommandSession::OutgoingAdHocCommandSession(const JID& to, const std::string& commandNode, IQRouter* iqRouter) : to_(to), commandNode_(commandNode), iqRouter_(iqRouter), isMultiStage_(false) { } OutgoingAdHocCommandSession::~OutgoingAdHocCommandSession() { connection_.disconnect(); } void OutgoingAdHocCommandSession::handleResponse(boost::shared_ptr payload, ErrorPayload::ref error) { if (error) { onError(error); } else { const std::vector& actions = payload->getAvailableActions(); actionStates_.clear(); if (payload->getStatus() == Command::Executing ) { actionStates_[Command::Cancel] = EnabledAndPresent; actionStates_[Command::Complete] = Present; if (std::find(actions.begin(), actions.end(), Command::Complete) != actions.end()) { actionStates_[Command::Complete] = EnabledAndPresent; } if (getIsMultiStage()) { actionStates_[Command::Next] = Present; actionStates_[Command::Prev] = Present; } if (std::find(actions.begin(), actions.end(), Command::Next) != actions.end()) { actionStates_[Command::Next] = EnabledAndPresent; } if (std::find(actions.begin(), actions.end(), Command::Prev) != actions.end()) { actionStates_[Command::Prev] = EnabledAndPresent; } } sessionID_ = payload->getSessionID(); if (std::find(actions.begin(), actions.end(), Command::Next) != actions.end() || std::find(actions.begin(), actions.end(), Command::Prev) != actions.end()) { isMultiStage_ = true; } onNextStageReceived(payload); } } bool OutgoingAdHocCommandSession::getIsMultiStage() const { return isMultiStage_; } void OutgoingAdHocCommandSession::start() { boost::shared_ptr > commandRequest = boost::make_shared< GenericRequest >(IQ::Set, to_, boost::make_shared(commandNode_), iqRouter_); connection_ = commandRequest->onResponse.connect(boost::bind(&OutgoingAdHocCommandSession::handleResponse, this, _1, _2)); commandRequest->send(); } void OutgoingAdHocCommandSession::cancel() { if (!sessionID_.empty()) { submitForm(Form::ref(), Command::Cancel); } } void OutgoingAdHocCommandSession::goBack() { submitForm(Form::ref(), Command::Prev); } void OutgoingAdHocCommandSession::complete(Form::ref form) { submitForm(form, Command::Complete); } void OutgoingAdHocCommandSession::goNext(Form::ref form) { submitForm(form, Command::Next); } void OutgoingAdHocCommandSession::submitForm(Form::ref form, Command::Action action) { boost::shared_ptr command(boost::make_shared(commandNode_, sessionID_, action)); command->setForm(form); boost::shared_ptr > commandRequest = boost::make_shared< GenericRequest >(IQ::Set, to_, command, iqRouter_); connection_.disconnect(); connection_ = commandRequest->onResponse.connect(boost::bind(&OutgoingAdHocCommandSession::handleResponse, this, _1, _2)); commandRequest->send(); } OutgoingAdHocCommandSession::ActionState OutgoingAdHocCommandSession::getActionState(Command::Action action) const { return get(actionStates_, action, Absent); } } swift-im-2.0+dev6/Swiften/AdHoc/OutgoingAdHocCommandSession.h0000644000175000017500000000514112227051774023767 0ustar kismithkismith/* * Copyright (c) 2010-2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include #include #include #include #include #include namespace Swift { class IQRouter; class MainWindow; class UIEventStream; class SWIFTEN_API OutgoingAdHocCommandSession { public: /** * Availability of action. */ enum ActionState { Absent /** Action isn't applicable to this command. */ = 0, Present /** Action is applicable to this command */= 1, Enabled /** Action is applicable and currently available */ = 2, EnabledAndPresent = 3}; OutgoingAdHocCommandSession(const JID& to, const std::string& commandNode, IQRouter* iqRouter); ~OutgoingAdHocCommandSession(); /** * Send initial request to the target. */ void start(); /** * Cancel command session with the target. */ void cancel(); /** * Return to the previous stage. */ void goBack(); /** * Send the form to complete the command. * \param form Form for submission - if missing the command will be submitted with no form. */ void complete(Form::ref form); /** * Send the form to advance to the next stage of the command. * \param form Form for submission - if missing the command will be submitted with no form. */ void goNext(Form::ref form); /** * Is the form multi-stage? */ bool getIsMultiStage() const; /** * Emitted when the form for the next stage is available. */ boost::signal onNextStageReceived; /** * Emitted on error. */ boost::signal onError; /** * Get the state of a given action. * This is useful for a UI to determine which buttons should be visible, * and which enabled. * Use for Next, Prev, Cancel and Complete only. * If no actions are available, the command has completed. */ ActionState getActionState(Command::Action action) const; private: void handleResponse(boost::shared_ptr payload, ErrorPayload::ref error); void submitForm(Form::ref, Command::Action action); private: JID to_; std::string commandNode_; IQRouter* iqRouter_; bool isMultiStage_; std::string sessionID_; std::map actionStates_; boost::bsignals::connection connection_; }; } swift-im-2.0+dev6/Swiften/FileTransfer/0000755000175000017500000000000012227051774017666 5ustar kismithkismithswift-im-2.0+dev6/Swiften/FileTransfer/SOCKS5BytestreamServerSession.h0000644000175000017500000000346712227051774025613 0ustar kismithkismith/* * 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 #include #include #include #include #include #include namespace Swift { class SOCKS5BytestreamRegistry; class SWIFTEN_API SOCKS5BytestreamServerSession { public: typedef boost::shared_ptr ref; public: enum State { Initial, WaitingForAuthentication, WaitingForRequest, ReadyForTransfer, ReadingData, WritingData, Finished, }; SOCKS5BytestreamServerSession(boost::shared_ptr connection, SOCKS5BytestreamRegistry* registry); ~SOCKS5BytestreamServerSession(); void setChunkSize(int chunkSize) { this->chunkSize = chunkSize; } void start(); void stop(); void startTransfer(); HostAddressPort getAddressPort() const; boost::signal)> onFinished; boost::signal onBytesSent; boost::signal onBytesReceived; private: void finish(bool error); void process(); void handleDataRead(boost::shared_ptr); void handleDisconnected(const boost::optional&); void handleDataAvailable(); void sendData(); private: boost::shared_ptr connection; SOCKS5BytestreamRegistry* bytestreams; ByteArray unprocessedData; State state; int chunkSize; boost::shared_ptr readBytestream; boost::shared_ptr writeBytestream; bool waitingForData; }; } swift-im-2.0+dev6/Swiften/FileTransfer/SConscript0000644000175000017500000000327112227051774021703 0ustar kismithkismithImport("swiften_env", "env") sources = [ "OutgoingFileTransfer.cpp", "OutgoingSIFileTransfer.cpp", "OutgoingJingleFileTransfer.cpp", "OutgoingFileTransferManager.cpp", "IncomingFileTransfer.cpp", "IncomingJingleFileTransfer.cpp", "IncomingFileTransferManager.cpp", "RemoteJingleTransportCandidateSelector.cpp", "RemoteJingleTransportCandidateSelectorFactory.cpp", "LocalJingleTransportCandidateGenerator.cpp", "LocalJingleTransportCandidateGeneratorFactory.cpp", "DefaultRemoteJingleTransportCandidateSelectorFactory.cpp", "DefaultLocalJingleTransportCandidateGeneratorFactory.cpp", "DefaultRemoteJingleTransportCandidateSelector.cpp", "DefaultLocalJingleTransportCandidateGenerator.cpp", "JingleTransport.cpp", "JingleIncomingIBBTransport.cpp", "ReadBytestream.cpp", "WriteBytestream.cpp", "FileReadBytestream.cpp", "FileWriteBytestream.cpp", "SOCKS5BytestreamClientSession.cpp", "SOCKS5BytestreamServer.cpp", "SOCKS5BytestreamServerSession.cpp", "SOCKS5BytestreamRegistry.cpp", "SOCKS5BytestreamProxy.cpp", "SOCKS5BytestreamProxyFinder.cpp", "IBBSendSession.cpp", "IBBReceiveSession.cpp", "FileTransferManager.cpp", "FileTransferManagerImpl.cpp", "IncrementalBytestreamHashCalculator.cpp", "ConnectivityManager.cpp", ] swiften_env.Append(SWIFTEN_OBJECTS = swiften_env.SwiftenObject(sources)) env.Append(UNITTEST_SOURCES = [ File("UnitTest/SOCKS5BytestreamServerSessionTest.cpp"), File("UnitTest/SOCKS5BytestreamClientSessionTest.cpp"), File("UnitTest/IBBSendSessionTest.cpp"), File("UnitTest/IBBReceiveSessionTest.cpp"), File("UnitTest/IncomingJingleFileTransferTest.cpp"), File("UnitTest/OutgoingJingleFileTransferTest.cpp"), ]) swift-im-2.0+dev6/Swiften/FileTransfer/FileTransferManager.cpp0000644000175000017500000000044012227051774024247 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include namespace Swift { FileTransferManager::~FileTransferManager() { } } swift-im-2.0+dev6/Swiften/FileTransfer/RemoteJingleTransportCandidateSelectorFactory.cpp0000644000175000017500000000055312227051774031524 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { RemoteJingleTransportCandidateSelectorFactory::~RemoteJingleTransportCandidateSelectorFactory() { } } swift-im-2.0+dev6/Swiften/FileTransfer/SOCKS5BytestreamRegistry.h0000644000175000017500000000410012227051774024572 0ustar kismithkismith/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Swift { class SWIFTEN_API SOCKS5BytestreamRegistry { public: SOCKS5BytestreamRegistry(); boost::shared_ptr getReadBytestream(const std::string& destination) const; void addReadBytestream(const std::string& destination, boost::shared_ptr byteStream); void removeReadBytestream(const std::string& destination); boost::shared_ptr getWriteBytestream(const std::string& destination) const; void addWriteBytestream(const std::string& destination, boost::shared_ptr byteStream); void removeWriteBytestream(const std::string& destination); /** * Generate a new session ID to use for new S5B streams. */ std::string generateSessionID(); /** * Start an actual transfer. */ SOCKS5BytestreamServerSession* getConnectedSession(const std::string& destination); public: static std::string getHostname(const std::string& sessionID, const JID& requester, const JID& target); private: friend class SOCKS5BytestreamServerSession; typedef std::map > ReadBytestreamMap; ReadBytestreamMap readBytestreams; typedef std::map > WriteBytestreamMap; WriteBytestreamMap writeBytestreams; std::map serverSessions; IDGenerator idGenerator; }; } swift-im-2.0+dev6/Swiften/FileTransfer/OutgoingFileTransferManager.h0000644000175000017500000000300212227051774025425 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include namespace Swift { class JingleSessionManager; class IQRouter; class EntityCapsProvider; class RemoteJingleTransportCandidateSelectorFactory; class LocalJingleTransportCandidateGeneratorFactory; class OutgoingFileTransfer; class JID; class IDGenerator; class ReadBytestream; class StreamInitiationFileInfo; class SOCKS5BytestreamRegistry; class SOCKS5BytestreamProxy; class OutgoingFileTransferManager { public: OutgoingFileTransferManager(JingleSessionManager* jingleSessionManager, IQRouter* router, EntityCapsProvider* capsProvider, RemoteJingleTransportCandidateSelectorFactory* remoteFactory, LocalJingleTransportCandidateGeneratorFactory* localFactory, SOCKS5BytestreamRegistry* bytestreamRegistry, SOCKS5BytestreamProxy* bytestreamProxy); ~OutgoingFileTransferManager(); boost::shared_ptr createOutgoingFileTransfer(const JID& from, const JID& to, boost::shared_ptr, const StreamInitiationFileInfo&); private: JingleSessionManager* jsManager; IQRouter* iqRouter; EntityCapsProvider* capsProvider; RemoteJingleTransportCandidateSelectorFactory* remoteFactory; LocalJingleTransportCandidateGeneratorFactory* localFactory; IDGenerator *idGenerator; SOCKS5BytestreamRegistry* bytestreamRegistry; SOCKS5BytestreamProxy* bytestreamProxy; }; } swift-im-2.0+dev6/Swiften/FileTransfer/JingleTransport.cpp0000644000175000017500000000042212227051774023515 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { JingleTransport::~JingleTransport() { } } swift-im-2.0+dev6/Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelectorFactory.h0000644000175000017500000000136412227051774032477 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include namespace Swift { class ConnectionFactory; class TimerFactory; class DefaultRemoteJingleTransportCandidateSelectorFactory : public RemoteJingleTransportCandidateSelectorFactory { public: DefaultRemoteJingleTransportCandidateSelectorFactory(ConnectionFactory*, TimerFactory*); virtual ~DefaultRemoteJingleTransportCandidateSelectorFactory(); RemoteJingleTransportCandidateSelector* createCandidateSelector(); private: ConnectionFactory* connectionFactory; TimerFactory* timerFactory; }; } swift-im-2.0+dev6/Swiften/FileTransfer/StreamInitiationRequest.h0000644000175000017500000000225612227051774024700 0ustar kismithkismith/* * 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 #include namespace Swift { class StreamInitiationRequest : public GenericRequest { public: typedef boost::shared_ptr ref; static ref create(const JID& jid, boost::shared_ptr payload, IQRouter* router) { return ref(new StreamInitiationRequest(jid, payload, router)); } static ref create(const JID& from, const JID& to, boost::shared_ptr payload, IQRouter* router) { return ref(new StreamInitiationRequest(from, to, payload, router)); } private: StreamInitiationRequest(const JID& jid, boost::shared_ptr payload, IQRouter* router) : GenericRequest(IQ::Set, jid, payload, router) { } StreamInitiationRequest(const JID& from, const JID& to, boost::shared_ptr payload, IQRouter* router) : GenericRequest(IQ::Set, from, to, payload, router) { } }; } swift-im-2.0+dev6/Swiften/FileTransfer/JingleTransport.h0000644000175000017500000000120412227051774023161 0ustar kismithkismith/* * 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 #include #include namespace Swift { class JingleTransport { public: typedef boost::shared_ptr ref; virtual ~JingleTransport(); virtual void start() = 0; virtual void stop() = 0; boost::signal&)> onDataReceived; boost::signal)> onFinished; }; } swift-im-2.0+dev6/Swiften/FileTransfer/IncomingFileTransferManager.cpp0000644000175000017500000000505012227051774025735 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include namespace Swift { IncomingFileTransferManager::IncomingFileTransferManager(JingleSessionManager* jingleSessionManager, IQRouter* router, RemoteJingleTransportCandidateSelectorFactory* remoteFactory, LocalJingleTransportCandidateGeneratorFactory* localFactory, SOCKS5BytestreamRegistry* bytestreamRegistry, SOCKS5BytestreamProxy* bytestreamProxy, TimerFactory* timerFactory) : jingleSessionManager(jingleSessionManager), router(router), remoteFactory(remoteFactory), localFactory(localFactory), bytestreamRegistry(bytestreamRegistry), bytestreamProxy(bytestreamProxy), timerFactory(timerFactory) { jingleSessionManager->addIncomingSessionHandler(this); } IncomingFileTransferManager::~IncomingFileTransferManager() { jingleSessionManager->removeIncomingSessionHandler(this); } bool IncomingFileTransferManager::handleIncomingJingleSession(JingleSession::ref session, const std::vector& contents, const JID& recipient) { if (JingleContentPayload::ref content = Jingle::getContentWithDescription(contents)) { if (content->getTransport() || content->getTransport()) { JingleFileTransferDescription::ref description = content->getDescription(); if (description && description->getOffers().size() == 1) { IncomingJingleFileTransfer::ref transfer = boost::make_shared(recipient, session, content, remoteFactory, localFactory, router, bytestreamRegistry, bytestreamProxy, timerFactory); onIncomingFileTransfer(transfer); } else { std::cerr << "Received a file-transfer request with no description or more than one file!" << std::endl; session->sendTerminate(JinglePayload::Reason::FailedApplication); } } else { session->sendTerminate(JinglePayload::Reason::UnsupportedTransports); } return true; } else { return false; } } } swift-im-2.0+dev6/Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.h0000644000175000017500000000232512227051774027640 0ustar kismithkismith/* * 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 #include #include #include #include #include namespace Swift { class SWIFTEN_API RemoteJingleTransportCandidateSelector { public: virtual ~RemoteJingleTransportCandidateSelector(); virtual void addRemoteTransportCandidates(JingleTransportPayload::ref) = 0; virtual void selectCandidate() = 0; virtual void setMinimumPriority(int) = 0; virtual void setRequesterTargtet(const JID&, const JID&) {} virtual SOCKS5BytestreamClientSession::ref getS5BSession() { return SOCKS5BytestreamClientSession::ref(); } virtual bool isActualCandidate(JingleTransportPayload::ref) = 0; virtual int getPriority(JingleTransportPayload::ref) = 0; virtual JingleTransport::ref selectTransport(JingleTransportPayload::ref) = 0; boost::signal onRemoteTransportCandidateSelectFinished; }; } swift-im-2.0+dev6/Swiften/FileTransfer/FileTransferManagerImpl.cpp0000644000175000017500000001502112227051774025072 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include #include #include #include #include "Swiften/Disco/EntityCapsProvider.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Swift { FileTransferManagerImpl::FileTransferManagerImpl(const JID& ownFullJID, JingleSessionManager* jingleSessionManager, IQRouter* router, EntityCapsProvider* capsProvider, PresenceOracle* presOracle, ConnectionFactory* connectionFactory, ConnectionServerFactory* connectionServerFactory, TimerFactory* timerFactory, NATTraverser* natTraverser) : ownJID(ownFullJID), jingleSM(jingleSessionManager), iqRouter(router), capsProvider(capsProvider), presenceOracle(presOracle), connectionServerFactory(connectionServerFactory), bytestreamServer(NULL), s5bProxyFinder(NULL) { assert(!ownFullJID.isBare()); connectivityManager = new ConnectivityManager(natTraverser); bytestreamRegistry = new SOCKS5BytestreamRegistry(); bytestreamProxy = new SOCKS5BytestreamProxy(connectionFactory, timerFactory); localCandidateGeneratorFactory = new DefaultLocalJingleTransportCandidateGeneratorFactory(connectivityManager, bytestreamRegistry, bytestreamProxy, ownFullJID); remoteCandidateSelectorFactory = new DefaultRemoteJingleTransportCandidateSelectorFactory(connectionFactory, timerFactory); outgoingFTManager = new OutgoingFileTransferManager(jingleSM, iqRouter, capsProvider, remoteCandidateSelectorFactory, localCandidateGeneratorFactory, bytestreamRegistry, bytestreamProxy); incomingFTManager = new IncomingFileTransferManager(jingleSM, iqRouter, remoteCandidateSelectorFactory, localCandidateGeneratorFactory, bytestreamRegistry, bytestreamProxy, timerFactory); incomingFTManager->onIncomingFileTransfer.connect(onIncomingFileTransfer); } FileTransferManagerImpl::~FileTransferManagerImpl() { if (s5bProxyFinder) { s5bProxyFinder->stop(); delete s5bProxyFinder; } if (bytestreamServer) { bytestreamServer->stop(); delete bytestreamServer; } delete incomingFTManager; delete outgoingFTManager; delete remoteCandidateSelectorFactory; delete localCandidateGeneratorFactory; delete connectivityManager; } void FileTransferManagerImpl::startListeningOnPort(int port) { // TODO: create a server for each interface we're on SWIFT_LOG(debug) << "Start listening on port " << port << " and hope it's not in use." << std::endl; boost::shared_ptr server = connectionServerFactory->createConnectionServer(HostAddress("0.0.0.0"), port); server->start(); bytestreamServer = new SOCKS5BytestreamServer(server, bytestreamRegistry); bytestreamServer->start(); connectivityManager->addListeningPort(port); s5bProxyFinder = new SOCKS5BytestreamProxyFinder(ownJID.getDomain(), iqRouter); s5bProxyFinder->onProxyFound.connect(boost::bind(&FileTransferManagerImpl::addS5BProxy, this, _1)); s5bProxyFinder->start(); } void FileTransferManagerImpl::addS5BProxy(S5BProxyRequest::ref proxy) { bytestreamProxy->addS5BProxy(proxy); } boost::optional FileTransferManagerImpl::highestPriorityJIDSupportingFileTransfer(const JID& bareJID) { JID fullReceipientJID; int priority = INT_MIN; //getAllPresence(bareJID) gives you all presences for the bare JID (i.e. all resources) Remko Tronçon @ 11:11 std::vector presences = presenceOracle->getAllPresence(bareJID); //iterate over them foreach(Presence::ref pres, presences) { if (pres->getPriority() > priority) { // look up caps from the jid DiscoInfo::ref info = capsProvider->getCaps(pres->getFrom()); if (info && info->hasFeature(DiscoInfo::JingleFeature) && info->hasFeature(DiscoInfo::JingleFTFeature) && (info->hasFeature(DiscoInfo::JingleTransportsIBBFeature) || info->hasFeature(DiscoInfo::JingleTransportsS5BFeature))) { priority = pres->getPriority(); fullReceipientJID = pres->getFrom(); } } } return fullReceipientJID.isValid() ? boost::optional(fullReceipientJID) : boost::optional(); } OutgoingFileTransfer::ref FileTransferManagerImpl::createOutgoingFileTransfer(const JID& to, const boost::filesystem::path& filepath, const std::string& description, boost::shared_ptr bytestream) { #if BOOST_FILESYSTEM_VERSION == 2 // TODO: Delete this when boost 1.44 becomes a minimum requirement, and we no longer need v2 std::string filename = filepath.filename(); #else std::string filename = filepath.filename().string(); #endif boost::uintmax_t sizeInBytes = boost::filesystem::file_size(filepath); boost::posix_time::ptime lastModified = boost::posix_time::from_time_t(boost::filesystem::last_write_time(filepath)); return createOutgoingFileTransfer(to, filename, description, sizeInBytes, lastModified, bytestream); } OutgoingFileTransfer::ref FileTransferManagerImpl::createOutgoingFileTransfer(const JID& to, const std::string& filename, const std::string& description, const boost::uintmax_t sizeInBytes, const boost::posix_time::ptime& lastModified, boost::shared_ptr bytestream) { StreamInitiationFileInfo fileInfo; fileInfo.setDate(lastModified); fileInfo.setSize(sizeInBytes); fileInfo.setName(filename); fileInfo.setDescription(description); JID receipient = to; if(receipient.isBare()) { boost::optional fullJID = highestPriorityJIDSupportingFileTransfer(receipient); if (fullJID.is_initialized()) { receipient = fullJID.get(); } else { return OutgoingFileTransfer::ref(); } } return outgoingFTManager->createOutgoingFileTransfer(ownJID, receipient, bytestream, fileInfo); } } swift-im-2.0+dev6/Swiften/FileTransfer/IncrementalBytestreamHashCalculator.h0000644000175000017500000000121212227051774027152 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include namespace Swift { class MD5; class SHA1; class IncrementalBytestreamHashCalculator { public: IncrementalBytestreamHashCalculator(bool doMD5, bool doSHA1); ~IncrementalBytestreamHashCalculator(); void feedData(const ByteArray& data); //void feedData(const SafeByteArray& data); std::string getSHA1String(); std::string getMD5String(); private: MD5* md5Hasher; SHA1* sha1Hasher; }; } swift-im-2.0+dev6/Swiften/FileTransfer/IBBReceiveSession.cpp0000644000175000017500000000637012227051774023643 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include namespace Swift { class IBBReceiveSession::IBBResponder : public SetResponder { public: IBBResponder(IBBReceiveSession* session, IQRouter* router) : SetResponder(router), session(session), sequenceNumber(0), receivedSize(0) { } virtual bool handleSetRequest(const JID& from, const JID&, const std::string& id, IBB::ref ibb) { if (from == session->from && ibb->getStreamID() == session->id) { if (ibb->getAction() == IBB::Data) { if (sequenceNumber == ibb->getSequenceNumber()) { session->onDataReceived(ibb->getData()); receivedSize += ibb->getData().size(); sequenceNumber++; sendResponse(from, id, IBB::ref()); if (receivedSize >= session->size) { if (receivedSize > session->size) { std::cerr << "Warning: Received more data than expected" << std::endl; } session->finish(boost::optional()); } } else { SWIFT_LOG(warning) << "Received data out of order" << std::endl; sendError(from, id, ErrorPayload::NotAcceptable, ErrorPayload::Cancel); session->finish(FileTransferError(FileTransferError::ClosedError)); } } else if (ibb->getAction() == IBB::Open) { SWIFT_LOG(debug) << "IBB open received" << std::endl; sendResponse(from, id, IBB::ref()); } else if (ibb->getAction() == IBB::Close) { SWIFT_LOG(debug) << "IBB close received" << std::endl; sendResponse(from, id, IBB::ref()); session->finish(FileTransferError(FileTransferError::ClosedError)); } return true; } SWIFT_LOG(debug) << "wrong from/sessionID: " << from << " == " << session->from << " / " <getStreamID() << " == " << session->id << std::endl; return false; } private: IBBReceiveSession* session; int sequenceNumber; size_t receivedSize; }; IBBReceiveSession::IBBReceiveSession( const std::string& id, const JID& from, const JID& to, size_t size, IQRouter* router) : id(id), from(from), to(to), size(size), router(router), active(false) { assert(!id.empty()); assert(from.isValid()); responder = new IBBResponder(this, router); } IBBReceiveSession::~IBBReceiveSession() { if (active) { SWIFT_LOG(warning) << "Session still active" << std::endl; } delete responder; } void IBBReceiveSession::start() { SWIFT_LOG(debug) << "receive session started" << std::endl; active = true; responder->start(); } void IBBReceiveSession::stop() { SWIFT_LOG(debug) << "receive session stopped" << std::endl; responder->stop(); if (active) { if (router->isAvailable()) { IBBRequest::create(to, from, IBB::createIBBClose(id), router)->send(); } finish(boost::optional()); } } void IBBReceiveSession::finish(boost::optional error) { active = false; onFinished(error); } } swift-im-2.0+dev6/Swiften/FileTransfer/FileReadBytestream.cpp0000644000175000017500000000211612227051774024105 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include namespace Swift { FileReadBytestream::FileReadBytestream(const boost::filesystem::path& file) : file(file), stream(NULL) { } FileReadBytestream::~FileReadBytestream() { if (stream) { stream->close(); stream = NULL; } } boost::shared_ptr FileReadBytestream::read(size_t size) { if (!stream) { stream = new boost::filesystem::ifstream(file, std::ios_base::in|std::ios_base::binary); } boost::shared_ptr result = boost::make_shared(); result->resize(size); assert(stream->good()); stream->read(reinterpret_cast(vecptr(*result)), size); result->resize(stream->gcount()); onRead(*result); return result; } bool FileReadBytestream::isFinished() const { return stream && !stream->good(); } } swift-im-2.0+dev6/Swiften/FileTransfer/BytestreamException.h0000644000175000017500000000047312227051774024041 0ustar kismithkismith/* * 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 namespace Swift { class BytestreamException : public std::exception { public: BytestreamException() { } }; } swift-im-2.0+dev6/Swiften/FileTransfer/FileTransferManager.h0000644000175000017500000000235612227051774023724 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include #include #include #include #include #include #include namespace Swift { class ReadBytestream; class S5BProxyRequest; class SWIFTEN_API FileTransferManager { public: virtual ~FileTransferManager(); virtual void startListeningOnPort(int port) = 0; virtual OutgoingFileTransfer::ref createOutgoingFileTransfer(const JID& to, const boost::filesystem::path& filepath, const std::string& description, boost::shared_ptr bytestream) = 0; virtual OutgoingFileTransfer::ref createOutgoingFileTransfer(const JID& to, const std::string& filename, const std::string& description, const boost::uintmax_t sizeInBytes, const boost::posix_time::ptime& lastModified, boost::shared_ptr bytestream) = 0; boost::signal onIncomingFileTransfer; }; } swift-im-2.0+dev6/Swiften/FileTransfer/ReadBytestream.h0000644000175000017500000000143112227051774022751 0ustar kismithkismith/* * 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 #include #include #include namespace Swift { class SWIFTEN_API ReadBytestream { public: virtual ~ReadBytestream(); /** * Return an empty vector if no more data is available. * Use onDataAvailable signal for signaling there is data available again. */ virtual boost::shared_ptr< std::vector > read(size_t size) = 0; virtual bool isFinished() const = 0; public: boost::signal onDataAvailable; boost::signal&)> onRead; }; } swift-im-2.0+dev6/Swiften/FileTransfer/FileTransferManagerImpl.h0000644000175000017500000000566012227051774024547 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include #include #include #include #include #include #include #include #include #include namespace Swift { class Client; class ConnectionFactory; class ConnectionServerFactory; class ConnectivityManager; class EntityCapsProvider; class IQRouter; class IncomingFileTransferManager; class JingleSessionManager; class LocalJingleTransportCandidateGeneratorFactory; class OutgoingFileTransferManager; class NATTraverser; class PresenceOracle; class ReadBytestream; class RemoteJingleTransportCandidateSelectorFactory; class SOCKS5BytestreamRegistry; class SOCKS5BytestreamServer; class SOCKS5BytestreamProxy; class TimerFactory; class SOCKS5BytestreamProxyFinder; class FileTransferManagerImpl : public FileTransferManager { public: FileTransferManagerImpl(const JID& ownFullJID, JingleSessionManager* jingleSessionManager, IQRouter* router, EntityCapsProvider* capsProvider, PresenceOracle* presOracle, ConnectionFactory* connectionFactory, ConnectionServerFactory* connectionServerFactory, TimerFactory* timerFactory, NATTraverser* natTraverser); ~FileTransferManagerImpl(); void startListeningOnPort(int port); void addS5BProxy(S5BProxyRequest::ref proxy); OutgoingFileTransfer::ref createOutgoingFileTransfer(const JID& to, const boost::filesystem::path& filepath, const std::string& description, boost::shared_ptr bytestream); OutgoingFileTransfer::ref createOutgoingFileTransfer(const JID& to, const std::string& filename, const std::string& description, const boost::uintmax_t sizeInBytes, const boost::posix_time::ptime& lastModified, boost::shared_ptr bytestream); private: boost::optional highestPriorityJIDSupportingFileTransfer(const JID& bareJID); private: JID ownJID; OutgoingFileTransferManager* outgoingFTManager; IncomingFileTransferManager* incomingFTManager; RemoteJingleTransportCandidateSelectorFactory* remoteCandidateSelectorFactory; LocalJingleTransportCandidateGeneratorFactory* localCandidateGeneratorFactory; JingleSessionManager* jingleSM; IQRouter* iqRouter; EntityCapsProvider* capsProvider; PresenceOracle* presenceOracle; ConnectionServerFactory* connectionServerFactory; SOCKS5BytestreamRegistry* bytestreamRegistry; SOCKS5BytestreamServer* bytestreamServer; SOCKS5BytestreamProxy* bytestreamProxy; ConnectivityManager* connectivityManager; SOCKS5BytestreamProxyFinder* s5bProxyFinder; }; } swift-im-2.0+dev6/Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.cpp0000644000175000017500000000052612227051774030174 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { RemoteJingleTransportCandidateSelector::~RemoteJingleTransportCandidateSelector() { } } swift-im-2.0+dev6/Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelector.h0000644000175000017500000000342112227051774031143 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include #include #include #include #include #include #include namespace Swift { class ConnectionFactory; class TimerFactory; class DefaultRemoteJingleTransportCandidateSelector : public RemoteJingleTransportCandidateSelector { public: DefaultRemoteJingleTransportCandidateSelector(ConnectionFactory*, TimerFactory*); virtual ~DefaultRemoteJingleTransportCandidateSelector(); virtual void addRemoteTransportCandidates(JingleTransportPayload::ref); virtual void selectCandidate(); virtual void setMinimumPriority(int); void setRequesterTargtet(const JID& requester, const JID& target); virtual SOCKS5BytestreamClientSession::ref getS5BSession(); virtual bool isActualCandidate(JingleTransportPayload::ref); virtual int getPriority(JingleTransportPayload::ref); virtual JingleTransport::ref selectTransport(JingleTransportPayload::ref); private: void tryNextCandidate(bool error); private: ConnectionFactory* connectionFactory; TimerFactory* timerFactory; std::priority_queue, JingleS5BTransportPayload::CompareCandidate> candidates; std::string transportSID; boost::shared_ptr connection; boost::shared_ptr s5bSession; JingleS5BTransportPayload::Candidate lastCandidate; JID requester; JID target; }; } swift-im-2.0+dev6/Swiften/FileTransfer/ByteArrayReadBytestream.h0000644000175000017500000000246112227051774024600 0ustar kismithkismith/* * 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 #include #include #include #include namespace Swift { class ByteArrayReadBytestream : public ReadBytestream { public: ByteArrayReadBytestream(const std::vector& data) : data(data), position(0), dataComplete(true) { } virtual boost::shared_ptr read(size_t size) { size_t readSize = size; if (position + readSize > data.size()) { readSize = data.size() - position; } boost::shared_ptr result = boost::make_shared(data.begin() + position, data.begin() + position + readSize); onRead(*result); position += readSize; return result; } virtual bool isFinished() const { return position >= data.size() && dataComplete; } virtual void setDataComplete(bool b) { dataComplete = b; } void addData(const std::vector& moreData) { append(data, moreData); onDataAvailable(); } private: std::vector data; size_t position; bool dataComplete; }; } swift-im-2.0+dev6/Swiften/FileTransfer/ConnectivityManager.h0000644000175000017500000000204412227051774024010 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include #include #include #include #include #include namespace Swift { class NATTraverser; class ConnectivityManager { public: ConnectivityManager(NATTraverser*); ~ConnectivityManager(); public: void addListeningPort(int port); void removeListeningPort(int port); std::vector getHostAddressPorts() const; std::vector getAssistedHostAddressPorts() const; private: void natTraversalGetPublicIPResult(boost::optional address); void natTraversalForwardPortResult(boost::optional mapping); private: NATTraverser* natTraversalWorker; std::set ports; boost::optional publicAddress; }; } swift-im-2.0+dev6/Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGeneratorFactory.cpp0000644000175000017500000000205112227051774032771 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include "DefaultLocalJingleTransportCandidateGeneratorFactory.h" #include #include namespace Swift { DefaultLocalJingleTransportCandidateGeneratorFactory::DefaultLocalJingleTransportCandidateGeneratorFactory(ConnectivityManager* connectivityManager, SOCKS5BytestreamRegistry* s5bRegistry, SOCKS5BytestreamProxy* s5bProxy, const JID& ownJID) : connectivityManager(connectivityManager), s5bRegistry(s5bRegistry), s5bProxy(s5bProxy), ownJID(ownJID) { } DefaultLocalJingleTransportCandidateGeneratorFactory::~DefaultLocalJingleTransportCandidateGeneratorFactory() { } LocalJingleTransportCandidateGenerator* DefaultLocalJingleTransportCandidateGeneratorFactory::createCandidateGenerator() { return new DefaultLocalJingleTransportCandidateGenerator(connectivityManager, s5bRegistry, s5bProxy, ownJID); } } swift-im-2.0+dev6/Swiften/FileTransfer/RemoteJingleTransportCandidateSelectorFactory.h0000644000175000017500000000076212227051774031173 0ustar kismithkismith/* * 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 namespace Swift { class RemoteJingleTransportCandidateSelector; class SWIFTEN_API RemoteJingleTransportCandidateSelectorFactory { public: virtual ~RemoteJingleTransportCandidateSelectorFactory(); virtual RemoteJingleTransportCandidateSelector* createCandidateSelector() = 0; }; } swift-im-2.0+dev6/Swiften/FileTransfer/FileReadBytestream.h0000644000175000017500000000131412227051774023551 0ustar kismithkismith/* * 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 #include #include #include namespace Swift { class SWIFTEN_API FileReadBytestream : public ReadBytestream { public: FileReadBytestream(const boost::filesystem::path& file); ~FileReadBytestream(); virtual boost::shared_ptr< std::vector > read(size_t size); virtual bool isFinished() const; private: boost::filesystem::path file; boost::filesystem::ifstream* stream; }; } swift-im-2.0+dev6/Swiften/FileTransfer/LocalJingleTransportCandidateGeneratorFactory.cpp0000644000175000017500000000055312227051774031471 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { LocalJingleTransportCandidateGeneratorFactory::~LocalJingleTransportCandidateGeneratorFactory() { } } swift-im-2.0+dev6/Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGeneratorFactory.h0000644000175000017500000000173612227051774032447 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include namespace Swift { class ConnectivityManager; class SOCKS5BytestreamRegistry; class SOCKS5BytestreamProxy; class DefaultLocalJingleTransportCandidateGeneratorFactory : public LocalJingleTransportCandidateGeneratorFactory{ public: DefaultLocalJingleTransportCandidateGeneratorFactory(ConnectivityManager* connectivityManager, SOCKS5BytestreamRegistry* s5bRegistry, SOCKS5BytestreamProxy* s5bProxy, const JID& ownJID); virtual ~DefaultLocalJingleTransportCandidateGeneratorFactory(); LocalJingleTransportCandidateGenerator* createCandidateGenerator(); private: ConnectivityManager* connectivityManager; SOCKS5BytestreamRegistry* s5bRegistry; SOCKS5BytestreamProxy* s5bProxy; JID ownJID; }; } swift-im-2.0+dev6/Swiften/FileTransfer/IBBReceiveSession.h0000644000175000017500000000255512227051774023311 0ustar kismithkismith/* * 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 #include #include #include #include #include #include #include namespace Swift { class IQRouter; class SWIFTEN_API IBBReceiveSession { public: IBBReceiveSession( const std::string& id, const JID& from, const JID& to, size_t size, IQRouter* router); ~IBBReceiveSession(); void start(); void stop(); const JID& getSender() const { return from; } const JID& getReceiver() const { return to; } boost::signal&)> onDataReceived; boost::signal)> onFinished; private: bool handleSetRequest(const JID& from, const JID& to, const std::string& id, IBB::ref payload); void finish(boost::optional); private: class IBBResponder; friend class IBBResponder; std::string id; JID from; JID to; size_t size; IQRouter* router; IBBResponder* responder; bool active; }; } swift-im-2.0+dev6/Swiften/FileTransfer/ReadBytestream.cpp0000644000175000017500000000041612227051774023306 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { ReadBytestream::~ReadBytestream() { } } swift-im-2.0+dev6/Swiften/FileTransfer/SOCKS5BytestreamProxyFinder.h0000644000175000017500000000200112227051774025231 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include #include #include namespace Swift { class JID; class IQRouter; class SOCKS5BytestreamProxyFinder { public: SOCKS5BytestreamProxyFinder(const JID& service, IQRouter *iqRouter); ~SOCKS5BytestreamProxyFinder(); void start(); void stop(); boost::signal)> onProxyFound; private: void sendBytestreamQuery(const JID&); void handleServiceFound(const JID&, boost::shared_ptr); void handleProxyResponse(boost::shared_ptr, ErrorPayload::ref); private: JID service; IQRouter* iqRouter; boost::shared_ptr serviceWalker; std::vector > > requests; }; } swift-im-2.0+dev6/Swiften/FileTransfer/SOCKS5BytestreamProxyFinder.cpp0000644000175000017500000000403312227051774025573 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include #include #include #include #include #include #include namespace Swift { SOCKS5BytestreamProxyFinder::SOCKS5BytestreamProxyFinder(const JID& service, IQRouter *iqRouter) : service(service), iqRouter(iqRouter) { } SOCKS5BytestreamProxyFinder::~SOCKS5BytestreamProxyFinder() { } void SOCKS5BytestreamProxyFinder::start() { serviceWalker = boost::make_shared(service, iqRouter); serviceWalker->onServiceFound.connect(boost::bind(&SOCKS5BytestreamProxyFinder::handleServiceFound, this, _1, _2)); serviceWalker->beginWalk(); } void SOCKS5BytestreamProxyFinder::stop() { serviceWalker->endWalk(); serviceWalker->onServiceFound.disconnect(boost::bind(&SOCKS5BytestreamProxyFinder::handleServiceFound, this, _1, _2)); serviceWalker.reset(); } void SOCKS5BytestreamProxyFinder::sendBytestreamQuery(const JID& jid) { S5BProxyRequest::ref proxyRequest = boost::make_shared(); boost::shared_ptr > request = boost::make_shared >(IQ::Get, jid, proxyRequest, iqRouter); request->onResponse.connect(boost::bind(&SOCKS5BytestreamProxyFinder::handleProxyResponse, this, _1, _2)); request->send(); } void SOCKS5BytestreamProxyFinder::handleServiceFound(const JID& jid, boost::shared_ptr discoInfo) { if (discoInfo->hasFeature(DiscoInfo::Bytestream)) { sendBytestreamQuery(jid); } } void SOCKS5BytestreamProxyFinder::handleProxyResponse(boost::shared_ptr request, ErrorPayload::ref error) { if (error) { SWIFT_LOG(debug) << "ERROR" << std::endl; } else { if (request) { onProxyFound(request); } else { //assert(false); } } } } swift-im-2.0+dev6/Swiften/FileTransfer/IncomingFileTransfer.cpp0000644000175000017500000000044012227051774024440 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { IncomingFileTransfer::~IncomingFileTransfer() { } } swift-im-2.0+dev6/Swiften/FileTransfer/WriteBytestream.cpp0000644000175000017500000000042112227051774023521 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { WriteBytestream::~WriteBytestream() { } } swift-im-2.0+dev6/Swiften/FileTransfer/SOCKS5BytestreamClientSession.cpp0000644000175000017500000001711412227051774026110 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include "SOCKS5BytestreamClientSession.h" #include #include #include #include #include #include #include #include #include #include namespace Swift { SOCKS5BytestreamClientSession::SOCKS5BytestreamClientSession(boost::shared_ptr connection, const HostAddressPort& addressPort, const std::string& destination, TimerFactory* timerFactory) : connection(connection), addressPort(addressPort), destination(destination), state(Initial), chunkSize(131072) { connection->onConnectFinished.connect(boost::bind(&SOCKS5BytestreamClientSession::handleConnectFinished, this, _1)); connection->onDisconnected.connect(boost::bind(&SOCKS5BytestreamClientSession::handleDisconnected, this, _1)); weFailedTimeout = timerFactory->createTimer(2000); weFailedTimeout->onTick.connect(boost::bind(&SOCKS5BytestreamClientSession::handleWeFailedTimeout, this)); } void SOCKS5BytestreamClientSession::start() { assert(state == Initial); SWIFT_LOG(debug) << "Trying to connect via TCP to " << addressPort.toString() << "." << std::endl; weFailedTimeout->start(); connection->connect(addressPort); } void SOCKS5BytestreamClientSession::stop() { connection->disconnect(); connection->onDataWritten.disconnect(boost::bind(&SOCKS5BytestreamClientSession::sendData, this)); connection->onDataRead.disconnect(boost::bind(&SOCKS5BytestreamClientSession::handleDataRead, this, _1)); readBytestream.reset(); state = Finished; } void SOCKS5BytestreamClientSession::process() { SWIFT_LOG(debug) << "unprocessedData.size(): " << unprocessedData.size() << std::endl; ByteArray bndAddress; switch(state) { case Initial: hello(); break; case Hello: if (unprocessedData.size() > 1) { unsigned char version = unprocessedData[0]; unsigned char authMethod = unprocessedData[1]; if (version != 5 || authMethod != 0) { // signal failure to upper level finish(true); return; } unprocessedData.clear(); authenticate(); } break; case Authenticating: if (unprocessedData.size() < 5) { // need more data to start progressing break; } if (unprocessedData[0] != '\x05') { // wrong version // disconnect & signal failure finish(true); break; } if (unprocessedData[1] != '\x00') { // no success // disconnect & signal failure finish(true); break; } if (unprocessedData[3] != '\x03') { // we expect x'03' = DOMAINNAME here // discconect & signal failure finish(true); break; } if (static_cast(unprocessedData[4]) + 1 > unprocessedData.size() + 5) { // complete domainname and port not available yet break; } bndAddress = createByteArray(&vecptr(unprocessedData)[5], unprocessedData[4]); if (unprocessedData[unprocessedData[4] + 5] != 0 && bndAddress == createByteArray(destination)) { // we expect a 0 as port // disconnect and fail finish(true); } unprocessedData.clear(); state = Ready; SWIFT_LOG(debug) << "session ready" << std::endl; // issue ready signal so the bytestream can be used for reading or writing weFailedTimeout->stop(); onSessionReady(false); break; case Ready: SWIFT_LOG(debug) << "Received further data in Ready state." << std::endl; break; case Reading: case Writing: case Finished: SWIFT_LOG(debug) << "Unexpected receive of data. Current state: " << state << std::endl; SWIFT_LOG(debug) << "Data: " << Hexify::hexify(unprocessedData) << std::endl; unprocessedData.clear(); //assert(false); } } void SOCKS5BytestreamClientSession::hello() { // Version 5, 1 auth method, No authentication const SafeByteArray hello = createSafeByteArray("\x05\x01\x00", 3); connection->write(hello); state = Hello; } void SOCKS5BytestreamClientSession::authenticate() { SWIFT_LOG(debug) << std::endl; SafeByteArray header = createSafeByteArray("\x05\x01\x00\x03", 4); SafeByteArray message = header; append(message, createSafeByteArray(destination.size())); authenticateAddress = createByteArray(destination); append(message, authenticateAddress); append(message, createSafeByteArray("\x00\x00", 2)); // 2 byte for port connection->write(message); state = Authenticating; } void SOCKS5BytestreamClientSession::startReceiving(boost::shared_ptr writeStream) { if (state == Ready) { state = Reading; writeBytestream = writeStream; writeBytestream->write(unprocessedData); onBytesReceived(unprocessedData.size()); unprocessedData.clear(); } else { SWIFT_LOG(debug) << "Session isn't ready for transfer yet!" << std::endl; } } void SOCKS5BytestreamClientSession::startSending(boost::shared_ptr readStream) { if (state == Ready) { state = Writing; readBytestream = readStream; connection->onDataWritten.connect(boost::bind(&SOCKS5BytestreamClientSession::sendData, this)); sendData(); } else { SWIFT_LOG(debug) << "Session isn't ready for transfer yet!" << std::endl; } } HostAddressPort SOCKS5BytestreamClientSession::getAddressPort() const { return addressPort; } void SOCKS5BytestreamClientSession::sendData() { if (!readBytestream->isFinished()) { try { boost::shared_ptr dataToSend = readBytestream->read(chunkSize); connection->write(createSafeByteArray(*dataToSend)); onBytesSent(dataToSend->size()); } catch (const BytestreamException&) { finish(true); } } else { finish(false); } } void SOCKS5BytestreamClientSession::finish(bool error) { weFailedTimeout->stop(); connection->disconnect(); connection->onDataWritten.disconnect(boost::bind(&SOCKS5BytestreamClientSession::sendData, this)); connection->onDataRead.disconnect(boost::bind(&SOCKS5BytestreamClientSession::handleDataRead, this, _1)); readBytestream.reset(); if (state == Initial || state == Hello || state == Authenticating) { onSessionReady(true); } else { state = Finished; if (error) { onFinished(boost::optional(FileTransferError::ReadError)); } else { onFinished(boost::optional()); } } } void SOCKS5BytestreamClientSession::handleConnectFinished(bool error) { if (error) { SWIFT_LOG(debug) << "Failed to connect via TCP to " << addressPort.toString() << "." << std::endl; finish(true); } else { SWIFT_LOG(debug) << "Successfully connected via TCP" << addressPort.toString() << "." << std::endl; weFailedTimeout->start(); connection->onDataRead.connect(boost::bind(&SOCKS5BytestreamClientSession::handleDataRead, this, _1)); process(); } } void SOCKS5BytestreamClientSession::handleDataRead(boost::shared_ptr data) { SWIFT_LOG(debug) << "state: " << state << " data.size() = " << data->size() << std::endl; if (state != Reading) { append(unprocessedData, *data); process(); } else { writeBytestream->write(createByteArray(vecptr(*data), data->size())); onBytesReceived(data->size()); } } void SOCKS5BytestreamClientSession::handleDisconnected(const boost::optional& error) { SWIFT_LOG(debug) << (error ? (error == Connection::ReadError ? "Read Error" : "Write Error") : "No Error") << std::endl; if (error) { finish(true); } } void SOCKS5BytestreamClientSession::handleWeFailedTimeout() { SWIFT_LOG(debug) << "Failed due to timeout!" << std::endl; finish(true); } } swift-im-2.0+dev6/Swiften/FileTransfer/FileWriteBytestream.h0000644000175000017500000000125212227051774023771 0ustar kismithkismith/* * 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 #include #include #include namespace Swift { class SWIFTEN_API FileWriteBytestream : public WriteBytestream { public: FileWriteBytestream(const boost::filesystem::path& file); ~FileWriteBytestream(); virtual void write(const std::vector&); void close(); private: boost::filesystem::path file; boost::filesystem::ofstream* stream; }; } swift-im-2.0+dev6/Swiften/FileTransfer/OutgoingFileTransferManager.cpp0000644000175000017500000000414012227051774025764 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include "OutgoingFileTransferManager.h" #include #include #include #include #include #include #include namespace Swift { OutgoingFileTransferManager::OutgoingFileTransferManager(JingleSessionManager* jingleSessionManager, IQRouter* router, EntityCapsProvider* capsProvider, RemoteJingleTransportCandidateSelectorFactory* remoteFactory, LocalJingleTransportCandidateGeneratorFactory* localFactory, SOCKS5BytestreamRegistry* bytestreamRegistry, SOCKS5BytestreamProxy* bytestreamProxy) : jsManager(jingleSessionManager), iqRouter(router), capsProvider(capsProvider), remoteFactory(remoteFactory), localFactory(localFactory), bytestreamRegistry(bytestreamRegistry), bytestreamProxy(bytestreamProxy) { idGenerator = new IDGenerator(); } OutgoingFileTransferManager::~OutgoingFileTransferManager() { delete idGenerator; } boost::shared_ptr OutgoingFileTransferManager::createOutgoingFileTransfer(const JID& from, const JID& receipient, boost::shared_ptr readBytestream, const StreamInitiationFileInfo& fileInfo) { // check if receipient support Jingle FT JingleSessionImpl::ref jingleSession = boost::make_shared(from, receipient, idGenerator->generateID(), iqRouter); //jsManager->getSession(receipient, idGenerator->generateID()); assert(jingleSession); jsManager->registerOutgoingSession(from, jingleSession); boost::shared_ptr jingleFT = boost::shared_ptr(new OutgoingJingleFileTransfer(jingleSession, remoteFactory, localFactory, iqRouter, idGenerator, from, receipient, readBytestream, fileInfo, bytestreamRegistry, bytestreamProxy)); // otherwise try SI // else fail return jingleFT; } } swift-im-2.0+dev6/Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.h0000644000175000017500000000174712227051774027614 0ustar kismithkismith/* * 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 #include #include #include namespace Swift { class SWIFTEN_API LocalJingleTransportCandidateGenerator { public: virtual ~LocalJingleTransportCandidateGenerator(); /** * Should call onLocalTransportCandidatesGenerated if it has finished discovering local candidates. */ virtual void generateLocalTransportCandidates(JingleTransportPayload::ref) = 0; virtual bool isActualCandidate(JingleTransportPayload::ref) = 0; virtual int getPriority(JingleTransportPayload::ref) = 0; virtual JingleTransport::ref selectTransport(JingleTransportPayload::ref) = 0; boost::signal onLocalTransportCandidatesGenerated; }; } swift-im-2.0+dev6/Swiften/FileTransfer/SOCKS5BytestreamServer.cpp0000644000175000017500000000375012227051774024575 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include namespace Swift { SOCKS5BytestreamServer::SOCKS5BytestreamServer(boost::shared_ptr connectionServer, SOCKS5BytestreamRegistry* registry) : connectionServer(connectionServer), registry(registry) { } void SOCKS5BytestreamServer::start() { connectionServer->onNewConnection.connect(boost::bind(&SOCKS5BytestreamServer::handleNewConnection, this, _1)); } void SOCKS5BytestreamServer::stop() { connectionServer->onNewConnection.disconnect(boost::bind(&SOCKS5BytestreamServer::handleNewConnection, this, _1)); } void SOCKS5BytestreamServer::addReadBytestream(const std::string& id, const JID& from, const JID& to, boost::shared_ptr byteStream) { registry->addReadBytestream(getSOCKSDestinationAddress(id, from, to), byteStream); } void SOCKS5BytestreamServer::removeReadBytestream(const std::string& id, const JID& from, const JID& to) { registry->removeReadBytestream(getSOCKSDestinationAddress(id, from, to)); } std::string SOCKS5BytestreamServer::getSOCKSDestinationAddress(const std::string& id, const JID& from, const JID& to) { return Hexify::hexify(SHA1::getHash(createByteArray(id + from.toString() + to.toString()))); } void SOCKS5BytestreamServer::handleNewConnection(boost::shared_ptr connection) { boost::shared_ptr session(new SOCKS5BytestreamServerSession(connection, registry)); sessions.push_back(session); session->start(); } HostAddressPort SOCKS5BytestreamServer::getAddressPort() const { return connectionServer->getAddressPort(); } } swift-im-2.0+dev6/Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelectorFactory.cpp0000644000175000017500000000165412227051774033034 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include "DefaultRemoteJingleTransportCandidateSelectorFactory.h" #include #include namespace Swift { DefaultRemoteJingleTransportCandidateSelectorFactory::DefaultRemoteJingleTransportCandidateSelectorFactory(ConnectionFactory* connectionFactory, TimerFactory* timerFactory) : connectionFactory(connectionFactory), timerFactory(timerFactory) { } DefaultRemoteJingleTransportCandidateSelectorFactory::~DefaultRemoteJingleTransportCandidateSelectorFactory() { } RemoteJingleTransportCandidateSelector* DefaultRemoteJingleTransportCandidateSelectorFactory::createCandidateSelector() { return new DefaultRemoteJingleTransportCandidateSelector(connectionFactory, timerFactory); } } swift-im-2.0+dev6/Swiften/FileTransfer/OutgoingFileTransfer.cpp0000644000175000017500000000044012227051774024470 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { OutgoingFileTransfer::~OutgoingFileTransfer() { } } swift-im-2.0+dev6/Swiften/FileTransfer/IncomingFileTransferManager.h0000644000175000017500000000314612227051774025406 0ustar kismithkismith/* * 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 #include #include #include namespace Swift { class IQRouter; class JingleSessionManager; class RemoteJingleTransportCandidateSelectorFactory; class LocalJingleTransportCandidateGeneratorFactory; class SOCKS5BytestreamRegistry; class SOCKS5BytestreamProxy; class TimerFactory; class IncomingFileTransferManager : public IncomingJingleSessionHandler { public: IncomingFileTransferManager(JingleSessionManager* jingleSessionManager, IQRouter* router, RemoteJingleTransportCandidateSelectorFactory* remoteFactory, LocalJingleTransportCandidateGeneratorFactory* localFactory, SOCKS5BytestreamRegistry* bytestreamRegistry, SOCKS5BytestreamProxy* bytestreamProxy, TimerFactory* timerFactory); ~IncomingFileTransferManager(); boost::signal onIncomingFileTransfer; private: bool handleIncomingJingleSession(JingleSession::ref session, const std::vector& contents, const JID& recipient); private: JingleSessionManager* jingleSessionManager; IQRouter* router; RemoteJingleTransportCandidateSelectorFactory* remoteFactory; LocalJingleTransportCandidateGeneratorFactory* localFactory; SOCKS5BytestreamRegistry* bytestreamRegistry; SOCKS5BytestreamProxy* bytestreamProxy; TimerFactory* timerFactory; }; } swift-im-2.0+dev6/Swiften/FileTransfer/LocalJingleTransportCandidateGeneratorFactory.h0000644000175000017500000000076312227051774031141 0ustar kismithkismith/* * 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 namespace Swift { class LocalJingleTransportCandidateGenerator; class SWIFTEN_API LocalJingleTransportCandidateGeneratorFactory { public: virtual ~LocalJingleTransportCandidateGeneratorFactory(); virtual LocalJingleTransportCandidateGenerator* createCandidateGenerator() = 0; }; } swift-im-2.0+dev6/Swiften/FileTransfer/ByteArrayWriteBytestream.h0000644000175000017500000000116512227051774025017 0ustar kismithkismith/* * 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 namespace Swift { class ByteArrayWriteBytestream : public WriteBytestream { public: ByteArrayWriteBytestream() { } virtual void write(const std::vector& bytes) { data.insert(data.end(), bytes.begin(), bytes.end()); onWrite(bytes); } const std::vector& getData() const { return data; } private: std::vector data; }; } swift-im-2.0+dev6/Swiften/FileTransfer/JingleIncomingIBBTransport.h0000644000175000017500000000125012227051774025163 0ustar kismithkismith/* * 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 #include #include namespace Swift { class JingleIncomingIBBTransport : public JingleTransport { public: typedef boost::shared_ptr ref; JingleIncomingIBBTransport(const JID& from, const JID& to, const std::string& id, size_t size, IQRouter* router); virtual void start(); virtual void stop(); private: IBBReceiveSession ibbSession; }; } swift-im-2.0+dev6/Swiften/FileTransfer/ConnectivityManager.cpp0000644000175000017500000000622412227051774024347 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include "ConnectivityManager.h" #include #include #include #include #include #include #include #include namespace Swift { ConnectivityManager::ConnectivityManager(NATTraverser* worker) : natTraversalWorker(worker) { } ConnectivityManager::~ConnectivityManager() { std::set leftOpenPorts = ports; foreach(int port, leftOpenPorts) { removeListeningPort(port); } } void ConnectivityManager::addListeningPort(int port) { ports.insert(port); boost::shared_ptr getIPRequest = natTraversalWorker->createGetPublicIPRequest(); if (getIPRequest) { getIPRequest->onResult.connect(boost::bind(&ConnectivityManager::natTraversalGetPublicIPResult, this, _1)); getIPRequest->run(); } boost::shared_ptr forwardPortRequest = natTraversalWorker->createForwardPortRequest(port, port); if (forwardPortRequest) { forwardPortRequest->onResult.connect(boost::bind(&ConnectivityManager::natTraversalForwardPortResult, this, _1)); forwardPortRequest->run(); } } void ConnectivityManager::removeListeningPort(int port) { SWIFT_LOG(debug) << "remove listening port " << port << std::endl; ports.erase(port); boost::shared_ptr removePortForwardingRequest = natTraversalWorker->createRemovePortForwardingRequest(port, port); if (removePortForwardingRequest) { removePortForwardingRequest->run(); } } std::vector ConnectivityManager::getHostAddressPorts() const { PlatformNetworkEnvironment env; std::vector results; std::vector addresses; std::vector networkInterfaces; foreach (const NetworkInterface& iface, networkInterfaces) { foreach (const HostAddress& address, iface.getAddresses()) { foreach (int port, ports) { results.push_back(HostAddressPort(address, port)); } } } return results; } std::vector ConnectivityManager::getAssistedHostAddressPorts() const { std::vector results; if (publicAddress) { foreach (int port, ports) { results.push_back(HostAddressPort(publicAddress.get(), port)); } } return results; } void ConnectivityManager::natTraversalGetPublicIPResult(boost::optional address) { if (address) { publicAddress = address; SWIFT_LOG(debug) << "Public IP discovered as " << publicAddress.get().toString() << "." << std::endl; } else { SWIFT_LOG(debug) << "No public IP discoverable." << std::endl; } } void ConnectivityManager::natTraversalForwardPortResult(boost::optional mapping) { if (mapping) { SWIFT_LOG(debug) << "Mapping port was successful." << std::endl; } else { SWIFT_LOG(debug) << "Mapping port has failed." << std::endl; } } } swift-im-2.0+dev6/Swiften/FileTransfer/UnitTest/0000755000175000017500000000000012227051774021445 5ustar kismithkismithswift-im-2.0+dev6/Swiften/FileTransfer/UnitTest/OutgoingJingleFileTransferTest.cpp0000644000175000017500000002366212227051774030253 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Swift; class OFakeRemoteJingleTransportCandidateSelector : public RemoteJingleTransportCandidateSelector { void addRemoteTransportCandidates(JingleTransportPayload::ref cand) { candidate = cand; } void selectCandidate() { JingleS5BTransportPayload::ref payload = boost::make_shared(); payload->setCandidateError(true); payload->setSessionID(candidate->getSessionID()); onRemoteTransportCandidateSelectFinished(payload); } void setMinimumPriority(int) { } bool isActualCandidate(JingleTransportPayload::ref) { return false; } int getPriority(JingleTransportPayload::ref) { return 0; } JingleTransport::ref selectTransport(JingleTransportPayload::ref) { return JingleTransport::ref(); } private: JingleTransportPayload::ref candidate; }; class OFakeRemoteJingleTransportCandidateSelectorFactory : public RemoteJingleTransportCandidateSelectorFactory { public: virtual ~OFakeRemoteJingleTransportCandidateSelectorFactory() { } virtual RemoteJingleTransportCandidateSelector* createCandidateSelector() { return new OFakeRemoteJingleTransportCandidateSelector(); } }; class OFakeLocalJingleTransportCandidateGenerator : public LocalJingleTransportCandidateGenerator { public: virtual void generateLocalTransportCandidates(JingleTransportPayload::ref /* payload */) { //JingleTransportPayload::ref payL = make_shared(); //payL->setSessionID(payload->getSessionID()); JingleS5BTransportPayload::ref payL = boost::make_shared(); onLocalTransportCandidatesGenerated(payL); } void emitonLocalTransportCandidatesGenerated(JingleTransportPayload::ref payload) { onLocalTransportCandidatesGenerated(payload); } virtual bool isActualCandidate(JingleTransportPayload::ref) { return false; } virtual int getPriority(JingleTransportPayload::ref) { return 0; } virtual JingleTransport::ref selectTransport(JingleTransportPayload::ref) { return JingleTransport::ref(); } }; class OFakeLocalJingleTransportCandidateGeneratorFactory : public LocalJingleTransportCandidateGeneratorFactory { public: virtual LocalJingleTransportCandidateGenerator* createCandidateGenerator() { return new OFakeLocalJingleTransportCandidateGenerator(); } }; class OutgoingJingleFileTransferTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(OutgoingJingleFileTransferTest); CPPUNIT_TEST(test_SendSessionInitiateOnStart); CPPUNIT_TEST(test_IBBStartsAfterSendingSessionAccept); CPPUNIT_TEST(test_ReceiveSessionTerminateAfterSessionInitiate); CPPUNIT_TEST_SUITE_END(); class FTStatusHelper { public: bool finishedCalled; FileTransferError::Type error; void handleFileTransferFinished(boost::optional error) { finishedCalled = true; if (error.is_initialized()) this->error = error.get().getType(); } }; public: boost::shared_ptr createTestling() { JID to("test@foo.com/bla"); StreamInitiationFileInfo fileInfo; fileInfo.setDescription("some file"); fileInfo.setName("test.bin"); fileInfo.setHash("asdjasdas"); fileInfo.setSize(1024 * 1024); return boost::shared_ptr(new OutgoingJingleFileTransfer(boost::shared_ptr(fakeJingleSession), fakeRJTCSF.get(), fakeLJTCF.get(), iqRouter, idGen, JID(), to, stream, fileInfo, s5bRegistry, s5bProxy)); } IQ::ref createIBBRequest(IBB::ref ibb, const JID& from, const std::string& id) { IQ::ref request = IQ::createRequest(IQ::Set, JID("foo@bar.com/baz"), id, ibb); request->setFrom(from); return request; } void setUp() { fakeJingleSession = new FakeJingleSession("foo@bar.com/baz", "mysession"); jingleContentPayload = boost::make_shared(); fakeRJTCSF = boost::make_shared(); fakeLJTCF = boost::make_shared(); stanzaChannel = new DummyStanzaChannel(); iqRouter = new IQRouter(stanzaChannel); eventLoop = new DummyEventLoop(); timerFactory = new DummyTimerFactory(); connectionFactory = new DummyConnectionFactory(eventLoop); s5bRegistry = new SOCKS5BytestreamRegistry(); s5bProxy = new SOCKS5BytestreamProxy(connectionFactory, timerFactory); data.clear(); for (int n=0; n < 1024 * 1024; ++n) { data.push_back(34); } stream = boost::make_shared(data); idGen = new IDGenerator(); } void tearDown() { delete idGen; delete s5bRegistry; delete connectionFactory; delete timerFactory; delete eventLoop; delete iqRouter; delete stanzaChannel; } void test_SendSessionInitiateOnStart() { boost::shared_ptr transfer = createTestling(); transfer->start(); FakeJingleSession::InitiateCall call = getCall(0); JingleFileTransferDescription::ref description = boost::dynamic_pointer_cast(call.description); CPPUNIT_ASSERT(description); CPPUNIT_ASSERT_EQUAL(static_cast(1), description->getOffers().size()); CPPUNIT_ASSERT(static_cast(1048576) == description->getOffers()[0].getSize()); JingleS5BTransportPayload::ref transport = boost::dynamic_pointer_cast(call.payload); CPPUNIT_ASSERT(transport); } void test_IBBStartsAfterSendingSessionAccept() { boost::shared_ptr transfer = createTestling(); transfer->start(); FakeJingleSession::InitiateCall call = getCall(0); // FIXME: we initiate with SOCSK5 now and not IBB, needs to be fixed. /* fakeJingleSession->onSessionAcceptReceived(call.id, call.description, call.payload); IQ::ref iqOpenStanza = stanzaChannel->getStanzaAtIndex(0); CPPUNIT_ASSERT(iqOpenStanza); */ } void test_ReceiveSessionTerminateAfterSessionInitiate() { boost::shared_ptr transfer = createTestling(); transfer->start(); getCall(0); FTStatusHelper helper; helper.finishedCalled = false; transfer->onFinished.connect(bind(&FTStatusHelper::handleFileTransferFinished, &helper, _1)); fakeJingleSession->onSessionTerminateReceived(JinglePayload::Reason(JinglePayload::Reason::Busy)); CPPUNIT_ASSERT_EQUAL(true, helper.finishedCalled); CPPUNIT_ASSERT(FileTransferError::PeerError == helper.error); } //TODO: some more testcases private: void addFileTransferDescription() { boost::shared_ptr desc = boost::make_shared(); desc->addOffer(StreamInitiationFileInfo()); jingleContentPayload->addDescription(desc); } boost::shared_ptr addJingleS5BPayload() { JingleS5BTransportPayload::ref payLoad = boost::make_shared(); payLoad->setSessionID("mysession"); jingleContentPayload->addTransport(payLoad); return payLoad; } boost::shared_ptr addJingleIBBPayload() { JingleIBBTransportPayload::ref payLoad = boost::make_shared(); payLoad->setSessionID("mysession"); jingleContentPayload->addTransport(payLoad); return payLoad; } JingleContentID getContentID() const { return JingleContentID(jingleContentPayload->getName(), jingleContentPayload->getCreator()); } template T getCall(int i) const { size_t index = static_cast(i); CPPUNIT_ASSERT(index < fakeJingleSession->calledCommands.size()); T* cmd = boost::get(&fakeJingleSession->calledCommands[index]); CPPUNIT_ASSERT(cmd); return *cmd; } private: std::vector data; boost::shared_ptr stream; FakeJingleSession* fakeJingleSession; boost::shared_ptr jingleContentPayload; boost::shared_ptr fakeRJTCSF; boost::shared_ptr fakeLJTCF; DummyStanzaChannel* stanzaChannel; IQRouter* iqRouter; IDGenerator* idGen; EventLoop *eventLoop; SOCKS5BytestreamRegistry* s5bRegistry; SOCKS5BytestreamProxy* s5bProxy; DummyTimerFactory* timerFactory; DummyConnectionFactory* connectionFactory; }; CPPUNIT_TEST_SUITE_REGISTRATION(OutgoingJingleFileTransferTest); swift-im-2.0+dev6/Swiften/FileTransfer/UnitTest/IBBSendSessionTest.cpp0000644000175000017500000002056512227051774025573 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include using namespace Swift; class IBBSendSessionTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(IBBSendSessionTest); CPPUNIT_TEST(testStart); CPPUNIT_TEST(testStart_ResponseStartsSending); CPPUNIT_TEST(testResponseContinuesSending); CPPUNIT_TEST(testRespondToAllFinishes); CPPUNIT_TEST(testErrorResponseFinishesWithError); CPPUNIT_TEST(testStopDuringSessionCloses); CPPUNIT_TEST(testStopAfterFinishedDoesNotClose); CPPUNIT_TEST(testDataStreamPauseStopsSendingData); CPPUNIT_TEST(testDataStreamResumeAfterPauseSendsData); CPPUNIT_TEST(testDataStreamResumeBeforePauseDoesNotSendData); CPPUNIT_TEST(testDataStreamResumeAfterResumeDoesNotSendData); CPPUNIT_TEST_SUITE_END(); public: void setUp() { stanzaChannel = new DummyStanzaChannel(); iqRouter = new IQRouter(stanzaChannel); bytestream = boost::make_shared(createByteArray("abcdefg")); finished = false; } void tearDown() { delete iqRouter; delete stanzaChannel; } void testStart() { boost::shared_ptr testling = createSession("foo@bar.com/baz"); testling->setBlockSize(1234); testling->start(); CPPUNIT_ASSERT_EQUAL(1, static_cast(stanzaChannel->sentStanzas.size())); CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex(0, JID("foo@bar.com/baz"), IQ::Set)); IBB::ref ibb = stanzaChannel->sentStanzas[0]->getPayload(); CPPUNIT_ASSERT_EQUAL(IBB::Open, ibb->getAction()); CPPUNIT_ASSERT_EQUAL(1234, ibb->getBlockSize()); CPPUNIT_ASSERT_EQUAL(std::string("myid"), ibb->getStreamID()); } void testStart_ResponseStartsSending() { boost::shared_ptr testling = createSession("foo@bar.com/baz"); testling->setBlockSize(3); testling->start(); stanzaChannel->onIQReceived(createIBBResult()); CPPUNIT_ASSERT_EQUAL(2, static_cast(stanzaChannel->sentStanzas.size())); CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex(1, JID("foo@bar.com/baz"), IQ::Set)); IBB::ref ibb = stanzaChannel->sentStanzas[1]->getPayload(); CPPUNIT_ASSERT_EQUAL(IBB::Data, ibb->getAction()); CPPUNIT_ASSERT(createByteArray("abc") == ibb->getData()); CPPUNIT_ASSERT_EQUAL(0, ibb->getSequenceNumber()); CPPUNIT_ASSERT_EQUAL(std::string("myid"), ibb->getStreamID()); } void testResponseContinuesSending() { boost::shared_ptr testling = createSession("foo@bar.com/baz"); testling->setBlockSize(3); testling->start(); stanzaChannel->onIQReceived(createIBBResult()); stanzaChannel->onIQReceived(createIBBResult()); CPPUNIT_ASSERT_EQUAL(3, static_cast(stanzaChannel->sentStanzas.size())); CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex(2, JID("foo@bar.com/baz"), IQ::Set)); IBB::ref ibb = stanzaChannel->sentStanzas[2]->getPayload(); CPPUNIT_ASSERT_EQUAL(IBB::Data, ibb->getAction()); CPPUNIT_ASSERT(createByteArray("def") == ibb->getData()); CPPUNIT_ASSERT_EQUAL(1, ibb->getSequenceNumber()); CPPUNIT_ASSERT_EQUAL(std::string("myid"), ibb->getStreamID()); } void testRespondToAllFinishes() { boost::shared_ptr testling = createSession("foo@bar.com/baz"); testling->setBlockSize(3); testling->start(); stanzaChannel->onIQReceived(createIBBResult()); stanzaChannel->onIQReceived(createIBBResult()); stanzaChannel->onIQReceived(createIBBResult()); stanzaChannel->onIQReceived(createIBBResult()); CPPUNIT_ASSERT(finished); CPPUNIT_ASSERT(!error); } void testErrorResponseFinishesWithError() { boost::shared_ptr testling = createSession("foo@bar.com/baz"); testling->setBlockSize(3); testling->start(); stanzaChannel->onIQReceived(IQ::createError(JID("baz@fum.com/foo"), stanzaChannel->sentStanzas[0]->getTo(), stanzaChannel->sentStanzas[0]->getID())); CPPUNIT_ASSERT(finished); CPPUNIT_ASSERT(error); } void testStopDuringSessionCloses() { boost::shared_ptr testling = createSession("foo@bar.com/baz"); testling->setBlockSize(3); testling->start(); testling->stop(); CPPUNIT_ASSERT_EQUAL(2, static_cast(stanzaChannel->sentStanzas.size())); CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex(1, JID("foo@bar.com/baz"), IQ::Set)); IBB::ref ibb = stanzaChannel->sentStanzas[1]->getPayload(); CPPUNIT_ASSERT_EQUAL(IBB::Close, ibb->getAction()); CPPUNIT_ASSERT_EQUAL(std::string("myid"), ibb->getStreamID()); CPPUNIT_ASSERT(finished); CPPUNIT_ASSERT(!error); } void testStopAfterFinishedDoesNotClose() { boost::shared_ptr testling = createSession("foo@bar.com/baz"); testling->setBlockSize(16); testling->start(); stanzaChannel->onIQReceived(createIBBResult()); stanzaChannel->onIQReceived(createIBBResult()); CPPUNIT_ASSERT(finished); testling->stop(); CPPUNIT_ASSERT_EQUAL(2, static_cast(stanzaChannel->sentStanzas.size())); } void testDataStreamPauseStopsSendingData() { boost::shared_ptr testling = createSession("foo@bar.com/baz"); bytestream->setDataComplete(false); testling->setBlockSize(3); testling->start(); stanzaChannel->onIQReceived(createIBBResult()); stanzaChannel->onIQReceived(createIBBResult()); stanzaChannel->onIQReceived(createIBBResult()); stanzaChannel->onIQReceived(createIBBResult()); CPPUNIT_ASSERT(!finished); CPPUNIT_ASSERT(!error); CPPUNIT_ASSERT_EQUAL(4, static_cast(stanzaChannel->sentStanzas.size())); } void testDataStreamResumeAfterPauseSendsData() { boost::shared_ptr testling = createSession("foo@bar.com/baz"); bytestream->setDataComplete(false); testling->setBlockSize(3); testling->start(); stanzaChannel->onIQReceived(createIBBResult()); stanzaChannel->onIQReceived(createIBBResult()); stanzaChannel->onIQReceived(createIBBResult()); stanzaChannel->onIQReceived(createIBBResult()); bytestream->addData(createByteArray("xyz")); CPPUNIT_ASSERT_EQUAL(5, static_cast(stanzaChannel->sentStanzas.size())); } void testDataStreamResumeBeforePauseDoesNotSendData() { boost::shared_ptr testling = createSession("foo@bar.com/baz"); bytestream->setDataComplete(false); testling->setBlockSize(3); testling->start(); stanzaChannel->onIQReceived(createIBBResult()); bytestream->addData(createByteArray("xyz")); CPPUNIT_ASSERT_EQUAL(2, static_cast(stanzaChannel->sentStanzas.size())); } void testDataStreamResumeAfterResumeDoesNotSendData() { boost::shared_ptr testling = createSession("foo@bar.com/baz"); bytestream->setDataComplete(false); testling->setBlockSize(3); testling->start(); stanzaChannel->onIQReceived(createIBBResult()); stanzaChannel->onIQReceived(createIBBResult()); stanzaChannel->onIQReceived(createIBBResult()); stanzaChannel->onIQReceived(createIBBResult()); bytestream->addData(createByteArray("xyz")); bytestream->addData(createByteArray("xuv")); CPPUNIT_ASSERT_EQUAL(5, static_cast(stanzaChannel->sentStanzas.size())); } private: IQ::ref createIBBResult() { return IQ::createResult(JID("baz@fum.com/dum"), stanzaChannel->sentStanzas[stanzaChannel->sentStanzas.size()-1]->getTo(), stanzaChannel->sentStanzas[stanzaChannel->sentStanzas.size()-1]->getID(), boost::shared_ptr()); } private: boost::shared_ptr createSession(const std::string& to) { boost::shared_ptr session(new IBBSendSession("myid", JID(), JID(to), bytestream, iqRouter)); session->onFinished.connect(boost::bind(&IBBSendSessionTest::handleFinished, this, _1)); return session; } void handleFinished(boost::optional error) { finished = true; this->error = error; } private: DummyStanzaChannel* stanzaChannel; IQRouter* iqRouter; bool finished; boost::optional error; boost::shared_ptr bytestream; }; CPPUNIT_TEST_SUITE_REGISTRATION(IBBSendSessionTest); swift-im-2.0+dev6/Swiften/FileTransfer/UnitTest/DummyFileTransferManager.h0000644000175000017500000000207412227051774026514 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include #include #include namespace Swift { class DummyFileTransferManager : public FileTransferManager { public: DummyFileTransferManager() : FileTransferManager() { } virtual OutgoingFileTransfer::ref createOutgoingFileTransfer(const JID&, const boost::filesystem::path&, const std::string&, boost::shared_ptr) { return OutgoingFileTransfer::ref(); } virtual OutgoingFileTransfer::ref createOutgoingFileTransfer(const JID&, const std::string&, const std::string&, const boost::uintmax_t, const boost::posix_time::ptime&, boost::shared_ptr) { return OutgoingFileTransfer::ref(); } virtual void startListeningOnPort(int) { } virtual void addS5BProxy(boost::shared_ptr) { } }; } swift-im-2.0+dev6/Swiften/FileTransfer/UnitTest/SOCKS5BytestreamServerSessionTest.cpp0000644000175000017500000001664412227051774030566 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include #include using namespace Swift; class SOCKS5BytestreamServerSessionTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(SOCKS5BytestreamServerSessionTest); CPPUNIT_TEST(testAuthenticate); CPPUNIT_TEST(testAuthenticate_Chunked); CPPUNIT_TEST(testRequest); CPPUNIT_TEST(testRequest_UnknownBytestream); CPPUNIT_TEST(testReceiveData); CPPUNIT_TEST(testReceiveData_Chunked); CPPUNIT_TEST(testDataStreamPauseStopsSendingData); CPPUNIT_TEST(testDataStreamResumeAfterPauseSendsData); CPPUNIT_TEST_SUITE_END(); public: void setUp() { receivedDataChunks = 0; eventLoop = new DummyEventLoop(); bytestreams = new SOCKS5BytestreamRegistry(); connection = boost::make_shared(eventLoop); connection->onDataSent.connect(boost::bind(&SOCKS5BytestreamServerSessionTest::handleDataWritten, this, _1)); stream1 = boost::make_shared(createByteArray("abcdefg")); finished = false; } void tearDown() { connection.reset(); delete bytestreams; delete eventLoop; } void testAuthenticate() { boost::shared_ptr testling(createSession()); StartStopper stopper(testling.get()); receive(createSafeByteArray("\x05\x02\x01\x02")); CPPUNIT_ASSERT(createByteArray("\x05\x00", 2) == receivedData); } void testAuthenticate_Chunked() { boost::shared_ptr testling(createSession()); StartStopper stopper(testling.get()); receive(createSafeByteArray("\x05\x02\x01")); CPPUNIT_ASSERT_EQUAL(0, static_cast(receivedData.size())); receive(createSafeByteArray("\x01")); CPPUNIT_ASSERT(createByteArray("\x05\x00", 2) == receivedData); } void testRequest() { boost::shared_ptr testling(createSession()); StartStopper stopper(testling.get()); bytestreams->addReadBytestream("abcdef", stream1); authenticate(); ByteArray hostname(createByteArray("abcdef")); receive(concat(createSafeByteArray("\x05\x01\x00\x03", 4), createSafeByteArray(hostname.size()), createSafeByteArray(hostname), createSafeByteArray("\x00\x00", 2))); CPPUNIT_ASSERT(createByteArray("\x05\x00\x00\x03\x06\x61\x62\x63\x64\x65\x66\x00\x00", 13) == createByteArray(&receivedData[0], 13)); } void testRequest_UnknownBytestream() { boost::shared_ptr testling(createSession()); StartStopper stopper(testling.get()); authenticate(); ByteArray hostname(createByteArray("abcdef")); receive(concat(createSafeByteArray("\x05\x01\x00\x03", 4), createSafeByteArray(hostname.size()), createSafeByteArray(hostname), createSafeByteArray("\x00\x00", 2))); CPPUNIT_ASSERT(createByteArray("\x05\x04\x00\x03\x06\x61\x62\x63\x64\x65\x66\x00\x00", 13) == receivedData); } void testReceiveData() { boost::shared_ptr testling(createSession()); StartStopper stopper(testling.get()); bytestreams->addReadBytestream("abcdef", stream1); authenticate(); request("abcdef"); eventLoop->processEvents(); testling->startTransfer(); skipHeader("abcdef"); eventLoop->processEvents(); CPPUNIT_ASSERT(createByteArray("abcdefg") == receivedData); CPPUNIT_ASSERT_EQUAL(2, receivedDataChunks); } void testReceiveData_Chunked() { boost::shared_ptr testling(createSession()); testling->setChunkSize(3); StartStopper stopper(testling.get()); bytestreams->addReadBytestream("abcdef", stream1); authenticate(); request("abcdef"); eventLoop->processEvents(); testling->startTransfer(); eventLoop->processEvents(); skipHeader("abcdef"); CPPUNIT_ASSERT(createByteArray("abcdefg") == receivedData); CPPUNIT_ASSERT_EQUAL(4, receivedDataChunks); } void testDataStreamPauseStopsSendingData() { boost::shared_ptr testling(createSession()); testling->setChunkSize(3); stream1->setDataComplete(false); StartStopper stopper(testling.get()); bytestreams->addReadBytestream("abcdef", stream1); authenticate(); request("abcdef"); eventLoop->processEvents(); testling->startTransfer(); eventLoop->processEvents(); skipHeader("abcdef"); CPPUNIT_ASSERT(createByteArray("abcdefg") == receivedData); CPPUNIT_ASSERT_EQUAL(4, receivedDataChunks); CPPUNIT_ASSERT(!finished); CPPUNIT_ASSERT(!error); } void testDataStreamResumeAfterPauseSendsData() { boost::shared_ptr testling(createSession()); testling->setChunkSize(3); stream1->setDataComplete(false); StartStopper stopper(testling.get()); bytestreams->addReadBytestream("abcdef", stream1); authenticate(); request("abcdef"); eventLoop->processEvents(); testling->startTransfer(); eventLoop->processEvents(); skipHeader("abcdef"); stream1->addData(createByteArray("xyz")); eventLoop->processEvents(); CPPUNIT_ASSERT(createByteArray("abcdefgxyz") == receivedData); CPPUNIT_ASSERT(!finished); CPPUNIT_ASSERT(!error); } private: void receive(const SafeByteArray& data) { connection->receive(data); eventLoop->processEvents(); } void authenticate() { receive(createSafeByteArray("\x05\x02\x01\x02")); receivedData.clear(); receivedDataChunks = 0; } void request(const std::string& hostname) { receive(concat(createSafeByteArray("\x05\x01\x00\x03", 4), createSafeByteArray(hostname.size()), createSafeByteArray(hostname), createSafeByteArray("\x00\x00", 2))); } void skipHeader(const std::string& hostname) { int headerSize = 7 + hostname.size(); receivedData = createByteArray(&receivedData[headerSize], receivedData.size() - headerSize); } void handleDataWritten(const SafeByteArray& data) { receivedData.insert(receivedData.end(), data.begin(), data.end()); receivedDataChunks++; } private: SOCKS5BytestreamServerSession* createSession() { SOCKS5BytestreamServerSession* session = new SOCKS5BytestreamServerSession(connection, bytestreams); session->onFinished.connect(boost::bind(&SOCKS5BytestreamServerSessionTest::handleFinished, this, _1)); return session; } void handleFinished(boost::optional error) { finished = true; this->error = error; } private: DummyEventLoop* eventLoop; SOCKS5BytestreamRegistry* bytestreams; boost::shared_ptr connection; std::vector receivedData; int receivedDataChunks; boost::shared_ptr stream1; bool finished; boost::optional error; }; CPPUNIT_TEST_SUITE_REGISTRATION(SOCKS5BytestreamServerSessionTest); swift-im-2.0+dev6/Swiften/FileTransfer/UnitTest/IBBReceiveSessionTest.cpp0000644000175000017500000001565412227051774026267 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include using namespace Swift; class IBBReceiveSessionTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(IBBReceiveSessionTest); CPPUNIT_TEST(testOpen); CPPUNIT_TEST(testReceiveData); CPPUNIT_TEST(testReceiveMultipleData); CPPUNIT_TEST(testReceiveDataForOtherSession); CPPUNIT_TEST(testReceiveDataOutOfOrder); CPPUNIT_TEST(testReceiveLastData); CPPUNIT_TEST(testReceiveClose); CPPUNIT_TEST(testStopWhileActive); CPPUNIT_TEST_SUITE_END(); public: void setUp() { stanzaChannel = new DummyStanzaChannel(); iqRouter = new IQRouter(stanzaChannel); finished = false; } void tearDown() { delete iqRouter; delete stanzaChannel; } void testOpen() { boost::shared_ptr testling(createSession("foo@bar.com/baz", "mysession")); testling->start(); stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBOpen("mysession", 0x10), "foo@bar.com/baz", "id-open")); CPPUNIT_ASSERT(stanzaChannel->isResultAtIndex(0, "id-open")); CPPUNIT_ASSERT(!finished); testling->stop(); } void testReceiveData() { boost::shared_ptr testling(createSession("foo@bar.com/baz", "mysession")); testling->start(); stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBOpen("mysession", 0x10), "foo@bar.com/baz", "id-open")); stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBData("mysession", 0, createByteArray("abc")), "foo@bar.com/baz", "id-a")); CPPUNIT_ASSERT(stanzaChannel->isResultAtIndex(1, "id-a")); CPPUNIT_ASSERT(createByteArray("abc") == receivedData); CPPUNIT_ASSERT(!finished); testling->stop(); } void testReceiveMultipleData() { boost::shared_ptr testling(createSession("foo@bar.com/baz", "mysession")); testling->start(); stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBOpen("mysession", 0x10), "foo@bar.com/baz", "id-open")); stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBData("mysession", 0, createByteArray("abc")), "foo@bar.com/baz", "id-a")); stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBData("mysession", 1, createByteArray("def")), "foo@bar.com/baz", "id-b")); CPPUNIT_ASSERT(stanzaChannel->isResultAtIndex(2, "id-b")); CPPUNIT_ASSERT(createByteArray("abcdef") == receivedData); CPPUNIT_ASSERT(!finished); testling->stop(); } void testReceiveDataForOtherSession() { boost::shared_ptr testling(createSession("foo@bar.com/baz", "mysession")); testling->start(); stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBOpen("mysession", 0x10), "foo@bar.com/baz", "id-open")); stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBData("othersession", 0, createByteArray("abc")), "foo@bar.com/baz", "id-a")); CPPUNIT_ASSERT(stanzaChannel->isErrorAtIndex(1, "id-a")); testling->stop(); } void testReceiveDataOutOfOrder() { boost::shared_ptr testling(createSession("foo@bar.com/baz", "mysession")); testling->start(); stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBOpen("mysession", 0x10), "foo@bar.com/baz", "id-open")); stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBData("mysession", 0, createByteArray("abc")), "foo@bar.com/baz", "id-a")); stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBData("mysession", 0, createByteArray("def")), "foo@bar.com/baz", "id-b")); CPPUNIT_ASSERT(stanzaChannel->isErrorAtIndex(2, "id-b")); CPPUNIT_ASSERT(finished); CPPUNIT_ASSERT(error); testling->stop(); } void testReceiveLastData() { boost::shared_ptr testling(createSession("foo@bar.com/baz", "mysession", 6)); testling->start(); stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBOpen("mysession", 0x10), "foo@bar.com/baz", "id-open")); stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBData("mysession", 0, createByteArray("abc")), "foo@bar.com/baz", "id-a")); stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBData("mysession", 1, createByteArray("def")), "foo@bar.com/baz", "id-b")); CPPUNIT_ASSERT(stanzaChannel->isResultAtIndex(2, "id-b")); CPPUNIT_ASSERT(createByteArray("abcdef") == receivedData); CPPUNIT_ASSERT(finished); CPPUNIT_ASSERT(!error); testling->stop(); } void testReceiveClose() { boost::shared_ptr testling(createSession("foo@bar.com/baz", "mysession")); testling->start(); stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBOpen("mysession", 0x10), "foo@bar.com/baz", "id-open")); stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBClose("mysession"), "foo@bar.com/baz", "id-close")); CPPUNIT_ASSERT(finished); CPPUNIT_ASSERT(error); testling->stop(); } void testStopWhileActive() { boost::shared_ptr testling(createSession("foo@bar.com/baz", "mysession")); testling->start(); stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBOpen("mysession", 0x10), "foo@bar.com/baz", "id-open")); testling->stop(); CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex(1, JID("foo@bar.com/baz"), IQ::Set)); IBB::ref ibb = stanzaChannel->sentStanzas[1]->getPayload(); CPPUNIT_ASSERT_EQUAL(IBB::Close, ibb->getAction()); CPPUNIT_ASSERT_EQUAL(std::string("mysession"), ibb->getStreamID()); CPPUNIT_ASSERT(finished); CPPUNIT_ASSERT(!error); } private: IQ::ref createIBBRequest(IBB::ref ibb, const JID& from, const std::string& id) { IQ::ref request = IQ::createRequest(IQ::Set, JID("baz@fum.com/dum"), id, ibb); request->setFrom(from); return request; } IBBReceiveSession* createSession(const std::string& from, const std::string& id, size_t size = 0x1000) { IBBReceiveSession* session = new IBBReceiveSession(id, JID(from), JID(), size, iqRouter); session->onDataReceived.connect(boost::bind(&IBBReceiveSessionTest::handleDataReceived, this, _1)); session->onFinished.connect(boost::bind(&IBBReceiveSessionTest::handleFinished, this, _1)); return session; } void handleFinished(boost::optional error) { finished = true; this->error = error; } void handleDataReceived(const std::vector& data) { receivedData.insert(receivedData.end(), data.begin(), data.end()); } private: DummyStanzaChannel* stanzaChannel; IQRouter* iqRouter; bool finished; boost::optional error; std::vector receivedData; }; CPPUNIT_TEST_SUITE_REGISTRATION(IBBReceiveSessionTest); swift-im-2.0+dev6/Swiften/FileTransfer/UnitTest/SOCKS5BytestreamClientSessionTest.cpp0000644000175000017500000002751412227051774030534 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Swift; boost::mt19937 randomGen; class SOCKS5BytestreamClientSessionTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(SOCKS5BytestreamClientSessionTest); CPPUNIT_TEST(testForSessionReady); CPPUNIT_TEST(testErrorHandlingHello); CPPUNIT_TEST(testErrorHandlingRequest); CPPUNIT_TEST(testWriteBytestream); CPPUNIT_TEST(testReadBytestream); CPPUNIT_TEST_SUITE_END(); const HostAddressPort destinationAddressPort; const std::string destination; public: SOCKS5BytestreamClientSessionTest() : destinationAddressPort(HostAddressPort(HostAddress("127.0.0.1"), 8888)), destination(SOCKS5BytestreamRegistry::getHostname("foo", JID("requester@example.com/test"), JID("target@example.com/test"))), eventLoop(NULL), timerFactory(NULL) { } void setUp() { randomGen.seed(time(NULL)); eventLoop = new DummyEventLoop(); timerFactory = new DummyTimerFactory(); connection = boost::make_shared(failingPorts, true, eventLoop); //connection->onDataSent.connect(boost::bind(&SOCKS5BytestreamServerSessionTest::handleDataWritten, this, _1)); //stream1 = boost::make_shared(createByteArray("abcdefg"))); // connection->onDataRead.connect(boost::bind(&SOCKS5BytestreamClientSessionTest::handleDataRead, this, _1)); } void tearDown() { //connection.reset(); delete timerFactory; delete eventLoop; } void testForSessionReady() { TestHelper helper; connection->onDataSent.connect(boost::bind(&TestHelper::handleConnectionDataWritten, &helper, _1)); SOCKS5BytestreamClientSession::ref clientSession = boost::make_shared(connection, destinationAddressPort, destination, timerFactory); clientSession->onSessionReady.connect(boost::bind(&TestHelper::handleSessionReady, &helper, _1)); clientSession->start(); eventLoop->processEvents(); CPPUNIT_ASSERT(createByteArray("\x05\x01\x00", 3) == helper.unprocessedInput); helper.unprocessedInput.clear(); serverRespondHelloOK(); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(createByteArray("\x05\x01\x00\x03", 4), createByteArray(&helper.unprocessedInput[0], 4)); CPPUNIT_ASSERT_EQUAL(createByteArray(destination.size()), createByteArray(helper.unprocessedInput[4])); CPPUNIT_ASSERT_EQUAL(createByteArray(destination), createByteArray(&helper.unprocessedInput[5], destination.size())); CPPUNIT_ASSERT_EQUAL(createByteArray("\x00", 1), createByteArray(&helper.unprocessedInput[5 + destination.size()], 1)); helper.unprocessedInput.clear(); serverRespondRequestOK(); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(true, helper.sessionReadyCalled); CPPUNIT_ASSERT_EQUAL(false, helper.sessionReadyError); } void testErrorHandlingHello() { TestHelper helper; connection->onDataSent.connect(boost::bind(&TestHelper::handleConnectionDataWritten, &helper, _1)); SOCKS5BytestreamClientSession::ref clientSession = boost::make_shared(connection, destinationAddressPort, destination, timerFactory); clientSession->onSessionReady.connect(boost::bind(&TestHelper::handleSessionReady, &helper, _1)); clientSession->start(); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(createByteArray("\x05\x01\x00", 3), helper.unprocessedInput); helper.unprocessedInput.clear(); serverRespondHelloAuthFail(); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(true, helper.sessionReadyCalled); CPPUNIT_ASSERT_EQUAL(true, helper.sessionReadyError); CPPUNIT_ASSERT_EQUAL(true, connection->disconnectCalled); } void testErrorHandlingRequest() { TestHelper helper; connection->onDataSent.connect(boost::bind(&TestHelper::handleConnectionDataWritten, &helper, _1)); SOCKS5BytestreamClientSession::ref clientSession = boost::make_shared(connection, destinationAddressPort, destination, timerFactory); clientSession->onSessionReady.connect(boost::bind(&TestHelper::handleSessionReady, &helper, _1)); clientSession->start(); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(createByteArray("\x05\x01\x00", 3), helper.unprocessedInput); helper.unprocessedInput.clear(); serverRespondHelloOK(); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(createByteArray("\x05\x01\x00\x03", 4), createByteArray(&helper.unprocessedInput[0], 4)); CPPUNIT_ASSERT_EQUAL(createByteArray(destination.size()), createByteArray(helper.unprocessedInput[4])); CPPUNIT_ASSERT_EQUAL(createByteArray(destination), createByteArray(&helper.unprocessedInput[5], destination.size())); CPPUNIT_ASSERT_EQUAL(createByteArray("\x00", 1), createByteArray(&helper.unprocessedInput[5 + destination.size()], 1)); helper.unprocessedInput.clear(); serverRespondRequestFail(); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(true, helper.sessionReadyCalled); CPPUNIT_ASSERT_EQUAL(true, helper.sessionReadyError); CPPUNIT_ASSERT_EQUAL(true, connection->disconnectCalled); } void testWriteBytestream() { TestHelper helper; connection->onDataSent.connect(boost::bind(&TestHelper::handleConnectionDataWritten, &helper, _1)); SOCKS5BytestreamClientSession::ref clientSession = boost::make_shared(connection, destinationAddressPort, destination, timerFactory); clientSession->onSessionReady.connect(boost::bind(&TestHelper::handleSessionReady, &helper, _1)); clientSession->start(); eventLoop->processEvents(); helper.unprocessedInput.clear(); serverRespondHelloOK(); eventLoop->processEvents(); helper.unprocessedInput.clear(); serverRespondRequestOK(); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(true, helper.sessionReadyCalled); CPPUNIT_ASSERT_EQUAL(false, helper.sessionReadyError); boost::shared_ptr output = boost::make_shared(); clientSession->startReceiving(output); ByteArray transferData = generateRandomByteArray(1024); connection->onDataRead(createSafeByteArrayRef(vecptr(transferData), transferData.size())); CPPUNIT_ASSERT_EQUAL(transferData, output->getData()); } void testReadBytestream() { TestHelper helper; connection->onDataSent.connect(boost::bind(&TestHelper::handleConnectionDataWritten, &helper, _1)); SOCKS5BytestreamClientSession::ref clientSession = boost::make_shared(connection, destinationAddressPort, destination, timerFactory); clientSession->onSessionReady.connect(boost::bind(&TestHelper::handleSessionReady, &helper, _1)); clientSession->start(); eventLoop->processEvents(); helper.unprocessedInput.clear(); serverRespondHelloOK(); eventLoop->processEvents(); helper.unprocessedInput.clear(); serverRespondRequestOK(); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(true, helper.sessionReadyCalled); CPPUNIT_ASSERT_EQUAL(false, helper.sessionReadyError); helper.unprocessedInput.clear(); ByteArray transferData = generateRandomByteArray(1024); boost::shared_ptr input = boost::make_shared(transferData); clientSession->startSending(input); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(createByteArray(vecptr(transferData), transferData.size()), helper.unprocessedInput); } private: static ByteArray generateRandomByteArray(size_t len) { boost::uniform_int<> dist(0, 255); boost::variate_generator > randomByte(randomGen, dist); ByteArray result(len); for (size_t i=0; i < len; ++i ) { result[i] = randomByte(); } return result; } // Server responses void serverRespondHelloOK() { connection->onDataRead(createSafeByteArrayRef("\x05\00", 2)); } void serverRespondHelloAuthFail() { connection->onDataRead(createSafeByteArrayRef("\x05\xFF", 2)); } void serverRespondRequestOK() { boost::shared_ptr dataToSend = createSafeByteArrayRef("\x05\x00\x00\x03", 4); append(*dataToSend, createSafeByteArray(destination.size())); append(*dataToSend, createSafeByteArray(destination)); append(*dataToSend, createSafeByteArray("\x00", 1)); connection->onDataRead(dataToSend); } void serverRespondRequestFail() { boost::shared_ptr correctData = createSafeByteArrayRef("\x05\x00\x00\x03", 4); append(*correctData, createSafeByteArray(destination.size())); append(*correctData, createSafeByteArray(destination)); append(*correctData, createSafeByteArray("\x00", 1)); boost::shared_ptr dataToSend; //ByteArray failingData = Hexify::unhexify("8417947d1d305c72c11520ea7d2c6e787396705e72c312c6ccc3f66613d7cae1b91b7ab48e8b59a17d559c15fb51"); //append(dataToSend, failingData); //SWIFT_LOG(debug) << "hexed: " << Hexify::hexify(failingData) << std::endl; do { ByteArray rndArray = generateRandomByteArray(correctData->size()); dataToSend = createSafeByteArrayRef(vecptr(rndArray), rndArray.size()); } while (*dataToSend == *correctData); connection->onDataRead(dataToSend); } private: struct TestHelper { TestHelper() : sessionReadyCalled(false), sessionReadyError(false) {} ByteArray unprocessedInput; bool sessionReadyCalled; bool sessionReadyError; void handleConnectionDataWritten(const SafeByteArray& data) { append(unprocessedInput, data); //SWIFT_LOG(debug) << "unprocessedInput (" << unprocessedInput.size() << "): " << Hexify::hexify(unprocessedInput) << std::endl; } void handleSessionReady(bool error) { sessionReadyCalled = true; sessionReadyError = error; } }; private: struct MockeryConnection : public Connection, public EventOwner, public boost::enable_shared_from_this { public: MockeryConnection(const std::vector& failingPorts, bool isResponsive, EventLoop* eventLoop) : eventLoop(eventLoop), failingPorts(failingPorts), isResponsive(isResponsive), disconnectCalled(false) {} void listen() { assert(false); } void connect(const HostAddressPort& address) { hostAddressPort = address; if (isResponsive) { bool fail = std::find(failingPorts.begin(), failingPorts.end(), address) != failingPorts.end(); eventLoop->postEvent(boost::bind(boost::ref(onConnectFinished), fail)); } } HostAddressPort getLocalAddress() const { return HostAddressPort(); } void disconnect() { disconnectCalled = true; } void write(const SafeByteArray& data) { eventLoop->postEvent(boost::ref(onDataWritten), shared_from_this()); onDataSent(data); } boost::signal onDataSent; EventLoop* eventLoop; boost::optional hostAddressPort; std::vector failingPorts; bool isResponsive; bool disconnectCalled; }; private: DummyEventLoop* eventLoop; DummyTimerFactory* timerFactory; boost::shared_ptr connection; const std::vector failingPorts; }; CPPUNIT_TEST_SUITE_REGISTRATION(SOCKS5BytestreamClientSessionTest); swift-im-2.0+dev6/Swiften/FileTransfer/UnitTest/IncomingJingleFileTransferTest.cpp0000644000175000017500000002556512227051774030227 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Swift; using namespace boost; class FakeRemoteJingleTransportCandidateSelector : public RemoteJingleTransportCandidateSelector { void addRemoteTransportCandidates(JingleTransportPayload::ref cand) { candidate = cand; } void selectCandidate() { boost::shared_ptr payload = make_shared(); payload->setCandidateError(true); payload->setSessionID(candidate->getSessionID()); onRemoteTransportCandidateSelectFinished(payload); } void setMinimumPriority(int) { } bool isActualCandidate(JingleTransportPayload::ref) { return false; } int getPriority(JingleTransportPayload::ref) { return 0; } JingleTransport::ref selectTransport(JingleTransportPayload::ref) { return JingleTransport::ref(); } private: JingleTransportPayload::ref candidate; }; class FakeRemoteJingleTransportCandidateSelectorFactory : public RemoteJingleTransportCandidateSelectorFactory { public: virtual ~FakeRemoteJingleTransportCandidateSelectorFactory() { } virtual RemoteJingleTransportCandidateSelector* createCandidateSelector() { return new FakeRemoteJingleTransportCandidateSelector(); } }; class FakeLocalJingleTransportCandidateGenerator : public LocalJingleTransportCandidateGenerator { public: virtual void generateLocalTransportCandidates(JingleTransportPayload::ref payload) { JingleS5BTransportPayload::ref payL = make_shared(); payL->setSessionID(payload->getSessionID()); onLocalTransportCandidatesGenerated(payL); } void emitonLocalTransportCandidatesGenerated(JingleTransportPayload::ref payload) { onLocalTransportCandidatesGenerated(payload); } virtual bool isActualCandidate(JingleTransportPayload::ref) { return false; } virtual int getPriority(JingleTransportPayload::ref) { return 0; } virtual JingleTransport::ref selectTransport(JingleTransportPayload::ref) { return JingleTransport::ref(); } }; class FakeLocalJingleTransportCandidateGeneratorFactory : public LocalJingleTransportCandidateGeneratorFactory { public: virtual LocalJingleTransportCandidateGenerator* createCandidateGenerator() { return new FakeLocalJingleTransportCandidateGenerator(); } }; class IncomingJingleFileTransferTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(IncomingJingleFileTransferTest); CPPUNIT_TEST(test_AcceptOnyIBBSendsSessionAccept); CPPUNIT_TEST(test_OnlyIBBTransferReceiveWorks); CPPUNIT_TEST(test_AcceptFailingS5BFallsBackToIBB); CPPUNIT_TEST_SUITE_END(); public: shared_ptr createTestling() { JID ourJID("our@jid.org/full"); return make_shared(ourJID, shared_ptr(fakeJingleSession), jingleContentPayload, fakeRJTCSF.get(), fakeLJTCF.get(), iqRouter, bytestreamRegistry, bytestreamProxy, timerFactory); } IQ::ref createIBBRequest(IBB::ref ibb, const JID& from, const std::string& id) { IQ::ref request = IQ::createRequest(IQ::Set, JID("foo@bar.com/baz"), id, ibb); request->setFrom(from); return request; } void setUp() { eventLoop = new DummyEventLoop(); fakeJingleSession = new FakeJingleSession("foo@bar.com/baz", "mysession"); jingleContentPayload = make_shared(); fakeRJTCSF = make_shared(); fakeLJTCF = make_shared(); stanzaChannel = new DummyStanzaChannel(); iqRouter = new IQRouter(stanzaChannel); bytestreamRegistry = new SOCKS5BytestreamRegistry(); timerFactory = new DummyTimerFactory(); connectionFactory = new DummyConnectionFactory(eventLoop); bytestreamProxy = new SOCKS5BytestreamProxy(connectionFactory, timerFactory); } void tearDown() { delete bytestreamProxy; delete connectionFactory; delete timerFactory; delete bytestreamRegistry; delete iqRouter; delete stanzaChannel; delete eventLoop; } // Tests whether IncomingJingleFileTransfer would accept a IBB only file transfer. void test_AcceptOnyIBBSendsSessionAccept() { //1. create your test incoming file transfer shared_ptr desc = make_shared(); desc->addOffer(StreamInitiationFileInfo("foo.txt", "", 10)); jingleContentPayload->addDescription(desc); JingleIBBTransportPayload::ref tpRef = make_shared(); tpRef->setSessionID("mysession"); jingleContentPayload->addTransport(tpRef); shared_ptr fileTransfer = createTestling(); //2. do 'accept' on a dummy writebytestream (you'll have to look if there already is one) shared_ptr byteStream = make_shared(); fileTransfer->accept(byteStream); // check whether accept has been called getCall(0); } void test_OnlyIBBTransferReceiveWorks() { //1. create your test incoming file transfer shared_ptr desc = make_shared(); desc->addOffer(StreamInitiationFileInfo("file.txt", "", 10)); jingleContentPayload->addDescription(desc); JingleIBBTransportPayload::ref tpRef = make_shared(); tpRef->setSessionID("mysession"); jingleContentPayload->addTransport(tpRef); shared_ptr fileTransfer = createTestling(); //2. do 'accept' on a dummy writebytestream (you'll have to look if there already is one) shared_ptr byteStream = make_shared(); fileTransfer->accept(byteStream); // check whether accept has been called getCall(0); stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBOpen("mysession", 0x10), "foo@bar.com/baz", "id-open")); stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBData("mysession", 0, createByteArray("abc")), "foo@bar.com/baz", "id-a")); CPPUNIT_ASSERT(createByteArray("abc") == byteStream->getData()); } void test_AcceptFailingS5BFallsBackToIBB() { //1. create your test incoming file transfer addFileTransferDescription(); // add SOCKS5BytestreamTransportPayload JingleS5BTransportPayload::ref payLoad = addJingleS5BPayload(); shared_ptr fileTransfer = createTestling(); //2. do 'accept' on a dummy writebytestream (you'll have to look if there already is one) shared_ptr byteStream = make_shared(); fileTransfer->accept(byteStream); // check whether accept has been called FakeJingleSession::AcceptCall acceptCall = getCall(0); CPPUNIT_ASSERT_EQUAL(payLoad->getSessionID(), acceptCall.payload->getSessionID()); // check for candidate error FakeJingleSession::InfoTransportCall infoTransportCall = getCall(1); JingleS5BTransportPayload::ref s5bPayload = dynamic_pointer_cast(infoTransportCall.payload); CPPUNIT_ASSERT(s5bPayload->hasCandidateError()); // indicate transport replace (Romeo) fakeJingleSession->onTransportReplaceReceived(getContentID(), addJingleIBBPayload()); FakeJingleSession::AcceptTransportCall acceptTransportCall = getCall(2); // send a bit of data stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBOpen("mysession", 0x10), "foo@bar.com/baz", "id-open")); stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBData("mysession", 0, createByteArray("abc")), "foo@bar.com/baz", "id-a")); CPPUNIT_ASSERT(createByteArray("abc") == byteStream->getData()); } void test_S5BTransferReceiveTest() { addFileTransferDescription(); JingleS5BTransportPayload::ref payLoad = addJingleS5BPayload(); } private: void addFileTransferDescription() { shared_ptr desc = make_shared(); desc->addOffer(StreamInitiationFileInfo("file.txt", "", 10)); jingleContentPayload->addDescription(desc); } shared_ptr addJingleS5BPayload() { JingleS5BTransportPayload::ref payLoad = make_shared(); payLoad->setSessionID("mysession"); jingleContentPayload->addTransport(payLoad); return payLoad; } shared_ptr addJingleIBBPayload() { JingleIBBTransportPayload::ref payLoad = make_shared(); payLoad->setSessionID("mysession"); jingleContentPayload->addTransport(payLoad); return payLoad; } JingleContentID getContentID() const { return JingleContentID(jingleContentPayload->getName(), jingleContentPayload->getCreator()); } template T getCall(int i) const { size_t index = static_cast(i); CPPUNIT_ASSERT(index < fakeJingleSession->calledCommands.size()); T* cmd = boost::get(&fakeJingleSession->calledCommands[index]); CPPUNIT_ASSERT(cmd); return *cmd; } private: EventLoop* eventLoop; FakeJingleSession* fakeJingleSession; shared_ptr jingleContentPayload; shared_ptr fakeRJTCSF; shared_ptr fakeLJTCF; DummyStanzaChannel* stanzaChannel; IQRouter* iqRouter; SOCKS5BytestreamRegistry* bytestreamRegistry; DummyConnectionFactory* connectionFactory; SOCKS5BytestreamProxy* bytestreamProxy; DummyTimerFactory* timerFactory; }; CPPUNIT_TEST_SUITE_REGISTRATION(IncomingJingleFileTransferTest); swift-im-2.0+dev6/Swiften/FileTransfer/OutgoingSIFileTransfer.cpp0000644000175000017500000000647112227051774024736 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include namespace Swift { OutgoingSIFileTransfer::OutgoingSIFileTransfer(const std::string& id, const JID& from, const JID& to, const std::string& name, int size, const std::string& description, boost::shared_ptr bytestream, IQRouter* iqRouter, SOCKS5BytestreamServer* socksServer) : id(id), from(from), to(to), name(name), size(size), description(description), bytestream(bytestream), iqRouter(iqRouter), socksServer(socksServer) { } void OutgoingSIFileTransfer::start() { StreamInitiation::ref streamInitiation(new StreamInitiation()); streamInitiation->setID(id); streamInitiation->setFileInfo(StreamInitiationFileInfo(name, description, size)); //streamInitiation->addProvidedMethod("http://jabber.org/protocol/bytestreams"); streamInitiation->addProvidedMethod("http://jabber.org/protocol/ibb"); StreamInitiationRequest::ref request = StreamInitiationRequest::create(to, streamInitiation, iqRouter); request->onResponse.connect(boost::bind(&OutgoingSIFileTransfer::handleStreamInitiationRequestResponse, this, _1, _2)); request->send(); } void OutgoingSIFileTransfer::stop() { } void OutgoingSIFileTransfer::handleStreamInitiationRequestResponse(StreamInitiation::ref response, ErrorPayload::ref error) { if (error) { finish(FileTransferError()); } else { if (response->getRequestedMethod() == "http://jabber.org/protocol/bytestreams") { socksServer->addReadBytestream(id, from, to, bytestream); Bytestreams::ref bytestreams(new Bytestreams()); bytestreams->setStreamID(id); HostAddressPort addressPort = socksServer->getAddressPort(); bytestreams->addStreamHost(Bytestreams::StreamHost(addressPort.getAddress().toString(), from, addressPort.getPort())); BytestreamsRequest::ref request = BytestreamsRequest::create(to, bytestreams, iqRouter); request->onResponse.connect(boost::bind(&OutgoingSIFileTransfer::handleBytestreamsRequestResponse, this, _1, _2)); request->send(); } else if (response->getRequestedMethod() == "http://jabber.org/protocol/ibb") { ibbSession = boost::make_shared(id, from, to, bytestream, iqRouter); ibbSession->onFinished.connect(boost::bind(&OutgoingSIFileTransfer::handleIBBSessionFinished, this, _1)); ibbSession->start(); } } } void OutgoingSIFileTransfer::handleBytestreamsRequestResponse(Bytestreams::ref, ErrorPayload::ref error) { if (error) { finish(FileTransferError()); } //socksServer->onTransferFinished.connect(); } void OutgoingSIFileTransfer::finish(boost::optional error) { if (ibbSession) { ibbSession->onFinished.disconnect(boost::bind(&OutgoingSIFileTransfer::handleIBBSessionFinished, this, _1)); ibbSession.reset(); } socksServer->removeReadBytestream(id, from, to); onFinished(error); } void OutgoingSIFileTransfer::handleIBBSessionFinished(boost::optional error) { finish(error); } } swift-im-2.0+dev6/Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelector.cpp0000644000175000017500000001112412227051774031475 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include "DefaultRemoteJingleTransportCandidateSelector.h" #include #include #include #include #include #include #include #include namespace Swift { DefaultRemoteJingleTransportCandidateSelector::DefaultRemoteJingleTransportCandidateSelector(ConnectionFactory* connectionFactory, TimerFactory* timerFactory) : connectionFactory(connectionFactory), timerFactory(timerFactory) { } DefaultRemoteJingleTransportCandidateSelector::~DefaultRemoteJingleTransportCandidateSelector() { } void DefaultRemoteJingleTransportCandidateSelector::addRemoteTransportCandidates(JingleTransportPayload::ref transportPayload) { JingleS5BTransportPayload::ref s5bPayload; transportSID = transportPayload->getSessionID(); if ((s5bPayload = boost::dynamic_pointer_cast(transportPayload))) { foreach(JingleS5BTransportPayload::Candidate c, s5bPayload->getCandidates()) { candidates.push(c); } } } void DefaultRemoteJingleTransportCandidateSelector::selectCandidate() { tryNextCandidate(true); } void DefaultRemoteJingleTransportCandidateSelector::tryNextCandidate(bool error) { if (error) { if (s5bSession) { SWIFT_LOG(debug) << "failed to connect" << std::endl; } if (candidates.empty()) { // failed to connect to any of the candidates // issue an error SWIFT_LOG(debug) << "out of candidates )=" << std::endl; JingleS5BTransportPayload::ref failed = boost::make_shared(); failed->setCandidateError(true); failed->setSessionID(transportSID); onRemoteTransportCandidateSelectFinished(failed); } else { lastCandidate = candidates.top(); // only try direct or assisted for now if (lastCandidate.type == JingleS5BTransportPayload::Candidate::DirectType || lastCandidate.type == JingleS5BTransportPayload::Candidate::AssistedType || lastCandidate.type == JingleS5BTransportPayload::Candidate::ProxyType ) { // create connection connection = connectionFactory->createConnection(); s5bSession = boost::make_shared(connection, lastCandidate.hostPort, SOCKS5BytestreamRegistry::getHostname(transportSID, requester, target), timerFactory); // bind onReady to this method s5bSession->onSessionReady.connect(boost::bind(&DefaultRemoteJingleTransportCandidateSelector::tryNextCandidate, this, _1)); std::string candidateType; if (lastCandidate.type == JingleS5BTransportPayload::Candidate::DirectType) { candidateType = "direct"; } else if (lastCandidate.type == JingleS5BTransportPayload::Candidate::AssistedType) { candidateType = "assisted"; } else if (lastCandidate.type == JingleS5BTransportPayload::Candidate::ProxyType) { candidateType = "proxy"; } // initiate connect SWIFT_LOG(debug) << "try to connect to candidate of type " << candidateType << " : " << lastCandidate.hostPort.toString() << std::endl; s5bSession->start(); // that's it. we're gonna be called back candidates.pop(); } else { s5bSession.reset(); candidates.pop(); tryNextCandidate(true); } } } else { // we have a working connection, hooray JingleS5BTransportPayload::ref success = boost::make_shared(); success->setCandidateUsed(lastCandidate.cid); success->setSessionID(transportSID); onRemoteTransportCandidateSelectFinished(success); } } void DefaultRemoteJingleTransportCandidateSelector::setMinimumPriority(int priority) { SWIFT_LOG(debug) << "priority: " << priority << std::endl; } void DefaultRemoteJingleTransportCandidateSelector::setRequesterTargtet(const JID& requester, const JID& target) { this->requester = requester; this->target = target; } SOCKS5BytestreamClientSession::ref DefaultRemoteJingleTransportCandidateSelector::getS5BSession() { return s5bSession; } bool DefaultRemoteJingleTransportCandidateSelector::isActualCandidate(JingleTransportPayload::ref /* transportPayload */) { return false; } int DefaultRemoteJingleTransportCandidateSelector::getPriority(JingleTransportPayload::ref /* transportPayload */) { return 0; } JingleTransport::ref DefaultRemoteJingleTransportCandidateSelector::selectTransport(JingleTransportPayload::ref /* transportPayload */) { return JingleTransport::ref(); } } swift-im-2.0+dev6/Swiften/FileTransfer/IBBRequest.h0000644000175000017500000000132612227051774022006 0ustar kismithkismith/* * 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 #include namespace Swift { class IBBRequest : public GenericRequest { public: typedef boost::shared_ptr ref; static ref create(const JID& from, const JID& to, boost::shared_ptr payload, IQRouter* router) { return ref(new IBBRequest(from, to, payload, router)); } private: IBBRequest(const JID& from, const JID& to, boost::shared_ptr payload, IQRouter* router) : GenericRequest(IQ::Set, from, to, payload, router) { } }; } swift-im-2.0+dev6/Swiften/FileTransfer/WriteBytestream.h0000644000175000017500000000110712227051774023170 0ustar kismithkismith/* * 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 #include #include #include namespace Swift { class SWIFTEN_API WriteBytestream { public: typedef boost::shared_ptr ref; virtual ~WriteBytestream(); virtual void write(const std::vector&) = 0; boost::signal&)> onWrite; }; } swift-im-2.0+dev6/Swiften/FileTransfer/SOCKS5BytestreamClientSession.h0000644000175000017500000000430212227051774025550 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include #include #include #include #include #include #include #include #include #include namespace Swift { class SOCKS5BytestreamRegistry; class Connection; class TimerFactory; /** * A session which has been connected to a SOCKS5 server (requester). * */ class SWIFTEN_API SOCKS5BytestreamClientSession { public: enum State { Initial, Hello, Authenticating, Ready, Writing, Reading, Finished, }; public: typedef boost::shared_ptr ref; public: SOCKS5BytestreamClientSession(boost::shared_ptr connection, const HostAddressPort&, const std::string&, TimerFactory*); void start(); void stop(); void startReceiving(boost::shared_ptr); void startSending(boost::shared_ptr); HostAddressPort getAddressPort() const; boost::signal onSessionReady; boost::signal)> onFinished; boost::signal onBytesSent; boost::signal onBytesReceived; private: void process(); void hello(); void authenticate(); void handleConnectFinished(bool error); void handleDataRead(boost::shared_ptr); void handleDisconnected(const boost::optional&); void handleWeFailedTimeout(); void finish(bool error); void sendData(); private: boost::shared_ptr connection; HostAddressPort addressPort; std::string destination; // hexify(SHA1(sessionID + requester + target)) State state; ByteArray unprocessedData; ByteArray authenticateAddress; int chunkSize; boost::shared_ptr writeBytestream; boost::shared_ptr readBytestream; Timer::ref weFailedTimeout; }; } swift-im-2.0+dev6/Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.cpp0000644000175000017500000000052612227051774030141 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { LocalJingleTransportCandidateGenerator::~LocalJingleTransportCandidateGenerator() { } } swift-im-2.0+dev6/Swiften/FileTransfer/SOCKS5BytestreamProxy.cpp0000644000175000017500000000506512227051774024451 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include "SOCKS5BytestreamProxy.h" #include #include #include #include namespace Swift { SOCKS5BytestreamProxy::SOCKS5BytestreamProxy(ConnectionFactory *connFactory, TimerFactory *timeFactory) : connectionFactory(connFactory), timerFactory(timeFactory) { } void SOCKS5BytestreamProxy::addS5BProxy(S5BProxyRequest::ref proxy) { localS5BProxies.push_back(proxy); } const std::vector& SOCKS5BytestreamProxy::getS5BProxies() const { return localS5BProxies; } void SOCKS5BytestreamProxy::connectToProxies(const std::string& sessionID) { SWIFT_LOG(debug) << "session ID: " << sessionID << std::endl; ProxyJIDClientSessionMap clientSessions; foreach(S5BProxyRequest::ref proxy, localS5BProxies) { boost::shared_ptr conn = connectionFactory->createConnection(); boost::shared_ptr session = boost::make_shared(conn, proxy->getStreamHost().get().addressPort, sessionID, timerFactory); clientSessions[proxy->getStreamHost().get().jid] = session; session->start(); } proxySessions[sessionID] = clientSessions; } boost::shared_ptr SOCKS5BytestreamProxy::getProxySessionAndCloseOthers(const JID& proxyJID, const std::string& sessionID) { // checking parameters if (proxySessions.find(sessionID) == proxySessions.end()) { return boost::shared_ptr(); } if (proxySessions[sessionID].find(proxyJID) == proxySessions[sessionID].end()) { return boost::shared_ptr(); } // get active session boost::shared_ptr activeSession = proxySessions[sessionID][proxyJID]; proxySessions[sessionID].erase(proxyJID); // close other sessions foreach(const ProxyJIDClientSessionMap::value_type& myPair, proxySessions[sessionID]) { myPair.second->stop(); } proxySessions.erase(sessionID); return activeSession; } boost::shared_ptr SOCKS5BytestreamProxy::createSOCKS5BytestreamClientSession(HostAddressPort addressPort, const std::string& destAddr) { SOCKS5BytestreamClientSession::ref connection = boost::make_shared(connectionFactory->createConnection(), addressPort, destAddr, timerFactory); return connection; } } swift-im-2.0+dev6/Swiften/FileTransfer/BytestreamsRequest.h0000644000175000017500000000221012227051774023705 0ustar kismithkismith/* * 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 #include #include namespace Swift { class BytestreamsRequest : public GenericRequest { public: typedef boost::shared_ptr ref; static ref create(const JID& jid, boost::shared_ptr payload, IQRouter* router) { return ref(new BytestreamsRequest(jid, payload, router)); } static ref create(const JID& from, const JID& to, boost::shared_ptr payload, IQRouter* router) { return ref(new BytestreamsRequest(from, to, payload, router)); } private: BytestreamsRequest(const JID& jid, boost::shared_ptr payload, IQRouter* router) : GenericRequest(IQ::Set, jid, payload, router) { } BytestreamsRequest(const JID& from, const JID& to, boost::shared_ptr payload, IQRouter* router) : GenericRequest(IQ::Set, from, to, payload, router) { } }; } swift-im-2.0+dev6/Swiften/FileTransfer/JingleIncomingIBBTransport.cpp0000644000175000017500000000125212227051774025520 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { JingleIncomingIBBTransport::JingleIncomingIBBTransport(const JID& from, const JID& to, const std::string& id, size_t size, IQRouter* router) : ibbSession(id, from, to, size, router) { ibbSession.onDataReceived.connect(boost::ref(onDataReceived)); ibbSession.onFinished.connect(boost::ref(onFinished)); } void JingleIncomingIBBTransport::start() { ibbSession.start(); } void JingleIncomingIBBTransport::stop() { ibbSession.stop(); } } swift-im-2.0+dev6/Swiften/FileTransfer/OutgoingJingleFileTransfer.h0000644000175000017500000000746612227051774025305 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Swift { class RemoteJingleTransportCandidateSelectorFactory; class RemoteJingleTransportCandidateSelector; class LocalJingleTransportCandidateGeneratorFactory; class LocalJingleTransportCandidateGenerator; class IQRouter; class ReadBytestream; class IBBSendSession; class IDGenerator; class IncrementalBytestreamHashCalculator; class SOCKS5BytestreamRegistry; class SOCKS5BytestreamProxy; class SWIFTEN_API OutgoingJingleFileTransfer : public OutgoingFileTransfer { public: OutgoingJingleFileTransfer(JingleSession::ref, RemoteJingleTransportCandidateSelectorFactory*, LocalJingleTransportCandidateGeneratorFactory*, IQRouter*, IDGenerator*, const JID& from, const JID& to, boost::shared_ptr, const StreamInitiationFileInfo&, SOCKS5BytestreamRegistry*, SOCKS5BytestreamProxy*); virtual ~OutgoingJingleFileTransfer(); void start(); void stop(); void cancel(); private: void handleSessionAcceptReceived(const JingleContentID&, JingleDescription::ref, JingleTransportPayload::ref); void handleSessionTerminateReceived(boost::optional reason); void handleTransportAcceptReceived(const JingleContentID&, JingleTransportPayload::ref); void handleTransportInfoReceived(const JingleContentID&, JingleTransportPayload::ref); void handleLocalTransportCandidatesGenerated(JingleTransportPayload::ref); void handleRemoteTransportCandidateSelectFinished(JingleTransportPayload::ref); private: void replaceTransportWithIBB(const JingleContentID&); void handleTransferFinished(boost::optional); void activateProxySession(const JID &proxy); void handleActivateProxySessionResult(boost::shared_ptr request, ErrorPayload::ref error); void proxySessionReady(const JID& proxy, bool error); private: typedef std::map CandidateMap; private: void startTransferViaOurCandidateChoice(JingleS5BTransportPayload::Candidate); void startTransferViaTheirCandidateChoice(JingleS5BTransportPayload::Candidate); void decideOnCandidates(); void fillCandidateMap(CandidateMap& map, JingleS5BTransportPayload::ref s5bPayload); private: void sendSessionInfoHash(); private: JingleSession::ref session; RemoteJingleTransportCandidateSelector* remoteCandidateSelector; LocalJingleTransportCandidateGenerator* localCandidateGenerator; IQRouter* router; IDGenerator* idGenerator; JID fromJID; JID toJID; boost::shared_ptr readStream; StreamInitiationFileInfo fileInfo; IncrementalBytestreamHashCalculator *hashCalculator; boost::shared_ptr ibbSession; JingleS5BTransportPayload::ref ourCandidateChoice; JingleS5BTransportPayload::ref theirCandidateChoice; CandidateMap ourCandidates; CandidateMap theirCandidates; SOCKS5BytestreamRegistry* s5bRegistry; SOCKS5BytestreamProxy* s5bProxy; SOCKS5BytestreamClientSession::ref clientSession; SOCKS5BytestreamServerSession* serverSession; JingleContentID contentID; std::string s5bSessionID; bool canceled; }; } swift-im-2.0+dev6/Swiften/FileTransfer/SOCKS5BytestreamRegistry.cpp0000644000175000017500000000456312227051774025142 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include namespace Swift { SOCKS5BytestreamRegistry::SOCKS5BytestreamRegistry() { } void SOCKS5BytestreamRegistry::addReadBytestream(const std::string& destination, boost::shared_ptr byteStream) { readBytestreams[destination] = byteStream; } void SOCKS5BytestreamRegistry::removeReadBytestream(const std::string& destination) { readBytestreams.erase(destination); } boost::shared_ptr SOCKS5BytestreamRegistry::getReadBytestream(const std::string& destination) const { ReadBytestreamMap::const_iterator i = readBytestreams.find(destination); if (i != readBytestreams.end()) { return i->second; } return boost::shared_ptr(); } void SOCKS5BytestreamRegistry::addWriteBytestream(const std::string& destination, boost::shared_ptr byteStream) { writeBytestreams[destination] = byteStream; } void SOCKS5BytestreamRegistry::removeWriteBytestream(const std::string& destination) { writeBytestreams.erase(destination); } boost::shared_ptr SOCKS5BytestreamRegistry::getWriteBytestream(const std::string& destination) const { WriteBytestreamMap::const_iterator i = writeBytestreams.find(destination); if (i != writeBytestreams.end()) { return i->second; } return boost::shared_ptr(); } std::string SOCKS5BytestreamRegistry::generateSessionID() { return idGenerator.generateID(); } SOCKS5BytestreamServerSession* SOCKS5BytestreamRegistry::getConnectedSession(const std::string& destination) { if (serverSessions.find(destination) != serverSessions.end()) { return serverSessions[destination]; } else { SWIFT_LOG(debug) << "No active connction for stream ID " << destination << " found!" << std::endl; return NULL; } } std::string SOCKS5BytestreamRegistry::getHostname(const std::string& sessionID, const JID& requester, const JID& target) { return Hexify::hexify(SHA1::getHash(createSafeByteArray(sessionID + requester.toString() + target.toString()))); } } swift-im-2.0+dev6/Swiften/FileTransfer/OutgoingJingleFileTransfer.cpp0000644000175000017500000004313512227051774025631 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include "OutgoingJingleFileTransfer.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Swift { OutgoingJingleFileTransfer::OutgoingJingleFileTransfer(JingleSession::ref session, RemoteJingleTransportCandidateSelectorFactory* remoteFactory, LocalJingleTransportCandidateGeneratorFactory* localFactory, IQRouter* router, IDGenerator *idGenerator, const JID& fromJID, const JID& toJID, boost::shared_ptr readStream, const StreamInitiationFileInfo &fileInfo, SOCKS5BytestreamRegistry* bytestreamRegistry, SOCKS5BytestreamProxy* bytestreamProxy) : session(session), router(router), idGenerator(idGenerator), fromJID(fromJID), toJID(toJID), readStream(readStream), fileInfo(fileInfo), s5bRegistry(bytestreamRegistry), s5bProxy(bytestreamProxy), serverSession(NULL), contentID(JingleContentID(idGenerator->generateID(), JingleContentPayload::InitiatorCreator)), canceled(false) { session->onSessionAcceptReceived.connect(boost::bind(&OutgoingJingleFileTransfer::handleSessionAcceptReceived, this, _1, _2, _3)); session->onSessionTerminateReceived.connect(boost::bind(&OutgoingJingleFileTransfer::handleSessionTerminateReceived, this, _1)); session->onTransportInfoReceived.connect(boost::bind(&OutgoingJingleFileTransfer::handleTransportInfoReceived, this, _1, _2)); session->onTransportAcceptReceived.connect(boost::bind(&OutgoingJingleFileTransfer::handleTransportAcceptReceived, this, _1, _2)); fileSizeInBytes = fileInfo.getSize(); filename = fileInfo.getName(); localCandidateGenerator = localFactory->createCandidateGenerator(); localCandidateGenerator->onLocalTransportCandidatesGenerated.connect(boost::bind(&OutgoingJingleFileTransfer::handleLocalTransportCandidatesGenerated, this, _1)); remoteCandidateSelector = remoteFactory->createCandidateSelector(); remoteCandidateSelector->onRemoteTransportCandidateSelectFinished.connect(boost::bind(&OutgoingJingleFileTransfer::handleRemoteTransportCandidateSelectFinished, this, _1)); // calculate both, MD5 and SHA-1 since we don't know which one the other side supports hashCalculator = new IncrementalBytestreamHashCalculator(true, true); this->readStream->onRead.connect(boost::bind(&IncrementalBytestreamHashCalculator::feedData, hashCalculator, _1)); } OutgoingJingleFileTransfer::~OutgoingJingleFileTransfer() { readStream->onRead.disconnect(boost::bind(&IncrementalBytestreamHashCalculator::feedData, hashCalculator, _1)); delete hashCalculator; } void OutgoingJingleFileTransfer::start() { onStateChange(FileTransfer::State(FileTransfer::State::WaitingForStart)); s5bSessionID = s5bRegistry->generateSessionID(); SWIFT_LOG(debug) << "S5B SessionID: " << s5bSessionID << std::endl; //s5bProxy->connectToProxies(s5bSessionID); JingleS5BTransportPayload::ref transport = boost::make_shared(); localCandidateGenerator->generateLocalTransportCandidates(transport); } void OutgoingJingleFileTransfer::stop() { } void OutgoingJingleFileTransfer::cancel() { canceled = true; session->sendTerminate(JinglePayload::Reason::Cancel); if (ibbSession) { ibbSession->stop(); } SOCKS5BytestreamServerSession *serverSession = s5bRegistry->getConnectedSession(SOCKS5BytestreamRegistry::getHostname(s5bSessionID, session->getInitiator(), toJID)); if (serverSession) { serverSession->stop(); } if (clientSession) { clientSession->stop(); } onStateChange(FileTransfer::State(FileTransfer::State::Canceled)); } void OutgoingJingleFileTransfer::handleSessionAcceptReceived(const JingleContentID& id, JingleDescription::ref /* decription */, JingleTransportPayload::ref transportPayload) { if (canceled) { return; } onStateChange(FileTransfer::State(FileTransfer::State::Negotiating)); JingleIBBTransportPayload::ref ibbPayload; JingleS5BTransportPayload::ref s5bPayload; if ((ibbPayload = boost::dynamic_pointer_cast(transportPayload))) { ibbSession = boost::make_shared(ibbPayload->getSessionID(), fromJID, toJID, readStream, router); ibbSession->setBlockSize(ibbPayload->getBlockSize()); ibbSession->onBytesSent.connect(boost::bind(boost::ref(onProcessedBytes), _1)); ibbSession->onFinished.connect(boost::bind(&OutgoingJingleFileTransfer::handleTransferFinished, this, _1)); ibbSession->start(); onStateChange(FileTransfer::State(FileTransfer::State::Transferring)); } else if ((s5bPayload = boost::dynamic_pointer_cast(transportPayload))) { fillCandidateMap(theirCandidates, s5bPayload); remoteCandidateSelector->setRequesterTargtet(toJID, session->getInitiator()); remoteCandidateSelector->addRemoteTransportCandidates(s5bPayload); remoteCandidateSelector->selectCandidate(); } else { // TODO: error handling SWIFT_LOG(debug) << "Unknown transport payload! Try replaceing with IBB." << std::endl; replaceTransportWithIBB(id); } } void OutgoingJingleFileTransfer::handleSessionTerminateReceived(boost::optional reason) { if (canceled) { return; } if (ibbSession) { ibbSession->stop(); } if (clientSession) { clientSession->stop(); } if (serverSession) { serverSession->stop(); } if (reason.is_initialized() && reason.get().type == JinglePayload::Reason::Cancel) { onStateChange(FileTransfer::State(FileTransfer::State::Canceled)); onFinished(FileTransferError(FileTransferError::PeerError)); } else if (reason.is_initialized() && reason.get().type == JinglePayload::Reason::Success) { onStateChange(FileTransfer::State(FileTransfer::State::Finished)); onFinished(boost::optional()); } else { onStateChange(FileTransfer::State(FileTransfer::State::Failed)); onFinished(FileTransferError(FileTransferError::PeerError)); } canceled = true; } void OutgoingJingleFileTransfer::handleTransportAcceptReceived(const JingleContentID& /* contentID */, JingleTransportPayload::ref transport) { if (canceled) { return; } if (JingleIBBTransportPayload::ref ibbPayload = boost::dynamic_pointer_cast(transport)) { ibbSession = boost::make_shared(ibbPayload->getSessionID(), fromJID, toJID, readStream, router); ibbSession->setBlockSize(ibbPayload->getBlockSize()); ibbSession->onBytesSent.connect(boost::bind(boost::ref(onProcessedBytes), _1)); ibbSession->onFinished.connect(boost::bind(&OutgoingJingleFileTransfer::handleTransferFinished, this, _1)); ibbSession->start(); onStateChange(FileTransfer::State(FileTransfer::State::Transferring)); } else { // error handling SWIFT_LOG(debug) << "Replacing with anything other than IBB isn't supported yet." << std::endl; session->sendTerminate(JinglePayload::Reason::FailedTransport); onStateChange(FileTransfer::State(FileTransfer::State::Failed)); } } void OutgoingJingleFileTransfer::startTransferViaOurCandidateChoice(JingleS5BTransportPayload::Candidate candidate) { SWIFT_LOG(debug) << "Transferring data using our candidate." << std::endl; if (candidate.type == JingleS5BTransportPayload::Candidate::ProxyType) { // get proxy client session from remoteCandidateSelector clientSession = remoteCandidateSelector->getS5BSession(); // wait on transport-info } else { clientSession = remoteCandidateSelector->getS5BSession(); clientSession->onBytesSent.connect(boost::bind(boost::ref(onProcessedBytes), _1)); clientSession->onFinished.connect(boost::bind(&OutgoingJingleFileTransfer::handleTransferFinished, this, _1)); clientSession->startSending(readStream); onStateChange(FileTransfer::State(FileTransfer::State::Transferring)); } assert(clientSession); } void OutgoingJingleFileTransfer::startTransferViaTheirCandidateChoice(JingleS5BTransportPayload::Candidate candidate) { SWIFT_LOG(debug) << "Transferring data using their candidate." << std::endl; if (candidate.type == JingleS5BTransportPayload::Candidate::ProxyType) { // connect to proxy clientSession = s5bProxy->createSOCKS5BytestreamClientSession(candidate.hostPort, SOCKS5BytestreamRegistry::getHostname(s5bSessionID, session->getInitiator(), toJID)); clientSession->onSessionReady.connect(boost::bind(&OutgoingJingleFileTransfer::proxySessionReady, this, candidate.jid, _1)); clientSession->start(); // on reply send activate } else { serverSession = s5bRegistry->getConnectedSession(SOCKS5BytestreamRegistry::getHostname(s5bSessionID, session->getInitiator(), toJID)); serverSession->onBytesSent.connect(boost::bind(boost::ref(onProcessedBytes), _1)); serverSession->onFinished.connect(boost::bind(&OutgoingJingleFileTransfer::handleTransferFinished, this, _1)); serverSession->startTransfer(); } onStateChange(FileTransfer::State(FileTransfer::State::Transferring)); } // decide on candidates according to http://xmpp.org/extensions/xep-0260.html#complete void OutgoingJingleFileTransfer::decideOnCandidates() { if (ourCandidateChoice && theirCandidateChoice) { std::string our_cid = ourCandidateChoice->getCandidateUsed(); std::string their_cid = theirCandidateChoice->getCandidateUsed(); if (ourCandidateChoice->hasCandidateError() && theirCandidateChoice->hasCandidateError()) { replaceTransportWithIBB(contentID); } else if (!our_cid.empty() && theirCandidateChoice->hasCandidateError()) { // use our candidate startTransferViaOurCandidateChoice(theirCandidates[our_cid]); } else if (!their_cid.empty() && ourCandidateChoice->hasCandidateError()) { // use their candidate startTransferViaTheirCandidateChoice(ourCandidates[their_cid]); } else if (!our_cid.empty() && !their_cid.empty()) { // compare priorites, if same we win if (ourCandidates.find(their_cid) == ourCandidates.end() || theirCandidates.find(our_cid) == theirCandidates.end()) { SWIFT_LOG(debug) << "Didn't recognize candidate IDs!" << std::endl; session->sendTerminate(JinglePayload::Reason::FailedTransport); onStateChange(FileTransfer::State(FileTransfer::State::Failed)); onFinished(FileTransferError(FileTransferError::PeerError)); return; } JingleS5BTransportPayload::Candidate ourCandidate = theirCandidates[our_cid]; JingleS5BTransportPayload::Candidate theirCandidate = ourCandidates[their_cid]; if (ourCandidate.priority > theirCandidate.priority) { startTransferViaOurCandidateChoice(ourCandidate); } else if (ourCandidate.priority < theirCandidate.priority) { startTransferViaTheirCandidateChoice(theirCandidate); } else { startTransferViaOurCandidateChoice(ourCandidate); } } } else { SWIFT_LOG(debug) << "Can't make a decision yet!" << std::endl; } } void OutgoingJingleFileTransfer::fillCandidateMap(CandidateMap& map, JingleS5BTransportPayload::ref s5bPayload) { map.clear(); foreach (JingleS5BTransportPayload::Candidate candidate, s5bPayload->getCandidates()) { map[candidate.cid] = candidate; } } void OutgoingJingleFileTransfer::proxySessionReady(const JID& proxy, bool error) { if (error) { // indicate proxy error } else { // activate proxy activateProxySession(proxy); } } void OutgoingJingleFileTransfer::activateProxySession(const JID& proxy) { S5BProxyRequest::ref proxyRequest = boost::make_shared(); proxyRequest->setSID(s5bSessionID); proxyRequest->setActivate(toJID); boost::shared_ptr > request = boost::make_shared >(IQ::Set, proxy, proxyRequest, router); request->onResponse.connect(boost::bind(&OutgoingJingleFileTransfer::handleActivateProxySessionResult, this, _1, _2)); request->send(); } void OutgoingJingleFileTransfer::handleActivateProxySessionResult(boost::shared_ptr /*request*/, ErrorPayload::ref error) { if (error) { SWIFT_LOG(debug) << "ERROR" << std::endl; } else { // send activated to other jingle party JingleS5BTransportPayload::ref proxyActivate = boost::make_shared(); proxyActivate->setActivated(theirCandidateChoice->getCandidateUsed()); session->sendTransportInfo(contentID, proxyActivate); // start transferring clientSession->onBytesSent.connect(boost::bind(boost::ref(onProcessedBytes), _1)); clientSession->onFinished.connect(boost::bind(&OutgoingJingleFileTransfer::handleTransferFinished, this, _1)); clientSession->startSending(readStream); onStateChange(FileTransfer::State(FileTransfer::State::Transferring)); } } void OutgoingJingleFileTransfer::sendSessionInfoHash() { SWIFT_LOG(debug) << std::endl; JingleFileTransferHash::ref hashElement = boost::make_shared(); hashElement->setHash("sha-1", hashCalculator->getSHA1String()); hashElement->setHash("md5", hashCalculator->getMD5String()); session->sendInfo(hashElement); } void OutgoingJingleFileTransfer::handleTransportInfoReceived(const JingleContentID& /* contentID */, JingleTransportPayload::ref transport) { if (canceled) { return; } if (JingleS5BTransportPayload::ref s5bPayload = boost::dynamic_pointer_cast(transport)) { if (s5bPayload->hasCandidateError() || !s5bPayload->getCandidateUsed().empty()) { theirCandidateChoice = s5bPayload; decideOnCandidates(); } else if(!s5bPayload->getActivated().empty()) { if (ourCandidateChoice->getCandidateUsed() == s5bPayload->getActivated()) { clientSession->onBytesSent.connect(boost::bind(boost::ref(onProcessedBytes), _1)); clientSession->onFinished.connect(boost::bind(&OutgoingJingleFileTransfer::handleTransferFinished, this, _1)); clientSession->startSending(readStream); onStateChange(FileTransfer::State(FileTransfer::State::Transferring)); } else { SWIFT_LOG(debug) << "ourCandidateChoice doesn't match activated proxy candidate!" << std::endl; JingleS5BTransportPayload::ref proxyError = boost::make_shared(); proxyError->setProxyError(true); proxyError->setSessionID(s5bSessionID); session->sendTransportInfo(contentID, proxyError); } } } } void OutgoingJingleFileTransfer::handleLocalTransportCandidatesGenerated(JingleTransportPayload::ref payload) { if (canceled) { return; } JingleFileTransferDescription::ref description = boost::make_shared(); description->addOffer(fileInfo); JingleTransportPayload::ref transport; if (JingleIBBTransportPayload::ref ibbTransport = boost::dynamic_pointer_cast(payload)) { ibbTransport->setBlockSize(4096); ibbTransport->setSessionID(idGenerator->generateID()); transport = ibbTransport; } else if (JingleS5BTransportPayload::ref s5bTransport = boost::dynamic_pointer_cast(payload)) { //fillCandidateMap(ourCandidates, s5bTransport); //s5bTransport->setSessionID(s5bSessionID); JingleS5BTransportPayload::ref emptyCandidates = boost::make_shared(); emptyCandidates->setSessionID(s5bTransport->getSessionID()); fillCandidateMap(ourCandidates, emptyCandidates); transport = emptyCandidates; s5bRegistry->addReadBytestream(SOCKS5BytestreamRegistry::getHostname(s5bSessionID, session->getInitiator(), toJID), readStream); } else { SWIFT_LOG(debug) << "Unknown tranport payload: " << typeid(*payload).name() << std::endl; return; } session->sendInitiate(contentID, description, transport); onStateChange(FileTransfer::State(FileTransfer::State::WaitingForAccept)); } void OutgoingJingleFileTransfer::handleRemoteTransportCandidateSelectFinished(JingleTransportPayload::ref payload) { if (canceled) { return; } if (JingleS5BTransportPayload::ref s5bPayload = boost::dynamic_pointer_cast(payload)) { ourCandidateChoice = s5bPayload; session->sendTransportInfo(contentID, s5bPayload); decideOnCandidates(); } } void OutgoingJingleFileTransfer::replaceTransportWithIBB(const JingleContentID& id) { SWIFT_LOG(debug) << "Both parties failed. Replace transport with IBB." << std::endl; JingleIBBTransportPayload::ref ibbTransport = boost::make_shared(); ibbTransport->setBlockSize(4096); ibbTransport->setSessionID(idGenerator->generateID()); session->sendTransportReplace(id, ibbTransport); } void OutgoingJingleFileTransfer::handleTransferFinished(boost::optional error) { if (error) { session->sendTerminate(JinglePayload::Reason::ConnectivityError); onStateChange(FileTransfer::State(FileTransfer::State::Failed)); onFinished(error); } else { sendSessionInfoHash(); /* session->terminate(JinglePayload::Reason::Success); onStateChange(FileTransfer::State(FileTransfer::State::Finished)); */ } // } } swift-im-2.0+dev6/Swiften/FileTransfer/IBBSendSession.h0000644000175000017500000000271512227051774022616 0ustar kismithkismith/* * 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 #include #include #include #include #include #include #include #include namespace Swift { class IQRouter; class SWIFTEN_API IBBSendSession { public: IBBSendSession(const std::string& id, const JID& from, const JID& to, boost::shared_ptr bytestream, IQRouter* router); ~IBBSendSession(); void start(); void stop(); const JID& getSender() const { return from; } const JID& getReceiver() const { return to; } void setBlockSize(int blockSize) { this->blockSize = blockSize; } boost::signal)> onFinished; boost::signal onBytesSent; private: void handleIBBResponse(IBB::ref, ErrorPayload::ref); void finish(boost::optional); void sendMoreData(); void handleDataAvailable(); private: std::string id; JID from; JID to; boost::shared_ptr bytestream; IQRouter* router; int blockSize; int sequenceNumber; bool active; bool waitingForData; }; } swift-im-2.0+dev6/Swiften/FileTransfer/FileWriteBytestream.cpp0000644000175000017500000000160512227051774024326 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include namespace Swift { FileWriteBytestream::FileWriteBytestream(const boost::filesystem::path& file) : file(file), stream(NULL) { } FileWriteBytestream::~FileWriteBytestream() { if (stream) { stream->close(); stream = NULL; } } void FileWriteBytestream::write(const std::vector& data) { if (!stream) { stream = new boost::filesystem::ofstream(file, std::ios_base::out|std::ios_base::binary); } assert(stream->good()); stream->write(reinterpret_cast(&data[0]), data.size()); onWrite(data); } void FileWriteBytestream::close() { if (stream) { stream->close(); stream = NULL; } } } swift-im-2.0+dev6/Swiften/FileTransfer/SOCKS5BytestreamServer.h0000644000175000017500000000265712227051774024247 0ustar kismithkismith/* * 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 #include #include #include #include #include #include namespace Swift { class SOCKS5BytestreamServerSession; class SOCKS5BytestreamServer { public: SOCKS5BytestreamServer(boost::shared_ptr connectionServer, SOCKS5BytestreamRegistry* registry); HostAddressPort getAddressPort() const; void start(); void stop(); void addReadBytestream(const std::string& id, const JID& from, const JID& to, boost::shared_ptr byteStream); void removeReadBytestream(const std::string& id, const JID& from, const JID& to); /*protected: boost::shared_ptr getBytestream(const std::string& dest);*/ private: void handleNewConnection(boost::shared_ptr connection); static std::string getSOCKSDestinationAddress(const std::string& id, const JID& from, const JID& to); private: friend class SOCKS5BytestreamServerSession; boost::shared_ptr connectionServer; SOCKS5BytestreamRegistry* registry; std::vector > sessions; }; } swift-im-2.0+dev6/Swiften/FileTransfer/SOCKS5BytestreamServerSession.cpp0000644000175000017500000001450112227051774026135 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include namespace Swift { SOCKS5BytestreamServerSession::SOCKS5BytestreamServerSession(boost::shared_ptr connection, SOCKS5BytestreamRegistry* bytestreams) : connection(connection), bytestreams(bytestreams), state(Initial), chunkSize(131072), waitingForData(false) { connection->onDisconnected.connect(boost::bind(&SOCKS5BytestreamServerSession::handleDisconnected, this, _1)); } SOCKS5BytestreamServerSession::~SOCKS5BytestreamServerSession() { if (state != Finished && state != Initial) { std::cerr << "Warning: SOCKS5BytestremServerSession unfinished" << std::endl; finish(false); } } void SOCKS5BytestreamServerSession::start() { SWIFT_LOG(debug) << std::endl; connection->onDataRead.connect(boost::bind(&SOCKS5BytestreamServerSession::handleDataRead, this, _1)); state = WaitingForAuthentication; } void SOCKS5BytestreamServerSession::stop() { connection->onDataWritten.disconnect(boost::bind(&SOCKS5BytestreamServerSession::sendData, this)); connection->onDataRead.disconnect(boost::bind(&SOCKS5BytestreamServerSession::handleDataRead, this, _1)); connection->disconnect(); if (readBytestream) { readBytestream->onDataAvailable.disconnect(boost::bind(&SOCKS5BytestreamServerSession::handleDataAvailable, this)); } state = Finished; } void SOCKS5BytestreamServerSession::startTransfer() { if (state == ReadyForTransfer) { if (readBytestream) { state = WritingData; connection->onDataWritten.connect(boost::bind(&SOCKS5BytestreamServerSession::sendData, this)); sendData(); } else if(writeBytestream) { state = ReadingData; writeBytestream->write(unprocessedData); onBytesReceived(unprocessedData.size()); unprocessedData.clear(); } } else { SWIFT_LOG(debug) << "Not ready for transfer!" << std::endl; } } HostAddressPort SOCKS5BytestreamServerSession::getAddressPort() const { return connection->getLocalAddress(); } void SOCKS5BytestreamServerSession::handleDataRead(boost::shared_ptr data) { if (state != ReadingData) { append(unprocessedData, *data); process(); } else { writeBytestream->write(createByteArray(vecptr(*data), data->size())); onBytesReceived(data->size()); } } void SOCKS5BytestreamServerSession::handleDataAvailable() { if (waitingForData) { sendData(); } } void SOCKS5BytestreamServerSession::handleDisconnected(const boost::optional& error) { SWIFT_LOG(debug) << (error ? (error == Connection::ReadError ? "Read Error" : "Write Error") : "No Error") << std::endl; if (error) { finish(true); } } void SOCKS5BytestreamServerSession::process() { if (state == WaitingForAuthentication) { if (unprocessedData.size() >= 2) { size_t authCount = unprocessedData[1]; size_t i = 2; while (i < 2 + authCount && i < unprocessedData.size()) { // Skip authentication mechanism ++i; } if (i == 2 + authCount) { // Authentication message is complete if (i != unprocessedData.size()) { SWIFT_LOG(debug) << "Junk after authentication mechanism" << std::endl; } unprocessedData.clear(); connection->write(createSafeByteArray("\x05\x00", 2)); state = WaitingForRequest; } } } else if (state == WaitingForRequest) { if (unprocessedData.size() >= 5) { ByteArray requestID; size_t i = 5; size_t hostnameSize = unprocessedData[4]; while (i < 5 + hostnameSize && i < unprocessedData.size()) { requestID.push_back(unprocessedData[i]); ++i; } // Skip the port: 2 byte large, one already skipped. Add one for comparison with size i += 2; if (i <= unprocessedData.size()) { if (i != unprocessedData.size()) { SWIFT_LOG(debug) << "Junk after authentication mechanism" << std::endl; } unprocessedData.clear(); std::string streamID = byteArrayToString(requestID); readBytestream = bytestreams->getReadBytestream(streamID); writeBytestream = bytestreams->getWriteBytestream(streamID); SafeByteArray result = createSafeByteArray("\x05", 1); result.push_back((readBytestream || writeBytestream) ? 0x0 : 0x4); append(result, createByteArray("\x00\x03", 2)); result.push_back(static_cast(requestID.size())); append(result, concat(requestID, createByteArray("\x00\x00", 2))); if (!readBytestream && !writeBytestream) { SWIFT_LOG(debug) << "Readstream or Wrtiestream with ID " << streamID << " not found!" << std::endl; connection->write(result); finish(true); } else { SWIFT_LOG(debug) << "Found " << (readBytestream ? "Readstream" : "Writestream") << ". Sent OK." << std::endl; connection->write(result); bytestreams->serverSessions[streamID] = this; state = ReadyForTransfer; if (readBytestream) { readBytestream->onDataAvailable.connect(boost::bind(&SOCKS5BytestreamServerSession::handleDataAvailable, this)); } } } } } } void SOCKS5BytestreamServerSession::sendData() { if (!readBytestream->isFinished()) { try { SafeByteArray dataToSend = createSafeByteArray(*readBytestream->read(chunkSize)); if (!dataToSend.empty()) { connection->write(dataToSend); onBytesSent(dataToSend.size()); waitingForData = false; } else { waitingForData = true; } } catch (const BytestreamException&) { finish(true); } } else { finish(false); } } void SOCKS5BytestreamServerSession::finish(bool error) { connection->onDataWritten.disconnect(boost::bind(&SOCKS5BytestreamServerSession::sendData, this)); connection->onDataRead.disconnect(boost::bind(&SOCKS5BytestreamServerSession::handleDataRead, this, _1)); connection->onDisconnected.disconnect(boost::bind(&SOCKS5BytestreamServerSession::handleDisconnected, this, _1)); readBytestream.reset(); state = Finished; if (error) { onFinished(boost::optional(FileTransferError::PeerError)); } else { onFinished(boost::optional()); } } } swift-im-2.0+dev6/Swiften/FileTransfer/FileTransfer.h0000644000175000017500000000222212227051774022421 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include #include #include #include namespace Swift { class FileTransfer { public: struct State { enum FTState { Canceled, Failed, Finished, Negotiating, Transferring, WaitingForStart, WaitingForAccept, }; FTState state; std::string message; State(FTState state) : state(state), message("") {} State(FTState state, std::string message) : state(state), message(message) {} }; public: typedef boost::shared_ptr ref; public: boost::uintmax_t fileSizeInBytes; std::string filename; std::string algo; std::string hash; public: virtual void cancel() = 0; public: boost::signal onProcessedBytes; boost::signal onStateChange; boost::signal)> onFinished; public: virtual ~FileTransfer() {} }; } swift-im-2.0+dev6/Swiften/FileTransfer/IncomingJingleFileTransfer.h0000644000175000017500000001203612227051774025242 0ustar kismithkismith/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Swift { class IQRouter; class RemoteJingleTransportCandidateSelectorFactory; class LocalJingleTransportCandidateGeneratorFactory; class RemoteJingleTransportCandidateSelector; class LocalJingleTransportCandidateGenerator; class SOCKS5BytestreamRegistry; class SOCKS5BytestreamProxy; class IncrementalBytestreamHashCalculator; class SWIFTEN_API IncomingJingleFileTransfer : public IncomingFileTransfer { public: typedef boost::shared_ptr ref; enum State { Initial, CreatingInitialTransports, NegotiatingTransport, Transferring, WaitingForFallbackOrTerminate, Terminated }; IncomingJingleFileTransfer( const JID& recipient, JingleSession::ref, JingleContentPayload::ref content, RemoteJingleTransportCandidateSelectorFactory*, LocalJingleTransportCandidateGeneratorFactory*, IQRouter* router, SOCKS5BytestreamRegistry* bytestreamRegistry, SOCKS5BytestreamProxy* bytestreamProxy, TimerFactory*); ~IncomingJingleFileTransfer(); virtual void accept(WriteBytestream::ref); virtual const JID& getSender() const; virtual const JID& getRecipient() const; void cancel(); private: void handleSessionTerminateReceived(boost::optional); void handleSessionInfoReceived(JinglePayload::ref); void handleTransportReplaceReceived(const JingleContentID&, JingleTransportPayload::ref); void handleTransportInfoReceived(const JingleContentID&, JingleTransportPayload::ref); void handleLocalTransportCandidatesGenerated(JingleTransportPayload::ref candidates); void handleRemoteTransportCandidateSelectFinished(JingleTransportPayload::ref candidate); void setActiveTransport(JingleTransport::ref transport); void handleTransportDataReceived(const std::vector& data); void handleWriteStreamDataReceived(const std::vector& data); void stopActiveTransport(); void checkCandidateSelected(); JingleIncomingIBBTransport::ref createIBBTransport(JingleIBBTransportPayload::ref ibbTransport); JingleContentID getContentID() const; void checkIfAllDataReceived(); bool verifyReceviedData(); void finishOffTransfer(); void handleTransferFinished(boost::optional); private: typedef std::map CandidateMap; private: void activateProxySession(const JID &proxy); void handleActivateProxySessionResult(boost::shared_ptr request, ErrorPayload::ref error); void proxySessionReady(const JID& proxy, bool error); private: void useOurCandidateChoiceForTransfer(JingleS5BTransportPayload::Candidate candidate); void useTheirCandidateChoiceForTransfer(JingleS5BTransportPayload::Candidate candidate); void decideOnUsedTransport(); void fillCandidateMap(CandidateMap& map, JingleS5BTransportPayload::ref s5bPayload); private: JID ourJID; JingleSession::ref session; IQRouter* router; JingleContentPayload::ref initialContent; State state; JingleFileTransferDescription::ref description; WriteBytestream::ref stream; boost::uintmax_t receivedBytes; IncrementalBytestreamHashCalculator* hashCalculator; Timer::ref waitOnHashTimer; IDGenerator idGenerator; RemoteJingleTransportCandidateSelector* candidateSelector; LocalJingleTransportCandidateGenerator* candidateGenerator; SOCKS5BytestreamRegistry* s5bRegistry; SOCKS5BytestreamProxy* s5bProxy; bool remoteTransportCandidateSelectFinished; JingleTransportPayload::ref selectedRemoteTransportCandidate; bool localTransportCandidateSelectFinished; JingleTransportPayload::ref selectedLocalTransportCandidate; JingleS5BTransportPayload::ref ourCandidate; JingleS5BTransportPayload::ref theirCandidate; CandidateMap ourCandidates; CandidateMap theirCandidates; SOCKS5BytestreamClientSession::ref clientSession; std::string s5bDestination; std::string s5bSessionID; SOCKS5BytestreamServerSession* serverSession; JingleTransport::ref activeTransport; }; } swift-im-2.0+dev6/Swiften/FileTransfer/IBBSendSession.cpp0000644000175000017500000000472012227051774023147 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include namespace Swift { IBBSendSession::IBBSendSession(const std::string& id, const JID& from, const JID& to, boost::shared_ptr bytestream, IQRouter* router) : id(id), from(from), to(to), bytestream(bytestream), router(router), blockSize(4096), sequenceNumber(0), active(false), waitingForData(false) { bytestream->onDataAvailable.connect(boost::bind(&IBBSendSession::handleDataAvailable, this)); } IBBSendSession::~IBBSendSession() { bytestream->onDataAvailable.disconnect(boost::bind(&IBBSendSession::handleDataAvailable, this)); } void IBBSendSession::start() { IBBRequest::ref request = IBBRequest::create(from, to, IBB::createIBBOpen(id, blockSize), router); request->onResponse.connect(boost::bind(&IBBSendSession::handleIBBResponse, this, _1, _2)); active = true; request->send(); } void IBBSendSession::stop() { if (active && router->isAvailable()) { IBBRequest::create(from, to, IBB::createIBBClose(id), router)->send(); } finish(boost::optional()); } void IBBSendSession::handleIBBResponse(IBB::ref, ErrorPayload::ref error) { if (!error && active) { if (!bytestream->isFinished()) { sendMoreData(); } else { finish(boost::optional()); } } else { finish(FileTransferError(FileTransferError::PeerError)); } } void IBBSendSession::sendMoreData() { try { boost::shared_ptr data = bytestream->read(blockSize); if (!data->empty()) { waitingForData = false; IBBRequest::ref request = IBBRequest::create(from, to, IBB::createIBBData(id, sequenceNumber, *data), router); sequenceNumber++; request->onResponse.connect(boost::bind(&IBBSendSession::handleIBBResponse, this, _1, _2)); request->send(); onBytesSent(data->size()); } else { waitingForData = true; } } catch (const BytestreamException&) { finish(FileTransferError(FileTransferError::ReadError)); } } void IBBSendSession::finish(boost::optional error) { active = false; onFinished(error); } void IBBSendSession::handleDataAvailable() { if (waitingForData) { sendMoreData(); } } } swift-im-2.0+dev6/Swiften/FileTransfer/FileTransferError.h0000644000175000017500000000071412227051774023437 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once namespace Swift { class FileTransferError { public: enum Type { UnknownError, PeerError, ReadError, ClosedError, }; FileTransferError(Type type = UnknownError) : type(type) {} Type getType() const { return type; } private: Type type; }; } swift-im-2.0+dev6/Swiften/FileTransfer/OutgoingFileTransfer.h0000644000175000017500000000076512227051774024147 0ustar kismithkismith/* * 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 #include namespace Swift { class OutgoingFileTransfer : public FileTransfer { public: typedef boost::shared_ptr ref; public: virtual ~OutgoingFileTransfer(); virtual void start() = 0; virtual void stop() = 0; }; } swift-im-2.0+dev6/Swiften/FileTransfer/IncrementalBytestreamHashCalculator.cpp0000644000175000017500000000275512227051774027522 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include #include #include #include namespace Swift { IncrementalBytestreamHashCalculator::IncrementalBytestreamHashCalculator(bool doMD5, bool doSHA1) { md5Hasher = doMD5 ? new MD5() : NULL; sha1Hasher = doSHA1 ? new SHA1() : NULL; } IncrementalBytestreamHashCalculator::~IncrementalBytestreamHashCalculator() { delete md5Hasher; delete sha1Hasher; } void IncrementalBytestreamHashCalculator::feedData(const ByteArray& data) { if (md5Hasher) { md5Hasher->update(data); } if (sha1Hasher) { sha1Hasher->update(data); } } /* void IncrementalBytestreamHashCalculator::feedData(const SafeByteArray& data) { if (md5Hasher) { md5Hasher->update(createByteArray(data.data(), data.size())); } if (sha1Hasher) { sha1Hasher->update(createByteArray(data.data(), data.size())); } }*/ std::string IncrementalBytestreamHashCalculator::getSHA1String() { if (sha1Hasher) { ByteArray result = sha1Hasher->getHash(); return Hexify::hexify(result); } else { return std::string(); } } std::string IncrementalBytestreamHashCalculator::getMD5String() { if (md5Hasher) { ByteArray result = md5Hasher->getHash(); return Hexify::hexify(result); } else { return std::string(); } } } swift-im-2.0+dev6/Swiften/FileTransfer/SOCKS5BytestreamProxy.h0000644000175000017500000000266212227051774024116 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include #include #include #include #include #include #include namespace Swift { /** * - manages list of working S5B proxies * - creates initial connections (for the candidates you provide) */ class SWIFTEN_API SOCKS5BytestreamProxy { public: SOCKS5BytestreamProxy(ConnectionFactory*, TimerFactory*); void addS5BProxy(S5BProxyRequest::ref); const std::vector& getS5BProxies() const; void connectToProxies(const std::string& sessionID); boost::shared_ptr getProxySessionAndCloseOthers(const JID& proxyJID, const std::string& sessionID); boost::shared_ptr createSOCKS5BytestreamClientSession(HostAddressPort addressPort, const std::string& destAddr); private: ConnectionFactory* connectionFactory; TimerFactory* timerFactory; typedef std::map > ProxyJIDClientSessionMap; std::map proxySessions; std::vector localS5BProxies; }; } swift-im-2.0+dev6/Swiften/FileTransfer/IncomingFileTransfer.h0000644000175000017500000000127612227051774024115 0ustar kismithkismith/* * 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 #include #include #include #include namespace Swift { class IncomingFileTransfer : public FileTransfer { public: typedef boost::shared_ptr ref; virtual ~IncomingFileTransfer(); virtual void accept(WriteBytestream::ref) = 0; virtual const JID& getSender() const = 0; virtual const JID& getRecipient() const = 0; }; } swift-im-2.0+dev6/Swiften/FileTransfer/IncomingJingleFileTransfer.cpp0000644000175000017500000005342512227051774025604 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Swift { IncomingJingleFileTransfer::IncomingJingleFileTransfer( const JID& ourJID, JingleSession::ref session, JingleContentPayload::ref content, RemoteJingleTransportCandidateSelectorFactory* candidateSelectorFactory, LocalJingleTransportCandidateGeneratorFactory* candidateGeneratorFactory, IQRouter* router, SOCKS5BytestreamRegistry* registry, SOCKS5BytestreamProxy* proxy, TimerFactory* timerFactory) : ourJID(ourJID), session(session), router(router), initialContent(content), state(Initial), receivedBytes(0), s5bRegistry(registry), s5bProxy(proxy), remoteTransportCandidateSelectFinished(false), localTransportCandidateSelectFinished(false), serverSession(0) { candidateSelector = candidateSelectorFactory->createCandidateSelector(); candidateSelector->onRemoteTransportCandidateSelectFinished.connect(boost::bind(&IncomingJingleFileTransfer::handleRemoteTransportCandidateSelectFinished, this, _1)); candidateGenerator = candidateGeneratorFactory->createCandidateGenerator(); candidateGenerator->onLocalTransportCandidatesGenerated.connect(boost::bind(&IncomingJingleFileTransfer::handleLocalTransportCandidatesGenerated, this, _1)); session->onTransportInfoReceived.connect(boost::bind(&IncomingJingleFileTransfer::handleTransportInfoReceived, this, _1, _2)); session->onTransportReplaceReceived.connect(boost::bind(&IncomingJingleFileTransfer::handleTransportReplaceReceived, this, _1, _2)); session->onSessionTerminateReceived.connect(boost::bind(&IncomingJingleFileTransfer::handleSessionTerminateReceived, this, _1)); session->onSessionInfoReceived.connect(boost::bind(&IncomingJingleFileTransfer::handleSessionInfoReceived, this, _1)); description = initialContent->getDescription(); assert(description); assert(description->getOffers().size() == 1); StreamInitiationFileInfo fileInfo = description->getOffers().front(); fileSizeInBytes = fileInfo.getSize(); filename = fileInfo.getName(); hash = fileInfo.getHash(); algo = fileInfo.getAlgo(); waitOnHashTimer = timerFactory->createTimer(5000); waitOnHashTimer->onTick.connect(boost::bind(&IncomingJingleFileTransfer::finishOffTransfer, this)); } IncomingJingleFileTransfer::~IncomingJingleFileTransfer() { stream->onWrite.disconnect(boost::bind(&IncrementalBytestreamHashCalculator::feedData, hashCalculator, _1)); delete hashCalculator; session->onSessionTerminateReceived.disconnect(boost::bind(&IncomingJingleFileTransfer::handleSessionTerminateReceived, this, _1)); session->onTransportReplaceReceived.disconnect(boost::bind(&IncomingJingleFileTransfer::handleTransportReplaceReceived, this, _1, _2)); session->onTransportInfoReceived.disconnect(boost::bind(&IncomingJingleFileTransfer::handleTransportInfoReceived, this, _1, _2)); candidateGenerator->onLocalTransportCandidatesGenerated.disconnect(boost::bind(&IncomingJingleFileTransfer::handleLocalTransportCandidatesGenerated, this, _1)); delete candidateGenerator; candidateSelector->onRemoteTransportCandidateSelectFinished.disconnect(boost::bind(&IncomingJingleFileTransfer::handleRemoteTransportCandidateSelectFinished, this, _1)); delete candidateSelector; } void IncomingJingleFileTransfer::accept(WriteBytestream::ref stream) { assert(!this->stream); this->stream = stream; hashCalculator = new IncrementalBytestreamHashCalculator( algo == "md5" || hash.empty() , algo == "sha-1" || hash.empty() ); stream->onWrite.connect(boost::bind(&IncrementalBytestreamHashCalculator::feedData, hashCalculator, _1)); stream->onWrite.connect(boost::bind(&IncomingJingleFileTransfer::handleWriteStreamDataReceived, this, _1)); onStateChange(FileTransfer::State(FileTransfer::State::Negotiating)); if (JingleIBBTransportPayload::ref ibbTransport = initialContent->getTransport()) { SWIFT_LOG(debug) << "Got IBB transport payload!" << std::endl; setActiveTransport(createIBBTransport(ibbTransport)); session->sendAccept(getContentID(), initialContent->getDescriptions()[0], ibbTransport); } else if (JingleS5BTransportPayload::ref s5bTransport = initialContent->getTransport()) { SWIFT_LOG(debug) << "Got S5B transport payload!" << std::endl; state = CreatingInitialTransports; s5bSessionID = s5bTransport->getSessionID().empty() ? idGenerator.generateID() : s5bTransport->getSessionID(); s5bDestination = SOCKS5BytestreamRegistry::getHostname(s5bSessionID, ourJID, session->getInitiator()); s5bRegistry->addWriteBytestream(s5bDestination, stream); fillCandidateMap(theirCandidates, s5bTransport); candidateSelector->addRemoteTransportCandidates(s5bTransport); candidateSelector->setRequesterTargtet(session->getInitiator(), ourJID); s5bTransport->setSessionID(s5bSessionID); candidateGenerator->generateLocalTransportCandidates(s5bTransport); } else { assert(false); } } const JID& IncomingJingleFileTransfer::getSender() const { return session->getInitiator(); } const JID& IncomingJingleFileTransfer::getRecipient() const { return ourJID; } void IncomingJingleFileTransfer::cancel() { session->sendTerminate(JinglePayload::Reason::Cancel); if (activeTransport) activeTransport->stop(); if (serverSession) serverSession->stop(); if (clientSession) clientSession->stop(); onStateChange(FileTransfer::State(FileTransfer::State::Canceled)); } void IncomingJingleFileTransfer::handleLocalTransportCandidatesGenerated(JingleTransportPayload::ref candidates) { if (state == CreatingInitialTransports) { if (JingleS5BTransportPayload::ref s5bCandidates = boost::dynamic_pointer_cast(candidates)) { //localTransportCandidateSelectFinished = true; //JingleS5BTransportPayload::ref emptyCandidates = boost::make_shared(); //emptyCandidates->setSessionID(s5bCandidates->getSessionID()); fillCandidateMap(ourCandidates, s5bCandidates); session->sendAccept(getContentID(), initialContent->getDescriptions()[0], s5bCandidates); state = NegotiatingTransport; candidateSelector->selectCandidate(); } } else { SWIFT_LOG(debug) << "Unhandled state!" << std::endl; } } void IncomingJingleFileTransfer::handleRemoteTransportCandidateSelectFinished(JingleTransportPayload::ref transport) { SWIFT_LOG(debug) << std::endl; if (state == Terminated) { return; } if (JingleS5BTransportPayload::ref s5bPayload = boost::dynamic_pointer_cast(transport)) { //remoteTransportCandidateSelectFinished = true; //selectedRemoteTransportCandidate = transport; ourCandidate = s5bPayload; //checkCandidateSelected(); decideOnUsedTransport(); session->sendTransportInfo(getContentID(), s5bPayload); } else { SWIFT_LOG(debug) << "Expected something different here." << std::endl; } } void IncomingJingleFileTransfer::checkCandidateSelected() { assert(false); if (localTransportCandidateSelectFinished && remoteTransportCandidateSelectFinished) { if (candidateGenerator->isActualCandidate(selectedLocalTransportCandidate) && candidateSelector->isActualCandidate(selectedRemoteTransportCandidate)) { if (candidateGenerator->getPriority(selectedLocalTransportCandidate) > candidateSelector->getPriority(selectedRemoteTransportCandidate)) { setActiveTransport(candidateGenerator->selectTransport(selectedLocalTransportCandidate)); } else { setActiveTransport(candidateSelector->selectTransport(selectedRemoteTransportCandidate)); } } else if (candidateSelector->isActualCandidate(selectedRemoteTransportCandidate)) { setActiveTransport(candidateSelector->selectTransport(selectedRemoteTransportCandidate)); } else if (candidateGenerator->isActualCandidate(selectedLocalTransportCandidate)) { setActiveTransport(candidateGenerator->selectTransport(selectedLocalTransportCandidate)); } else { state = WaitingForFallbackOrTerminate; } } } void IncomingJingleFileTransfer::setActiveTransport(JingleTransport::ref transport) { state = Transferring; onStateChange(FileTransfer::State(FileTransfer::State::Transferring)); activeTransport = transport; activeTransport->onDataReceived.connect(boost::bind(&IncomingJingleFileTransfer::handleTransportDataReceived, this, _1)); activeTransport->onFinished.connect(boost::bind(&IncomingJingleFileTransfer::handleTransferFinished, this, _1)); activeTransport->start(); } bool IncomingJingleFileTransfer::verifyReceviedData() { if (algo.empty() || hash.empty()) { SWIFT_LOG(debug) << "no verification possible, skipping" << std::endl; return true; } else { if (algo == "sha-1") { SWIFT_LOG(debug) << "verify data via SHA-1 hash: " << (hash == hashCalculator->getSHA1String()) << std::endl; return hash == hashCalculator->getSHA1String(); } else if (algo == "md5") { SWIFT_LOG(debug) << "verify data via MD5 hash: " << (hash == hashCalculator->getMD5String()) << std::endl; return hash == hashCalculator->getMD5String(); } else { SWIFT_LOG(debug) << "no verification possible, skipping" << std::endl; return true; } } } void IncomingJingleFileTransfer::finishOffTransfer() { if (verifyReceviedData()) { onStateChange(FileTransfer::State(FileTransfer::State::Finished)); session->sendTerminate(JinglePayload::Reason::Success); } else { onStateChange(FileTransfer::State(FileTransfer::State::Failed, "Verification failed.")); session->sendTerminate(JinglePayload::Reason::MediaError); } state = Terminated; waitOnHashTimer->stop(); } void IncomingJingleFileTransfer::handleSessionInfoReceived(JinglePayload::ref jinglePayload) { if (state == Terminated) { return; } JingleFileTransferHash::ref transferHash = jinglePayload->getPayload(); if (transferHash) { SWIFT_LOG(debug) << "Recevied hash information." << std::endl; if (transferHash->getHashes().find("sha-1") != transferHash->getHashes().end()) { algo = "sha-1"; hash = transferHash->getHashes().find("sha-1")->second; } else if (transferHash->getHashes().find("md5") != transferHash->getHashes().end()) { algo = "md5"; hash = transferHash->getHashes().find("md5")->second; } checkIfAllDataReceived(); } } void IncomingJingleFileTransfer::handleSessionTerminateReceived(boost::optional reason) { SWIFT_LOG(debug) << "session terminate received" << std::endl; if (activeTransport) activeTransport->stop(); if (reason && reason.get().type == JinglePayload::Reason::Cancel) { onStateChange(FileTransfer::State(FileTransfer::State::Canceled, "Other user canceled the transfer.")); } else if (reason && reason.get().type == JinglePayload::Reason::Success) { /*if (verifyReceviedData()) { onStateChange(FileTransfer::State(FileTransfer::State::Finished)); } else { onStateChange(FileTransfer::State(FileTransfer::State::Failed, "Verification failed.")); }*/ } state = Terminated; } void IncomingJingleFileTransfer::checkIfAllDataReceived() { if (receivedBytes == fileSizeInBytes) { SWIFT_LOG(debug) << "All data received." << std::endl; if (hash.empty()) { SWIFT_LOG(debug) << "No hash information yet. Waiting 5 seconds on hash info." << std::endl; waitOnHashTimer->start(); } else { SWIFT_LOG(debug) << "We already have hash info using " << algo << " algorithm. Finishing off transfer." << std::endl; finishOffTransfer(); } } else if (receivedBytes > fileSizeInBytes) { SWIFT_LOG(debug) << "We got more than we could handle!" << std::endl; } } void IncomingJingleFileTransfer::handleTransportDataReceived(const std::vector& data) { SWIFT_LOG(debug) << data.size() << " bytes received" << std::endl; onProcessedBytes(data.size()); stream->write(data); receivedBytes += data.size(); checkIfAllDataReceived(); } void IncomingJingleFileTransfer::handleWriteStreamDataReceived(const std::vector& data) { receivedBytes += data.size(); checkIfAllDataReceived(); } void IncomingJingleFileTransfer::useOurCandidateChoiceForTransfer(JingleS5BTransportPayload::Candidate candidate) { SWIFT_LOG(debug) << std::endl; if (candidate.type == JingleS5BTransportPayload::Candidate::ProxyType) { // get proxy client session from remoteCandidateSelector clientSession = candidateSelector->getS5BSession(); // wait on transport-info } else { // ask s5b client clientSession = candidateSelector->getS5BSession(); if (clientSession) { state = Transferring; SWIFT_LOG(debug) << clientSession->getAddressPort().toString() << std::endl; clientSession->onBytesReceived.connect(boost::bind(boost::ref(onProcessedBytes), _1)); clientSession->onFinished.connect(boost::bind(&IncomingJingleFileTransfer::handleTransferFinished, this, _1)); clientSession->startReceiving(stream); } else { SWIFT_LOG(debug) << "No S5B client session found!!!" << std::endl; } } } void IncomingJingleFileTransfer::useTheirCandidateChoiceForTransfer(JingleS5BTransportPayload::Candidate candidate) { SWIFT_LOG(debug) << std::endl; if (candidate.type == JingleS5BTransportPayload::Candidate::ProxyType) { // get proxy client session from s5bRegistry clientSession = s5bProxy->createSOCKS5BytestreamClientSession(candidate.hostPort, SOCKS5BytestreamRegistry::getHostname(s5bSessionID, ourJID, session->getInitiator())); clientSession->onSessionReady.connect(boost::bind(&IncomingJingleFileTransfer::proxySessionReady, this, candidate.jid, _1)); clientSession->start(); // on reply send activate } else { // ask s5b server serverSession = s5bRegistry->getConnectedSession(s5bDestination); if (serverSession) { state = Transferring; serverSession->onBytesReceived.connect(boost::bind(boost::ref(onProcessedBytes), _1)); serverSession->onFinished.connect(boost::bind(&IncomingJingleFileTransfer::handleTransferFinished, this, _1)); serverSession->startTransfer(); } else { SWIFT_LOG(debug) << "No S5B server session found!!!" << std::endl; } } } void IncomingJingleFileTransfer::fillCandidateMap(CandidateMap& map, JingleS5BTransportPayload::ref s5bPayload) { map.clear(); foreach (JingleS5BTransportPayload::Candidate candidate, s5bPayload->getCandidates()) { map[candidate.cid] = candidate; } } void IncomingJingleFileTransfer::decideOnUsedTransport() { if (ourCandidate && theirCandidate) { if (ourCandidate->hasCandidateError() && theirCandidate->hasCandidateError()) { state = WaitingForFallbackOrTerminate; return; } std::string our_cid = ourCandidate->getCandidateUsed(); std::string their_cid = theirCandidate->getCandidateUsed(); if (ourCandidate->hasCandidateError() && !their_cid.empty()) { useTheirCandidateChoiceForTransfer(ourCandidates[their_cid]); onStateChange(FileTransfer::State(FileTransfer::State::Transferring)); } else if (theirCandidate->hasCandidateError() && !our_cid.empty()) { useOurCandidateChoiceForTransfer(theirCandidates[our_cid]); onStateChange(FileTransfer::State(FileTransfer::State::Transferring)); } else if (!our_cid.empty() && !their_cid.empty()) { // compare priorites, if same they win if (ourCandidates.find(their_cid) == ourCandidates.end() || theirCandidates.find(our_cid) == theirCandidates.end()) { SWIFT_LOG(debug) << "Didn't recognize candidate IDs!" << std::endl; session->sendTerminate(JinglePayload::Reason::FailedTransport); onStateChange(FileTransfer::State(FileTransfer::State::Canceled, "Failed to negotiate candidate.")); onFinished(FileTransferError(FileTransferError::PeerError)); return; } JingleS5BTransportPayload::Candidate our_candidate = theirCandidates[our_cid]; JingleS5BTransportPayload::Candidate their_candidate = ourCandidates[their_cid]; if (our_candidate.priority > their_candidate.priority) { useOurCandidateChoiceForTransfer(our_candidate); } else if (our_candidate.priority < their_candidate.priority) { useTheirCandidateChoiceForTransfer(their_candidate); } else { useTheirCandidateChoiceForTransfer(their_candidate); } onStateChange(FileTransfer::State(FileTransfer::State::Transferring)); } else { assert(false); } } else { SWIFT_LOG(debug) << "Can't make a transport decision yet." << std::endl; } } void IncomingJingleFileTransfer::proxySessionReady(const JID& proxy, bool error) { if (error) { // indicate proxy error } else { // activate proxy activateProxySession(proxy); } } void IncomingJingleFileTransfer::activateProxySession(const JID &proxy) { S5BProxyRequest::ref proxyRequest = boost::make_shared(); proxyRequest->setSID(s5bSessionID); proxyRequest->setActivate(session->getInitiator()); boost::shared_ptr > request = boost::make_shared >(IQ::Set, proxy, proxyRequest, router); request->onResponse.connect(boost::bind(&IncomingJingleFileTransfer::handleActivateProxySessionResult, this, _1, _2)); request->send(); } void IncomingJingleFileTransfer::handleActivateProxySessionResult(boost::shared_ptr /*request*/, ErrorPayload::ref error) { SWIFT_LOG(debug) << std::endl; if (error) { SWIFT_LOG(debug) << "ERROR" << std::endl; } else { // send activated to other jingle party JingleS5BTransportPayload::ref proxyActivate = boost::make_shared(); proxyActivate->setActivated(theirCandidate->getCandidateUsed()); session->sendTransportInfo(getContentID(), proxyActivate); // start transferring clientSession->onBytesReceived.connect(boost::bind(boost::ref(onProcessedBytes), _1)); clientSession->onFinished.connect(boost::bind(&IncomingJingleFileTransfer::handleTransferFinished, this, _1)); clientSession->startReceiving(stream); onStateChange(FileTransfer::State(FileTransfer::State::Transferring)); } } void IncomingJingleFileTransfer::handleTransportInfoReceived(const JingleContentID&, JingleTransportPayload::ref transport) { SWIFT_LOG(debug) << "transport info received" << std::endl; if (state == Terminated) { return; } if (JingleS5BTransportPayload::ref s5bPayload = boost::dynamic_pointer_cast(transport)) { if (!s5bPayload->getActivated().empty()) { if (ourCandidate->getCandidateUsed() == s5bPayload->getActivated()) { clientSession->onBytesReceived.connect(boost::bind(boost::ref(onProcessedBytes), _1)); clientSession->onFinished.connect(boost::bind(&IncomingJingleFileTransfer::handleTransferFinished, this, _1)); clientSession->startReceiving(stream); onStateChange(FileTransfer::State(FileTransfer::State::Transferring)); } else { SWIFT_LOG(debug) << "ourCandidateChoice doesn't match activated proxy candidate!" << std::endl; JingleS5BTransportPayload::ref proxyError = boost::make_shared(); proxyError->setProxyError(true); proxyError->setSessionID(s5bSessionID); session->sendTransportInfo(getContentID(), proxyError); } } else { theirCandidate = s5bPayload; decideOnUsedTransport(); } } else { SWIFT_LOG(debug) << "Expected something different here." << std::endl; } /*localTransportCandidateSelectFinished = true; selectedLocalTransportCandidate = transport; if (candidateGenerator->isActualCandidate(transport)) { candidateSelector->setMinimumPriority(candidateGenerator->getPriority(transport)); }*/ //checkCandidateSelected(); } void IncomingJingleFileTransfer::handleTransportReplaceReceived(const JingleContentID& content, JingleTransportPayload::ref transport) { if (state == Terminated) { return; } if (JingleIBBTransportPayload::ref ibbTransport = boost::dynamic_pointer_cast(transport)) { SWIFT_LOG(debug) << "transport replaced with IBB" << std::endl; setActiveTransport(createIBBTransport(ibbTransport)); session->sendTransportAccept(content, ibbTransport); } else { SWIFT_LOG(debug) << "transport replaced failed" << std::endl; session->sendTransportReject(content, transport); } } void IncomingJingleFileTransfer::stopActiveTransport() { if (activeTransport) { activeTransport->stop(); activeTransport->onDataReceived.disconnect(boost::bind(&IncomingJingleFileTransfer::handleTransportDataReceived, this, _1)); } } JingleIncomingIBBTransport::ref IncomingJingleFileTransfer::createIBBTransport(JingleIBBTransportPayload::ref ibbTransport) { // TODO: getOffer() -> getOffers correction return boost::make_shared(session->getInitiator(), getRecipient(), ibbTransport->getSessionID(), description->getOffers()[0].getSize(), router); } JingleContentID IncomingJingleFileTransfer::getContentID() const { return JingleContentID(initialContent->getName(), initialContent->getCreator()); } void IncomingJingleFileTransfer::handleTransferFinished(boost::optional error) { if (state == Terminated) { return; } if (error) { session->sendTerminate(JinglePayload::Reason::ConnectivityError); onStateChange(FileTransfer::State(FileTransfer::State::Failed)); onFinished(error); } // } } swift-im-2.0+dev6/Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGenerator.cpp0000644000175000017500000000777612227051774031464 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include "DefaultLocalJingleTransportCandidateGenerator.h" #include #include #include #include #include #include #include #include #include #include namespace Swift { DefaultLocalJingleTransportCandidateGenerator::DefaultLocalJingleTransportCandidateGenerator(ConnectivityManager* connectivityManager, SOCKS5BytestreamRegistry* s5bRegistry, SOCKS5BytestreamProxy* s5bProxy, JID& ownJID) : connectivityManager(connectivityManager), s5bRegistry(s5bRegistry), s5bProxy(s5bProxy), ownJID(ownJID) { } DefaultLocalJingleTransportCandidateGenerator::~DefaultLocalJingleTransportCandidateGenerator() { } void DefaultLocalJingleTransportCandidateGenerator::generateLocalTransportCandidates(JingleTransportPayload::ref transportPayload) { if (boost::dynamic_pointer_cast(transportPayload)) { JingleTransportPayload::ref payL = boost::make_shared(); payL->setSessionID(transportPayload->getSessionID()); onLocalTransportCandidatesGenerated(payL); } if (boost::dynamic_pointer_cast(transportPayload)) { JingleS5BTransportPayload::ref payL = boost::make_shared(); payL->setSessionID(transportPayload->getSessionID()); payL->setMode(JingleS5BTransportPayload::TCPMode); const unsigned long localPreference = 0; // get direct candidates std::vector directCandidates = connectivityManager->getHostAddressPorts(); foreach(HostAddressPort addressPort, directCandidates) { JingleS5BTransportPayload::Candidate candidate; candidate.type = JingleS5BTransportPayload::Candidate::DirectType; candidate.jid = ownJID; candidate.hostPort = addressPort; candidate.priority = 65536 * 126 + localPreference; candidate.cid = idGenerator.generateID(); payL->addCandidate(candidate); } // get assissted candidates std::vector assisstedCandidates = connectivityManager->getAssistedHostAddressPorts(); foreach(HostAddressPort addressPort, assisstedCandidates) { JingleS5BTransportPayload::Candidate candidate; candidate.type = JingleS5BTransportPayload::Candidate::AssistedType; candidate.jid = ownJID; candidate.hostPort = addressPort; candidate.priority = 65536 * 120 + localPreference; candidate.cid = idGenerator.generateID(); payL->addCandidate(candidate); } // get proxy candidates std::vector proxyCandidates = s5bProxy->getS5BProxies(); foreach(S5BProxyRequest::ref proxy, proxyCandidates) { if (proxy->getStreamHost()) { // FIXME: Added this test, because there were cases where this wasn't initialized. Investigate this. (Remko) JingleS5BTransportPayload::Candidate candidate; candidate.type = JingleS5BTransportPayload::Candidate::ProxyType; candidate.jid = (*proxy->getStreamHost()).jid; candidate.hostPort = (*proxy->getStreamHost()).addressPort; candidate.priority = 65536 * 10 + localPreference; candidate.cid = idGenerator.generateID(); payL->addCandidate(candidate); } } onLocalTransportCandidatesGenerated(payL); } } bool DefaultLocalJingleTransportCandidateGenerator::isActualCandidate(JingleTransportPayload::ref transportPayload) { if (!transportPayload.get()) return false; return false; } int DefaultLocalJingleTransportCandidateGenerator::getPriority(JingleTransportPayload::ref /* transportPayload */) { return 0; } JingleTransport::ref DefaultLocalJingleTransportCandidateGenerator::selectTransport(JingleTransportPayload::ref /* transportPayload */) { return JingleTransport::ref(); } } swift-im-2.0+dev6/Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGenerator.h0000644000175000017500000000227712227051774031120 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include #include namespace Swift { class SOCKS5BytestreamRegistry; class SOCKS5BytestreamProxy; class ConnectivityManager; class DefaultLocalJingleTransportCandidateGenerator : public LocalJingleTransportCandidateGenerator { public: DefaultLocalJingleTransportCandidateGenerator(ConnectivityManager* connectivityManager, SOCKS5BytestreamRegistry* s5bRegistry, SOCKS5BytestreamProxy* s5bProxy, JID& ownJID); virtual ~DefaultLocalJingleTransportCandidateGenerator(); virtual void generateLocalTransportCandidates(JingleTransportPayload::ref); virtual bool isActualCandidate(JingleTransportPayload::ref); virtual int getPriority(JingleTransportPayload::ref); virtual JingleTransport::ref selectTransport(JingleTransportPayload::ref); private: IDGenerator idGenerator; ConnectivityManager* connectivityManager; SOCKS5BytestreamRegistry* s5bRegistry; SOCKS5BytestreamProxy* s5bProxy; JID ownJID; }; } swift-im-2.0+dev6/Swiften/FileTransfer/OutgoingSIFileTransfer.h0000644000175000017500000000337712227051774024405 0ustar kismithkismith/* * 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 #include #include #include #include #include #include #include #include #include #include namespace Swift { class IQRouter; class SOCKS5BytestreamServer; class OutgoingSIFileTransfer : public OutgoingFileTransfer { public: OutgoingSIFileTransfer(const std::string& id, const JID& from, const JID& to, const std::string& name, int size, const std::string& description, boost::shared_ptr bytestream, IQRouter* iqRouter, SOCKS5BytestreamServer* socksServer); virtual void start(); virtual void stop(); boost::signal&)> onFinished; private: void handleStreamInitiationRequestResponse(StreamInitiation::ref, ErrorPayload::ref); void handleBytestreamsRequestResponse(Bytestreams::ref, ErrorPayload::ref); void finish(boost::optional error); void handleIBBSessionFinished(boost::optional error); private: std::string id; JID from; JID to; std::string name; int size; std::string description; boost::shared_ptr bytestream; IQRouter* iqRouter; SOCKS5BytestreamServer* socksServer; boost::shared_ptr ibbSession; }; } swift-im-2.0+dev6/Swiften/Client/0000755000175000017500000000000012227051774016520 5ustar kismithkismithswift-im-2.0+dev6/Swiften/Client/BlockListImpl.h0000644000175000017500000000141012227051774021375 0ustar kismithkismith/* * 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 namespace Swift { class BlockListImpl : public BlockList { public: BlockListImpl(); virtual State getState() const { return state; } void setState(State state); virtual const std::set& getItems() const { return items; } void setItems(const std::vector& items); void addItem(const JID& item); void removeItem(const JID& item); void addItems(const std::vector& items); void removeItems(const std::vector& items); void removeAllItems(); private: State state; std::set items; }; } swift-im-2.0+dev6/Swiften/Client/MemoryStorages.cpp0000644000175000017500000000263612227051774022213 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include namespace Swift { MemoryStorages::MemoryStorages() { vcardStorage = new VCardMemoryStorage(); capsStorage = new CapsMemoryStorage(); avatarStorage = new AvatarMemoryStorage(); rosterStorage = new RosterMemoryStorage(); #ifdef SWIFT_EXPERIMENTAL_HISTORY historyStorage = new SQLiteHistoryStorage(":memory:"); #endif } MemoryStorages::~MemoryStorages() { delete rosterStorage; delete avatarStorage; delete capsStorage; delete vcardStorage; #ifdef SWIFT_EXPERIMENTAL_HISTORY delete historyStorage; #endif } VCardStorage* MemoryStorages::getVCardStorage() const { return vcardStorage; } CapsStorage* MemoryStorages::getCapsStorage() const { return capsStorage; } AvatarStorage* MemoryStorages::getAvatarStorage() const { return avatarStorage; } RosterStorage* MemoryStorages::getRosterStorage() const { return rosterStorage; } HistoryStorage* MemoryStorages::getHistoryStorage() const { #ifdef SWIFT_EXPERIMENTAL_HISTORY return historyStorage; #else return NULL; #endif } } swift-im-2.0+dev6/Swiften/Client/BlockListImpl.cpp0000644000175000017500000000207312227051774021736 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include using namespace Swift; BlockListImpl::BlockListImpl() { } void BlockListImpl::setItems(const std::vector& items) { this->items = std::set(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& items) { foreach (const JID& item, items) { addItem(item); } } void BlockListImpl::removeItems(const std::vector& items) { foreach (const JID& item, items) { removeItem(item); } } void BlockListImpl::removeAllItems() { foreach (const JID& item, items) { removeItem(item); } } swift-im-2.0+dev6/Swiften/Client/ClientXMLTracer.h0000644000175000017500000000117312227051774021633 0ustar kismithkismith/* * 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 #include #include #include namespace Swift { class SWIFTEN_API ClientXMLTracer { public: ClientXMLTracer(CoreClient* client, bool bosh = false); ~ClientXMLTracer(); private: void printData(char direction, const SafeByteArray& data); void printLine(char c); private: XMLBeautifier *beautifier; bool bosh; }; } swift-im-2.0+dev6/Swiften/Client/DummyStanzaChannel.h0000644000175000017500000000470212227051774022441 0ustar kismithkismith/* * 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 #include namespace Swift { class DummyStanzaChannel : public StanzaChannel { public: DummyStanzaChannel() : available_(true) {} virtual void sendStanza(boost::shared_ptr stanza) { sentStanzas.push_back(stanza); } void setAvailable(bool available) { available_ = available; onAvailableChanged(available); } virtual void sendIQ(boost::shared_ptr iq) { sentStanzas.push_back(iq); } virtual void sendMessage(boost::shared_ptr message) { sentStanzas.push_back(message); } virtual void sendPresence(boost::shared_ptr presence) { sentStanzas.push_back(presence); } virtual std::string getNewIQID() { return "test-id"; } virtual bool isAvailable() const { return available_; } virtual bool getStreamManagementEnabled() const { return false; } template bool isRequestAtIndex(size_t index, const JID& jid, IQ::Type type) { if (index >= sentStanzas.size()) { return false; } boost::shared_ptr iqStanza = boost::dynamic_pointer_cast(sentStanzas[index]); return iqStanza && iqStanza->getType() == type && iqStanza->getTo() == jid && iqStanza->getPayload(); } bool isResultAtIndex(size_t index, const std::string& id) { if (index >= sentStanzas.size()) { return false; } boost::shared_ptr iqStanza = boost::dynamic_pointer_cast(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 iqStanza = boost::dynamic_pointer_cast(sentStanzas[index]); return iqStanza && iqStanza->getType() == IQ::Error && iqStanza->getID() == id; } template boost::shared_ptr getStanzaAtIndex(size_t index) { if (sentStanzas.size() <= index) { return boost::shared_ptr(); } return boost::dynamic_pointer_cast(sentStanzas[index]); } std::vector getPeerCertificateChain() const { return std::vector(); } std::vector > sentStanzas; bool available_; }; } swift-im-2.0+dev6/Swiften/Client/Client.cpp0000644000175000017500000001352612227051774020451 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef SWIFT_EXPERIMENTAL_FT #include #endif namespace Swift { 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, getStorages()->getRosterStorage()); subscriptionManager = new SubscriptionManager(getStanzaChannel()); presenceOracle = new PresenceOracle(getStanzaChannel()); presenceOracle->onPresenceChange.connect(boost::ref(onPresenceChange)); stanzaChannelPresenceSender = new StanzaChannelPresenceSender(getStanzaChannel()); directedPresenceSender = new DirectedPresenceSender(stanzaChannelPresenceSender); discoManager = new ClientDiscoManager(getIQRouter(), directedPresenceSender); mucRegistry = new MUCRegistry(); mucManager = new MUCManager(getStanzaChannel(), getIQRouter(), directedPresenceSender, mucRegistry); vcardManager = new VCardManager(jid, getIQRouter(), getStorages()->getVCardStorage()); avatarManager = new AvatarManagerImpl(vcardManager, getStanzaChannel(), getStorages()->getAvatarStorage(), mucRegistry); capsManager = new CapsManager(getStorages()->getCapsStorage(), getStanzaChannel(), getIQRouter()); entityCapsManager = new EntityCapsManager(capsManager, getStanzaChannel()); nickManager = new NickManagerImpl(jid.toBare(), vcardManager); nickResolver = new NickResolver(jid.toBare(), roster, vcardManager, mucRegistry); blindCertificateTrustChecker = new BlindCertificateTrustChecker(); jingleSessionManager = new JingleSessionManager(getIQRouter()); fileTransferManager = NULL; whiteboardSessionManager = NULL; #ifdef SWIFT_EXPERIMENTAL_WB whiteboardSessionManager = new WhiteboardSessionManager(getIQRouter(), getStanzaChannel(), presenceOracle, getEntityCapsProvider()); #endif } Client::~Client() { delete whiteboardSessionManager; delete fileTransferManager; delete jingleSessionManager; delete blindCertificateTrustChecker; delete nickResolver; delete nickManager; delete entityCapsManager; delete capsManager; delete avatarManager; delete vcardManager; delete mucManager; delete mucRegistry; delete discoManager; delete directedPresenceSender; delete stanzaChannelPresenceSender; delete presenceOracle; delete subscriptionManager; delete rosterController; delete roster; softwareVersionResponder->stop(); delete softwareVersionResponder; delete memoryStorages; } XMPPRoster* Client::getRoster() const { return roster; } void Client::setSoftwareVersion(const std::string& name, const std::string& version, const std::string& os) { 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 discoManager->handleConnected(); } 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(); } Presence::ref Client::getLastPresence(const JID& jid) const { return presenceOracle->getLastPresence(jid); } Presence::ref Client::getHighestPriorityPresence(const JID& bareJID) const { return presenceOracle->getHighestPriorityPresence(bareJID); } Storages* Client::getStorages() const { if (storages) { return storages; } return memoryStorages; } PresenceSender* Client::getPresenceSender() const { return discoManager->getPresenceSender(); } EntityCapsProvider* Client::getEntityCapsProvider() const { return entityCapsManager; } void Client::setAlwaysTrustCertificates() { setCertificateTrustChecker(blindCertificateTrustChecker); } NickManager* Client::getNickManager() const { return nickManager; } FileTransferManager* Client::getFileTransferManager() const { return fileTransferManager; } WhiteboardSessionManager* Client::getWhiteboardSessionManager() const { return whiteboardSessionManager; } } swift-im-2.0+dev6/Swiften/Client/Storages.h0000644000175000017500000000137112227051774020462 0ustar kismithkismith/* * 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 namespace Swift { class VCardStorage; class AvatarStorage; class CapsStorage; class RosterStorage; class HistoryStorage; /** * An interface to hold storage classes for different * controllers. */ class SWIFTEN_API Storages { public: virtual ~Storages(); virtual VCardStorage* getVCardStorage() const = 0; virtual AvatarStorage* getAvatarStorage() const = 0; virtual CapsStorage* getCapsStorage() const = 0; virtual RosterStorage* getRosterStorage() const = 0; virtual HistoryStorage* getHistoryStorage() const = 0; }; } swift-im-2.0+dev6/Swiften/Client/StanzaChannel.h0000644000175000017500000000204112227051774021417 0ustar kismithkismith/* * 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 #include #include #include #include #include namespace Swift { class StanzaChannel : public IQChannel { public: virtual void sendMessage(boost::shared_ptr) = 0; virtual void sendPresence(boost::shared_ptr) = 0; virtual bool isAvailable() const = 0; virtual bool getStreamManagementEnabled() const = 0; virtual std::vector getPeerCertificateChain() const = 0; boost::signal onAvailableChanged; boost::signal)> onMessageReceived; boost::signal) > onPresenceReceived; boost::signal)> onStanzaAcked; }; } swift-im-2.0+dev6/Swiften/Client/ClientSession.h0000644000175000017500000001060712227051774021457 0ustar kismithkismith/* * 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 #include #include #include #include #include #include #include #include #include #include namespace Swift { class ClientAuthenticator; class CertificateTrustChecker; class SWIFTEN_API ClientSession : public boost::enable_shared_from_this { public: enum State { Initial, WaitingForStreamStart, Negotiating, Compressing, WaitingForEncrypt, Encrypting, WaitingForCredentials, Authenticating, EnablingSessionManagement, BindingResource, StartingSession, Initialized, Finishing, Finished }; struct Error : public Swift::Error { enum Type { AuthenticationFailedError, CompressionFailedError, ServerVerificationFailedError, NoSupportedAuthMechanismsError, UnexpectedElementError, ResourceBindError, SessionStartError, TLSClientCertificateError, TLSError, StreamError, } type; Error(Type type) : type(type) {} }; enum UseTLS { NeverUseTLS, UseTLSWhenAvailable, RequireTLS }; ~ClientSession(); static boost::shared_ptr create(const JID& jid, boost::shared_ptr stream) { return boost::shared_ptr(new ClientSession(jid, stream)); } State getState() const { return state; } void setAllowPLAINOverNonTLS(bool b) { allowPLAINOverNonTLS = b; } void setUseStreamCompression(bool b) { useStreamCompression = b; } void setUseTLS(UseTLS b) { useTLS = b; } void setUseAcks(bool b) { useAcks = b; } bool getStreamManagementEnabled() const { return stanzaAckRequester_; } bool getRosterVersioningSupported() const { return rosterVersioningSupported; } std::vector getPeerCertificateChain() const { return stream->getPeerCertificateChain(); } const JID& getLocalJID() const { return localJID; } void start(); void finish(); bool isFinished() const { return getState() == Finished; } void sendCredentials(const SafeByteArray& password); void sendStanza(boost::shared_ptr); void setCertificateTrustChecker(CertificateTrustChecker* checker) { certificateTrustChecker = checker; } public: boost::signal onNeedCredentials; boost::signal onInitialized; boost::signal)> onFinished; boost::signal)> onStanzaReceived; boost::signal)> onStanzaAcked; private: ClientSession( const JID& jid, boost::shared_ptr); void finishSession(Error::Type error); void finishSession(boost::shared_ptr error); JID getRemoteJID() const { return JID("", localJID.getDomain()); } void sendStreamHeader(); void handleElement(boost::shared_ptr); void handleStreamStart(const ProtocolHeader&); void handleStreamClosed(boost::shared_ptr); void handleTLSEncrypted(); bool checkState(State); void continueSessionInitialization(); void requestAck(); void handleStanzaAcked(boost::shared_ptr stanza); void ack(unsigned int handledStanzasCount); void continueAfterTLSEncrypted(); void checkTrustOrFinish(const std::vector& certificateChain, boost::shared_ptr error); private: JID localJID; State state; boost::shared_ptr stream; bool allowPLAINOverNonTLS; bool useStreamCompression; UseTLS useTLS; bool useAcks; bool needSessionStart; bool needResourceBind; bool needAcking; bool rosterVersioningSupported; ClientAuthenticator* authenticator; boost::shared_ptr stanzaAckRequester_; boost::shared_ptr stanzaAckResponder_; boost::shared_ptr error_; CertificateTrustChecker* certificateTrustChecker; }; } swift-im-2.0+dev6/Swiften/Client/MemoryStorages.h0000644000175000017500000000156612227051774021661 0ustar kismithkismith/* * 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 namespace Swift { class VCardMemoryStorage; /** * An implementation of Storages for storing all * controller data in memory. */ class MemoryStorages : public Storages { public: MemoryStorages(); ~MemoryStorages(); virtual VCardStorage* getVCardStorage() const; virtual AvatarStorage* getAvatarStorage() const; virtual CapsStorage* getCapsStorage() const; virtual RosterStorage* getRosterStorage() const; virtual HistoryStorage* getHistoryStorage() const; private: VCardMemoryStorage* vcardStorage; AvatarStorage* avatarStorage; CapsStorage* capsStorage; RosterStorage* rosterStorage; HistoryStorage* historyStorage; }; } swift-im-2.0+dev6/Swiften/Client/Client.h0000644000175000017500000001251712227051774020115 0ustar kismithkismith/* * 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 #include #include namespace Swift { class SoftwareVersionResponder; class BlindCertificateTrustChecker; class XMPPRoster; class XMPPRosterImpl; class MUCManager; class XMPPRosterController; class PresenceOracle; class PresenceSender; class DirectedPresenceSender; class StanzaChannelPresenceSender; class MUCRegistry; class Storages; class MemoryStorages; class VCardManager; class AvatarManager; class CapsManager; class EntityCapsManager; class EntityCapsProvider; class NickResolver; class SubscriptionManager; class ClientDiscoManager; class NickManager; class FileTransferManager; class JingleSessionManager; class FileTransferManager; class WhiteboardSessionManager; /** * Provides the core functionality for writing XMPP client software. * * Besides connecting to an XMPP server, this class also provides interfaces for * performing most tasks on the XMPP network. */ class SWIFTEN_API Client : public CoreClient { public: /** * Constructs a client for the given JID with the given password. * * \param storages The interfaces for storing cache information etc. If * this is NULL, * all data will be stored in memory (and be lost on shutdown) */ Client(const JID& jid, const SafeString& password, NetworkFactories* networkFactories, Storages* storages = NULL); ~Client(); /** * Sets the software version of the client. * * This will be used to respond to version queries from other entities. */ void setSoftwareVersion(const std::string& name, const std::string& version, const std::string& os = ""); /** * Returns a representation of the roster. * * The roster is initially empty. To populate it, call requestRoster(), * which will request the roster from the server. When the roster has * been requested, it will also be kept up to date when it is updated on * the server side. * * This pointer remains the same across the lifetime of Client. All * changes to the roster (e.g. after the initial roster request, or after * subsequent roster updates) are notified through the XMPPRoster's * signals. * * \see requestRoster() */ XMPPRoster* getRoster() const; /** * Requests the roster from the server. * * \see getRoster() */ void requestRoster(); /** * Returns the last received presence for the given (full) JID. */ boost::shared_ptr getLastPresence(const JID& jid) const; /** * Returns the presence with the highest priority received for the given JID. */ boost::shared_ptr getHighestPriorityPresence(const JID& bareJID) const; PresenceOracle* getPresenceOracle() const { return presenceOracle; } PresenceSender* getPresenceSender() const; MUCManager* getMUCManager() const { return mucManager; } MUCRegistry* getMUCRegistry() const { return mucRegistry; } VCardManager* getVCardManager() const { return vcardManager; } AvatarManager* getAvatarManager() const { return avatarManager; } EntityCapsProvider* getEntityCapsProvider() const; NickManager* getNickManager() const; NickResolver* getNickResolver() const { return nickResolver; } SubscriptionManager* getSubscriptionManager() const { return subscriptionManager; } 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 * TLS certificate from the server. * This is equivalent to setting a BlindCertificateTrustChecker * using setCertificateTrustChecker(). */ void setAlwaysTrustCertificates(); WhiteboardSessionManager* getWhiteboardSessionManager() const; public: /** * This signal is emitted when a JID changes presence. */ boost::signal)> onPresenceChange; private: Storages* getStorages() const; protected: void handleConnected(); private: Storages* storages; MemoryStorages* memoryStorages; SoftwareVersionResponder* softwareVersionResponder; XMPPRosterImpl* roster; XMPPRosterController* rosterController; PresenceOracle* presenceOracle; DirectedPresenceSender* directedPresenceSender; StanzaChannelPresenceSender* stanzaChannelPresenceSender; MUCRegistry* mucRegistry; VCardManager* vcardManager; AvatarManager* avatarManager; CapsManager* capsManager; EntityCapsManager* entityCapsManager; NickManager* nickManager; NickResolver* nickResolver; SubscriptionManager* subscriptionManager; MUCManager* mucManager; ClientDiscoManager* discoManager; JingleSessionManager* jingleSessionManager; FileTransferManager* fileTransferManager; BlindCertificateTrustChecker* blindCertificateTrustChecker; WhiteboardSessionManager* whiteboardSessionManager; }; } swift-im-2.0+dev6/Swiften/Client/ClientError.h0000644000175000017500000000244412227051774021125 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once namespace Swift { class ClientError { public: enum Type { UnknownError, DomainNameResolveError, ConnectionError, ConnectionReadError, ConnectionWriteError, XMLError, AuthenticationFailedError, CompressionFailedError, ServerVerificationFailedError, NoSupportedAuthMechanismsError, UnexpectedElementError, ResourceBindError, SessionStartError, StreamError, TLSError, ClientCertificateLoadError, ClientCertificateError, // Certifate on smartcard was removed CertificateCardRemoved, // Certificate verification errors UnknownCertificateError, CertificateExpiredError, CertificateNotYetValidError, CertificateSelfSignedError, CertificateRejectedError, CertificateUntrustedError, InvalidCertificatePurposeError, CertificatePathLengthExceededError, InvalidCertificateSignatureError, InvalidCAError, InvalidServerIdentityError, RevokedError, RevocationCheckFailedError }; ClientError(Type type = UnknownError) : type_(type) {} Type getType() const { return type_; } private: Type type_; }; } swift-im-2.0+dev6/Swiften/Client/XMLBeautifier.cpp0000644000175000017500000000637012227051774021672 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include #include #include #include #include #include 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 << ""; lastWasStepDown = true; } void XMLBeautifier::handleCharacterData(const std::string& data) { buffer << data; lastWasStepDown = false; } } swift-im-2.0+dev6/Swiften/Client/ClientSessionStanzaChannel.h0000644000175000017500000000264012227051774024127 0ustar kismithkismith/* * 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 #include #include #include #include #include #include namespace Swift { /** * StanzaChannel implementation around a ClientSession. */ class ClientSessionStanzaChannel : public StanzaChannel { public: void setSession(boost::shared_ptr session); void sendIQ(boost::shared_ptr iq); void sendMessage(boost::shared_ptr message); void sendPresence(boost::shared_ptr presence); bool getStreamManagementEnabled() const; virtual std::vector getPeerCertificateChain() const; bool isAvailable() const { return session && session->getState() == ClientSession::Initialized; } private: std::string getNewIQID(); void send(boost::shared_ptr stanza); void handleSessionFinished(boost::shared_ptr error); void handleStanza(boost::shared_ptr stanza); void handleStanzaAcked(boost::shared_ptr stanza); void handleSessionInitialized(); private: IDGenerator idGenerator; boost::shared_ptr session; }; } swift-im-2.0+dev6/Swiften/Client/ClientBlockListManager.cpp0000644000175000017500000000634012227051774023547 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include using namespace Swift; namespace { class BlockResponder : public SetResponder { public: BlockResponder(boost::shared_ptr blockList, IQRouter* iqRouter) : SetResponder(iqRouter), blockList(blockList) { } virtual bool handleSetRequest(const JID& from, const JID&, const std::string& id, boost::shared_ptr payload) { if (getIQRouter()->isAccountJID(from)) { if (payload) { blockList->addItems(payload->getItems()); } sendResponse(from, id, boost::shared_ptr()); } else { sendError(from, id, ErrorPayload::NotAuthorized, ErrorPayload::Cancel); } return true; } private: boost::shared_ptr blockList; }; class UnblockResponder : public SetResponder { public: UnblockResponder(boost::shared_ptr blockList, IQRouter* iqRouter) : SetResponder(iqRouter), blockList(blockList) { } virtual bool handleSetRequest(const JID& from, const JID&, const std::string& id, boost::shared_ptr payload) { if (getIQRouter()->isAccountJID(from)) { if (payload) { if (payload->getItems().empty()) { blockList->removeAllItems(); } else { blockList->removeItems(payload->getItems()); } } sendResponse(from, id, boost::shared_ptr()); } else { sendError(from, id, ErrorPayload::NotAuthorized, ErrorPayload::Cancel); } return true; } private: boost::shared_ptr 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 ClientBlockListManager::getBlockList() { if (!blockList) { blockList = boost::make_shared(); blockList->setState(BlockList::Requesting); assert(!getRequest); getRequest = boost::make_shared< GenericRequest >(IQ::Get, JID(), boost::make_shared(), iqRouter); getRequest->onResponse.connect(boost::bind(&ClientBlockListManager::handleBlockListReceived, this, _1, _2)); getRequest->send(); } return blockList; } void ClientBlockListManager::handleBlockListReceived(boost::shared_ptr payload, ErrorPayload::ref error) { if (error || !payload) { blockList->setState(BlockList::Error); } else { blockList->setState(BlockList::Available); blockList->setItems(payload->getItems()); blockResponder = boost::make_shared(blockList, iqRouter); blockResponder->start(); unblockResponder = boost::make_shared(blockList, iqRouter); unblockResponder->start(); } } swift-im-2.0+dev6/Swiften/Client/CoreClient.cpp0000644000175000017500000004433712227051774021266 0ustar kismithkismith/* * Copyright (c) 2010-2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Swift { CoreClient::CoreClient(const JID& jid, const SafeByteArray& password, NetworkFactories* networkFactories) : jid_(jid), password_(password), networkFactories(networkFactories), disconnectRequested_(false), certificateTrustChecker(NULL) { stanzaChannel_ = new ClientSessionStanzaChannel(); stanzaChannel_->onMessageReceived.connect(boost::bind(&CoreClient::handleMessageReceived, this, _1)); stanzaChannel_->onPresenceReceived.connect(boost::bind(&CoreClient::handlePresenceReceived, this, _1)); stanzaChannel_->onStanzaAcked.connect(boost::bind(&CoreClient::handleStanzaAcked, this, _1)); stanzaChannel_->onAvailableChanged.connect(boost::bind(&CoreClient::handleStanzaChannelAvailableChanged, this, _1)); iqRouter_ = new IQRouter(stanzaChannel_); iqRouter_->setJID(jid); } CoreClient::~CoreClient() { forceReset(); delete iqRouter_; stanzaChannel_->onAvailableChanged.disconnect(boost::bind(&CoreClient::handleStanzaChannelAvailableChanged, this, _1)); stanzaChannel_->onMessageReceived.disconnect(boost::bind(&CoreClient::handleMessageReceived, this, _1)); stanzaChannel_->onPresenceReceived.disconnect(boost::bind(&CoreClient::handlePresenceReceived, this, _1)); stanzaChannel_->onStanzaAcked.disconnect(boost::bind(&CoreClient::handleStanzaAcked, this, _1)); delete stanzaChannel_; } void CoreClient::connect(const ClientOptions& o) { SWIFT_LOG(debug) << "Connecting "; forceReset(); disconnectRequested_ = false; options = o; // Determine connection types to use assert(proxyConnectionFactories.empty()); 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() << ":" << systemHTTPConnectProxy.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; } } std::vector connectionFactories(proxyConnectionFactories); if (useDirectConnection) { connectionFactories.push_back(networkFactories->getConnectionFactory()); } // Create connector std::string host = o.manualHostname.empty() ? jid_.getDomain() : o.manualHostname; int port = o.manualPort; assert(!connector_); if (options.boshURL.isEmpty()) { connector_ = boost::make_shared(host, port, o.manualHostname.empty(), networkFactories->getDomainNameResolver(), connectionFactories, networkFactories->getTimerFactory()); connector_->onConnectFinished.connect(boost::bind(&CoreClient::handleConnectorFinished, this, _1, _2)); connector_->setTimeoutMilliseconds(2*60*1000); connector_->start(); } else { /* Autodiscovery of which proxy works is largely ok with a TCP session, because this is a one-off. With BOSH * it would be quite painful given that potentially every stanza could be sent on a new connection. */ //sessionStream_ = boost::make_shared(boost::make_shared(options.boshURL, networkFactories->getConnectionFactory(), networkFactories->getXMLParserFactory(), networkFactories->getTLSContextFactory()), getPayloadParserFactories(), getPayloadSerializers(), networkFactories->getTLSContextFactory(), networkFactories->getTimerFactory(), networkFactories->getXMLParserFactory(), networkFactories->getEventLoop(), host, options.boshHTTPConnectProxyURL, options.boshHTTPConnectProxyAuthID, options.boshHTTPConnectProxyAuthPassword); sessionStream_ = boost::shared_ptr(new BOSHSessionStream( options.boshURL, getPayloadParserFactories(), getPayloadSerializers(), networkFactories->getConnectionFactory(), networkFactories->getTLSContextFactory(), networkFactories->getTimerFactory(), networkFactories->getXMLParserFactory(), networkFactories->getEventLoop(), networkFactories->getDomainNameResolver(), host, options.boshHTTPConnectProxyURL, options.boshHTTPConnectProxyAuthID, options.boshHTTPConnectProxyAuthPassword)); sessionStream_->onDataRead.connect(boost::bind(&CoreClient::handleDataRead, this, _1)); sessionStream_->onDataWritten.connect(boost::bind(&CoreClient::handleDataWritten, this, _1)); bindSessionToStream(); } } void CoreClient::bindSessionToStream() { session_ = ClientSession::create(jid_, sessionStream_); session_->setCertificateTrustChecker(certificateTrustChecker); session_->setUseStreamCompression(options.useStreamCompression); session_->setAllowPLAINOverNonTLS(options.allowPLAINWithoutTLS); switch(options.useTLS) { case ClientOptions::UseTLSWhenAvailable: session_->setUseTLS(ClientSession::UseTLSWhenAvailable); break; case ClientOptions::NeverUseTLS: session_->setUseTLS(ClientSession::NeverUseTLS); break; case ClientOptions::RequireTLS: session_->setUseTLS(ClientSession::RequireTLS); break; } session_->setUseAcks(options.useAcks); stanzaChannel_->setSession(session_); session_->onFinished.connect(boost::bind(&CoreClient::handleSessionFinished, this, _1)); session_->onNeedCredentials.connect(boost::bind(&CoreClient::handleNeedCredentials, this)); session_->start(); } /** * Only called for TCP sessions. BOSH is handled inside the BOSHSessionStream. */ void CoreClient::handleConnectorFinished(boost::shared_ptr connection, boost::shared_ptr error) { resetConnector(); if (!connection) { if (options.forgetPassword) { purgePassword(); } boost::optional clientError; if (!disconnectRequested_) { clientError = boost::dynamic_pointer_cast(error) ? boost::optional(ClientError::DomainNameResolveError) : boost::optional(ClientError::ConnectionError); } onDisconnected(clientError); } else { assert(!connection_); connection_ = connection; assert(!sessionStream_); sessionStream_ = boost::make_shared(ClientStreamType, connection_, getPayloadParserFactories(), getPayloadSerializers(), networkFactories->getTLSContextFactory(), networkFactories->getTimerFactory(), networkFactories->getXMLParserFactory()); if (certificate_ && !certificate_->isNull()) { sessionStream_->setTLSCertificate(certificate_); } sessionStream_->onDataRead.connect(boost::bind(&CoreClient::handleDataRead, this, _1)); sessionStream_->onDataWritten.connect(boost::bind(&CoreClient::handleDataWritten, this, _1)); bindSessionToStream(); } } void CoreClient::disconnect() { // FIXME: We should be able to do without this boolean. We just have to make sure we can tell the difference between // connector finishing without a connection due to an error or because of a disconnect. disconnectRequested_ = true; if (session_ && !session_->isFinished()) { session_->finish(); } else if (connector_) { connector_->stop(); } } void CoreClient::setCertificate(CertificateWithKey::ref certificate) { certificate_ = certificate; } void CoreClient::handleSessionFinished(boost::shared_ptr error) { if (options.forgetPassword) { purgePassword(); } resetSession(); boost::optional actualError; if (error) { ClientError clientError; if (boost::shared_ptr actualError = boost::dynamic_pointer_cast(error)) { switch(actualError->type) { case ClientSession::Error::AuthenticationFailedError: clientError = ClientError(ClientError::AuthenticationFailedError); break; case ClientSession::Error::CompressionFailedError: clientError = ClientError(ClientError::CompressionFailedError); break; case ClientSession::Error::ServerVerificationFailedError: clientError = ClientError(ClientError::ServerVerificationFailedError); break; case ClientSession::Error::NoSupportedAuthMechanismsError: clientError = ClientError(ClientError::NoSupportedAuthMechanismsError); break; case ClientSession::Error::UnexpectedElementError: clientError = ClientError(ClientError::UnexpectedElementError); break; case ClientSession::Error::ResourceBindError: clientError = ClientError(ClientError::ResourceBindError); break; case ClientSession::Error::SessionStartError: clientError = ClientError(ClientError::SessionStartError); break; case ClientSession::Error::TLSError: clientError = ClientError(ClientError::TLSError); break; case ClientSession::Error::TLSClientCertificateError: clientError = ClientError(ClientError::ClientCertificateError); break; case ClientSession::Error::StreamError: clientError = ClientError(ClientError::StreamError); break; } } else if (boost::shared_ptr actualError = boost::dynamic_pointer_cast(error)) { switch(actualError->getType()) { case TLSError::CertificateCardRemoved: clientError = ClientError(ClientError::CertificateCardRemoved); break; case TLSError::UnknownError: clientError = ClientError(ClientError::TLSError); break; } } else if (boost::shared_ptr actualError = boost::dynamic_pointer_cast(error)) { switch(actualError->type) { case SessionStream::SessionStreamError::ParseError: clientError = ClientError(ClientError::XMLError); break; case SessionStream::SessionStreamError::TLSError: clientError = ClientError(ClientError::TLSError); break; case SessionStream::SessionStreamError::InvalidTLSCertificateError: clientError = ClientError(ClientError::ClientCertificateLoadError); break; case SessionStream::SessionStreamError::ConnectionReadError: clientError = ClientError(ClientError::ConnectionReadError); break; case SessionStream::SessionStreamError::ConnectionWriteError: clientError = ClientError(ClientError::ConnectionWriteError); break; } } else if (boost::shared_ptr verificationError = boost::dynamic_pointer_cast(error)) { switch(verificationError->getType()) { case CertificateVerificationError::UnknownError: clientError = ClientError(ClientError::UnknownCertificateError); break; case CertificateVerificationError::Expired: clientError = ClientError(ClientError::CertificateExpiredError); break; case CertificateVerificationError::NotYetValid: clientError = ClientError(ClientError::CertificateNotYetValidError); break; case CertificateVerificationError::SelfSigned: clientError = ClientError(ClientError::CertificateSelfSignedError); break; case CertificateVerificationError::Rejected: clientError = ClientError(ClientError::CertificateRejectedError); break; case CertificateVerificationError::Untrusted: clientError = ClientError(ClientError::CertificateUntrustedError); break; case CertificateVerificationError::InvalidPurpose: clientError = ClientError(ClientError::InvalidCertificatePurposeError); break; case CertificateVerificationError::PathLengthExceeded: clientError = ClientError(ClientError::CertificatePathLengthExceededError); break; case CertificateVerificationError::InvalidSignature: clientError = ClientError(ClientError::InvalidCertificateSignatureError); break; case CertificateVerificationError::InvalidCA: clientError = ClientError(ClientError::InvalidCAError); break; case CertificateVerificationError::InvalidServerIdentity: clientError = ClientError(ClientError::InvalidServerIdentityError); break; case CertificateVerificationError::Revoked: clientError = ClientError(ClientError::RevokedError); break; case CertificateVerificationError::RevocationCheckFailed: clientError = ClientError(ClientError::RevocationCheckFailedError); break; } } actualError = boost::optional(clientError); } onDisconnected(actualError); } void CoreClient::handleNeedCredentials() { assert(session_); session_->sendCredentials(password_); if (options.forgetPassword) { purgePassword(); } } void CoreClient::handleDataRead(const SafeByteArray& data) { onDataRead(data); } void CoreClient::handleDataWritten(const SafeByteArray& data) { onDataWritten(data); } void CoreClient::handleStanzaChannelAvailableChanged(bool available) { if (available) { iqRouter_->setJID(session_->getLocalJID()); handleConnected(); onConnected(); } } void CoreClient::sendMessage(boost::shared_ptr message) { stanzaChannel_->sendMessage(message); } void CoreClient::sendPresence(boost::shared_ptr presence) { stanzaChannel_->sendPresence(presence); } void CoreClient::sendData(const std::string& data) { sessionStream_->writeData(data); } bool CoreClient::isActive() const { return (session_ && !session_->isFinished()) || connector_; } void CoreClient::setCertificateTrustChecker(CertificateTrustChecker* checker) { certificateTrustChecker = checker; } void CoreClient::handlePresenceReceived(Presence::ref presence) { onPresenceReceived(presence); } void CoreClient::handleMessageReceived(Message::ref message) { onMessageReceived(message); } void CoreClient::handleStanzaAcked(Stanza::ref stanza) { onStanzaAcked(stanza); } bool CoreClient::isAvailable() const { return stanzaChannel_->isAvailable(); } bool CoreClient::getStreamManagementEnabled() const { return stanzaChannel_->getStreamManagementEnabled(); } bool CoreClient::isStreamEncrypted() const { return sessionStream_->isTLSEncrypted(); } StanzaChannel* CoreClient::getStanzaChannel() const { return stanzaChannel_; } const JID& CoreClient::getJID() const { if (session_) { return session_->getLocalJID(); } else { return jid_; } } void CoreClient::purgePassword() { safeClear(password_); } void CoreClient::resetConnector() { connector_->onConnectFinished.disconnect(boost::bind(&CoreClient::handleConnectorFinished, this, _1, _2)); connector_.reset(); foreach(ConnectionFactory* f, proxyConnectionFactories) { delete f; } proxyConnectionFactories.clear(); } void CoreClient::resetSession() { session_->onFinished.disconnect(boost::bind(&CoreClient::handleSessionFinished, this, _1)); session_->onNeedCredentials.disconnect(boost::bind(&CoreClient::handleNeedCredentials, this)); sessionStream_->onDataRead.disconnect(boost::bind(&CoreClient::handleDataRead, this, _1)); sessionStream_->onDataWritten.disconnect(boost::bind(&CoreClient::handleDataWritten, this, _1)); if (connection_) { connection_->disconnect(); } else if (boost::dynamic_pointer_cast(sessionStream_)) { sessionStream_->close(); } sessionStream_.reset(); connection_.reset(); } void CoreClient::forceReset() { if (connector_) { std::cerr << "Warning: Client not disconnected properly: Connector still active" << std::endl; resetConnector(); } if (sessionStream_ || connection_) { std::cerr << "Warning: Client not disconnected properly: Session still active" << std::endl; resetSession(); } } } swift-im-2.0+dev6/Swiften/Client/ClientSessionStanzaChannel.cpp0000644000175000017500000000600712227051774024463 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include namespace Swift { void ClientSessionStanzaChannel::setSession(boost::shared_ptr session) { assert(!this->session); this->session = session; session->onInitialized.connect(boost::bind(&ClientSessionStanzaChannel::handleSessionInitialized, this)); session->onFinished.connect(boost::bind(&ClientSessionStanzaChannel::handleSessionFinished, this, _1)); session->onStanzaReceived.connect(boost::bind(&ClientSessionStanzaChannel::handleStanza, this, _1)); session->onStanzaAcked.connect(boost::bind(&ClientSessionStanzaChannel::handleStanzaAcked, this, _1)); } void ClientSessionStanzaChannel::sendIQ(boost::shared_ptr iq) { send(iq); } void ClientSessionStanzaChannel::sendMessage(boost::shared_ptr message) { send(message); } void ClientSessionStanzaChannel::sendPresence(boost::shared_ptr presence) { send(presence); } std::string ClientSessionStanzaChannel::getNewIQID() { return idGenerator.generateID(); } void ClientSessionStanzaChannel::send(boost::shared_ptr stanza) { if (!isAvailable()) { std::cerr << "Warning: Client: Trying to send a stanza while disconnected." << std::endl; return; } session->sendStanza(stanza); } void ClientSessionStanzaChannel::handleSessionFinished(boost::shared_ptr) { session->onFinished.disconnect(boost::bind(&ClientSessionStanzaChannel::handleSessionFinished, this, _1)); session->onStanzaReceived.disconnect(boost::bind(&ClientSessionStanzaChannel::handleStanza, this, _1)); session->onStanzaAcked.disconnect(boost::bind(&ClientSessionStanzaChannel::handleStanzaAcked, this, _1)); session->onInitialized.disconnect(boost::bind(&ClientSessionStanzaChannel::handleSessionInitialized, this)); session.reset(); onAvailableChanged(false); } void ClientSessionStanzaChannel::handleStanza(boost::shared_ptr stanza) { boost::shared_ptr message = boost::dynamic_pointer_cast(stanza); if (message) { onMessageReceived(message); return; } boost::shared_ptr presence = boost::dynamic_pointer_cast(stanza); if (presence) { onPresenceReceived(presence); return; } boost::shared_ptr iq = boost::dynamic_pointer_cast(stanza); if (iq) { onIQReceived(iq); return; } } bool ClientSessionStanzaChannel::getStreamManagementEnabled() const { if (session) { return session->getStreamManagementEnabled(); } return false; } std::vector ClientSessionStanzaChannel::getPeerCertificateChain() const { if (session) { return session->getPeerCertificateChain(); } return std::vector(); } void ClientSessionStanzaChannel::handleStanzaAcked(boost::shared_ptr stanza) { onStanzaAcked(stanza); } void ClientSessionStanzaChannel::handleSessionInitialized() { onAvailableChanged(true); } } swift-im-2.0+dev6/Swiften/Client/NickManagerImpl.h0000644000175000017500000000141212227051774021670 0ustar kismithkismith/* * 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 #include #include #include namespace Swift { class VCardManager; class NickManagerImpl : public NickManager { public: NickManagerImpl(const JID& ownJID, VCardManager* vcardManager); ~NickManagerImpl(); std::string getOwnNick() const; void setOwnNick(const std::string& nick); private: void handleVCardReceived(const JID& jid, VCard::ref vCard); void updateOwnNickFromVCard(VCard::ref vcard); private: JID ownJID; VCardManager* vcardManager; std::string ownNick; }; } swift-im-2.0+dev6/Swiften/Client/BlockList.cpp0000644000175000017500000000037412227051774021116 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include using namespace Swift; BlockList::~BlockList() { } swift-im-2.0+dev6/Swiften/Client/NickResolver.cpp0000644000175000017500000000503412227051774021634 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include // 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, // the nickresolver should request the vcard. // FIXME: The ownJID functionality should probably be removed, and NickManager should be used directly. namespace Swift { NickResolver::NickResolver(const JID& ownJID, XMPPRoster* xmppRoster, VCardManager* vcardManager, MUCRegistry* mucRegistry) : ownJID_(ownJID) { xmppRoster_ = xmppRoster; vcardManager_ = vcardManager; if (vcardManager_) { vcardManager_->onVCardChanged.connect(boost::bind(&NickResolver::handleVCardReceived, this, _1, _2)); } mucRegistry_ = mucRegistry; xmppRoster_->onJIDUpdated.connect(boost::bind(&NickResolver::handleJIDUpdated, this, _1, _2, _3)); xmppRoster_->onJIDAdded.connect(boost::bind(&NickResolver::handleJIDAdded, this, _1)); } void NickResolver::handleJIDUpdated(const JID& jid, const std::string& previousNick, const std::vector& /*groups*/) { onNickChanged(jid, previousNick); } void NickResolver::handleJIDAdded(const JID& jid) { std::string oldNick(jidToNick(jid)); onNickChanged(jid, oldNick); } std::string NickResolver::jidToNick(const JID& jid) { if (jid.toBare() == ownJID_) { if (!ownNick_.empty()) { return ownNick_; } } std::string nick; if (mucRegistry_ && mucRegistry_->isMUC(jid.toBare()) ) { return jid.getResource().empty() ? jid.toBare().toString() : jid.getResource(); } if (xmppRoster_->containsJID(jid) && !xmppRoster_->getNameForJID(jid).empty()) { return xmppRoster_->getNameForJID(jid); } return jid.toBare(); } void NickResolver::handleVCardReceived(const JID& jid, VCard::ref ownVCard) { if (!jid.equals(ownJID_, JID::WithoutResource)) { return; } std::string initialNick = ownNick_; ownNick_ = ownJID_.toString(); if (ownVCard) { if (!ownVCard->getNickname().empty()) { ownNick_ = ownVCard->getNickname(); } else if (!ownVCard->getGivenName().empty()) { ownNick_ = ownVCard->getGivenName(); } else if (!ownVCard->getFullName().empty()) { ownNick_ = ownVCard->getFullName(); } } } } swift-im-2.0+dev6/Swiften/Client/NickManagerImpl.cpp0000644000175000017500000000241412227051774022226 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include namespace Swift { NickManagerImpl::NickManagerImpl(const JID& ownJID, VCardManager* vcardManager) : ownJID(ownJID), vcardManager(vcardManager) { vcardManager->onVCardChanged.connect(boost::bind(&NickManagerImpl::handleVCardReceived, this, _1, _2)); updateOwnNickFromVCard(vcardManager->getVCard(ownJID.toBare())); } NickManagerImpl::~NickManagerImpl() { vcardManager->onVCardChanged.disconnect(boost::bind(&NickManagerImpl::handleVCardReceived, this, _1, _2)); } std::string NickManagerImpl::getOwnNick() const { return ownNick; } void NickManagerImpl::setOwnNick(const std::string&) { } void NickManagerImpl::handleVCardReceived(const JID& jid, VCard::ref vcard) { if (!jid.equals(ownJID, JID::WithoutResource)) { return; } updateOwnNickFromVCard(vcard); } void NickManagerImpl::updateOwnNickFromVCard(VCard::ref vcard) { std::string nick; if (vcard && !vcard->getNickname().empty()) { nick = vcard->getNickname(); } if (ownNick != nick) { ownNick = nick; onOwnNickChanged(ownNick); } } } swift-im-2.0+dev6/Swiften/Client/NickManager.h0000644000175000017500000000101612227051774021046 0ustar kismithkismith/* * 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 #include #include namespace Swift { class SWIFTEN_API NickManager { public: virtual ~NickManager(); virtual std::string getOwnNick() const = 0; virtual void setOwnNick(const std::string& nick) = 0; boost::signal onOwnNickChanged; }; } swift-im-2.0+dev6/Swiften/Client/ClientXMLTracer.cpp0000644000175000017500000000261412227051774022167 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include namespace Swift { ClientXMLTracer::ClientXMLTracer(CoreClient* client, bool bosh) : bosh(bosh) { beautifier = new XMLBeautifier(true, true); client->onDataRead.connect(boost::bind(&ClientXMLTracer::printData, this, '<', _1)); client->onDataWritten.connect(boost::bind(&ClientXMLTracer::printData, this, '>', _1)); } ClientXMLTracer::~ClientXMLTracer() { delete beautifier; } void ClientXMLTracer::printData(char direction, const SafeByteArray& data) { printLine(direction); if (bosh) { std::string line = byteArrayToString(ByteArray(data.begin(), data.end())); size_t endOfHTTP = line.find("\r\n\r\n"); if (false && endOfHTTP != std::string::npos) { /* Disabled because it swallows bits of XML (namespaces, if I recall) */ std::cerr << line.substr(0, endOfHTTP) << std::endl << beautifier->beautify(line.substr(endOfHTTP)) << std::endl; } else { std::cerr << line << std::endl; } } else { std::cerr << beautifier->beautify(byteArrayToString(ByteArray(data.begin(), data.end()))) << std::endl; } } void ClientXMLTracer::printLine(char c) { for (unsigned int i = 0; i < 80; ++i) { std::cerr << c; } std::cerr << std::endl; } } swift-im-2.0+dev6/Swiften/Client/NickManager.cpp0000644000175000017500000000040012227051774021375 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { NickManager::~NickManager() { } } swift-im-2.0+dev6/Swiften/Client/UnitTest/0000755000175000017500000000000012227051774020277 5ustar kismithkismithswift-im-2.0+dev6/Swiften/Client/UnitTest/NickResolverTest.cpp0000644000175000017500000001064112227051774024253 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include using namespace Swift; class NickResolverTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(NickResolverTest); CPPUNIT_TEST(testNoMatch); CPPUNIT_TEST(testZeroLengthMatch); CPPUNIT_TEST(testMatch); CPPUNIT_TEST(testOverwrittenMatch); CPPUNIT_TEST(testRemovedMatch); CPPUNIT_TEST(testMUCNick); CPPUNIT_TEST(testMUCNoNick); CPPUNIT_TEST(testRemovedMatch); CPPUNIT_TEST(testOwnNickFullOnly); CPPUNIT_TEST(testOwnNickGivenAndFull); CPPUNIT_TEST(testOwnNickNickEtAl); CPPUNIT_TEST_SUITE_END(); public: void setUp() { ownJID_ = JID("kev@wonderland.lit"); xmppRoster_ = new XMPPRosterImpl(); stanzaChannel_ = new DummyStanzaChannel(); iqRouter_ = new IQRouter(stanzaChannel_); vCardStorage_ = new VCardMemoryStorage(); vCardManager_ = new VCardManager(ownJID_, iqRouter_, vCardStorage_); registry_ = new MUCRegistry(); resolver_ = new NickResolver(ownJID_, xmppRoster_, vCardManager_, registry_); } void tearDown() { delete resolver_; delete registry_; delete vCardManager_; delete iqRouter_; delete stanzaChannel_; delete vCardStorage_; delete xmppRoster_; } void testMUCNick() { registry_->addMUC(JID("foo@bar")); JID testJID("foo@bar/baz"); CPPUNIT_ASSERT_EQUAL(std::string("baz"), resolver_->jidToNick(testJID)); } void testMUCNoNick() { registry_->addMUC(JID("foo@bar")); JID testJID("foo@bar"); CPPUNIT_ASSERT_EQUAL(std::string("foo@bar"), resolver_->jidToNick(testJID)); } void testNoMatch() { JID testJID("foo@bar/baz"); CPPUNIT_ASSERT_EQUAL(std::string("foo@bar"), resolver_->jidToNick(testJID)); } void testZeroLengthMatch() { JID testJID("foo@bar/baz"); xmppRoster_->addContact(testJID, "", groups_, RosterItemPayload::Both); CPPUNIT_ASSERT_EQUAL(std::string("foo@bar"), resolver_->jidToNick(testJID)); } void testMatch() { JID testJID("foo@bar/baz"); xmppRoster_->addContact(testJID, "Test", groups_, RosterItemPayload::Both); CPPUNIT_ASSERT_EQUAL(std::string("Test"), resolver_->jidToNick(testJID)); } void testOverwrittenMatch() { JID testJID("foo@bar/baz"); xmppRoster_->addContact(testJID, "FailTest", groups_, RosterItemPayload::Both); xmppRoster_->addContact(testJID, "Test", groups_, RosterItemPayload::Both); CPPUNIT_ASSERT_EQUAL(std::string("Test"), resolver_->jidToNick(testJID)); } void testRemovedMatch() { JID testJID("foo@bar/baz"); xmppRoster_->addContact(testJID, "FailTest", groups_, RosterItemPayload::Both); xmppRoster_->removeContact(testJID); CPPUNIT_ASSERT_EQUAL(std::string("foo@bar"), resolver_->jidToNick(testJID)); } void testOwnNickFullOnly() { populateOwnVCard("", "", "Kevin Smith"); CPPUNIT_ASSERT_EQUAL(std::string("Kevin Smith"), resolver_->jidToNick(ownJID_)); } void testOwnNickGivenAndFull() { populateOwnVCard("", "Kevin", "Kevin Smith"); CPPUNIT_ASSERT_EQUAL(std::string("Kevin"), resolver_->jidToNick(ownJID_)); } void testOwnNickNickEtAl() { populateOwnVCard("Kev", "Kevin", "Kevin Smith"); CPPUNIT_ASSERT_EQUAL(std::string("Kev"), resolver_->jidToNick(ownJID_)); } void populateOwnVCard(const std::string& nick, const std::string& given, const std::string& full) { VCard::ref vcard(new VCard()); if (!nick.empty()) { vcard->setNickname(nick); } if (!given.empty()) { vcard->setGivenName(given); } if (!full.empty()) { vcard->setFullName(full); } vCardManager_->requestVCard(ownJID_); IQ::ref result(IQ::createResult(JID(), stanzaChannel_->sentStanzas[0]->getID(), vcard)); stanzaChannel_->onIQReceived(result); } private: std::vector groups_; XMPPRosterImpl* xmppRoster_; VCardStorage* vCardStorage_; IQRouter* iqRouter_; DummyStanzaChannel* stanzaChannel_; VCardManager* vCardManager_; MUCRegistry* registry_; NickResolver* resolver_; JID ownJID_; }; CPPUNIT_TEST_SUITE_REGISTRATION(NickResolverTest); swift-im-2.0+dev6/Swiften/Client/UnitTest/ClientSessionTest.cpp0000644000175000017500000006670112227051774024437 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Swift; class ClientSessionTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(ClientSessionTest); CPPUNIT_TEST(testStart_Error); CPPUNIT_TEST(testStart_StreamError); CPPUNIT_TEST(testStartTLS); CPPUNIT_TEST(testStartTLS_ServerError); CPPUNIT_TEST(testStartTLS_ConnectError); CPPUNIT_TEST(testStartTLS_InvalidIdentity); CPPUNIT_TEST(testStart_StreamFeaturesWithoutResourceBindingFails); CPPUNIT_TEST(testAuthenticate); CPPUNIT_TEST(testAuthenticate_Unauthorized); CPPUNIT_TEST(testAuthenticate_NoValidAuthMechanisms); 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); /* CPPUNIT_TEST(testResourceBind); CPPUNIT_TEST(testResourceBind_ChangeResource); CPPUNIT_TEST(testResourceBind_EmptyResource); CPPUNIT_TEST(testResourceBind_Error); CPPUNIT_TEST(testSessionStart); CPPUNIT_TEST(testSessionStart_Error); CPPUNIT_TEST(testSessionStart_AfterResourceBind); CPPUNIT_TEST(testWhitespacePing); CPPUNIT_TEST(testReceiveElementAfterSessionStarted); CPPUNIT_TEST(testSendElement); */ CPPUNIT_TEST_SUITE_END(); public: void setUp() { server = boost::make_shared(); sessionFinishedReceived = false; needCredentials = false; blindCertificateTrustChecker = new BlindCertificateTrustChecker(); } void tearDown() { delete blindCertificateTrustChecker; } void testStart_Error() { boost::shared_ptr session(createSession()); session->start(); server->breakConnection(); CPPUNIT_ASSERT_EQUAL(ClientSession::Finished, session->getState()); CPPUNIT_ASSERT(sessionFinishedReceived); CPPUNIT_ASSERT(sessionFinishedError); } void testStart_StreamError() { boost::shared_ptr session(createSession()); session->start(); server->sendStreamStart(); server->sendStreamError(); CPPUNIT_ASSERT_EQUAL(ClientSession::Finished, session->getState()); CPPUNIT_ASSERT(sessionFinishedReceived); CPPUNIT_ASSERT(sessionFinishedError); } void testStartTLS() { boost::shared_ptr session(createSession()); session->setCertificateTrustChecker(blindCertificateTrustChecker); session->start(); server->receiveStreamStart(); server->sendStreamStart(); server->sendStreamFeaturesWithStartTLS(); server->receiveStartTLS(); CPPUNIT_ASSERT(!server->tlsEncrypted); server->sendTLSProceed(); CPPUNIT_ASSERT(server->tlsEncrypted); server->onTLSEncrypted(); server->receiveStreamStart(); server->sendStreamStart(); session->finish(); } void testStartTLS_ServerError() { boost::shared_ptr session(createSession()); session->start(); server->receiveStreamStart(); server->sendStreamStart(); server->sendStreamFeaturesWithStartTLS(); server->receiveStartTLS(); server->sendTLSFailure(); CPPUNIT_ASSERT(!server->tlsEncrypted); CPPUNIT_ASSERT_EQUAL(ClientSession::Finished, session->getState()); CPPUNIT_ASSERT(sessionFinishedReceived); CPPUNIT_ASSERT(sessionFinishedError); } void testStartTLS_ConnectError() { boost::shared_ptr session(createSession()); session->start(); server->receiveStreamStart(); server->sendStreamStart(); server->sendStreamFeaturesWithStartTLS(); server->receiveStartTLS(); server->sendTLSProceed(); server->breakTLS(); CPPUNIT_ASSERT_EQUAL(ClientSession::Finished, session->getState()); CPPUNIT_ASSERT(sessionFinishedReceived); CPPUNIT_ASSERT(sessionFinishedError); } void testStartTLS_InvalidIdentity() { boost::shared_ptr session(createSession()); session->start(); server->receiveStreamStart(); server->sendStreamStart(); server->sendStreamFeaturesWithStartTLS(); server->receiveStartTLS(); CPPUNIT_ASSERT(!server->tlsEncrypted); server->sendTLSProceed(); CPPUNIT_ASSERT(server->tlsEncrypted); server->onTLSEncrypted(); CPPUNIT_ASSERT_EQUAL(ClientSession::Finished, session->getState()); CPPUNIT_ASSERT(sessionFinishedReceived); CPPUNIT_ASSERT(sessionFinishedError); CPPUNIT_ASSERT_EQUAL(CertificateVerificationError::InvalidServerIdentity, boost::dynamic_pointer_cast(sessionFinishedError)->getType()); } void testStart_StreamFeaturesWithoutResourceBindingFails() { boost::shared_ptr session(createSession()); session->start(); server->receiveStreamStart(); server->sendStreamStart(); server->sendEmptyStreamFeatures(); CPPUNIT_ASSERT_EQUAL(ClientSession::Finished, session->getState()); CPPUNIT_ASSERT(sessionFinishedReceived); CPPUNIT_ASSERT(sessionFinishedError); } void testAuthenticate() { boost::shared_ptr session(createSession()); session->start(); server->receiveStreamStart(); server->sendStreamStart(); server->sendStreamFeaturesWithPLAINAuthentication(); CPPUNIT_ASSERT(needCredentials); CPPUNIT_ASSERT_EQUAL(ClientSession::WaitingForCredentials, session->getState()); session->sendCredentials(createSafeByteArray("mypass")); server->receiveAuthRequest("PLAIN"); server->sendAuthSuccess(); server->receiveStreamStart(); session->finish(); } void testAuthenticate_Unauthorized() { boost::shared_ptr session(createSession()); session->start(); server->receiveStreamStart(); server->sendStreamStart(); server->sendStreamFeaturesWithPLAINAuthentication(); CPPUNIT_ASSERT(needCredentials); CPPUNIT_ASSERT_EQUAL(ClientSession::WaitingForCredentials, session->getState()); session->sendCredentials(createSafeByteArray("mypass")); server->receiveAuthRequest("PLAIN"); server->sendAuthFailure(); CPPUNIT_ASSERT_EQUAL(ClientSession::Finished, session->getState()); CPPUNIT_ASSERT(sessionFinishedReceived); CPPUNIT_ASSERT(sessionFinishedError); } void testAuthenticate_PLAINOverNonTLS() { boost::shared_ptr session(createSession()); session->setAllowPLAINOverNonTLS(false); session->start(); server->receiveStreamStart(); server->sendStreamStart(); server->sendStreamFeaturesWithPLAINAuthentication(); CPPUNIT_ASSERT_EQUAL(ClientSession::Finished, session->getState()); CPPUNIT_ASSERT(sessionFinishedReceived); CPPUNIT_ASSERT(sessionFinishedError); } void testAuthenticate_RequireTLS() { boost::shared_ptr 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 session(createSession()); session->start(); server->receiveStreamStart(); server->sendStreamStart(); server->sendStreamFeaturesWithUnknownAuthentication(); CPPUNIT_ASSERT_EQUAL(ClientSession::Finished, session->getState()); CPPUNIT_ASSERT(sessionFinishedReceived); CPPUNIT_ASSERT(sessionFinishedError); } void testAuthenticate_EXTERNAL() { boost::shared_ptr session(createSession()); session->start(); server->receiveStreamStart(); server->sendStreamStart(); server->sendStreamFeaturesWithEXTERNALAuthentication(); server->receiveAuthRequest("EXTERNAL"); server->sendAuthSuccess(); server->receiveStreamStart(); session->finish(); } void testUnexpectedChallenge() { boost::shared_ptr 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 session(createSession()); 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(); CPPUNIT_ASSERT(session->getStreamManagementEnabled()); // TODO: Test if the requesters & responders do their work CPPUNIT_ASSERT_EQUAL(ClientSession::Initialized, session->getState()); session->finish(); } void testStreamManagement_Failed() { boost::shared_ptr session(createSession()); 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->sendStreamManagementFailed(); CPPUNIT_ASSERT(!session->getStreamManagementEnabled()); CPPUNIT_ASSERT_EQUAL(ClientSession::Initialized, session->getState()); session->finish(); } void testFinishAcksStanzas() { boost::shared_ptr session(createSession()); initializeSession(session); server->sendMessage(); server->sendMessage(); server->sendMessage(); session->finish(); server->receiveAck(3); } private: boost::shared_ptr createSession() { boost::shared_ptr session = ClientSession::create(JID("me@foo.com"), server); session->onFinished.connect(boost::bind(&ClientSessionTest::handleSessionFinished, this, _1)); session->onNeedCredentials.connect(boost::bind(&ClientSessionTest::handleSessionNeedCredentials, this)); session->setAllowPLAINOverNonTLS(true); return session; } void initializeSession(boost::shared_ptr 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) { sessionFinishedReceived = true; sessionFinishedError = error; } void handleSessionNeedCredentials() { needCredentials = true; } class MockSessionStream : public SessionStream { public: struct Event { Event(boost::shared_ptr element) : element(element), footer(false) {} Event(const ProtocolHeader& header) : header(header), footer(false) {} Event() : footer(true) {} boost::shared_ptr element; boost::optional header; bool footer; }; MockSessionStream() : available(true), canTLSEncrypt(true), tlsEncrypted(false), compressed(false), whitespacePingEnabled(false), resetCount(0) { } virtual void close() { onClosed(boost::shared_ptr()); } virtual bool isOpen() { return available; } virtual void writeHeader(const ProtocolHeader& header) { receivedEvents.push_back(Event(header)); } virtual void writeFooter() { receivedEvents.push_back(Event()); } virtual void writeElement(boost::shared_ptr element) { receivedEvents.push_back(Event(element)); } virtual void writeData(const std::string&) { } virtual bool supportsTLSEncryption() { return canTLSEncrypt; } virtual void addTLSEncryption() { tlsEncrypted = true; } virtual bool isTLSEncrypted() { return tlsEncrypted; } virtual ByteArray getTLSFinishMessage() const { return ByteArray(); } virtual Certificate::ref getPeerCertificate() const { return Certificate::ref(new SimpleCertificate()); } virtual std::vector getPeerCertificateChain() const { return std::vector(); } virtual boost::shared_ptr getPeerCertificateVerificationError() const { return boost::shared_ptr(); } virtual bool supportsZLibCompression() { return true; } virtual void addZLibCompression() { compressed = true; } virtual void setWhitespacePingEnabled(bool enabled) { whitespacePingEnabled = enabled; } virtual void resetXMPPParser() { resetCount++; } void breakConnection() { onClosed(boost::make_shared(SessionStream::SessionStreamError::ConnectionReadError)); } void breakTLS() { onClosed(boost::make_shared(SessionStream::SessionStreamError::TLSError)); } void sendStreamStart() { ProtocolHeader header; header.setTo("foo.com"); return onStreamStartReceived(header); } void sendStreamFeaturesWithStartTLS() { boost::shared_ptr streamFeatures(new StreamFeatures()); streamFeatures->setHasStartTLS(); onElementReceived(streamFeatures); } void sendChallenge() { onElementReceived(boost::make_shared()); } void sendStreamError() { onElementReceived(boost::make_shared()); } void sendTLSProceed() { onElementReceived(boost::make_shared()); } void sendTLSFailure() { onElementReceived(boost::make_shared()); } void sendStreamFeaturesWithMultipleAuthentication() { boost::shared_ptr streamFeatures(new StreamFeatures()); streamFeatures->addAuthenticationMechanism("PLAIN"); streamFeatures->addAuthenticationMechanism("DIGEST-MD5"); streamFeatures->addAuthenticationMechanism("SCRAM-SHA1"); onElementReceived(streamFeatures); } void sendStreamFeaturesWithPLAINAuthentication() { boost::shared_ptr streamFeatures(new StreamFeatures()); streamFeatures->addAuthenticationMechanism("PLAIN"); onElementReceived(streamFeatures); } void sendStreamFeaturesWithEXTERNALAuthentication() { boost::shared_ptr streamFeatures(new StreamFeatures()); streamFeatures->addAuthenticationMechanism("EXTERNAL"); onElementReceived(streamFeatures); } void sendStreamFeaturesWithUnknownAuthentication() { boost::shared_ptr streamFeatures(new StreamFeatures()); streamFeatures->addAuthenticationMechanism("UNKNOWN"); onElementReceived(streamFeatures); } void sendStreamFeaturesWithBindAndStreamManagement() { boost::shared_ptr streamFeatures(new StreamFeatures()); streamFeatures->setHasResourceBind(); streamFeatures->setHasStreamManagement(); onElementReceived(streamFeatures); } void sendEmptyStreamFeatures() { onElementReceived(boost::make_shared()); } void sendAuthSuccess() { onElementReceived(boost::make_shared()); } void sendAuthFailure() { onElementReceived(boost::make_shared()); } void sendStreamManagementEnabled() { onElementReceived(boost::make_shared()); } void sendStreamManagementFailed() { onElementReceived(boost::make_shared()); } void sendBindResult() { boost::shared_ptr resourceBind(new ResourceBind()); resourceBind->setJID(JID("foo@bar.com/bla")); boost::shared_ptr iq = IQ::createResult(JID("foo@bar.com"), bindID, resourceBind); onElementReceived(iq); } void sendMessage() { boost::shared_ptr message = boost::make_shared(); message->setTo(JID("foo@bar.com/bla")); onElementReceived(message); } void receiveStreamStart() { Event event = popEvent(); CPPUNIT_ASSERT(event.header); } void receiveStartTLS() { Event event = popEvent(); CPPUNIT_ASSERT(event.element); CPPUNIT_ASSERT(boost::dynamic_pointer_cast(event.element)); } void receiveAuthRequest(const std::string& mech) { Event event = popEvent(); CPPUNIT_ASSERT(event.element); boost::shared_ptr request(boost::dynamic_pointer_cast(event.element)); CPPUNIT_ASSERT(request); CPPUNIT_ASSERT_EQUAL(mech, request->getMechanism()); } void receiveStreamManagementEnable() { Event event = popEvent(); CPPUNIT_ASSERT(event.element); CPPUNIT_ASSERT(boost::dynamic_pointer_cast(event.element)); } void receiveBind() { Event event = popEvent(); CPPUNIT_ASSERT(event.element); boost::shared_ptr iq = boost::dynamic_pointer_cast(event.element); CPPUNIT_ASSERT(iq); CPPUNIT_ASSERT(iq->getPayload()); bindID = iq->getID(); } void receiveAck(unsigned int n) { Event event = popEvent(); CPPUNIT_ASSERT(event.element); boost::shared_ptr ack = boost::dynamic_pointer_cast(event.element); CPPUNIT_ASSERT(ack); CPPUNIT_ASSERT_EQUAL(n, ack->getHandledStanzasCount()); } Event popEvent() { CPPUNIT_ASSERT(!receivedEvents.empty()); Event event = receivedEvents.front(); receivedEvents.pop_front(); return event; } bool available; bool canTLSEncrypt; bool tlsEncrypted; bool compressed; bool whitespacePingEnabled; std::string bindID; int resetCount; std::deque receivedEvents; }; boost::shared_ptr server; bool sessionFinishedReceived; bool needCredentials; boost::shared_ptr sessionFinishedError; BlindCertificateTrustChecker* blindCertificateTrustChecker; }; CPPUNIT_TEST_SUITE_REGISTRATION(ClientSessionTest); #if 0 void testAuthenticate() { boost::shared_ptr session(createSession("me@foo.com/Bar")); session->onNeedCredentials.connect(boost::bind(&ClientSessionTest::setNeedCredentials, this)); getMockServer()->expectStreamStart(); getMockServer()->sendStreamStart(); getMockServer()->sendStreamFeaturesWithAuthentication(); session->startSession(); processEvents(); CPPUNIT_ASSERT_EQUAL(ClientSession::WaitingForCredentials, session->getState()); CPPUNIT_ASSERT(needCredentials_); getMockServer()->expectAuth("me", "mypass"); getMockServer()->sendAuthSuccess(); getMockServer()->expectStreamStart(); getMockServer()->sendStreamStart(); session->sendCredentials("mypass"); CPPUNIT_ASSERT_EQUAL(ClientSession::Authenticating, session->getState()); processEvents(); CPPUNIT_ASSERT_EQUAL(ClientSession::Negotiating, session->getState()); } void testAuthenticate_Unauthorized() { boost::shared_ptr session(createSession("me@foo.com/Bar")); getMockServer()->expectStreamStart(); getMockServer()->sendStreamStart(); getMockServer()->sendStreamFeaturesWithAuthentication(); session->startSession(); processEvents(); getMockServer()->expectAuth("me", "mypass"); getMockServer()->sendAuthFailure(); session->sendCredentials("mypass"); processEvents(); CPPUNIT_ASSERT_EQUAL(ClientSession::Error, session->getState()); CPPUNIT_ASSERT_EQUAL(ClientSession::AuthenticationFailedError, *session->getError()); } void testAuthenticate_NoValidAuthMechanisms() { boost::shared_ptr session(createSession("me@foo.com/Bar")); getMockServer()->expectStreamStart(); getMockServer()->sendStreamStart(); getMockServer()->sendStreamFeaturesWithUnsupportedAuthentication(); session->startSession(); processEvents(); CPPUNIT_ASSERT_EQUAL(ClientSession::Error, session->getState()); CPPUNIT_ASSERT_EQUAL(ClientSession::NoSupportedAuthMechanismsError, *session->getError()); } void testResourceBind() { boost::shared_ptr session(createSession("me@foo.com/Bar")); getMockServer()->expectStreamStart(); getMockServer()->sendStreamStart(); getMockServer()->sendStreamFeaturesWithResourceBind(); getMockServer()->expectResourceBind("Bar", "session-bind"); // FIXME: Check CPPUNIT_ASSERT_EQUAL(ClientSession::BindingResource, session->getState()); getMockServer()->sendResourceBindResponse("me@foo.com/Bar", "session-bind"); session->startSession(); processEvents(); CPPUNIT_ASSERT_EQUAL(ClientSession::SessionStarted, session->getState()); CPPUNIT_ASSERT_EQUAL(JID("me@foo.com/Bar"), session->getLocalJID()); } void testResourceBind_ChangeResource() { boost::shared_ptr session(createSession("me@foo.com/Bar")); getMockServer()->expectStreamStart(); getMockServer()->sendStreamStart(); getMockServer()->sendStreamFeaturesWithResourceBind(); getMockServer()->expectResourceBind("Bar", "session-bind"); getMockServer()->sendResourceBindResponse("me@foo.com/Bar123", "session-bind"); session->startSession(); processEvents(); CPPUNIT_ASSERT_EQUAL(ClientSession::SessionStarted, session->getState()); CPPUNIT_ASSERT_EQUAL(JID("me@foo.com/Bar123"), session->getLocalJID()); } void testResourceBind_EmptyResource() { boost::shared_ptr session(createSession("me@foo.com")); getMockServer()->expectStreamStart(); getMockServer()->sendStreamStart(); getMockServer()->sendStreamFeaturesWithResourceBind(); getMockServer()->expectResourceBind("", "session-bind"); getMockServer()->sendResourceBindResponse("me@foo.com/NewResource", "session-bind"); session->startSession(); processEvents(); CPPUNIT_ASSERT_EQUAL(ClientSession::SessionStarted, session->getState()); CPPUNIT_ASSERT_EQUAL(JID("me@foo.com/NewResource"), session->getLocalJID()); } void testResourceBind_Error() { boost::shared_ptr session(createSession("me@foo.com")); getMockServer()->expectStreamStart(); getMockServer()->sendStreamStart(); getMockServer()->sendStreamFeaturesWithResourceBind(); getMockServer()->expectResourceBind("", "session-bind"); getMockServer()->sendError("session-bind"); session->startSession(); processEvents(); CPPUNIT_ASSERT_EQUAL(ClientSession::Error, session->getState()); CPPUNIT_ASSERT_EQUAL(ClientSession::ResourceBindError, *session->getError()); } void testSessionStart() { boost::shared_ptr session(createSession("me@foo.com/Bar")); session->onSessionStarted.connect(boost::bind(&ClientSessionTest::setSessionStarted, this)); getMockServer()->expectStreamStart(); getMockServer()->sendStreamStart(); getMockServer()->sendStreamFeaturesWithSession(); getMockServer()->expectSessionStart("session-start"); // FIXME: Check CPPUNIT_ASSERT_EQUAL(ClientSession::StartingSession, session->getState()); getMockServer()->sendSessionStartResponse("session-start"); session->startSession(); processEvents(); CPPUNIT_ASSERT_EQUAL(ClientSession::SessionStarted, session->getState()); CPPUNIT_ASSERT(sessionStarted_); } void testSessionStart_Error() { boost::shared_ptr session(createSession("me@foo.com/Bar")); getMockServer()->expectStreamStart(); getMockServer()->sendStreamStart(); getMockServer()->sendStreamFeaturesWithSession(); getMockServer()->expectSessionStart("session-start"); getMockServer()->sendError("session-start"); session->startSession(); processEvents(); CPPUNIT_ASSERT_EQUAL(ClientSession::Error, session->getState()); CPPUNIT_ASSERT_EQUAL(ClientSession::SessionStartError, *session->getError()); } void testSessionStart_AfterResourceBind() { boost::shared_ptr session(createSession("me@foo.com/Bar")); session->onSessionStarted.connect(boost::bind(&ClientSessionTest::setSessionStarted, this)); getMockServer()->expectStreamStart(); getMockServer()->sendStreamStart(); getMockServer()->sendStreamFeaturesWithResourceBindAndSession(); getMockServer()->expectResourceBind("Bar", "session-bind"); getMockServer()->sendResourceBindResponse("me@foo.com/Bar", "session-bind"); getMockServer()->expectSessionStart("session-start"); getMockServer()->sendSessionStartResponse("session-start"); session->startSession(); processEvents(); CPPUNIT_ASSERT_EQUAL(ClientSession::SessionStarted, session->getState()); CPPUNIT_ASSERT(sessionStarted_); } void testWhitespacePing() { boost::shared_ptr session(createSession("me@foo.com/Bar")); getMockServer()->expectStreamStart(); getMockServer()->sendStreamStart(); getMockServer()->sendStreamFeatures(); session->startSession(); processEvents(); CPPUNIT_ASSERT(session->getWhitespacePingLayer()); } void testReceiveElementAfterSessionStarted() { boost::shared_ptr session(createSession("me@foo.com/Bar")); getMockServer()->expectStreamStart(); getMockServer()->sendStreamStart(); getMockServer()->sendStreamFeatures(); session->startSession(); processEvents(); getMockServer()->expectMessage(); session->sendElement(boost::make_shared())); } void testSendElement() { boost::shared_ptr session(createSession("me@foo.com/Bar")); session->onElementReceived.connect(boost::bind(&ClientSessionTest::addReceivedElement, this, _1)); getMockServer()->expectStreamStart(); getMockServer()->sendStreamStart(); getMockServer()->sendStreamFeatures(); getMockServer()->sendMessage(); session->startSession(); processEvents(); CPPUNIT_ASSERT_EQUAL(1, static_cast(receivedElements_.size())); CPPUNIT_ASSERT(boost::dynamic_pointer_cast(receivedElements_[0])); } #endif swift-im-2.0+dev6/Swiften/Client/ClientSession.cpp0000644000175000017500000003737012227051774022020 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef SWIFTEN_PLATFORM_WIN32 #include #endif #define CHECK_STATE_OR_RETURN(a) \ if (!checkState(a)) { return; } namespace Swift { ClientSession::ClientSession( const JID& jid, boost::shared_ptr stream) : localJID(jid), state(Initial), stream(stream), allowPLAINOverNonTLS(false), useStreamCompression(true), useTLS(UseTLSWhenAvailable), useAcks(true), needSessionStart(false), needResourceBind(false), needAcking(false), rosterVersioningSupported(false), authenticator(NULL), certificateTrustChecker(NULL) { #ifdef SWIFTEN_PLATFORM_WIN32 if (WindowsRegistry::isFIPSEnabled()) { SWIFT_LOG(info) << "Windows is running in FIPS-140 mode. Some authentication methods will be unavailable." << std::endl; } #endif } ClientSession::~ClientSession() { } void ClientSession::start() { stream->onStreamStartReceived.connect(boost::bind(&ClientSession::handleStreamStart, shared_from_this(), _1)); stream->onElementReceived.connect(boost::bind(&ClientSession::handleElement, shared_from_this(), _1)); stream->onClosed.connect(boost::bind(&ClientSession::handleStreamClosed, shared_from_this(), _1)); stream->onTLSEncrypted.connect(boost::bind(&ClientSession::handleTLSEncrypted, shared_from_this())); assert(state == Initial); state = WaitingForStreamStart; sendStreamHeader(); } void ClientSession::sendStreamHeader() { ProtocolHeader header; header.setTo(getRemoteJID()); stream->writeHeader(header); } void ClientSession::sendStanza(boost::shared_ptr stanza) { stream->writeElement(stanza); if (stanzaAckRequester_) { stanzaAckRequester_->handleStanzaSent(stanza); } } void ClientSession::handleStreamStart(const ProtocolHeader&) { CHECK_STATE_OR_RETURN(WaitingForStreamStart); state = Negotiating; } void ClientSession::handleElement(boost::shared_ptr element) { if (boost::shared_ptr stanza = boost::dynamic_pointer_cast(element)) { if (stanzaAckResponder_) { stanzaAckResponder_->handleStanzaReceived(); } if (getState() == Initialized) { onStanzaReceived(stanza); } else if (boost::shared_ptr iq = boost::dynamic_pointer_cast(element)) { if (state == BindingResource) { boost::shared_ptr resourceBind(iq->getPayload()); if (iq->getType() == IQ::Error && iq->getID() == "session-bind") { finishSession(Error::ResourceBindError); } else if (!resourceBind) { finishSession(Error::UnexpectedElementError); } else if (iq->getType() == IQ::Result) { localJID = resourceBind->getJID(); if (!localJID.isValid()) { finishSession(Error::ResourceBindError); } needResourceBind = false; continueSessionInitialization(); } else { finishSession(Error::UnexpectedElementError); } } else if (state == StartingSession) { if (iq->getType() == IQ::Result) { needSessionStart = false; continueSessionInitialization(); } else if (iq->getType() == IQ::Error) { finishSession(Error::SessionStartError); } else { finishSession(Error::UnexpectedElementError); } } else { finishSession(Error::UnexpectedElementError); } } } else if (boost::dynamic_pointer_cast(element)) { if (stanzaAckResponder_) { stanzaAckResponder_->handleAckRequestReceived(); } } else if (boost::shared_ptr ack = boost::dynamic_pointer_cast(element)) { if (stanzaAckRequester_) { if (ack->isValid()) { stanzaAckRequester_->handleAckReceived(ack->getHandledStanzasCount()); } else { std::cerr << "Warning: Got invalid ack from server" << std::endl; } } else { std::cerr << "Warning: Ignoring ack" << std::endl; } } else if (StreamError::ref streamError = boost::dynamic_pointer_cast(element)) { finishSession(Error::StreamError); } else if (getState() == Initialized) { boost::shared_ptr stanza = boost::dynamic_pointer_cast(element); if (stanza) { if (stanzaAckResponder_) { stanzaAckResponder_->handleStanzaReceived(); } onStanzaReceived(stanza); } } else if (StreamFeatures* streamFeatures = dynamic_cast(element.get())) { CHECK_STATE_OR_RETURN(Negotiating); if (streamFeatures->hasStartTLS() && stream->supportsTLSEncryption() && useTLS != NeverUseTLS) { state = WaitingForEncrypt; stream->writeElement(boost::make_shared()); } else if (useTLS == RequireTLS && !stream->isTLSEncrypted()) { finishSession(Error::NoSupportedAuthMechanismsError); } else if (useStreamCompression && stream->supportsZLibCompression() && streamFeatures->hasCompressionMethod("zlib")) { state = Compressing; stream->writeElement(boost::make_shared("zlib")); } else if (streamFeatures->hasAuthenticationMechanisms()) { if (stream->hasTLSCertificate()) { if (streamFeatures->hasAuthenticationMechanism("EXTERNAL")) { authenticator = new EXTERNALClientAuthenticator(); state = Authenticating; stream->writeElement(boost::make_shared("EXTERNAL", createSafeByteArray(""))); } else { finishSession(Error::TLSClientCertificateError); } } else if (streamFeatures->hasAuthenticationMechanism("EXTERNAL")) { authenticator = new EXTERNALClientAuthenticator(); state = Authenticating; stream->writeElement(boost::make_shared("EXTERNAL", createSafeByteArray(""))); } else if (streamFeatures->hasAuthenticationMechanism("SCRAM-SHA-1") || streamFeatures->hasAuthenticationMechanism("SCRAM-SHA-1-PLUS")) { std::ostringstream s; ByteArray finishMessage; bool plus = stream->isTLSEncrypted() && streamFeatures->hasAuthenticationMechanism("SCRAM-SHA-1-PLUS"); if (plus) { finishMessage = stream->getTLSFinishMessage(); plus &= !finishMessage.empty(); } s << boost::uuids::random_generator()(); SCRAMSHA1ClientAuthenticator* scramAuthenticator = new SCRAMSHA1ClientAuthenticator(s.str(), plus); if (plus) { scramAuthenticator->setTLSChannelBindingData(finishMessage); } authenticator = scramAuthenticator; state = WaitingForCredentials; onNeedCredentials(); } else if ((stream->isTLSEncrypted() || allowPLAINOverNonTLS) && streamFeatures->hasAuthenticationMechanism("PLAIN")) { authenticator = new PLAINClientAuthenticator(); state = WaitingForCredentials; onNeedCredentials(); } else if (streamFeatures->hasAuthenticationMechanism("DIGEST-MD5") && DIGESTMD5ClientAuthenticator::canBeUsed()) { std::ostringstream s; s << boost::uuids::random_generator()(); // FIXME: Host should probably be the actual host authenticator = new DIGESTMD5ClientAuthenticator(localJID.getDomain(), s.str()); state = WaitingForCredentials; onNeedCredentials(); } else { finishSession(Error::NoSupportedAuthMechanismsError); } } else { // Start the session rosterVersioningSupported = streamFeatures->hasRosterVersioning(); stream->setWhitespacePingEnabled(true); needSessionStart = streamFeatures->hasSession(); needResourceBind = streamFeatures->hasResourceBind(); needAcking = streamFeatures->hasStreamManagement() && useAcks; if (!needResourceBind) { // Resource binding is a MUST finishSession(Error::ResourceBindError); } else { continueSessionInitialization(); } } } else if (boost::dynamic_pointer_cast(element)) { CHECK_STATE_OR_RETURN(Compressing); state = WaitingForStreamStart; stream->addZLibCompression(); stream->resetXMPPParser(); sendStreamHeader(); } else if (boost::dynamic_pointer_cast(element)) { finishSession(Error::CompressionFailedError); } else if (boost::dynamic_pointer_cast(element)) { stanzaAckRequester_ = boost::make_shared(); stanzaAckRequester_->onRequestAck.connect(boost::bind(&ClientSession::requestAck, shared_from_this())); stanzaAckRequester_->onStanzaAcked.connect(boost::bind(&ClientSession::handleStanzaAcked, shared_from_this(), _1)); stanzaAckResponder_ = boost::make_shared(); stanzaAckResponder_->onAck.connect(boost::bind(&ClientSession::ack, shared_from_this(), _1)); needAcking = false; continueSessionInitialization(); } else if (boost::dynamic_pointer_cast(element)) { needAcking = false; continueSessionInitialization(); } else if (AuthChallenge* challenge = dynamic_cast(element.get())) { CHECK_STATE_OR_RETURN(Authenticating); assert(authenticator); if (authenticator->setChallenge(challenge->getValue())) { stream->writeElement(boost::make_shared(authenticator->getResponse())); } else { finishSession(Error::AuthenticationFailedError); } } else if (AuthSuccess* authSuccess = dynamic_cast(element.get())) { CHECK_STATE_OR_RETURN(Authenticating); assert(authenticator); if (!authenticator->setChallenge(authSuccess->getValue())) { finishSession(Error::ServerVerificationFailedError); } else { state = WaitingForStreamStart; delete authenticator; authenticator = NULL; stream->resetXMPPParser(); sendStreamHeader(); } } else if (dynamic_cast(element.get())) { finishSession(Error::AuthenticationFailedError); } else if (dynamic_cast(element.get())) { CHECK_STATE_OR_RETURN(WaitingForEncrypt); state = Encrypting; stream->addTLSEncryption(); } else if (dynamic_cast(element.get())) { finishSession(Error::TLSError); } else { // FIXME Not correct? state = Initialized; onInitialized(); } } void ClientSession::continueSessionInitialization() { if (needResourceBind) { state = BindingResource; boost::shared_ptr resourceBind(boost::make_shared()); if (!localJID.getResource().empty()) { resourceBind->setResource(localJID.getResource()); } sendStanza(IQ::createRequest(IQ::Set, JID(), "session-bind", resourceBind)); } else if (needAcking) { state = EnablingSessionManagement; stream->writeElement(boost::make_shared()); } else if (needSessionStart) { state = StartingSession; sendStanza(IQ::createRequest(IQ::Set, JID(), "session-start", boost::make_shared())); } else { state = Initialized; onInitialized(); } } bool ClientSession::checkState(State state) { if (this->state != state) { finishSession(Error::UnexpectedElementError); return false; } return true; } void ClientSession::sendCredentials(const SafeByteArray& password) { assert(WaitingForCredentials); assert(authenticator); state = Authenticating; authenticator->setCredentials(localJID.getNode(), password); stream->writeElement(boost::make_shared(authenticator->getName(), authenticator->getResponse())); } void ClientSession::handleTLSEncrypted() { CHECK_STATE_OR_RETURN(Encrypting); std::vector certificateChain = stream->getPeerCertificateChain(); boost::shared_ptr verificationError = stream->getPeerCertificateVerificationError(); if (verificationError) { checkTrustOrFinish(certificateChain, verificationError); } else { ServerIdentityVerifier identityVerifier(localJID); if (!certificateChain.empty() && identityVerifier.certificateVerifies(certificateChain[0])) { continueAfterTLSEncrypted(); } else { checkTrustOrFinish(certificateChain, boost::make_shared(CertificateVerificationError::InvalidServerIdentity)); } } } void ClientSession::checkTrustOrFinish(const std::vector& certificateChain, boost::shared_ptr error) { if (certificateTrustChecker && certificateTrustChecker->isCertificateTrusted(certificateChain)) { continueAfterTLSEncrypted(); } else { finishSession(error); } } void ClientSession::continueAfterTLSEncrypted() { state = WaitingForStreamStart; stream->resetXMPPParser(); sendStreamHeader(); } void ClientSession::handleStreamClosed(boost::shared_ptr streamError) { State previousState = state; state = Finished; if (stanzaAckRequester_) { stanzaAckRequester_->onRequestAck.disconnect(boost::bind(&ClientSession::requestAck, shared_from_this())); stanzaAckRequester_->onStanzaAcked.disconnect(boost::bind(&ClientSession::handleStanzaAcked, shared_from_this(), _1)); stanzaAckRequester_.reset(); } if (stanzaAckResponder_) { stanzaAckResponder_->onAck.disconnect(boost::bind(&ClientSession::ack, shared_from_this(), _1)); stanzaAckResponder_.reset(); } stream->setWhitespacePingEnabled(false); stream->onStreamStartReceived.disconnect(boost::bind(&ClientSession::handleStreamStart, shared_from_this(), _1)); stream->onElementReceived.disconnect(boost::bind(&ClientSession::handleElement, shared_from_this(), _1)); stream->onClosed.disconnect(boost::bind(&ClientSession::handleStreamClosed, shared_from_this(), _1)); stream->onTLSEncrypted.disconnect(boost::bind(&ClientSession::handleTLSEncrypted, shared_from_this())); if (previousState == Finishing) { onFinished(error_); } else { onFinished(streamError); } } void ClientSession::finish() { finishSession(boost::shared_ptr()); } void ClientSession::finishSession(Error::Type error) { finishSession(boost::make_shared(error)); } void ClientSession::finishSession(boost::shared_ptr error) { state = Finishing; if (!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(); } void ClientSession::requestAck() { stream->writeElement(boost::make_shared()); } void ClientSession::handleStanzaAcked(boost::shared_ptr stanza) { onStanzaAcked(stanza); } void ClientSession::ack(unsigned int handledStanzasCount) { stream->writeElement(boost::make_shared(handledStanzasCount)); } } swift-im-2.0+dev6/Swiften/Client/ClientBlockListManager.h0000644000175000017500000000231512227051774023212 0ustar kismithkismith/* * 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 #include #include #include #include #include #include #include #include namespace Swift { class IQRouter; class ClientBlockListManager { public: ClientBlockListManager(IQRouter *iqRouter); ~ClientBlockListManager(); bool isSupported() const; /** * Returns the blocklist. */ boost::shared_ptr getBlockList(); private: void handleBlockListReceived(boost::shared_ptr payload, ErrorPayload::ref); private: IQRouter* iqRouter; boost::shared_ptr > getRequest; boost::shared_ptr > blockResponder; boost::shared_ptr > unblockResponder; boost::shared_ptr blockList; }; } swift-im-2.0+dev6/Swiften/Client/DummyNickManager.h0000644000175000017500000000064512227051774022071 0ustar kismithkismith/* * 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 namespace Swift { class VCardManager; class DummyNickManager : public NickManager { public: std::string getOwnNick() const { return ""; } void setOwnNick(const std::string&) { } }; } swift-im-2.0+dev6/Swiften/Client/BlockList.h0000644000175000017500000000121312227051774020554 0ustar kismithkismith/* * 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 #include #include namespace Swift { class BlockList { public: enum State { Requesting, Available, Error, }; virtual ~BlockList(); virtual State getState() const = 0; virtual const std::set& getItems() const = 0; public: boost::signal onStateChanged; boost::signal onItemAdded; boost::signal onItemRemoved; }; } swift-im-2.0+dev6/Swiften/Client/CoreClient.h0000644000175000017500000001515412227051774020726 0ustar kismithkismith/* * Copyright (c) 2010-2012 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include #include #include #include #include #include #include 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 SessionStream; class CertificateTrustChecker; class NetworkFactories; class ClientSessionStanzaChannel; /** * The central class for communicating with an XMPP server. * * This class is responsible for setting up the connection with the XMPP * server, authenticating, and initializing the session. * * This class can be used directly in your application, although the Client * subclass provides more functionality and interfaces, and is better suited * for most needs. */ 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); ~CoreClient(); /** * Set a client certificate to use for strong authentication with the server. * Ensure that it is of the correct type for the TLS engine in use. * This means, largely, PKCS12Certificate for OpenSSL and CAPICertificate for CAPI. */ void setCertificate(CertificateWithKey::ref certificate); /** * Connects the client to the server. * * After the connection is established, the client will set * initialize the stream and authenticate. */ void connect(const ClientOptions& = ClientOptions()); /** * Disconnects the client from the server. */ void disconnect(); /** * Sends a message. */ void sendMessage(boost::shared_ptr); /** * Sends a presence stanza. */ void sendPresence(boost::shared_ptr); /** * Sends raw, unchecked data. */ void sendData(const std::string& data); /** * Returns the IQ router for this client. */ IQRouter* getIQRouter() const { return iqRouter_; } /** * Checks whether the client is connected to the server, * and stanzas can be sent. */ bool isAvailable() const; /** * Checks whether the client is active. * * A client is active when it is connected or connecting to the server. */ bool isActive() const; /** * Returns the JID of the client. * After the session was initialized, this returns the bound JID. */ const JID& getJID() const; /** * Checks whether stream management is enabled. * * If stream management is enabled, onStanzaAcked will be * emitted when a stanza is received by the server. * * \see onStanzaAcked */ bool getStreamManagementEnabled() const; /** * Checks whether stream encryption (TLS) is currently active. */ bool isStreamEncrypted() const; StanzaChannel* getStanzaChannel() const; /** * Sets the certificate trust checker. * * This checker will be called when the server sends a * TLS certificate that does not validate. If the trust checker * says the certificate is trusted, then connecting will proceed; * if not, the connection will end with an error. */ void setCertificateTrustChecker(CertificateTrustChecker*); public: /** * Emitted when the client was disconnected from the network. * * If the connection was due to a non-recoverable error, the type * of error will be passed as a parameter. */ boost::signal&)> onDisconnected; /** * Emitted when the client is connected and authenticated, * and stanzas can be sent. */ boost::signal onConnected; /** * Emitted when the client receives data. * * This signal is emitted before the XML data is parsed, * so this data is unformatted. */ boost::signal onDataRead; /** * Emitted when the client sends data. * * This signal is emitted after the XML was serialized, and * is unformatted. */ boost::signal onDataWritten; /** * Emitted when a message is received. */ boost::signal)> onMessageReceived; /** * Emitted when a presence stanza is received. */ boost::signal) > onPresenceReceived; /** * Emitted when the server acknowledges receipt of a * stanza (if acknowledgements are available). * * \see getStreamManagementEnabled() */ boost::signal)> onStanzaAcked; protected: boost::shared_ptr getSession() const { return session_; } NetworkFactories* getNetworkFactories() const { return networkFactories; } /** * Called before onConnected signal is emmitted. */ virtual void handleConnected() {}; private: void handleConnectorFinished(boost::shared_ptr, boost::shared_ptr error); void handleStanzaChannelAvailableChanged(bool available); void handleSessionFinished(boost::shared_ptr); void handleNeedCredentials(); void handleDataRead(const SafeByteArray&); void handleDataWritten(const SafeByteArray&); void handlePresenceReceived(boost::shared_ptr); void handleMessageReceived(boost::shared_ptr); void handleStanzaAcked(boost::shared_ptr); void purgePassword(); void bindSessionToStream(); void resetConnector(); void resetSession(); void forceReset(); private: JID jid_; SafeByteArray password_; NetworkFactories* networkFactories; ClientSessionStanzaChannel* stanzaChannel_; IQRouter* iqRouter_; ClientOptions options; boost::shared_ptr connector_; std::vector proxyConnectionFactories; boost::shared_ptr connection_; boost::shared_ptr sessionStream_; boost::shared_ptr session_; CertificateWithKey::ref certificate_; bool disconnectRequested_; CertificateTrustChecker* certificateTrustChecker; }; } swift-im-2.0+dev6/Swiften/Client/ClientOptions.h0000644000175000017500000000561212227051774021467 0ustar kismithkismith/* * 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 #include namespace Swift { struct ClientOptions { enum UseTLS { NeverUseTLS, UseTLSWhenAvailable, RequireTLS }; 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("") { } /** * 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; /** * 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) */ URL boshURL; /** * If non-empty, BOSH connections will try to connect over this HTTP CONNECT * proxy instead of directly. * Default: empty (no proxy) */ URL boshHTTPConnectProxyURL; /** * If this and matching Password are non-empty, BOSH connections over * HTTP CONNECT proxies will use these credentials for proxy access. * Default: empty (no authentication needed by the proxy) */ SafeString boshHTTPConnectProxyAuthID; SafeString boshHTTPConnectProxyAuthPassword; }; } swift-im-2.0+dev6/Swiften/Client/XMLBeautifier.h0000644000175000017500000000244312227051774021334 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include #include #include #include #include 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 parentNSs; }; } swift-im-2.0+dev6/Swiften/Client/NickResolver.h0000644000175000017500000000212512227051774021277 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include namespace Swift { class XMPPRoster; class MUCRegistry; class VCardManager; class SWIFTEN_API NickResolver { public: NickResolver(const JID& ownJID, XMPPRoster* xmppRoster, VCardManager* vcardManager, MUCRegistry* mucRegistry); std::string jidToNick(const JID& jid); boost::signal onNickChanged; private: void handleVCardReceived(const JID& jid, VCard::ref vCard); void handleJIDUpdated(const JID& jid, const std::string& previousNick, const std::vector& groups); void handleJIDAdded(const JID& jid); private: JID ownJID_; std::string ownNick_; XMPPRoster* xmppRoster_; MUCRegistry* mucRegistry_; VCardManager* vcardManager_; }; } swift-im-2.0+dev6/Swiften/Client/Storages.cpp0000644000175000017500000000037012227051774021013 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include using namespace Swift; Storages::~Storages() { } swift-im-2.0+dev6/Swiften/LinkLocal/0000755000175000017500000000000012227051774017152 5ustar kismithkismithswift-im-2.0+dev6/Swiften/LinkLocal/LinkLocalService.cpp0000644000175000017500000000133412227051774023050 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { std::string LinkLocalService::getDescription() const { LinkLocalServiceInfo info = getInfo(); if (!info.getNick().empty()) { return info.getNick(); } else if (!info.getFirstName().empty()) { std::string result = info.getFirstName(); if (!info.getLastName().empty()) { result += " " + info.getLastName(); } return result; } else if (!info.getLastName().empty()) { return info.getLastName(); } return getName(); } JID LinkLocalService::getJID() const { return JID(getName()); } } swift-im-2.0+dev6/Swiften/LinkLocal/SConscript0000644000175000017500000000231112227051774021161 0ustar kismithkismithImport("swiften_env") myenv = swiften_env.Clone() myenv.MergeFlags(swiften_env.get("BONJOUR_FLAGS", "")) sources = [ "DNSSD/DNSSDBrowseQuery.cpp", "DNSSD/DNSSDQuerier.cpp", "DNSSD/DNSSDRegisterQuery.cpp", "DNSSD/DNSSDResolveHostnameQuery.cpp", "DNSSD/DNSSDResolveServiceQuery.cpp", "DNSSD/DNSSDServiceID.cpp", "DNSSD/Fake/FakeDNSSDQuerier.cpp", "DNSSD/Fake/FakeDNSSDQuery.cpp", "DNSSD/PlatformDNSSDQuerierFactory.cpp", "IncomingLinkLocalSession.cpp", "LinkLocalConnector.cpp", "LinkLocalService.cpp", "LinkLocalServiceBrowser.cpp", "LinkLocalServiceInfo.cpp", "OutgoingLinkLocalSession.cpp", ] if myenv.get("HAVE_BONJOUR", 0) : myenv.Append(CPPDEFINES = "HAVE_BONJOUR") sources += [ "DNSSD/Bonjour/BonjourQuerier.cpp", "DNSSD/Bonjour/BonjourQuery.cpp", ] elif myenv.get("HAVE_AVAHI", 0) : myenv.Append(CPPDEFINES = ["HAVE_AVAHI"]) sources += [ "DNSSD/Avahi/AvahiQuerier.cpp", "DNSSD/Avahi/AvahiQuery.cpp", "DNSSD/Avahi/AvahiResolveHostnameQuery.cpp", "DNSSD/Avahi/AvahiResolveServiceQuery.cpp", "DNSSD/Avahi/AvahiRegisterQuery.cpp", "DNSSD/Avahi/AvahiBrowseQuery.cpp", ] objects = myenv.SwiftenObject(sources) swiften_env.Append(SWIFTEN_OBJECTS = [objects]) swift-im-2.0+dev6/Swiften/LinkLocal/LinkLocalConnector.h0000644000175000017500000000352212227051774023050 0ustar kismithkismith/* * 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 #include #include #include #include #include #include namespace Swift { class ConnectionFactory; class HostAddress; class Element; class PayloadParserFactoryCollection; class PayloadSerializerCollection; class DNSSDQuerier; class DNSSDResolveHostnameQuery; class SWIFTEN_API LinkLocalConnector : public boost::enable_shared_from_this { public: LinkLocalConnector( const LinkLocalService& service, boost::shared_ptr querier, boost::shared_ptr connection); ~LinkLocalConnector(); const LinkLocalService& getService() const { return service; } void connect(); void cancel(); void queueElement(boost::shared_ptr element); const std::vector >& getQueuedElements() const { return queuedElements; } boost::shared_ptr getConnection() const { return connection; } boost::signal onConnectFinished; private: void handleHostnameResolved(const boost::optional& address); void handleConnected(bool error); private: LinkLocalService service; boost::shared_ptr querier; boost::shared_ptr resolveQuery; boost::bsignals::connection resolveQueryHostNameResolvedConnection; boost::shared_ptr connection; boost::bsignals::connection connectionConnectFinishedConnection; std::vector > queuedElements; }; } swift-im-2.0+dev6/Swiften/LinkLocal/LinkLocalServiceInfo.h0000644000175000017500000000341412227051774023332 0ustar kismithkismith/* * 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 #include #include #include #include namespace Swift { class SWIFTEN_API LinkLocalServiceInfo { public: enum Status { Available, Away, DND }; LinkLocalServiceInfo() : status(Available) {} const std::string& getFirstName() const { return firstName; } void setFirstName(const std::string& f) { firstName = f; } const std::string& getLastName() const { return lastName; } void setLastName(const std::string& l) { lastName = l; } const std::string& getEMail() const { return email; } void setEMail(const std::string& e) { email = e; } const JID& getJID() const { return jid; } void setJID(const JID& j) { jid = j; } const std::string& getMessage() const { return message; } void setMessage(const std::string& m) { message = m; } const std::string& getNick() const { return nick; } void setNick(const std::string& n) { nick = n; } Status getStatus() const { return status; } void setStatus(Status s) { status = s; } boost::optional getPort() const { return port; } void setPort(int p) { port = p; } ByteArray toTXTRecord() const; static LinkLocalServiceInfo createFromTXTRecord(const ByteArray& record); private: static ByteArray getEncoded(const std::string&); static std::pair readEntry(const ByteArray&, size_t*); private: std::string firstName; std::string lastName; std::string email; JID jid; std::string message; std::string nick; Status status; boost::optional port; }; } swift-im-2.0+dev6/Swiften/LinkLocal/IncomingLinkLocalSession.h0000644000175000017500000000212712227051774024225 0ustar kismithkismith/* * 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 #include #include #include #include namespace Swift { class ProtocolHeader; class XMLParserFactory; class Element; class PayloadParserFactoryCollection; class PayloadSerializerCollection; class IncomingLinkLocalSession : public Session { public: IncomingLinkLocalSession( const JID& localJID, boost::shared_ptr connection, PayloadParserFactoryCollection* payloadParserFactories, PayloadSerializerCollection* payloadSerializers, XMLParserFactory* xmlParserFactory); boost::signal onSessionStarted; private: void handleElement(boost::shared_ptr); void handleStreamStart(const ProtocolHeader&); void setInitialized(); bool isInitialized() const { return initialized; } bool initialized; }; } swift-im-2.0+dev6/Swiften/LinkLocal/LinkLocalServiceBrowser.cpp0000644000175000017500000001114312227051774024413 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include namespace Swift { LinkLocalServiceBrowser::LinkLocalServiceBrowser(boost::shared_ptr querier) : querier(querier), haveError(false) { } LinkLocalServiceBrowser::~LinkLocalServiceBrowser() { if (isRunning()) { std::cerr << "WARNING: LinkLocalServiceBrowser still running on destruction" << std::endl; } } void LinkLocalServiceBrowser::start() { assert(!isRunning()); haveError = false; browseQuery = querier->createBrowseQuery(); browseQuery->onServiceAdded.connect( boost::bind(&LinkLocalServiceBrowser::handleServiceAdded, this, _1)); browseQuery->onServiceRemoved.connect( boost::bind(&LinkLocalServiceBrowser::handleServiceRemoved, this, _1)); browseQuery->onError.connect( boost::bind(&LinkLocalServiceBrowser::handleBrowseError, this)); browseQuery->startBrowsing(); } void LinkLocalServiceBrowser::stop() { assert(isRunning()); if (isRegistered()) { unregisterService(); } for (ResolveQueryMap::const_iterator i = resolveQueries.begin(); i != resolveQueries.end(); ++i) { i->second->stop(); } resolveQueries.clear(); services.clear(); browseQuery->stopBrowsing(); browseQuery.reset(); onStopped(haveError); } bool LinkLocalServiceBrowser::isRunning() const { return browseQuery; } bool LinkLocalServiceBrowser::hasError() const { return haveError; } bool LinkLocalServiceBrowser::isRegistered() const { return registerQuery; } void LinkLocalServiceBrowser::registerService(const std::string& name, int port, const LinkLocalServiceInfo& info) { assert(!registerQuery); registerQuery = querier->createRegisterQuery(name, port, info.toTXTRecord()); registerQuery->onRegisterFinished.connect( boost::bind(&LinkLocalServiceBrowser::handleRegisterFinished, this, _1)); registerQuery->registerService(); } void LinkLocalServiceBrowser::updateService(const LinkLocalServiceInfo& info) { assert(registerQuery); registerQuery->updateServiceInfo(info.toTXTRecord()); } void LinkLocalServiceBrowser::unregisterService() { assert(registerQuery); registerQuery->unregisterService(); registerQuery.reset(); selfService.reset(); } std::vector LinkLocalServiceBrowser::getServices() const { std::vector result; for (ServiceMap::const_iterator i = services.begin(); i != services.end(); ++i) { result.push_back(LinkLocalService(i->first, i->second)); } return result; } void LinkLocalServiceBrowser::handleServiceAdded(const DNSSDServiceID& service) { if (selfService && service == *selfService) { return; } std::pair r = resolveQueries.insert(std::make_pair(service, boost::shared_ptr())); if (r.second) { // There was no existing query yet. Start a new query. boost::shared_ptr resolveQuery = querier->createResolveServiceQuery(service); resolveQuery->onServiceResolved.connect( boost::bind(&LinkLocalServiceBrowser::handleServiceResolved, this, service, _1)); r.first->second = resolveQuery; resolveQuery->start(); } } void LinkLocalServiceBrowser::handleServiceRemoved(const DNSSDServiceID& service) { ResolveQueryMap::iterator i = resolveQueries.find(service); if (i == resolveQueries.end()) { // Can happen after an unregister(), when getting the old 'self' // service remove notification. return; } i->second->stop(); resolveQueries.erase(i); ServiceMap::iterator j = services.find(service); assert(j != services.end()); LinkLocalService linkLocalService(j->first, j->second); services.erase(j); onServiceRemoved(linkLocalService); } void LinkLocalServiceBrowser::handleServiceResolved(const DNSSDServiceID& service, const boost::optional& result) { if (result) { std::pair r = services.insert(std::make_pair(service, *result)); if (r.second) { onServiceAdded(LinkLocalService(r.first->first, r.first->second)); } else { r.first->second = *result; onServiceChanged(LinkLocalService(r.first->first, r.first->second)); } } } void LinkLocalServiceBrowser::handleRegisterFinished(const boost::optional& result) { if (result) { selfService = result; onServiceRegistered(*result); } else { haveError = true; stop(); } } void LinkLocalServiceBrowser::handleBrowseError() { haveError = true; stop(); } } swift-im-2.0+dev6/Swiften/LinkLocal/LinkLocalService.h0000644000175000017500000000216412227051774022517 0ustar kismithkismith/* * 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 #include #include #include #include #include namespace Swift { class SWIFTEN_API LinkLocalService { public: LinkLocalService( const DNSSDServiceID& id, const DNSSDResolveServiceQuery::Result& info) : id(id), info(info) {} const DNSSDServiceID& getID() const { return id; } const std::string& getName() const { return id.getName(); } int getPort() const { return info.port; } const std::string& getHostname() const { return info.host; } LinkLocalServiceInfo getInfo() const { return LinkLocalServiceInfo::createFromTXTRecord(info.info); } std::string getDescription() const; JID getJID() const; private: DNSSDServiceID id; DNSSDResolveServiceQuery::Result info; }; } swift-im-2.0+dev6/Swiften/LinkLocal/LinkLocalServiceBrowser.h0000644000175000017500000000474012227051774024065 0ustar kismithkismith/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include namespace Swift { class SWIFTEN_API LinkLocalServiceBrowser { public: LinkLocalServiceBrowser(boost::shared_ptr querier); ~LinkLocalServiceBrowser(); void start(); void stop(); bool isRunning() const; bool hasError() const; void registerService( const std::string& name, int port, const LinkLocalServiceInfo& info = LinkLocalServiceInfo()); void updateService( const LinkLocalServiceInfo& info = LinkLocalServiceInfo()); void unregisterService(); bool isRegistered() const; std::vector getServices() const; // FIXME: Ugly that we need this boost::shared_ptr getQuerier() const { return querier; } boost::signal onServiceAdded; boost::signal onServiceChanged; boost::signal onServiceRemoved; boost::signal onServiceRegistered; boost::signal onStopped; private: void handleServiceAdded(const DNSSDServiceID&); void handleServiceRemoved(const DNSSDServiceID&); void handleServiceResolved(const DNSSDServiceID& service, const boost::optional& result); void handleRegisterFinished(const boost::optional&); void handleBrowseError(); private: boost::shared_ptr querier; boost::optional selfService; boost::shared_ptr browseQuery; boost::shared_ptr registerQuery; typedef std::map > ResolveQueryMap; ResolveQueryMap resolveQueries; typedef std::map ServiceMap; ServiceMap services; bool haveError; }; } swift-im-2.0+dev6/Swiften/LinkLocal/OutgoingLinkLocalSession.h0000644000175000017500000000222312227051774024252 0ustar kismithkismith/* * 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 #include #include #include #include #include namespace Swift { class ConnectionFactory; class XMLParserFactory; class Element; class PayloadParserFactoryCollection; class PayloadSerializerCollection; class OutgoingLinkLocalSession : public Session { public: OutgoingLinkLocalSession( const JID& localJID, const JID& remoteJID, boost::shared_ptr connection, PayloadParserFactoryCollection* payloadParserFactories, PayloadSerializerCollection* payloadSerializers, XMLParserFactory* xmlParserFactory); void queueElement(boost::shared_ptr element); private: void handleSessionStarted(); void handleElement(boost::shared_ptr); void handleStreamStart(const ProtocolHeader&); private: std::vector > queuedElements_; }; } swift-im-2.0+dev6/Swiften/LinkLocal/UnitTest/0000755000175000017500000000000012227051774020731 5ustar kismithkismithswift-im-2.0+dev6/Swiften/LinkLocal/UnitTest/LinkLocalServiceInfoTest.cpp0000644000175000017500000000526512227051774026312 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include using namespace Swift; class LinkLocalServiceInfoTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(LinkLocalServiceInfoTest); CPPUNIT_TEST(testGetTXTRecord); CPPUNIT_TEST(testCreateFromTXTRecord); CPPUNIT_TEST(testCreateFromTXTRecord_InvalidSize); CPPUNIT_TEST(testGetTXTRecordCreateFromTXTRecord_RoundTrip); CPPUNIT_TEST_SUITE_END(); public: void testGetTXTRecord() { LinkLocalServiceInfo info; info.setFirstName("Remko"); info.setLastName("Tron\xc3\xe7on"); info.setStatus(LinkLocalServiceInfo::Away); CPPUNIT_ASSERT_EQUAL(createByteArray("\x09txtvers=1\x09" + std::string("1st=Remko\x0dlast=Tron\xc3\xe7on\x0bstatus=away")), info.toTXTRecord()); } void testCreateFromTXTRecord() { LinkLocalServiceInfo info = LinkLocalServiceInfo::createFromTXTRecord(createByteArray("\x09txtvers=1\x09" + std::string("1st=Remko\x0dlast=Tron\xc3\xe7on\x0bstatus=away"))); CPPUNIT_ASSERT_EQUAL(std::string("Remko"), info.getFirstName()); CPPUNIT_ASSERT_EQUAL(std::string("Tron\xc3\xe7on"), info.getLastName()); CPPUNIT_ASSERT_EQUAL(LinkLocalServiceInfo::Away, info.getStatus()); } void testCreateFromTXTRecord_InvalidSize() { LinkLocalServiceInfo info = LinkLocalServiceInfo::createFromTXTRecord(createByteArray("\x10last=a")); CPPUNIT_ASSERT_EQUAL(std::string("a"), info.getLastName()); } void testGetTXTRecordCreateFromTXTRecord_RoundTrip() { LinkLocalServiceInfo info; info.setFirstName("Remko"); info.setLastName("Tron\xc3\xe7on"); info.setEMail("remko-email@swift.im"); info.setJID(JID("remko-jid@swift.im")); info.setMessage("I'm busy"); info.setNick("el-tramo"); info.setStatus(LinkLocalServiceInfo::DND); info.setPort(1234); LinkLocalServiceInfo info2 = LinkLocalServiceInfo::createFromTXTRecord(info.toTXTRecord()); CPPUNIT_ASSERT_EQUAL(info.getFirstName(), info2.getFirstName()); CPPUNIT_ASSERT_EQUAL(info.getLastName(), info2.getLastName()); CPPUNIT_ASSERT_EQUAL(info.getEMail(), info2.getEMail()); CPPUNIT_ASSERT_EQUAL(info.getJID(), info2.getJID()); CPPUNIT_ASSERT_EQUAL(info.getMessage(), info2.getMessage()); CPPUNIT_ASSERT_EQUAL(info.getNick(), info2.getNick()); CPPUNIT_ASSERT(info.getStatus() == info2.getStatus()); CPPUNIT_ASSERT(info.getPort() == info2.getPort()); } }; CPPUNIT_TEST_SUITE_REGISTRATION(LinkLocalServiceInfoTest); swift-im-2.0+dev6/Swiften/LinkLocal/UnitTest/LinkLocalServiceBrowserTest.cpp0000644000175000017500000003471512227051774027044 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include #include #include using namespace Swift; class LinkLocalServiceBrowserTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(LinkLocalServiceBrowserTest); CPPUNIT_TEST(testConstructor); CPPUNIT_TEST(testStart); CPPUNIT_TEST(testServiceAdded); CPPUNIT_TEST(testServiceAdded_NoServiceInfo); CPPUNIT_TEST(testServiceAdded_RegisteredService); CPPUNIT_TEST(testServiceAdded_UnregisteredService); CPPUNIT_TEST(testServiceAdded_Twice); CPPUNIT_TEST(testServiceChanged); CPPUNIT_TEST(testServiceRemoved); CPPUNIT_TEST(testServiceRemoved_UnregisteredService); CPPUNIT_TEST(testError_BrowseErrorAfterStart); CPPUNIT_TEST(testError_BrowseErrorAfterResolve); CPPUNIT_TEST(testRegisterService); CPPUNIT_TEST(testRegisterService_Error); CPPUNIT_TEST(testRegisterService_Reregister); CPPUNIT_TEST(testUpdateService); CPPUNIT_TEST_SUITE_END(); public: void setUp() { eventLoop = new DummyEventLoop(); querier = boost::make_shared("wonderland.lit", eventLoop); aliceServiceID = new DNSSDServiceID("alice", "wonderland.lit"); aliceServiceInfo = new DNSSDResolveServiceQuery::Result("_presence._tcp.wonderland.lit", "xmpp.wonderland.lit", 1234, LinkLocalServiceInfo().toTXTRecord()); testServiceID = new DNSSDServiceID("foo", "bar.local"); testServiceInfo = new DNSSDResolveServiceQuery::Result("_presence._tcp.bar.local", "xmpp.bar.local", 1234, LinkLocalServiceInfo().toTXTRecord()); testServiceInfo2 = new DNSSDResolveServiceQuery::Result("_presence.tcp.bar.local", "xmpp.foo.local", 2345, LinkLocalServiceInfo().toTXTRecord()); errorStopReceived = false; normalStopReceived = false; } void tearDown() { querier->clearAllQueriesEverRun(); addedServices.clear(); removedServices.clear(); changedServices.clear(); delete aliceServiceID; delete aliceServiceInfo; delete testServiceInfo2; delete testServiceInfo; delete testServiceID; delete eventLoop; } void testConstructor() { boost::shared_ptr testling = createTestling(); CPPUNIT_ASSERT(!testling->isRunning()); CPPUNIT_ASSERT(!testling->hasError()); } void testStart() { boost::shared_ptr testling = createTestling(); testling->start(); CPPUNIT_ASSERT(testling->isRunning()); CPPUNIT_ASSERT(!testling->hasError()); testling->stop(); } void testServiceAdded() { boost::shared_ptr testling = createTestling(); testling->start(); eventLoop->processEvents(); querier->setServiceInfo(*testServiceID,*testServiceInfo); querier->addService(*testServiceID); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(1, static_cast(addedServices.size())); CPPUNIT_ASSERT(addedServices[0].getID() == *testServiceID); CPPUNIT_ASSERT_EQUAL(0, static_cast(changedServices.size())); CPPUNIT_ASSERT_EQUAL(0, static_cast(removedServices.size())); std::vector services = testling->getServices(); CPPUNIT_ASSERT_EQUAL(1, static_cast(services.size())); CPPUNIT_ASSERT(*testServiceID == services[0].getID()); CPPUNIT_ASSERT(testServiceInfo->port == services[0].getPort()); CPPUNIT_ASSERT(testServiceInfo->host == services[0].getHostname()); testling->stop(); } void testServiceAdded_NoServiceInfo() { boost::shared_ptr testling = createTestling(); testling->start(); eventLoop->processEvents(); querier->addService(*testServiceID); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(0, static_cast(addedServices.size())); CPPUNIT_ASSERT_EQUAL(0, static_cast(testling->getServices().size())); testling->stop(); } void testServiceAdded_RegisteredService() { boost::shared_ptr testling = createTestling(); testling->start(); eventLoop->processEvents(); testling->registerService("alice", 1234, LinkLocalServiceInfo()); eventLoop->processEvents(); querier->setServiceInfo(*aliceServiceID, *aliceServiceInfo); querier->addService(*aliceServiceID); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(0, static_cast(addedServices.size())); CPPUNIT_ASSERT_EQUAL(0, static_cast(testling->getServices().size())); testling->stop(); } void testServiceAdded_UnregisteredService() { boost::shared_ptr testling = createTestling(); testling->start(); eventLoop->processEvents(); testling->registerService("alice", 1234, LinkLocalServiceInfo()); eventLoop->processEvents(); testling->unregisterService(); eventLoop->processEvents(); querier->setServiceInfo(*aliceServiceID, *aliceServiceInfo); querier->addService(*aliceServiceID); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(1, static_cast(addedServices.size())); CPPUNIT_ASSERT(addedServices[0].getID() == *aliceServiceID); CPPUNIT_ASSERT_EQUAL(0, static_cast(changedServices.size())); CPPUNIT_ASSERT_EQUAL(0, static_cast(removedServices.size())); std::vector services = testling->getServices(); CPPUNIT_ASSERT_EQUAL(1, static_cast(services.size())); CPPUNIT_ASSERT(*aliceServiceID == services[0].getID()); CPPUNIT_ASSERT(aliceServiceInfo->port == services[0].getPort()); CPPUNIT_ASSERT(aliceServiceInfo->host == services[0].getHostname()); testling->stop(); } void testServiceRemoved_UnregisteredService() { boost::shared_ptr testling = createTestling(); testling->start(); eventLoop->processEvents(); testling->registerService("alice", 1234, LinkLocalServiceInfo()); eventLoop->processEvents(); testling->unregisterService(); eventLoop->processEvents(); querier->removeService(*aliceServiceID); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(0, static_cast(removedServices.size())); testling->stop(); } void testServiceAdded_Twice() { boost::shared_ptr testling = createTestling(); testling->start(); eventLoop->processEvents(); querier->setServiceInfo(*testServiceID,*testServiceInfo); querier->addService(*testServiceID); querier->addService(*testServiceID); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(1, static_cast(querier->getAllQueriesEverRun().size())); CPPUNIT_ASSERT_EQUAL(1, static_cast(addedServices.size())); CPPUNIT_ASSERT(addedServices[0].getID() == *testServiceID); CPPUNIT_ASSERT_EQUAL(0, static_cast(changedServices.size())); CPPUNIT_ASSERT_EQUAL(0, static_cast(removedServices.size())); std::vector services = testling->getServices(); CPPUNIT_ASSERT_EQUAL(1, static_cast(services.size())); CPPUNIT_ASSERT(*testServiceID == services[0].getID()); CPPUNIT_ASSERT(testServiceInfo->port == services[0].getPort()); CPPUNIT_ASSERT(testServiceInfo->host == services[0].getHostname()); testling->stop(); } void testServiceChanged() { boost::shared_ptr testling = createTestling(); testling->start(); querier->setServiceInfo(*testServiceID,*testServiceInfo); querier->addService(*testServiceID); eventLoop->processEvents(); querier->setServiceInfo(*testServiceID,*testServiceInfo2); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(1, static_cast(addedServices.size())); CPPUNIT_ASSERT_EQUAL(1, static_cast(changedServices.size())); CPPUNIT_ASSERT_EQUAL(0, static_cast(removedServices.size())); CPPUNIT_ASSERT(changedServices[0].getID() == *testServiceID); std::vector services = testling->getServices(); CPPUNIT_ASSERT_EQUAL(1, static_cast(services.size())); CPPUNIT_ASSERT(*testServiceID == services[0].getID()); CPPUNIT_ASSERT(testServiceInfo2->port == services[0].getPort()); CPPUNIT_ASSERT(testServiceInfo2->host == services[0].getHostname()); testling->stop(); } void testServiceRemoved() { boost::shared_ptr testling = createTestling(); testling->start(); querier->setServiceInfo(*testServiceID,*testServiceInfo); querier->addService(*testServiceID); eventLoop->processEvents(); querier->removeService(*testServiceID); eventLoop->processEvents(); querier->setServiceInfo(*testServiceID,*testServiceInfo2); eventLoop->processEvents(); CPPUNIT_ASSERT_EQUAL(1, static_cast(addedServices.size())); CPPUNIT_ASSERT_EQUAL(0, static_cast(changedServices.size())); CPPUNIT_ASSERT_EQUAL(1, static_cast(removedServices.size())); CPPUNIT_ASSERT(removedServices[0].getID() == *testServiceID); CPPUNIT_ASSERT_EQUAL(0, static_cast(testling->getServices().size())); testling->stop(); } void testError_BrowseErrorAfterStart() { boost::shared_ptr testling = createTestling(); testling->start(); querier->setBrowseError(); eventLoop->processEvents(); CPPUNIT_ASSERT(!testling->isRunning()); CPPUNIT_ASSERT(testling->hasError()); CPPUNIT_ASSERT(errorStopReceived); } void testError_BrowseErrorAfterResolve() { boost::shared_ptr testling = createTestling(); testling->start(); querier->setServiceInfo(*testServiceID,*testServiceInfo); querier->addService(*testServiceID); eventLoop->processEvents(); querier->setBrowseError(); eventLoop->processEvents(); querier->setServiceInfo(*testServiceID,*testServiceInfo2); eventLoop->processEvents(); CPPUNIT_ASSERT(!testling->isRunning()); CPPUNIT_ASSERT(testling->hasError()); CPPUNIT_ASSERT_EQUAL(0, static_cast(testling->getServices().size())); CPPUNIT_ASSERT_EQUAL(0, static_cast(changedServices.size())); CPPUNIT_ASSERT(errorStopReceived); } void testRegisterService() { boost::shared_ptr testling = createTestling(); testling->start(); eventLoop->processEvents(); LinkLocalServiceInfo info; info.setFirstName("Foo"); testling->registerService("foo@bar", 1234, info); eventLoop->processEvents(); CPPUNIT_ASSERT(querier->isServiceRegistered("foo@bar", 1234, info.toTXTRecord())); CPPUNIT_ASSERT_EQUAL(1, static_cast(registeredServices.size())); CPPUNIT_ASSERT(registeredServices[0] == DNSSDServiceID("foo@bar", "wonderland.lit")); testling->stop(); } void testRegisterService_Error() { boost::shared_ptr testling = createTestling(); testling->start(); LinkLocalServiceInfo info; testling->registerService("foo@bar", 1234, info); eventLoop->processEvents(); querier->setRegisterError(); eventLoop->processEvents(); CPPUNIT_ASSERT(!testling->isRunning()); CPPUNIT_ASSERT(testling->hasError()); CPPUNIT_ASSERT(errorStopReceived); CPPUNIT_ASSERT(!querier->isServiceRegistered("foo@bar", 1234, info.toTXTRecord())); } void testRegisterService_Reregister() { boost::shared_ptr testling = createTestling(); testling->start(); eventLoop->processEvents(); LinkLocalServiceInfo info; info.setFirstName("Foo"); testling->registerService("foo@bar", 1234, info); eventLoop->processEvents(); testling->unregisterService(); eventLoop->processEvents(); info.setFirstName("Bar"); testling->registerService("bar@baz", 3456, info); eventLoop->processEvents(); CPPUNIT_ASSERT(querier->isServiceRegistered("bar@baz", 3456, info.toTXTRecord())); testling->stop(); } void testUpdateService() { boost::shared_ptr testling = createTestling(); testling->start(); eventLoop->processEvents(); LinkLocalServiceInfo info; info.setFirstName("Foo"); testling->registerService("foo@bar", 1234, info); eventLoop->processEvents(); info.setFirstName("Bar"); testling->updateService(info); CPPUNIT_ASSERT(querier->isServiceRegistered("foo@bar", 1234, info.toTXTRecord())); testling->stop(); } private: boost::shared_ptr createTestling() { boost::shared_ptr testling( new LinkLocalServiceBrowser(querier)); testling->onServiceAdded.connect(boost::bind( &LinkLocalServiceBrowserTest::handleServiceAdded, this, _1)); testling->onServiceChanged.connect(boost::bind( &LinkLocalServiceBrowserTest::handleServiceChanged, this, _1)); testling->onServiceRemoved.connect(boost::bind( &LinkLocalServiceBrowserTest::handleServiceRemoved, this, _1)); testling->onServiceRegistered.connect(boost::bind( &LinkLocalServiceBrowserTest::handleServiceRegistered, this, _1)); testling->onStopped.connect(boost::bind( &LinkLocalServiceBrowserTest::handleStopped, this, _1)); return testling; } void handleServiceAdded(const LinkLocalService& service) { addedServices.push_back(service); } void handleServiceRemoved(const LinkLocalService& service) { removedServices.push_back(service); } void handleServiceChanged(const LinkLocalService& service) { changedServices.push_back(service); } void handleServiceRegistered(const DNSSDServiceID& service) { registeredServices.push_back(service); } void handleStopped(bool error) { CPPUNIT_ASSERT(!errorStopReceived); CPPUNIT_ASSERT(!normalStopReceived); if (error) { errorStopReceived = true; } else { normalStopReceived = true; } } private: DummyEventLoop* eventLoop; boost::shared_ptr querier; std::vector addedServices; std::vector changedServices; std::vector removedServices; std::vector registeredServices; DNSSDServiceID* aliceServiceID; DNSSDResolveServiceQuery::Result* aliceServiceInfo; DNSSDServiceID* testServiceID; DNSSDResolveServiceQuery::Result* testServiceInfo; DNSSDResolveServiceQuery::Result* testServiceInfo2; bool errorStopReceived; bool normalStopReceived; }; CPPUNIT_TEST_SUITE_REGISTRATION(LinkLocalServiceBrowserTest); swift-im-2.0+dev6/Swiften/LinkLocal/UnitTest/LinkLocalConnectorTest.cpp0000644000175000017500000001071612227051774026025 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include using namespace Swift; class LinkLocalConnectorTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(LinkLocalConnectorTest); CPPUNIT_TEST(testConnect); CPPUNIT_TEST(testConnect_UnableToResolve); CPPUNIT_TEST(testConnect_UnableToConnect); CPPUNIT_TEST(testCancel_DuringResolve); CPPUNIT_TEST(testCancel_DuringConnect); CPPUNIT_TEST_SUITE_END(); public: void setUp() { eventLoop = new DummyEventLoop(); querier = boost::make_shared("rabbithole.local", eventLoop); connection = boost::make_shared(eventLoop); connectFinished = false; } void tearDown() { querier->clearAllQueriesEverRun(); delete eventLoop; } void testConnect() { boost::shared_ptr testling(createConnector("rabbithole.local", 1234)); querier->setAddress("rabbithole.local", HostAddress("192.168.1.1")); testling->connect(); eventLoop->processEvents(); CPPUNIT_ASSERT(connectFinished); CPPUNIT_ASSERT(!connectError); CPPUNIT_ASSERT(connection->connectedTo); CPPUNIT_ASSERT_EQUAL(std::string(connection->connectedTo->getAddress().toString()), std::string("192.168.1.1")); CPPUNIT_ASSERT_EQUAL(connection->connectedTo->getPort(), 1234); } void testConnect_UnableToResolve() { boost::shared_ptr testling(createConnector("rabbithole.local", 1234)); querier->setAddress("rabbithole.local", boost::optional()); testling->connect(); eventLoop->processEvents(); CPPUNIT_ASSERT(connectFinished); CPPUNIT_ASSERT(connectError); CPPUNIT_ASSERT(!connection->connectedTo); } void testConnect_UnableToConnect() { boost::shared_ptr testling(createConnector("rabbithole.local", 1234)); querier->setAddress("rabbithole.local", HostAddress("192.168.1.1")); connection->setError(Connection::ReadError); testling->connect(); eventLoop->processEvents(); CPPUNIT_ASSERT(connectFinished); CPPUNIT_ASSERT(connectError); CPPUNIT_ASSERT(!connection->connectedTo); } void testCancel_DuringResolve() { boost::shared_ptr testling(createConnector("rabbithole.local", 1234)); testling->connect(); eventLoop->processEvents(); CPPUNIT_ASSERT(!connectFinished); testling->cancel(); eventLoop->processEvents(); querier->setAddress("rabbithole.local", HostAddress("192.168.1.1")); eventLoop->processEvents(); CPPUNIT_ASSERT(FakeConnection::Disconnected == connection->state); } void testCancel_DuringConnect() { boost::shared_ptr testling(createConnector("rabbithole.local", 1234)); querier->setAddress("rabbithole.local", HostAddress("192.168.1.1")); connection->setDelayConnect(); testling->connect(); eventLoop->processEvents(); CPPUNIT_ASSERT(FakeConnection::Connecting == connection->state); testling->cancel(); eventLoop->processEvents(); CPPUNIT_ASSERT(FakeConnection::Disconnected == connection->state); } private: boost::shared_ptr createConnector(const std::string& hostname, int port) { LinkLocalService service( DNSSDServiceID("myname", "local."), DNSSDResolveServiceQuery::Result( "myname._presence._tcp.local", hostname, port, LinkLocalServiceInfo().toTXTRecord())); boost::shared_ptr result( new LinkLocalConnector(service, querier, connection)); result->onConnectFinished.connect( boost::bind(&LinkLocalConnectorTest::handleConnected, this, _1)); return result; } void handleConnected(bool e) { connectFinished = true; connectError = e; } private: DummyEventLoop* eventLoop; boost::shared_ptr querier; boost::shared_ptr connection; bool connectFinished; bool connectError; }; CPPUNIT_TEST_SUITE_REGISTRATION(LinkLocalConnectorTest); swift-im-2.0+dev6/Swiften/LinkLocal/UnitTest/LinkLocalServiceTest.cpp0000644000175000017500000000453712227051774025477 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include using namespace Swift; class LinkLocalServiceTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(LinkLocalServiceTest); CPPUNIT_TEST(testGetDescription_WithNick); CPPUNIT_TEST(testGetDescription_WithFirstName); CPPUNIT_TEST(testGetDescription_WithLastName); CPPUNIT_TEST(testGetDescription_WithFirstAndLastName); CPPUNIT_TEST(testGetDescription_NoInfo); CPPUNIT_TEST_SUITE_END(); public: void testGetDescription_WithNick() { LinkLocalService testling = createService("alice@wonderland", "Alice", "Alice In", "Wonderland"); CPPUNIT_ASSERT_EQUAL(std::string("Alice"), testling.getDescription()); } void testGetDescription_WithFirstName() { LinkLocalService testling = createService("alice@wonderland", "", "Alice In"); CPPUNIT_ASSERT_EQUAL(std::string("Alice In"), testling.getDescription()); } void testGetDescription_WithLastName() { LinkLocalService testling = createService("alice@wonderland", "", "", "Wonderland"); CPPUNIT_ASSERT_EQUAL(std::string("Wonderland"), testling.getDescription()); } void testGetDescription_WithFirstAndLastName() { LinkLocalService testling = createService("alice@wonderland", "", "Alice In", "Wonderland"); CPPUNIT_ASSERT_EQUAL(std::string("Alice In Wonderland"), testling.getDescription()); } void testGetDescription_NoInfo() { LinkLocalService testling = createService("alice@wonderland"); CPPUNIT_ASSERT_EQUAL(std::string("alice@wonderland"), testling.getDescription()); } private: LinkLocalService createService(const std::string& name, const std::string& nickName = std::string(), const std::string& firstName = std::string(), const std::string& lastName = std::string()) { DNSSDServiceID service(name, "local."); LinkLocalServiceInfo info; info.setFirstName(firstName); info.setLastName(lastName); info.setNick(nickName); return LinkLocalService(service, DNSSDResolveServiceQuery::Result( name + "._presence._tcp.local", "rabbithole.local", 1234, info.toTXTRecord())); } }; CPPUNIT_TEST_SUITE_REGISTRATION(LinkLocalServiceTest); swift-im-2.0+dev6/Swiften/LinkLocal/DNSSD/0000755000175000017500000000000012227051774020025 5ustar kismithkismithswift-im-2.0+dev6/Swiften/LinkLocal/DNSSD/DNSSDBrowseQuery.cpp0000644000175000017500000000042712227051774023617 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { DNSSDBrowseQuery::~DNSSDBrowseQuery() { } } swift-im-2.0+dev6/Swiften/LinkLocal/DNSSD/DNSSDResolveHostnameQuery.h0000644000175000017500000000103212227051774025132 0ustar kismithkismith/* * 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 #include #include namespace Swift { class DNSSDResolveHostnameQuery { public: virtual ~DNSSDResolveHostnameQuery(); virtual void run() = 0; virtual void finish() = 0; boost::signal&)> onHostnameResolved; }; } swift-im-2.0+dev6/Swiften/LinkLocal/DNSSD/DNSSDServiceID.h0000644000175000017500000000303612227051774022611 0ustar kismithkismith/* * 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 #include namespace Swift { class SWIFTEN_API DNSSDServiceID { public: static const char* PresenceServiceType; DNSSDServiceID( const std::string& name, const std::string& domain, const std::string& type = PresenceServiceType, int networkInterface = -1) : name(name), domain(domain), type(type), networkInterface(networkInterface) { } bool operator==(const DNSSDServiceID& o) const { return name == o.name && domain == o.domain && type == o.type && (networkInterface != 0 && o.networkInterface != 0 ? networkInterface == o.networkInterface : true); } bool operator<(const DNSSDServiceID& o) const { if (o.name == name) { if (o.domain == domain) { if (o.type == type) { return networkInterface < o.networkInterface; } else { return type < o.type; } } else { return domain < o.domain; } } else { return o.name < name; } } const std::string& getName() const { return name; } const std::string& getDomain() const { return domain; } const std::string& getType() const { return type; } int getNetworkInterfaceID() const { return networkInterface; } private: std::string name; std::string domain; std::string type; int networkInterface; }; } swift-im-2.0+dev6/Swiften/LinkLocal/DNSSD/Avahi/0000755000175000017500000000000012227051774021055 5ustar kismithkismithswift-im-2.0+dev6/Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveServiceQuery.cpp0000644000175000017500000000531012227051774026517 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include namespace Swift { void AvahiResolveServiceQuery::start() { std::cout << "Start resolving " << service.getName() << " " << service.getType() << " " << service.getDomain() << std::endl; avahi_threaded_poll_lock(querier->getThreadedPoll()); assert(!resolver); resolver = avahi_service_resolver_new(querier->getClient(), service.getNetworkInterfaceID(), AVAHI_PROTO_UNSPEC, service.getName().c_str(), service.getType().c_str(), service.getDomain().c_str(), AVAHI_PROTO_UNSPEC, static_cast(0), handleServiceResolvedStatic, this); if (!resolver) { std::cout << "Error starting resolver" << std::endl; eventLoop->postEvent(boost::bind(boost::ref(onServiceResolved), boost::optional()), shared_from_this()); } avahi_threaded_poll_unlock(querier->getThreadedPoll()); } void AvahiResolveServiceQuery::stop() { std::cout << "Stop resolving" << std::endl; avahi_threaded_poll_lock(querier->getThreadedPoll()); avahi_service_resolver_free(resolver); resolver = NULL; avahi_threaded_poll_unlock(querier->getThreadedPoll()); } void AvahiResolveServiceQuery::handleServiceResolved(AvahiServiceResolver* resolver, AvahiIfIndex, AvahiProtocol, AvahiResolverEvent event, const char *name, const char * type, const char* domain, const char * /*host_name*/, const AvahiAddress *address, uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags) { std::cout << "Resolve finished" << std::endl; switch(event) { case AVAHI_RESOLVER_FAILURE: std::cout << "Resolve error " << avahi_strerror(avahi_client_errno(avahi_service_resolver_get_client(resolver))) << std::endl; eventLoop->postEvent(boost::bind(boost::ref(onServiceResolved), boost::optional()), shared_from_this()); break; case AVAHI_RESOLVER_FOUND: { std::cout << "Success" << std::endl; char a[AVAHI_ADDRESS_STR_MAX]; avahi_address_snprint(a, sizeof(a), address); ByteArray txtRecord; txtRecord.resize(1024); avahi_string_list_serialize(txt, vecptr(txtRecord), txtRecord.size()); // FIXME: Probably not accurate std::string fullname = std::string(name) + "." + std::string(type) + "." + std::string(domain) + "."; std::cout << "Result: " << fullname << "->" << std::string(a) << ":" << port << std::endl; eventLoop->postEvent( boost::bind( boost::ref(onServiceResolved), Result(fullname, std::string(a), port, txtRecord)), shared_from_this()); break; } } } } swift-im-2.0+dev6/Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveServiceQuery.h0000644000175000017500000000331012227051774026162 0ustar kismithkismith/* * 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 #include #include #include #include #include namespace Swift { class AvahiQuerier; class AvahiResolveServiceQuery : public DNSSDResolveServiceQuery, public AvahiQuery { public: AvahiResolveServiceQuery(const DNSSDServiceID& service, boost::shared_ptr querier, EventLoop* eventLoop) : AvahiQuery(querier, eventLoop), service(service), resolver(NULL) { } void start(); void stop(); private: static void handleServiceResolvedStatic(AvahiServiceResolver* resolver, AvahiIfIndex interfaceIndex, AvahiProtocol protocol, AvahiResolverEvent event, const char *name, const char *type, const char *domain, const char *host_name, const AvahiAddress *address, uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags flags, void* context) { static_cast(context)->handleServiceResolved(resolver, interfaceIndex, protocol, event, name, type, domain, host_name, address, port, txt, flags); } void handleServiceResolved(AvahiServiceResolver* resolver, AvahiIfIndex, AvahiProtocol, AvahiResolverEvent event, const char *name, const char * type, const char* domain, const char * /*host_name*/, const AvahiAddress *address, uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags); private: DNSSDServiceID service; AvahiServiceResolver* resolver; }; } swift-im-2.0+dev6/Swiften/LinkLocal/DNSSD/Avahi/AvahiRegisterQuery.cpp0000644000175000017500000000656512227051774025360 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include namespace Swift { void AvahiRegisterQuery::registerService() { std::cout << "Registering service " << name << ":" << port << std::endl; avahi_threaded_poll_lock(querier->getThreadedPoll()); if (!group) { std::cout << "Creating entry group" << std::endl; group = avahi_entry_group_new(querier->getClient(), handleEntryGroupChange, this); if (!group) { std::cout << "Error ceating entry group" << std::endl; eventLoop->postEvent(boost::bind(boost::ref(onRegisterFinished), boost::optional()), shared_from_this()); } } doRegisterService(); avahi_threaded_poll_unlock(querier->getThreadedPoll()); } void AvahiRegisterQuery::unregisterService() { if (group) { avahi_entry_group_free(group); group = NULL; } } void AvahiRegisterQuery::updateServiceInfo(const ByteArray& txtRecord) { this->txtRecord = txtRecord; avahi_threaded_poll_lock(querier->getThreadedPoll()); assert(group); avahi_entry_group_reset(group); doRegisterService(); avahi_threaded_poll_unlock(querier->getThreadedPoll()); } void AvahiRegisterQuery::doRegisterService() { AvahiStringList* txtList; avahi_string_list_parse(vecptr(txtRecord), txtRecord.size(), &txtList); int result = avahi_entry_group_add_service_strlst(group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, static_cast(0), name.c_str(), "_presence._tcp", NULL, NULL, port, txtList); if (result < 0) { std::cout << "Error registering service: " << avahi_strerror(result) << std::endl; eventLoop->postEvent(boost::bind(boost::ref(onRegisterFinished), boost::optional()), shared_from_this()); } result = avahi_entry_group_commit(group); if (result < 0) { std::cout << "Error registering service: " << avahi_strerror(result) << std::endl; } } void AvahiRegisterQuery::handleEntryGroupChange(AvahiEntryGroup* g, AvahiEntryGroupState state) { std::cout << "ENtry group callback: " << state << std::endl; switch (state) { case AVAHI_ENTRY_GROUP_ESTABLISHED : // Domain is a hack! eventLoop->postEvent(boost::bind(boost::ref(onRegisterFinished), boost::optional(DNSSDServiceID(name, "local", "_presence._tcp", 0))), shared_from_this()); std::cout << "Entry group established" << std::endl; break; case AVAHI_ENTRY_GROUP_COLLISION : { std::cout << "Entry group collision" << std::endl; /*char *n; n = avahi_alternative_service_name(name); avahi_free(name); name = n;*/ break; } case AVAHI_ENTRY_GROUP_FAILURE : std::cout << "Entry group failure " << avahi_strerror(avahi_client_errno(avahi_entry_group_get_client(g))) << std::endl; break; case AVAHI_ENTRY_GROUP_UNCOMMITED: case AVAHI_ENTRY_GROUP_REGISTERING: ; /* DNSServiceErrorType result = DNSServiceRegister( &sdRef, 0, 0, name.c_str(), "_presence._tcp", NULL, NULL, port, txtRecord.getSize(), txtRecord.getData(), &AvahiRegisterQuery::handleServiceRegisteredStatic, this); if (result != kDNSServiceErr_NoError) { sdRef = NULL; }*/ //eventLoop->postEvent(boost::bind(boost::ref(onRegisterFinished), boost::optional()), shared_from_this()); } } } swift-im-2.0+dev6/Swiften/LinkLocal/DNSSD/Avahi/AvahiBrowseQuery.cpp0000644000175000017500000000435612227051774025031 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include namespace Swift { void AvahiBrowseQuery::startBrowsing() { std::cout << "Start browsing" << std::endl; assert(!browser); avahi_threaded_poll_lock(querier->getThreadedPoll()); browser = avahi_service_browser_new(querier->getClient(), AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "_presence._tcp", NULL, static_cast(0), &handleServiceDiscoveredStatic, this); if (!browser) { std::cout << "Error" << std::endl; eventLoop->postEvent(boost::bind(boost::ref(onError)), shared_from_this()); } avahi_threaded_poll_unlock(querier->getThreadedPoll()); } void AvahiBrowseQuery::stopBrowsing() { std::cout << "Stop browsing" << std::endl; avahi_threaded_poll_lock(querier->getThreadedPoll()); avahi_service_browser_free(browser); browser = NULL; avahi_threaded_poll_unlock(querier->getThreadedPoll()); } void AvahiBrowseQuery::handleServiceDiscovered(AvahiServiceBrowser *, AvahiIfIndex interfaceIndex, AvahiProtocol, AvahiBrowserEvent event, const char *name, const char *type, const char *domain, AvahiLookupResultFlags) { switch (event) { case AVAHI_BROWSER_FAILURE: std::cout << "Service browse error" << std::endl; eventLoop->postEvent(boost::bind(boost::ref(onError)), shared_from_this()); break; case AVAHI_BROWSER_NEW: { DNSSDServiceID service(name, domain, type, interfaceIndex); std::cout << "Service discovered " << name << " " << domain << " " << type << " " << interfaceIndex << std::endl; eventLoop->postEvent(boost::bind(boost::ref(onServiceAdded), service), shared_from_this()); break; } case AVAHI_BROWSER_REMOVE: { std::cout << "Service went away " << name << " " << domain << " " << type << " " << interfaceIndex << std::endl; DNSSDServiceID service(name, domain, type, interfaceIndex); eventLoop->postEvent(boost::bind(boost::ref(onServiceRemoved), service), shared_from_this()); break; } case AVAHI_BROWSER_ALL_FOR_NOW: case AVAHI_BROWSER_CACHE_EXHAUSTED: break; } } } swift-im-2.0+dev6/Swiften/LinkLocal/DNSSD/Avahi/AvahiQuery.cpp0000644000175000017500000000067412227051774023646 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include namespace Swift { AvahiQuery::AvahiQuery(boost::shared_ptr q, EventLoop* eventLoop) : querier(q), eventLoop(eventLoop) { } AvahiQuery::~AvahiQuery() { } } swift-im-2.0+dev6/Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveHostnameQuery.cpp0000644000175000017500000000136112227051774026677 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include namespace Swift { AvahiResolveHostnameQuery::AvahiResolveHostnameQuery(const std::string& hostname, int, boost::shared_ptr querier, EventLoop* eventLoop) : AvahiQuery(querier, eventLoop), hostname(hostname) { std::cout << "Resolving hostname " << hostname << std::endl; } void AvahiResolveHostnameQuery::run() { eventLoop->postEvent(boost::bind(boost::ref(onHostnameResolved), boost::optional(HostAddress(hostname))), shared_from_this()); } } swift-im-2.0+dev6/Swiften/LinkLocal/DNSSD/Avahi/AvahiQuerier.cpp0000644000175000017500000000441312227051774024150 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include namespace Swift { AvahiQuerier::AvahiQuerier(EventLoop* eventLoop) : eventLoop(eventLoop), client(NULL), threadedPoll(NULL) { } AvahiQuerier::~AvahiQuerier() { } boost::shared_ptr AvahiQuerier::createBrowseQuery() { return boost::shared_ptr(new AvahiBrowseQuery(shared_from_this(), eventLoop)); } boost::shared_ptr AvahiQuerier::createRegisterQuery(const std::string& name, int port, const ByteArray& info) { return boost::shared_ptr(new AvahiRegisterQuery(name, port, info, shared_from_this(), eventLoop)); } boost::shared_ptr AvahiQuerier::createResolveServiceQuery(const DNSSDServiceID& service) { return boost::shared_ptr(new AvahiResolveServiceQuery(service, shared_from_this(), eventLoop)); } boost::shared_ptr AvahiQuerier::createResolveHostnameQuery(const std::string& hostname, int interfaceIndex) { return boost::shared_ptr(new AvahiResolveHostnameQuery(hostname, interfaceIndex, shared_from_this(), eventLoop)); } void AvahiQuerier::start() { std::cout << "Starrting querier" << std::endl; assert(!threadedPoll); threadedPoll = avahi_threaded_poll_new(); int error; assert(!client); client = avahi_client_new( avahi_threaded_poll_get(threadedPoll), static_cast(0), NULL, this, &error); // TODO if (!client) { // TODO std::cerr << "Avahi Error: " << avahi_strerror(error) << std::endl; return; } std::cout << "Starrting event loop" << std::endl; avahi_threaded_poll_start(threadedPoll); } void AvahiQuerier::stop() { assert(threadedPoll); avahi_threaded_poll_stop(threadedPoll); assert(client); avahi_client_free(client); avahi_threaded_poll_free(threadedPoll); } } swift-im-2.0+dev6/Swiften/LinkLocal/DNSSD/Avahi/AvahiQuerier.h0000644000175000017500000000265512227051774023623 0ustar kismithkismith/* * 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 #include #include #include #include #include #include #include #include #include namespace Swift { class EventLoop; class AvahiQuerier : public DNSSDQuerier, public boost::enable_shared_from_this { public: AvahiQuerier(EventLoop* eventLoop); ~AvahiQuerier(); boost::shared_ptr createBrowseQuery(); boost::shared_ptr createRegisterQuery( const std::string& name, int port, const ByteArray& info); boost::shared_ptr createResolveServiceQuery( const DNSSDServiceID&); boost::shared_ptr createResolveHostnameQuery( const std::string& hostname, int interfaceIndex); void start(); void stop(); AvahiThreadedPoll* getThreadedPoll() const { return threadedPoll; } AvahiClient* getClient() const { return client; } private: EventLoop* eventLoop; AvahiClient* client; AvahiThreadedPoll* threadedPoll; }; } swift-im-2.0+dev6/Swiften/LinkLocal/DNSSD/Avahi/AvahiBrowseQuery.h0000644000175000017500000000244512227051774024473 0ustar kismithkismith/* * 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 #include #include #include namespace Swift { class AvahiQuerier; class AvahiBrowseQuery : public DNSSDBrowseQuery, public AvahiQuery { public: AvahiBrowseQuery(boost::shared_ptr q, EventLoop* eventLoop) : AvahiQuery(q, eventLoop), browser(NULL) { } void startBrowsing(); void stopBrowsing(); private: static void handleServiceDiscoveredStatic(AvahiServiceBrowser *b, AvahiIfIndex interfaceIndex, AvahiProtocol protocol, AvahiBrowserEvent event, const char *name, const char *type, const char *domain, AvahiLookupResultFlags flags, void* context) { static_cast(context)->handleServiceDiscovered(b, interfaceIndex, protocol, event, name, type, domain, flags); } void handleServiceDiscovered(AvahiServiceBrowser *, AvahiIfIndex interfaceIndex, AvahiProtocol, AvahiBrowserEvent event, const char *name, const char *type, const char *domain, AvahiLookupResultFlags); private: AvahiServiceBrowser* browser; }; } swift-im-2.0+dev6/Swiften/LinkLocal/DNSSD/Avahi/AvahiQuery.h0000644000175000017500000000121212227051774023300 0ustar kismithkismith/* * 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 #include #include namespace Swift { class AvahiQuerier; class EventLoop; class AvahiQuery : public EventOwner, public boost::enable_shared_from_this { public: AvahiQuery(boost::shared_ptr, EventLoop* eventLoop); virtual ~AvahiQuery(); protected: boost::shared_ptr querier; EventLoop* eventLoop; }; } swift-im-2.0+dev6/Swiften/LinkLocal/DNSSD/Avahi/AvahiRegisterQuery.h0000644000175000017500000000361012227051774025011 0ustar kismithkismith/* * 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 #include #include #include #include namespace Swift { class AvahiQuerier; class AvahiRegisterQuery : public DNSSDRegisterQuery, public AvahiQuery { public: AvahiRegisterQuery(const std::string& name, int port, const ByteArray& txtRecord, boost::shared_ptr querier, EventLoop* eventLoop) : AvahiQuery(querier, eventLoop), name(name), port(port), txtRecord(txtRecord), group(0) { } void registerService(); void unregisterService(); void updateServiceInfo(const ByteArray& txtRecord); private: void doRegisterService(); static void handleEntryGroupChange(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) { static_cast(userdata)->handleEntryGroupChange(g, state); } void handleEntryGroupChange(AvahiEntryGroup* g, AvahiEntryGroupState state); /* static void handleServiceRegisteredStatic(DNSServiceRef, DNSServiceFlags, DNSServiceErrorType errorCode, const char *name, const char *regtype, const char *domain, void *context) { static_cast(context)->handleServiceRegistered(errorCode, name, regtype, domain); } void handleServiceRegistered(DNSServiceErrorType errorCode, const char *name, const char *regtype, const char *domain) { if (errorCode != kDNSServiceErr_NoError) { eventLoop->postEvent(boost::bind(boost::ref(onRegisterFinished), boost::optional()), shared_from_this()); } else { } } */ private: std::string name; int port; ByteArray txtRecord; AvahiEntryGroup* group; }; } swift-im-2.0+dev6/Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveHostnameQuery.h0000644000175000017500000000143412227051774026345 0ustar kismithkismith/* * 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 #include #include #include #include #include namespace Swift { class AvahiQuerier; class AvahiResolveHostnameQuery : public DNSSDResolveHostnameQuery, public AvahiQuery { public: AvahiResolveHostnameQuery(const std::string& hostname, int, boost::shared_ptr querier, EventLoop* eventLoop); void run(); void finish() { } private: HostAddress hostAddress; std::string hostname; }; } swift-im-2.0+dev6/Swiften/LinkLocal/DNSSD/DNSSDResolveHostnameQuery.cpp0000644000175000017500000000046212227051774025473 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { DNSSDResolveHostnameQuery::~DNSSDResolveHostnameQuery() { } } swift-im-2.0+dev6/Swiften/LinkLocal/DNSSD/Bonjour/0000755000175000017500000000000012227051774021443 5ustar kismithkismithswift-im-2.0+dev6/Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuery.h0000644000175000017500000000156012227051774024262 0ustar kismithkismith/* * 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 #include #include #include #include namespace Swift { class BonjourQuerier; class EventLoop; class BonjourQuery : public EventOwner, public boost::enable_shared_from_this { public: BonjourQuery(boost::shared_ptr, EventLoop* eventLoop); virtual ~BonjourQuery(); void processResult(); int getSocketID() const; protected: void run(); void finish(); protected: EventLoop* eventLoop; boost::shared_ptr querier; mutable boost::mutex sdRefMutex; DNSServiceRef sdRef; }; } swift-im-2.0+dev6/Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuerier.h0000644000175000017500000000317612227051774024576 0ustar kismithkismith/* * 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 #include #include #include #include #include #include #include namespace Swift { class BonjourQuerier : public DNSSDQuerier, public boost::enable_shared_from_this { public: BonjourQuerier(EventLoop* eventLoop); ~BonjourQuerier(); boost::shared_ptr createBrowseQuery(); boost::shared_ptr createRegisterQuery( const std::string& name, int port, const ByteArray& info); boost::shared_ptr createResolveServiceQuery( const DNSSDServiceID&); boost::shared_ptr createResolveHostnameQuery( const std::string& hostname, int interfaceIndex); void start(); void stop(); private: friend class BonjourQuery; void addRunningQuery(boost::shared_ptr); void removeRunningQuery(boost::shared_ptr); void interruptSelect(); void run(); private: EventLoop* eventLoop; bool stopRequested; boost::thread* thread; boost::mutex runningQueriesMutex; std::list< boost::shared_ptr > runningQueries; int interruptSelectReadSocket; int interruptSelectWriteSocket; boost::condition_variable runningQueriesAvailableEvent; }; } swift-im-2.0+dev6/Swiften/LinkLocal/DNSSD/Bonjour/BonjourRegisterQuery.h0000644000175000017500000000433412227051774025771 0ustar kismithkismith/* * 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 #include #include #include namespace Swift { class BonjourQuerier; class BonjourRegisterQuery : public DNSSDRegisterQuery, public BonjourQuery { public: BonjourRegisterQuery(const std::string& name, int port, const ByteArray& txtRecord, boost::shared_ptr querier, EventLoop* eventLoop) : BonjourQuery(querier, eventLoop) { DNSServiceErrorType result = DNSServiceRegister( &sdRef, 0, 0, name.c_str(), "_presence._tcp", NULL, NULL, port, txtRecord.size(), vecptr(txtRecord), &BonjourRegisterQuery::handleServiceRegisteredStatic, this); if (result != kDNSServiceErr_NoError) { sdRef = NULL; } } void registerService() { if (sdRef) { run(); } else { eventLoop->postEvent(boost::bind(boost::ref(onRegisterFinished), boost::optional()), shared_from_this()); } } void unregisterService() { finish(); } void updateServiceInfo(const ByteArray& txtRecord) { boost::lock_guard lock(sdRefMutex); DNSServiceUpdateRecord(sdRef, NULL, 0, txtRecord.size(), vecptr(txtRecord), 0); } private: static void handleServiceRegisteredStatic(DNSServiceRef, DNSServiceFlags, DNSServiceErrorType errorCode, const char *name, const char *regtype, const char *domain, void *context) { static_cast(context)->handleServiceRegistered(errorCode, name, regtype, domain); } void handleServiceRegistered(DNSServiceErrorType errorCode, const char *name, const char *regtype, const char *domain) { if (errorCode != kDNSServiceErr_NoError) { eventLoop->postEvent(boost::bind(boost::ref(onRegisterFinished), boost::optional()), shared_from_this()); } else { eventLoop->postEvent(boost::bind(boost::ref(onRegisterFinished), boost::optional(DNSSDServiceID(name, domain, regtype, 0))), shared_from_this()); } } }; } swift-im-2.0+dev6/Swiften/LinkLocal/DNSSD/Bonjour/BonjourResolveServiceQuery.h0000644000175000017500000000461012227051774027142 0ustar kismithkismith/* * 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 #include #include #include #include namespace Swift { class BonjourQuerier; class BonjourResolveServiceQuery : public DNSSDResolveServiceQuery, public BonjourQuery { public: BonjourResolveServiceQuery(const DNSSDServiceID& service, boost::shared_ptr querier, EventLoop* eventLoop) : BonjourQuery(querier, eventLoop) { DNSServiceErrorType result = DNSServiceResolve( &sdRef, 0, service.getNetworkInterfaceID(), service.getName().c_str(), service.getType().c_str(), service.getDomain().c_str(), &BonjourResolveServiceQuery::handleServiceResolvedStatic, this); if (result != kDNSServiceErr_NoError) { sdRef = NULL; } } void start() { if (sdRef) { run(); } else { eventLoop->postEvent(boost::bind(boost::ref(onServiceResolved), boost::optional()), shared_from_this()); } } void stop() { finish(); } private: static void handleServiceResolvedStatic(DNSServiceRef, DNSServiceFlags, uint32_t, DNSServiceErrorType errorCode, const char *fullname, const char *hosttarget, uint16_t port, uint16_t txtLen, const unsigned char *txtRecord, void *context) { static_cast(context)->handleServiceResolved(errorCode, fullname, hosttarget, port, txtLen, txtRecord); } void handleServiceResolved(DNSServiceErrorType errorCode, const char* fullName, const char* host, uint16_t port, uint16_t txtLen, const unsigned char *txtRecord) { if (errorCode != kDNSServiceErr_NoError) { eventLoop->postEvent(boost::bind(boost::ref(onServiceResolved), boost::optional()), shared_from_this()); } else { //std::cout << "Service resolved: name:" << fullName << " host:" << host << " port:" << port << std::endl; eventLoop->postEvent( boost::bind( boost::ref(onServiceResolved), Result(std::string(fullName), std::string(host), port, createByteArray(reinterpret_cast(txtRecord), txtLen))), shared_from_this()); } } }; } swift-im-2.0+dev6/Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuery.cpp0000644000175000017500000000162612227051774024620 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include namespace Swift { BonjourQuery::BonjourQuery(boost::shared_ptr q, EventLoop* eventLoop) : eventLoop(eventLoop), querier(q), sdRef(0) { } BonjourQuery::~BonjourQuery() { DNSServiceRefDeallocate(sdRef); } void BonjourQuery::processResult() { boost::lock_guard lock(sdRefMutex); DNSServiceProcessResult(sdRef); } int BonjourQuery::getSocketID() const { boost::lock_guard lock(sdRefMutex); return DNSServiceRefSockFD(sdRef); } void BonjourQuery::run() { querier->addRunningQuery(shared_from_this()); } void BonjourQuery::finish() { querier->removeRunningQuery(shared_from_this()); } } swift-im-2.0+dev6/Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuerier.cpp0000644000175000017500000000770612227051774025134 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include namespace Swift { BonjourQuerier::BonjourQuerier(EventLoop* eventLoop) : eventLoop(eventLoop), stopRequested(false), thread(0) { int fds[2]; int result = pipe(fds); assert(result == 0); (void) result; interruptSelectReadSocket = fds[0]; fcntl(interruptSelectReadSocket, F_SETFL, fcntl(interruptSelectReadSocket, F_GETFL)|O_NONBLOCK); interruptSelectWriteSocket = fds[1]; } BonjourQuerier::~BonjourQuerier() { assert(!thread); } boost::shared_ptr BonjourQuerier::createBrowseQuery() { return boost::shared_ptr(new BonjourBrowseQuery(shared_from_this(), eventLoop)); } boost::shared_ptr BonjourQuerier::createRegisterQuery(const std::string& name, int port, const ByteArray& info) { return boost::shared_ptr(new BonjourRegisterQuery(name, port, info, shared_from_this(), eventLoop)); } boost::shared_ptr BonjourQuerier::createResolveServiceQuery(const DNSSDServiceID& service) { return boost::shared_ptr(new BonjourResolveServiceQuery(service, shared_from_this(), eventLoop)); } boost::shared_ptr BonjourQuerier::createResolveHostnameQuery(const std::string& hostname, int interfaceIndex) { return boost::shared_ptr(new BonjourResolveHostnameQuery(hostname, interfaceIndex, shared_from_this(), eventLoop)); } void BonjourQuerier::addRunningQuery(boost::shared_ptr query) { { boost::lock_guard lock(runningQueriesMutex); runningQueries.push_back(query); } runningQueriesAvailableEvent.notify_one(); interruptSelect(); } void BonjourQuerier::removeRunningQuery(boost::shared_ptr query) { { boost::lock_guard lock(runningQueriesMutex); erase(runningQueries, query); } } void BonjourQuerier::interruptSelect() { char c = 0; write(interruptSelectWriteSocket, &c, 1); } void BonjourQuerier::start() { assert(!thread); thread = new boost::thread(boost::bind(&BonjourQuerier::run, shared_from_this())); } void BonjourQuerier::stop() { if (thread) { stopRequested = true; assert(runningQueries.empty()); runningQueriesAvailableEvent.notify_one(); interruptSelect(); thread->join(); delete thread; thread = NULL; stopRequested = false; } } void BonjourQuerier::run() { while (!stopRequested) { fd_set fdSet; int maxSocket; { boost::unique_lock lock(runningQueriesMutex); if (runningQueries.empty()) { runningQueriesAvailableEvent.wait(lock); if (runningQueries.empty()) { continue; } } // Run all running queries FD_ZERO(&fdSet); maxSocket = interruptSelectReadSocket; FD_SET(interruptSelectReadSocket, &fdSet); foreach(const boost::shared_ptr& query, runningQueries) { int socketID = query->getSocketID(); maxSocket = std::max(maxSocket, socketID); FD_SET(socketID, &fdSet); } } if (select(maxSocket+1, &fdSet, NULL, NULL, 0) <= 0) { continue; } if (FD_ISSET(interruptSelectReadSocket, &fdSet)) { char dummy; while (read(interruptSelectReadSocket, &dummy, 1) > 0) {} } { boost::lock_guard lock(runningQueriesMutex); foreach(boost::shared_ptr query, runningQueries) { if (FD_ISSET(query->getSocketID(), &fdSet)) { query->processResult(); } } } } } } swift-im-2.0+dev6/Swiften/LinkLocal/DNSSD/Bonjour/BonjourResolveHostnameQuery.h0000644000175000017500000000447612227051774027332 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #pragma GCC diagnostic ignored "-Wold-style-cast" #include #include #include #include #include #include namespace Swift { class BonjourQuerier; class BonjourResolveHostnameQuery : public DNSSDResolveHostnameQuery, public BonjourQuery { public: BonjourResolveHostnameQuery(const std::string& hostname, int interfaceIndex, boost::shared_ptr querier, EventLoop* eventLoop) : BonjourQuery(querier, eventLoop) { DNSServiceErrorType result = DNSServiceGetAddrInfo( &sdRef, 0, interfaceIndex, kDNSServiceProtocol_IPv4, hostname.c_str(), &BonjourResolveHostnameQuery::handleHostnameResolvedStatic, this); if (result != kDNSServiceErr_NoError) { sdRef = NULL; } } //void DNSSDResolveHostnameQuery::run() { void run() { if (sdRef) { BonjourQuery::run(); } else { eventLoop->postEvent(boost::bind(boost::ref(onHostnameResolved), boost::optional()), shared_from_this()); } } void finish() { BonjourQuery::finish(); } private: static void handleHostnameResolvedStatic(DNSServiceRef, DNSServiceFlags, uint32_t, DNSServiceErrorType errorCode, const char*, const struct sockaddr *address, uint32_t, void *context) { static_cast(context)->handleHostnameResolved(errorCode, address); } void handleHostnameResolved(DNSServiceErrorType errorCode, const struct sockaddr *rawAddress) { if (errorCode) { eventLoop->postEvent( boost::bind(boost::ref(onHostnameResolved), boost::optional()), shared_from_this()); } else { assert(rawAddress->sa_family == AF_INET); const sockaddr_in* sa = reinterpret_cast(rawAddress); uint32_t address = ntohl(sa->sin_addr.s_addr); eventLoop->postEvent(boost::bind( boost::ref(onHostnameResolved), HostAddress(reinterpret_cast(&address), 4)), shared_from_this()); } } }; } swift-im-2.0+dev6/Swiften/LinkLocal/DNSSD/Bonjour/BonjourBrowseQuery.h0000644000175000017500000000413612227051774025446 0ustar kismithkismith/* * 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 #include #include namespace Swift { class BonjourQuerier; class BonjourBrowseQuery : public DNSSDBrowseQuery, public BonjourQuery { public: BonjourBrowseQuery(boost::shared_ptr q, EventLoop* eventLoop) : BonjourQuery(q, eventLoop) { DNSServiceErrorType result = DNSServiceBrowse( &sdRef, 0, 0, "_presence._tcp", 0, &BonjourBrowseQuery::handleServiceDiscoveredStatic, this); if (result != kDNSServiceErr_NoError) { sdRef = NULL; } } void startBrowsing() { if (!sdRef) { eventLoop->postEvent(boost::bind(boost::ref(onError)), shared_from_this()); } else { run(); } } void stopBrowsing() { finish(); } private: static void handleServiceDiscoveredStatic(DNSServiceRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *name, const char *type, const char *domain, void *context) { static_cast(context)->handleServiceDiscovered(flags, interfaceIndex, errorCode, name, type, domain); } void handleServiceDiscovered(DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *name, const char *type, const char *domain) { if (errorCode != kDNSServiceErr_NoError) { eventLoop->postEvent(boost::bind(boost::ref(onError)), shared_from_this()); } else { //std::cout << "Discovered service: name:" << name << " domain:" << domain << " type: " << type << std::endl; DNSSDServiceID service(name, domain, type, interfaceIndex); if (flags & kDNSServiceFlagsAdd) { eventLoop->postEvent(boost::bind(boost::ref(onServiceAdded), service), shared_from_this()); } else { eventLoop->postEvent(boost::bind(boost::ref(onServiceRemoved), service), shared_from_this()); } } } }; } swift-im-2.0+dev6/Swiften/LinkLocal/DNSSD/DNSSDRegisterQuery.h0000644000175000017500000000121412227051774023602 0ustar kismithkismith/* * 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 #include #include #include namespace Swift { class DNSSDRegisterQuery { public: virtual ~DNSSDRegisterQuery(); virtual void registerService() = 0; virtual void unregisterService() = 0; virtual void updateServiceInfo(const ByteArray& info) = 0; boost::signal)> onRegisterFinished; }; } swift-im-2.0+dev6/Swiften/LinkLocal/DNSSD/PlatformDNSSDQuerierFactory.h0000644000175000017500000000101712227051774025442 0ustar kismithkismith/* * 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 #include namespace Swift { class DNSSDQuerier; class EventLoop; class SWIFTEN_API PlatformDNSSDQuerierFactory { public: PlatformDNSSDQuerierFactory(EventLoop* eventLoop); boost::shared_ptr createQuerier(); bool canCreate(); private: EventLoop* eventLoop; }; } swift-im-2.0+dev6/Swiften/LinkLocal/DNSSD/DNSSDServiceID.cpp0000644000175000017500000000045712227051774023150 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { const char* DNSSDServiceID::PresenceServiceType = "_presence._tcp"; } swift-im-2.0+dev6/Swiften/LinkLocal/DNSSD/DNSSDRegisterQuery.cpp0000644000175000017500000000043512227051774024141 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { DNSSDRegisterQuery::~DNSSDRegisterQuery() { } } swift-im-2.0+dev6/Swiften/LinkLocal/DNSSD/DNSSDQuerier.h0000644000175000017500000000174612227051774022416 0ustar kismithkismith/* * 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 #include namespace Swift { class DNSSDServiceID; class DNSSDBrowseQuery; class DNSSDRegisterQuery; class DNSSDResolveServiceQuery; class DNSSDResolveHostnameQuery; class DNSSDQuerier { public: virtual ~DNSSDQuerier(); virtual void start() = 0; virtual void stop() = 0; virtual boost::shared_ptr createBrowseQuery() = 0; virtual boost::shared_ptr createRegisterQuery( const std::string& name, int port, const ByteArray& info) = 0; virtual boost::shared_ptr createResolveServiceQuery( const DNSSDServiceID&) = 0; virtual boost::shared_ptr createResolveHostnameQuery( const std::string& hostname, int interfaceIndex) = 0; }; } swift-im-2.0+dev6/Swiften/LinkLocal/DNSSD/PlatformDNSSDQuerierFactory.cpp0000644000175000017500000000175512227051774026006 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #if defined(HAVE_BONJOUR) #include #elif defined(HAVE_AVAHI) #include #endif namespace Swift { PlatformDNSSDQuerierFactory::PlatformDNSSDQuerierFactory(EventLoop* eventLoop) : eventLoop(eventLoop) { } boost::shared_ptr PlatformDNSSDQuerierFactory::createQuerier() { #if defined(HAVE_BONJOUR) return boost::shared_ptr(new BonjourQuerier(eventLoop)); #elif defined(HAVE_AVAHI) return boost::shared_ptr(new AvahiQuerier(eventLoop)); #else return boost::shared_ptr(); #endif } bool PlatformDNSSDQuerierFactory::canCreate() { #if defined(HAVE_BONJOUR) || defined(HAVE_AVAHI) return true; #else return false; #endif } } swift-im-2.0+dev6/Swiften/LinkLocal/DNSSD/DNSSDQuerier.cpp0000644000175000017500000000041312227051774022737 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { DNSSDQuerier::~DNSSDQuerier() { } } swift-im-2.0+dev6/Swiften/LinkLocal/DNSSD/DNSSDResolveServiceQuery.h0000644000175000017500000000152312227051774024761 0ustar kismithkismith/* * 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 #include #include #include namespace Swift { class DNSSDResolveServiceQuery { public: struct Result { Result(const std::string& fullName, const std::string& host, int port, const ByteArray& info) : fullName(fullName), host(host), port(port), info(info) {} std::string fullName; std::string host; int port; ByteArray info; }; virtual ~DNSSDResolveServiceQuery(); virtual void start() = 0; virtual void stop() = 0; boost::signal&)> onServiceResolved; }; } swift-im-2.0+dev6/Swiften/LinkLocal/DNSSD/DNSSDResolveServiceQuery.cpp0000644000175000017500000000045712227051774025321 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { DNSSDResolveServiceQuery::~DNSSDResolveServiceQuery() { } } swift-im-2.0+dev6/Swiften/LinkLocal/DNSSD/Fake/0000755000175000017500000000000012227051774020673 5ustar kismithkismithswift-im-2.0+dev6/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuery.cpp0000644000175000017500000000113112227051774024063 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include namespace Swift { FakeDNSSDQuery::FakeDNSSDQuery(boost::shared_ptr querier) : querier(querier) { } FakeDNSSDQuery::~FakeDNSSDQuery() { } void FakeDNSSDQuery::run() { querier->addRunningQuery(shared_from_this()); } void FakeDNSSDQuery::finish() { querier->removeRunningQuery(shared_from_this()); } } swift-im-2.0+dev6/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDResolveServiceQuery.h0000644000175000017500000000137112227051774026417 0ustar kismithkismith/* * 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 #include #include namespace Swift { class FakeDNSSDQuerier; class FakeDNSSDResolveServiceQuery : public DNSSDResolveServiceQuery, public FakeDNSSDQuery { public: FakeDNSSDResolveServiceQuery(const DNSSDServiceID& service, boost::shared_ptr querier) : FakeDNSSDQuery(querier), service(service) { } void start() { run(); } void stop() { finish(); } DNSSDServiceID service; }; } swift-im-2.0+dev6/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.cpp0000644000175000017500000001354612227051774024407 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include namespace Swift { FakeDNSSDQuerier::FakeDNSSDQuerier(const std::string& domain, EventLoop* eventLoop) : domain(domain), eventLoop(eventLoop) { } FakeDNSSDQuerier::~FakeDNSSDQuerier() { if (!runningQueries.empty()) { std::cerr << "FakeDNSSDQuerier: Running queries not empty at destruction time" << std::endl; } } boost::shared_ptr FakeDNSSDQuerier::createBrowseQuery() { return boost::shared_ptr(new FakeDNSSDBrowseQuery(shared_from_this())); } boost::shared_ptr FakeDNSSDQuerier::createRegisterQuery(const std::string& name, int port, const ByteArray& info) { return boost::shared_ptr(new FakeDNSSDRegisterQuery(name, port, info, shared_from_this())); } boost::shared_ptr FakeDNSSDQuerier::createResolveServiceQuery(const DNSSDServiceID& service) { return boost::shared_ptr(new FakeDNSSDResolveServiceQuery(service, shared_from_this())); } boost::shared_ptr FakeDNSSDQuerier::createResolveHostnameQuery(const std::string& hostname, int interfaceIndex) { return boost::shared_ptr(new FakeDNSSDResolveHostnameQuery(hostname, interfaceIndex, shared_from_this())); } void FakeDNSSDQuerier::addRunningQuery(boost::shared_ptr query) { runningQueries.push_back(query); allQueriesEverRun.push_back(query); if (boost::shared_ptr browseQuery = boost::dynamic_pointer_cast(query)) { foreach(const DNSSDServiceID& service, services) { eventLoop->postEvent(boost::bind(boost::ref(browseQuery->onServiceAdded), service), shared_from_this()); } } else if (boost::shared_ptr resolveQuery = boost::dynamic_pointer_cast(query)) { for(ServiceInfoMap::const_iterator i = serviceInfo.begin(); i != serviceInfo.end(); ++i) { if (i->first == resolveQuery->service) { eventLoop->postEvent(boost::bind(boost::ref(resolveQuery->onServiceResolved), i->second), shared_from_this()); } } } else if (boost::shared_ptr registerQuery = boost::dynamic_pointer_cast(query)) { DNSSDServiceID service(registerQuery->name, domain); eventLoop->postEvent(boost::bind(boost::ref(registerQuery->onRegisterFinished), service), shared_from_this()); } else if (boost::shared_ptr resolveHostnameQuery = boost::dynamic_pointer_cast(query)) { std::map >::const_iterator i = addresses.find(resolveHostnameQuery->hostname); if (i != addresses.end()) { eventLoop->postEvent( boost::bind( boost::ref(resolveHostnameQuery->onHostnameResolved), i->second), shared_from_this()); } } } void FakeDNSSDQuerier::removeRunningQuery(boost::shared_ptr query) { erase(runningQueries, query); } void FakeDNSSDQuerier::addService(const DNSSDServiceID& id) { services.insert(id); foreach(const boost::shared_ptr& query, getQueries()) { eventLoop->postEvent(boost::bind(boost::ref(query->onServiceAdded), id), shared_from_this()); } } void FakeDNSSDQuerier::removeService(const DNSSDServiceID& id) { services.erase(id); serviceInfo.erase(id); foreach(const boost::shared_ptr& query, getQueries()) { eventLoop->postEvent(boost::bind(boost::ref(query->onServiceRemoved), id), shared_from_this()); } } void FakeDNSSDQuerier::setServiceInfo(const DNSSDServiceID& id, const DNSSDResolveServiceQuery::Result& info) { std::pair r = serviceInfo.insert(std::make_pair(id, info)); if (!r.second) { r.first->second = info; } foreach(const boost::shared_ptr& query, getQueries()) { if (query->service == id) { eventLoop->postEvent(boost::bind(boost::ref(query->onServiceResolved), info), shared_from_this()); } } } bool FakeDNSSDQuerier::isServiceRegistered(const std::string& name, int port, const ByteArray& info) { foreach(const boost::shared_ptr& query, getQueries()) { if (query->name == name && query->port == port && query->info == info) { return true; } } return false; } void FakeDNSSDQuerier::setBrowseError() { foreach(const boost::shared_ptr& query, getQueries()) { eventLoop->postEvent(boost::ref(query->onError), shared_from_this()); } } void FakeDNSSDQuerier::setRegisterError() { foreach(const boost::shared_ptr& query, getQueries()) { eventLoop->postEvent(boost::bind(boost::ref(query->onRegisterFinished), boost::optional()), shared_from_this()); } } void FakeDNSSDQuerier::setAddress(const std::string& hostname, boost::optional address) { addresses[hostname] = address; foreach(const boost::shared_ptr& query, getQueries()) { if (query->hostname == hostname) { eventLoop->postEvent(boost::bind( boost::ref(query->onHostnameResolved), address), shared_from_this()); } } } } swift-im-2.0+dev6/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDRegisterQuery.h0000644000175000017500000000160512227051774025243 0ustar kismithkismith/* * 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 #include #include #include namespace Swift { class FakeDNSSDQuerier; class FakeDNSSDRegisterQuery : public DNSSDRegisterQuery, public FakeDNSSDQuery { public: FakeDNSSDRegisterQuery(const std::string& name, int port, const ByteArray& info, boost::shared_ptr querier) : FakeDNSSDQuery(querier), name(name), port(port), info(info) { } void registerService() { run(); } void updateServiceInfo(const ByteArray& i) { info = i; } void unregisterService() { finish(); } std::string name; int port; ByteArray info; }; } swift-im-2.0+dev6/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuery.h0000644000175000017500000000122512227051774023534 0ustar kismithkismith/* * 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 #include #include namespace Swift { class FakeDNSSDQuerier; class FakeDNSSDQuery : public EventOwner, public boost::enable_shared_from_this { public: FakeDNSSDQuery(boost::shared_ptr); virtual ~FakeDNSSDQuery(); protected: void run(); void finish(); protected: boost::shared_ptr querier; }; } swift-im-2.0+dev6/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.h0000644000175000017500000000620512227051774024046 0ustar kismithkismith/* * 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 #include #include #include #include #include #include #include #include #include #include namespace Swift { class FakeDNSSDQuery; class FakeDNSSDBrowseQuery; class EventLoop; class SWIFTEN_API FakeDNSSDQuerier : public DNSSDQuerier, public EventOwner, public boost::enable_shared_from_this { public: FakeDNSSDQuerier(const std::string& domain, EventLoop* eventLoop); ~FakeDNSSDQuerier(); void start() {} void stop() {} void clearAllQueriesEverRun() { allQueriesEverRun.clear(); } boost::shared_ptr createBrowseQuery(); boost::shared_ptr createRegisterQuery( const std::string& name, int port, const ByteArray& info); boost::shared_ptr createResolveServiceQuery( const DNSSDServiceID&); boost::shared_ptr createResolveHostnameQuery( const std::string& hostname, int interfaceIndex); void addRunningQuery(boost::shared_ptr); void removeRunningQuery(boost::shared_ptr); void addService(const DNSSDServiceID& id); void removeService(const DNSSDServiceID& id); void setServiceInfo(const DNSSDServiceID& id, const DNSSDResolveServiceQuery::Result& info); bool isServiceRegistered(const std::string& name, int port, const ByteArray& info); void setAddress(const std::string& hostname, boost::optional address); void setBrowseError(); void setRegisterError(); public: template std::vector< boost::shared_ptr > getAllQueriesEverRun() const { std::vector< boost::shared_ptr > result; for (QueryList::const_iterator i = allQueriesEverRun.begin(); i != allQueriesEverRun.end(); ++i) { if (boost::shared_ptr resultQuery = boost::dynamic_pointer_cast(*i)) { result.push_back(resultQuery); } } return result; } private: template std::vector< boost::shared_ptr > getQueries() const { std::vector< boost::shared_ptr > result; for (QueryList::const_iterator i = runningQueries.begin(); i != runningQueries.end(); ++i) { if (boost::shared_ptr resultQuery = boost::dynamic_pointer_cast(*i)) { result.push_back(resultQuery); } } return result; } private: std::string domain; EventLoop* eventLoop; typedef std::list< boost::shared_ptr > QueryList; QueryList runningQueries; QueryList allQueriesEverRun; std::set services; typedef std::map ServiceInfoMap; ServiceInfoMap serviceInfo; std::map > addresses; }; } swift-im-2.0+dev6/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDResolveHostnameQuery.h0000644000175000017500000000155512227051774026601 0ustar kismithkismith/* * 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 #include #include #include namespace Swift { class FakeDNSSDQuerier; class FakeDNSSDResolveHostnameQuery : public DNSSDResolveHostnameQuery, public FakeDNSSDQuery { public: FakeDNSSDResolveHostnameQuery(const std::string& hostname, int interfaceIndex, boost::shared_ptr querier) : FakeDNSSDQuery(querier), hostname(hostname), interfaceIndex(interfaceIndex) { } void run() { FakeDNSSDQuery::run(); } void finish() { FakeDNSSDQuery::finish(); } std::string hostname; int interfaceIndex; }; } swift-im-2.0+dev6/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDBrowseQuery.h0000644000175000017500000000115012227051774024713 0ustar kismithkismith/* * 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 #include namespace Swift { class FakeDNSSDQuerier; class FakeDNSSDBrowseQuery : public DNSSDBrowseQuery, public FakeDNSSDQuery { public: FakeDNSSDBrowseQuery(boost::shared_ptr querier) : FakeDNSSDQuery(querier) { } void startBrowsing() { run(); } void stopBrowsing() { finish(); } }; } swift-im-2.0+dev6/Swiften/LinkLocal/DNSSD/DNSSDBrowseQuery.h0000644000175000017500000000112712227051774023262 0ustar kismithkismith/* * 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 #include namespace Swift { class DNSSDBrowseQuery { public: virtual ~DNSSDBrowseQuery(); virtual void startBrowsing() = 0; virtual void stopBrowsing() = 0; boost::signal onServiceAdded; boost::signal onServiceRemoved; boost::signal onError; }; } swift-im-2.0+dev6/Swiften/LinkLocal/LinkLocalServiceInfo.cpp0000644000175000017500000000560112227051774023665 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include namespace Swift { ByteArray LinkLocalServiceInfo::toTXTRecord() const { ByteArray result(getEncoded("txtvers=1")); if (!firstName.empty()) { append(result, getEncoded("1st=" + firstName)); } if (!lastName.empty()) { append(result, getEncoded("last=" + lastName)); } if (!email.empty()) { append(result, getEncoded("email=" + email)); } if (jid.isValid()) { append(result, getEncoded("jid=" + jid.toString())); } if (!message.empty()) { append(result, getEncoded("msg=" + message)); } if (!nick.empty()) { append(result, getEncoded("nick=" + nick)); } if (port) { append(result, getEncoded("port.p2pj=" + std::string(boost::lexical_cast(*port)))); } switch (status) { case Available: append(result, getEncoded("status=avail")); break; case Away: append(result, getEncoded("status=away")); break; case DND: append(result, getEncoded("status=dnd")); break; } return result; } ByteArray LinkLocalServiceInfo::getEncoded(const std::string& s) { ByteArray sizeByte; sizeByte.resize(1); sizeByte[0] = s.size(); return concat(sizeByte, createByteArray(s)); } LinkLocalServiceInfo LinkLocalServiceInfo::createFromTXTRecord(const ByteArray& record) { LinkLocalServiceInfo info; size_t i = 0; size_t recordCount = record.size(); while (i < recordCount) { std::pair entry = readEntry(record, &i); if (entry.first.empty()) { break; } else if (entry.first == "1st") { info.setFirstName(entry.second); } else if (entry.first == "last") { info.setLastName(entry.second); } else if (entry.first == "email") { info.setEMail(entry.second); } else if (entry.first == "jid") { info.setJID(JID(entry.second)); } else if (entry.first == "msg") { info.setMessage(entry.second); } else if (entry.first == "nick") { info.setNick(entry.second); } else if (entry.first == "port.p2pj") { info.setPort(boost::lexical_cast(entry.second)); } else if (entry.first == "status") { if (entry.second == "away") { info.setStatus(Away); } else if (entry.second == "dnd") { info.setStatus(DND); } } } return info; } std::pair LinkLocalServiceInfo::readEntry(const ByteArray& record, size_t* index) { size_t& i = *index; std::string key; std::string value; size_t entryEnd = i + 1 + record[i]; ++i; bool inKey = true; while (i < entryEnd && i < record.size()) { if (inKey) { if (record[i] == '=') { inKey = false; } else { key += record[i]; } } else { value += record[i]; } ++i; } return std::make_pair(key, value); } } swift-im-2.0+dev6/Swiften/LinkLocal/IncomingLinkLocalSession.cpp0000644000175000017500000000357312227051774024566 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include namespace Swift { IncomingLinkLocalSession::IncomingLinkLocalSession( const JID& localJID, boost::shared_ptr connection, PayloadParserFactoryCollection* payloadParserFactories, PayloadSerializerCollection* payloadSerializers, XMLParserFactory* xmlParserFactory) : Session(connection, payloadParserFactories, payloadSerializers, xmlParserFactory), initialized(false) { setLocalJID(localJID); } void IncomingLinkLocalSession::handleStreamStart(const ProtocolHeader& incomingHeader) { setRemoteJID(JID(incomingHeader.getFrom())); if (!getRemoteJID().isValid()) { finishSession(); return; } ProtocolHeader header; header.setFrom(getLocalJID()); getXMPPLayer()->writeHeader(header); if (incomingHeader.getVersion() == "1.0") { getXMPPLayer()->writeElement(boost::make_shared()); } else { setInitialized(); } } void IncomingLinkLocalSession::handleElement(boost::shared_ptr element) { boost::shared_ptr stanza = boost::dynamic_pointer_cast(element); // If we get our first stanza before streamfeatures, our session is implicitly // initialized if (stanza && !isInitialized()) { setInitialized(); } onElementReceived(element); } void IncomingLinkLocalSession::setInitialized() { initialized = true; onSessionStarted(); } } swift-im-2.0+dev6/Swiften/LinkLocal/LinkLocalConnector.cpp0000644000175000017500000000440612227051774023405 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include namespace Swift { LinkLocalConnector::LinkLocalConnector( const LinkLocalService& service, boost::shared_ptr querier, boost::shared_ptr connection) : service(service), querier(querier), connection(connection) { } LinkLocalConnector::~LinkLocalConnector() { assert(!resolveQuery); } void LinkLocalConnector::connect() { resolveQuery = querier->createResolveHostnameQuery( service.getHostname(), service.getID().getNetworkInterfaceID()); resolveQueryHostNameResolvedConnection = resolveQuery->onHostnameResolved.connect(boost::bind( &LinkLocalConnector::handleHostnameResolved, boost::dynamic_pointer_cast(shared_from_this()), _1)); resolveQuery->run(); } void LinkLocalConnector::cancel() { if (resolveQuery) { resolveQuery->finish(); resolveQueryHostNameResolvedConnection.disconnect(); resolveQuery.reset(); } connectionConnectFinishedConnection.disconnect(); connection->disconnect(); } void LinkLocalConnector::handleHostnameResolved(const boost::optional& address) { resolveQuery->finish(); resolveQueryHostNameResolvedConnection.disconnect(); resolveQuery.reset(); if (address) { connectionConnectFinishedConnection = connection->onConnectFinished.connect( boost::bind(&LinkLocalConnector::handleConnected, shared_from_this(), _1)); connection->connect(HostAddressPort(*address, service.getPort())); } else { onConnectFinished(true); } } void LinkLocalConnector::handleConnected(bool error) { onConnectFinished(error); assert(connectionConnectFinishedConnection.connected()); connectionConnectFinishedConnection.disconnect(); } void LinkLocalConnector::queueElement(boost::shared_ptr element) { queuedElements.push_back(element); } } swift-im-2.0+dev6/Swiften/LinkLocal/OutgoingLinkLocalSession.cpp0000644000175000017500000000272112227051774024610 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include namespace Swift { OutgoingLinkLocalSession::OutgoingLinkLocalSession( const JID& localJID, const JID& remoteJID, boost::shared_ptr connection, PayloadParserFactoryCollection* payloadParserFactories, PayloadSerializerCollection* payloadSerializers, XMLParserFactory* xmlParserFactory) : Session(connection, payloadParserFactories, payloadSerializers, xmlParserFactory) { setLocalJID(localJID); setRemoteJID(remoteJID); } void OutgoingLinkLocalSession::handleSessionStarted() { ProtocolHeader header; header.setFrom(getLocalJID()); getXMPPLayer()->writeHeader(header); } void OutgoingLinkLocalSession::handleStreamStart(const ProtocolHeader&) { foreach(const boost::shared_ptr& stanza, queuedElements_) { sendElement(stanza); } queuedElements_.clear(); } void OutgoingLinkLocalSession::handleElement(boost::shared_ptr element) { onElementReceived(element); } void OutgoingLinkLocalSession::queueElement(boost::shared_ptr element) { queuedElements_.push_back(element); } } swift-im-2.0+dev6/BuildTools/0000755000175000017500000000000012227051774015743 5ustar kismithkismithswift-im-2.0+dev6/BuildTools/Coverage/0000755000175000017500000000000012227051774017476 5ustar kismithkismithswift-im-2.0+dev6/BuildTools/Coverage/GenerateSummary.py0000755000175000017500000000151312227051773023162 0ustar kismithkismith#!/usr/bin/env python import sys, re assert(len(sys.argv) == 3) inputFile = open(sys.argv[1]) currentFile = "" coverage = {} for line in inputFile.readlines() : line = line.strip() m = re.match("^SF:(.*)", line) if m : currentFile = m.group(1) else : m = re.match("^DA:(\d+),(\d+)", line) if m : currentFileCoverage = coverage.get(currentFile, {}) line = int(m.group(1)) count = int(m.group(2)) currentFileCoverage[line] = currentFileCoverage.get(line, 0) + count coverage[currentFile] = currentFileCoverage inputFile.close() totalLines = 0 coveredLines = 0 for c in coverage.values() : totalLines += len(c) for l in c.values() : if l > 0 : coveredLines += 1 outputFile = open(sys.argv[2], 'w') outputFile.write(str(coveredLines) + "/" + str(totalLines)) outputFile.close() swift-im-2.0+dev6/BuildTools/Coverage/FilterLCovData.py0000755000175000017500000000150212227051773022653 0ustar kismithkismith#!/usr/bin/env python # TODO: Add uncovered non-ignored files import sys, re, os.path assert(len(sys.argv) == 2) def isIgnored(file) : return (file.find("/Swiften/") == -1 and file.find("/Slimber/") == -1 and file.find("/Swift/") == -1) or (file.find("/UnitTest/") != -1 or file.find("/QA/") != -1) output = [] inputFile = open(sys.argv[1]) inIgnoredFile = False for line in inputFile.readlines() : if inIgnoredFile : if line == "end_of_record\n" : inIgnoredFile = False else : if line.startswith("SF:") and isIgnored(line) : inIgnoredFile = True else : m = re.match("SF:(.*)", line) if m : line = "SF:" + os.path.realpath(m.group(1)) + "\n" output.append(line) inputFile.close() outputFile = open(sys.argv[1], 'w') outputFile.write(''.join(output)) outputFile.close() swift-im-2.0+dev6/BuildTools/Coverage/GenerateCoverageResults.sh0000755000175000017500000000203212227051773024621 0ustar kismithkismith#!/bin/sh # This script assumes that it is run from the toplevel directory SOURCE_DIR=. SCRIPT_DIR=BuildTools/Coverage LCOVDIR=3rdParty/LCov #RESULTS_DIR=BuildTools/Coverage/results #OUTPUT_DIR=$RESULTS_DIR/coverage-`git log --pretty=format:%ct-%h | head -n 1` OUTPUT_DIR=BuildTools/Coverage/coverage rm -rf $OUTPUT_DIR if [ ! -d $OUTPUT_DIR ]; then mkdir -p $OUTPUT_DIR fi # Reset counters $LCOVDIR/lcov --zerocounters --directory $SOURCE_DIR # Build & run all tests ./scons coverage=1 test=all $@ # Run SCons $LCOVDIR/lcov --capture --directory $SOURCE_DIR -b $SOURCE_DIR --output-file $OUTPUT_DIR/all.info --test-name all #cp $OUTPUT_DIR/all.info $OUTPUT_DIR/all.info.orig $SCRIPT_DIR/FilterLCovData.py $OUTPUT_DIR/all.info # Generate HTML $LCOVDIR/gendesc -o $OUTPUT_DIR/descriptions $SCRIPT_DIR/descriptions.txt $LCOVDIR/genhtml --prefix $PWD --no-function-coverage --title "Swift Coverage" --output-directory $OUTPUT_DIR $OUTPUT_DIR/all.info # Generate summary $SCRIPT_DIR/GenerateSummary.py $OUTPUT_DIR/all.info $OUTPUT_DIR/summary swift-im-2.0+dev6/BuildTools/Coverage/descriptions.txt0000644000175000017500000000002112227051773022735 0ustar kismithkismithall All tests swift-im-2.0+dev6/BuildTools/TestVersioningScheme.sh0000755000175000017500000000163312227051773022414 0ustar kismithkismith#!/bin/bash function test_versions() { versions=$@ previous_version= for version in $versions; do if [ "$previous_version" ]; then debian_version=`./DebianizeVersion.py $version` debian_previous_version=`./DebianizeVersion.py $previous_version` dpkg --compare-versions $debian_version gt $debian_previous_version result=$? if [ "$result" != "0" ]; then echo "TEST FAILED: $debian_version > $debian_previous_version" fi fi previous_version=$version done } DEVELOPMENT_VERSIONS=" swift-1.0beta1 swift-1.0beta1-dev3 swift-1.0beta2 swift-1.0rc1 swift-1.0rc1-dev2 swift-1.0 swift-1.0-dev2 swift-1.0-dev4 swift-2.0" test_versions $DEVELOPMENT_VERSIONS BETA_VERSIONS=" swift-1.0beta1 swift-1.0beta2 swift-1.0rc1 swift-1.0 swift-1.1rc1 swift-1.1 swift-2.0" test_versions $BETA_VERSIONS RELEASE_VERSIONS=" swift-1.0 swift-1.1 swift-2.0" test_versions $RELEASE_VERSIONS swift-im-2.0+dev6/BuildTools/CheckHeaders.py0000755000175000017500000000143212227051773020630 0ustar kismithkismith#!/usr/bin/env python import os, sys foundBadHeaders = False for (path, dirs, files) in os.walk(".") : if "3rdParty" in path or ".sconf" in path or ".framework" in path : continue if not "Swiften" in path : continue for filename in [os.path.join(path, file) for file in files if file.endswith(".h")] : file = open(filename, "r") for line in file.readlines() : for include in ["iostream", "algorithm", "cassert", "boost/bind.hpp", "boost/filesystem.hpp", "Base/foreach.h", "Base/Log.h", "boost/date_time/date_time.hpp", "boost/filesystem/filesystem.hpp"] : if "#include" in line and include in line and not "Base/Log" in filename : print "Found " + include + " include in " + filename foundBadHeaders = True sys.exit(foundBadHeaders) swift-im-2.0+dev6/BuildTools/DocBook/0000755000175000017500000000000012227051773017262 5ustar kismithkismithswift-im-2.0+dev6/BuildTools/DocBook/Stylesheets/0000755000175000017500000000000012227051773021576 5ustar kismithkismithswift-im-2.0+dev6/BuildTools/DocBook/Stylesheets/html/0000755000175000017500000000000012227051773022542 5ustar kismithkismithswift-im-2.0+dev6/BuildTools/DocBook/Stylesheets/html/docbook.xsl0000644000175000017500000000161612227051773024716 0ustar kismithkismith appendix nop article/appendix nop article toc,title book toc,title chapter nop part nop preface nop qandadiv nop qandaset nop reference nop sect1 nop sect2 nop sect3 nop sect4 nop sect5 nop section nop set nop swift-im-2.0+dev6/BuildTools/DocBook/Stylesheets/fo/0000755000175000017500000000000012227051773022202 5ustar kismithkismithswift-im-2.0+dev6/BuildTools/DocBook/Stylesheets/fo/docbook.test.xsl0000644000175000017500000001407412227051773025336 0ustar kismithkismith 0.4em 0.3em 0.5em 0.4em 0.3em 0.5em 0.4em 0.3em 0.5em 0.4em 0.3em 0.5em 0.3em 0.4em 0.5em 0.3em 0.4em 0.5em false no-wrap false preserve preserve start 10pt .8em 5pt #F0F0F0 5pt pt pt pt 10pt bold normal book toc,title swift-im-2.0+dev6/BuildTools/DocBook/Stylesheets/fo/inline.xsl0000644000175000017500000000072612227051773024215 0ustar kismithkismith swift-im-2.0+dev6/BuildTools/DocBook/Stylesheets/fo/titlepage.xsl0000644000175000017500000000056512227051773024716 0ustar kismithkismith swift-im-2.0+dev6/BuildTools/DocBook/Stylesheets/fo/component.xsl0000644000175000017500000000240712227051773024737 0ustar kismithkismith swift-im-2.0+dev6/BuildTools/DocBook/Stylesheets/fo/docbook.xsl0000644000175000017500000001412512227051773024355 0ustar kismithkismith 0.4em 0.3em 0.5em 0.4em 0.3em 0.5em 0.4em 0.3em 0.5em 0.4em 0.3em 0.5em 0.3em 0.4em 0.5em 0.3em 0.4em 0.5em false no-wrap false preserve preserve start 10pt .8em 5pt #F0F0F0 5pt pt pt pt 10pt bold normal book toc,title swift-im-2.0+dev6/BuildTools/DocBook/Stylesheets/common/0000755000175000017500000000000012227051773023066 5ustar kismithkismithswift-im-2.0+dev6/BuildTools/DocBook/Stylesheets/common/inline.xsl0000644000175000017500000000160712227051773025100 0ustar kismithkismith swift-im-2.0+dev6/BuildTools/DocBook/Stylesheets/common/params.xsl0000644000175000017500000000044012227051773025077 0ustar kismithkismith swift-im-2.0+dev6/BuildTools/DocBook/SCons/0000755000175000017500000000000012227051773020307 5ustar kismithkismithswift-im-2.0+dev6/BuildTools/DocBook/SCons/DocBook.py0000644000175000017500000000714212227051773022205 0ustar kismithkismith################################################################################ # DocBook pseudobuilder ################################################################################ import SCons.Util, SCons.Action import xml.dom.minidom, re, os.path, sys def generate(env) : # Location of stylesheets and catalogs docbook_dir = "#/BuildTools/DocBook" docbook_xsl_style_dir = env.Dir(docbook_dir + "/Stylesheets").abspath docbook_xml_catalog = env.File("catalog.xml").abspath if "DOCBOOK_XML_DIR" in env : docbook_xml_dir = env.Dir("$DOCBOOK_XML_DIR").abspath else : docbook_xml_dir = env.Dir("#/3rdParty/DocBook/XML").abspath if "DOCBOOK_XSL_DIR" in env : docbook_xsl_dir = env.Dir("$DOCBOOK_XSL_DIR").abspath else : docbook_xsl_dir = env.Dir("#/3rdParty/DocBook/XSL").abspath fop_fonts_dir = env.Dir(docbook_dir + "/Fonts").abspath # Generates a catalog from paths to external tools def buildCatalog(target, source, env) : catalog = """ """ docbook_xml_dir = source[0].get_contents() docbook_xsl_dir = source[1].get_contents() if env["PLATFORM"] == "win32" : docbook_xml_dir = docbook_xml_dir.replace("\\","/") docbook_xsl_dir = docbook_xsl_dir.replace("\\","/") file = open(target[0].abspath, "w") file.write(catalog % { "docbook_xml_dir" : docbook_xml_dir, "docbook_xsl_dir" : docbook_xsl_dir, }) file.close() # Generates a FOP config file def buildFopConfig(target, source, env) : fopcfg = """ %(fonts_dir)s """ file = open(target[0].abspath, "w") file.write(fopcfg % { "fonts_dir" : source[0].get_contents() }) file.close() # Builds a DocBook file def buildDocBook(env, source) : db_env = env.Clone() db_env["XMLCATALOGS"] = [docbook_xml_catalog] db_env["ENV"].update({"OS" : os.environ.get("OS", "")}) db_env["XMLLINT"] = env.WhereIs("xmllint") db_env["XSLT"] = env.WhereIs("xsltproc") db_env["FO"] = env.WhereIs("fop") if not db_env["XMLLINT"] or not db_env["XSLT"] : return # PDF generation if db_env["FO"] : fo = db_env.XSLT(os.path.splitext(source)[0] + ".fo", source, XSLTSTYLESHEET = db_env["DOCBOOK_XSL_FO"]) pdf = db_env.FO(fo) # HTML generation db_env.XSLT(os.path.splitext(source)[0] + ".html", source, XSLTSTYLESHEET = db_env["DOCBOOK_XSL_HTML"]) # Import tools env.Tool("FO", toolpath = [docbook_dir + "/SCons"]) env.Tool("XSLT", toolpath = [docbook_dir + "/SCons"]) # Catalog file generation env.Command("catalog.xml", [env.Value(docbook_xml_dir), env.Value(docbook_xsl_dir)], SCons.Action.Action(buildCatalog, cmdstr = "$GENCOMSTR")) # FO config file generation env["FOCFG"] = env.File("fop.cfg").abspath env.Command("fop.cfg", [env.Value(fop_fonts_dir)], SCons.Action.Action(buildFopConfig, cmdstr = "$GENCOMSTR")) # DocBook stylesheets env["DOCBOOK_XSL_FO"] = docbook_xsl_style_dir + "/fo/docbook.xsl" env["DOCBOOK_XSL_HTML"] = docbook_xsl_style_dir + "/html/docbook.xsl" env.AddMethod(buildDocBook, "DocBook") def exists(env) : return True swift-im-2.0+dev6/BuildTools/DocBook/SCons/FO.py0000644000175000017500000000264612227051773021175 0ustar kismithkismithimport SCons.Util import xml.dom.minidom, re ################################################################################ # XSL-FO builder ################################################################################ def generate(env) : def generate_actions(source, target, env, for_signature) : if len(env["FOCFG"]) > 0 : cmd = "$FO -c $FOCFG $FOFLAGS $SOURCE $TARGET" else : cmd = "$FO $FOFLAGS $SOURCE $TARGET" return SCons.Action.Action(cmd, cmdstr = "$FOCOMSTR") def modify_sources(target, source, env) : if len(env["FOCFG"]) > 0 : source.append(env["FOCFG"]) return target, source def scan_fo(node, env, path) : dependencies = set() try : document = xml.dom.minidom.parseString(node.get_contents()) except xml.parsers.expat.ExpatError: return [] for include in document.getElementsByTagNameNS("http://www.w3.org/1999/XSL/Format", "external-graphic") : m = re.match("url\((.*)\)", include.getAttribute("src")) if m : dependencies.add(m.group(1)) return list(dependencies) env["FO"] = "fop" env["FOFLAGS"] = "" env["FOCFG"] = "" env["BUILDERS"]["FO"] = SCons.Builder.Builder( generator = generate_actions, emitter = modify_sources, source_scanner = SCons.Scanner.Scanner(function = scan_fo, skeys = [".fo"]), suffix = ".pdf", src_suffix = ".fo" ) def exists(env) : return True swift-im-2.0+dev6/BuildTools/DocBook/SCons/XSLT.py0000644000175000017500000000432412227051773021456 0ustar kismithkismithimport SCons.Util import xml.dom.minidom, os, os.path ################################################################################ # XSLT processor ################################################################################ def generate(env) : def generate_actions(source, target, env, for_signature) : if not env.has_key("XSLTSTYLESHEET") : raise SCons.Errors.UserError, "The XSLTSTYLESHEET construction variable must be defined" # Process the XML catalog files # FIXME: It's probably not clean to do an ENV assignment globally env["ENV"]["XML_CATALOG_FILES"] = " ".join(env.get("XMLCATALOGS", "")) # Build the XMLLint command xmllintcmd = ["$XMLLINT", "--nonet", "--xinclude", "--postvalid", "--noout", "$SOURCE"] # Build the XSLT command xsltcmd = ["$XSLT", "--nonet", "--xinclude"] for (param, value) in env["XSLTPARAMS"] : xsltcmd += ["--stringparam", param, value] xsltcmd += ["-o", "$TARGET", "$XSLTSTYLESHEET", "$SOURCE"] return [ SCons.Action.Action([xmllintcmd], cmdstr = "$XMLLINTCOMSTR"), SCons.Action.Action([xsltcmd], cmdstr = "$XSLTCOMSTR")] def modify_sources(target, source, env) : if len(env["FOCFG"]) > 0 : source.append(env["FOCFG"]) source.append(env.get("XMLCATALOGS", [])) return target, source def scan_xml(node, env, path) : dependencies = set() nodes = [node] while len(nodes) > 0 : node = nodes.pop() try : document = xml.dom.minidom.parseString(node.get_contents()) except xml.parsers.expat.ExpatError: continue for include in document.getElementsByTagNameNS("http://www.w3.org/2001/XInclude", "include") : include_file = include.getAttribute("href") dependencies.add(include_file) if include.getAttribute("parse") != "text" : nodes.append(env.File(include_file)) return list(dependencies) env["XMLLINT"] = "xmllint" env["XSLT"] = "xsltproc" env["XSLTPARAMS"] = [] env["BUILDERS"]["XSLT"] = SCons.Builder.Builder( generator = generate_actions, emitter = modify_sources, source_scanner = SCons.Scanner.Scanner(function = scan_xml), src_suffix = ".xml" ) def exists(env) : return True swift-im-2.0+dev6/BuildTools/DocBook/Fonts/0000755000175000017500000000000012227051773020353 5ustar kismithkismithswift-im-2.0+dev6/BuildTools/DocBook/Fonts/Gentium Basic/0000755000175000017500000000000012227051773022765 5ustar kismithkismithswift-im-2.0+dev6/BuildTools/DocBook/Fonts/Gentium Basic/GenBasI.ttf0000644000175000017500000100252012227051773024754 0ustar kismithkismith`Feat$xTGDEFٷl8GPOS-M@HGSUBCGlat:LPGlocӔWFOS/2gpJ`Silfas$..Sill,$cmap>tJ`Tcvt Z$fpgmY7Zsgasp\Lglyf\\head4/l6hhea $hmtx) A@ plocaY :maxp?& namekV' AKpost}S>hXgprep #$=D]bJL]__ace57 **"(%$&' #       !  )    &Dlatn IPA markmkmk "<8? X ,- +      2 P V \ b h n t z t V V V      " ( . . . 4 : V \ b h P V \ b h @ F L R X ^  d j p j v |     " F L R X  $*06  " F L R X<BHNTZ  P V \ b h`flfrx~~ | F L R X       P V \ b h     BHNT&,,,28 F L R X  Z  >BHNTD06  "JPV\bh  n  tzz P b h \ b h   L R X ^  n   j p j v    v$ V \ b hn  " j p j v(.H.4 d j: j@F F L R X @ F L R XL  ^  R X ^ ^ d F L R Xj   pvvv| @ F L R X06  " j p j v 06  "$  *06 <BHBNT Z  `fflrxxx~ V \ b h  0 n z  P V \ b h F L R Xr V \ b hh  06  " 06  "&,,,2 F L R X V \ b h06  " &,\2 F L R X   \ b h ^ 8 >d F L R XD   JPPV\6  06  "b V \ b hh F L R Xn 06  "  ttt F L R Xz$  ,,,2 F L R Xn    b h ^     F L R X | 06  "    P V \ b h vD    06  " "(  .06  " P V \ b h 4 4 ":@FLR ^    X F L R X^djpv4 4 F L R X(  V V V|*<BHNT    0  06  "    :@FLR F L R X F L R X0d F L R X z  F L R X0 V \ b h P V \ b h F L R X  F L R X  0\$06  "<BHNT*006  "6    <BHBN P V \ b h     nT zT  |ZZZ`fNlr   @ F L R X x6  "~L   V \ b h444 P V \ b h     X*x6  "44406  "  P V \ b h   L R X P V \ b h6  0 F L R X   F L R X   L R X V \ b hZ    F L R X j p j v  06  "   F L R X&,2,806  ">Dflfr F L R XJBHNTP8 f{hmf,iphfflfs(fP(f~h#fflh~fpN{f&>(|aff`(Yf(fi,!ZW>f*(b1fh(3Df7(u{fGXfh0f3U6>/fj(5fXyIfWI/((fR( hf[|fYZ(@7fhhf4*dT^fZZ(+fIGlfHhyZ(K( 1fhfEZchCI2Wff 5'f2"Z(Bf (Gf((^fZ f(f1Zi(f(lfzD\7Z6>!h-fxZXZ(Nfc(f(yfO(efBbv\fLhgzh-(f/Ix3wffDhf,ZZZ(of(\f(M(f+ZEh=fo"(fh2Z hDxsv?,Y%fwZdhfIfVG*ff6Z(}(a(fhHIZCOfh0Z"fj(f*IfiZhdh f4I[hmufh-Z(#f0[yDf,zef)h(  +       4 d j p v |       $ * * * 0 6 < < < B H N T N Z ` f l r x ~ f l r x j p v |  &,28> vDJPVVV\bhnht z  f f f j p v |hnht "(((. j p v |4 f l r x:@F@LRXXXt^dd j j p v |pv|v x l r x   < < < B  j p v |     $*0666<B f l r x d j p v |H NTZT`flFlLrxx~  d j p v | j p v | < < < B &,2TZT`   &,2 j p v |B f l r x   f l r x\ d   ~& & rxx~, 2 < < <8 d>D D JP vDJV\\\ b hnntzzz   p| xhnht   < < < B p f l r x  < < < B^ j j p v | f f f  $* d j p v |   & "(.|. x4:::@F d j p v |LRRRX f l r^d f f fjpvvv |    @F@L d j p v |        t T ZB f l r x B~t < < < B   $*&,0j j p v |6 T Z<BBBtH f f fN T Z`fffl rxx~  d j p v |   LRRRXB f l r x hnhtTZT`ntZ` f l r x&" d j p v |(.|. x  f l r x V  &&,2  , 2 8>>> D J\ PD D V\\\b &"hn p v | htttz < < < B&&,2 H T ZZ2V  TZT` f l r x     &&,2B f f ft Z`  j p v |  4 f l r x     d  j p v |nn"n"t  f f f(... d j p v | &"&      4 4  &   :TZT` d j p v |@FFFLRD D X^djpv |\::: TZT` t 2n p v |  f f fj D D rxx pJ>>>J & h\&&,2 &&,2&&,2   &&,2 < < < B   \t  V\ 8 fh0f(>f+3Dfh=f|fx&@f:Z f!hhf(f~l'ffyZf^(f-(fGlf hf (0f^bv\ffdf hf(#fzZfI(vITf(fjrfyIj(c5fjh|fZ_fhyf(Lf^IfNr`#i,:Ei#Z.Zdhfa(%Zhfs(kf(c(faUf?IwfH(fq(s3"f-QhNf"IfI(feZ(fh\fhxhfhf h(%(,Z9f;[((s>(7fM(;t(ZZf-ZkeZWf{iZxZf2({+fYhfl 1fhhiffm(f( h(`ZI_<af(nlz@b(zf5'gZh|hdf/(wZ`ZhY(fhxhEfzhefh({ZHmh*pfDy'f(tD(+Zh%Zh f(hZZh 1fWIpIf,BZfYuD"l(*I(r(7f(f]hdL(h$Z#IcIhIZ**"(%$&' #       !  ) 1Z+$$/%%&&]''X((o))**?++,,--T..l//|001122033445566J7778899::!;;_<<n==DD EEuFFbGGHHII.JJKK+LLMM9NNOOPPiQQRRSSITTUU(VVWWXXYYZZY[[\\O]](bbccxddVeeff'gghhhiijjkkkll6mm$nnfooDppaqqrrssttuuvvswwxx#yyzz{{r||}}~~j< m-ydc z2-#p{"MqN*"1S  5    &  0  G@C,&>RQ  !!""##*$$L%%&&''((/))!**`++,,--..$//0011 22K3344=55.6677889:;;;<<== >>U??B@@%AABBHCCDD4EEFF[GGWHHIIJJALLMMNNOOPPPQQ RRSS)TTUUVVWWXX\YYZZ[[v\\]]e__'aabbccee ff~gghh iijj,kk3ll}mmnnooppqqrrssttuu)vv wwxxyywzz{{||:}}~~  F8Etg%^ 12)1-w&"!HNkG|h,B[X+ V{`$(ALv5i )0Wl(gJ%?cZY 1m  9        /\+ty>  !!""##$$%%&&''&((2))P*+D,,--..//K0011U2233^445577o8899R::;;<< == >>}??@@AABBCCDD*EEsFF#GGHH2IIJJ!KKLLMMINN@OOPPQQRRSSTTUUVVWWXXYYZZ[["\\d]]^^__`` aabbccddzeeffggQhh=iiSjjkk ll'mmnnooppqqprrss-ttuuvvuwwxxyyzz{{||q}}~~*f3M:b;x a.3C% ,Tj4 'rFn0]~8$. _ 67Oe</ &  #Z(I(Z(((Z((I(((Z&%# !"        $ #"         ! 44   JzcyrllatnIPA VIT aaltccmpccmp$0TpPbb &6H &6HLM   LM&Dblv JJdCCoCCJJppopp0u&&(( 22 88FFHHRR XXddoo??FFffoo}}  N ,  F ')uy^+bw%d!{`    $$&&((**]] __aaccttvvxx zz  f0     EM   DD"JJ(in DDLL!#$%&')*,./XXXXd@ JSIL  D  4 t~!%-3:DHU]adq~37=BEKQTWY[cikru #(1?_';Io     " & 0 : D !"!&"""""""+"H"`"e%%,b1Bj $(09AGJX`dht&7=ADJQSVY[chkru#'1?^.>Lx     & 0 9 D !"!&"""""""+"H"`"d%%,`0BjT1hw8:"+ xjyޖދtq_/0TOn  2:>BDFFrxz|VFJNBBD8<:<>@B@bcdeYfgh<jikmlnoqprsutvwTxzy{}|~9.#F?G@IBHAUZQi}fl~g&!GAHBVMXOYPik}gq|f.0?u-"~hpmonq/1625/0o) $* + @>:DSRt{;7=9<8JCVNXPWOkjp#%(':7;8ICKELFJD[R\SWNZQ]Tc_ead`jl^43 "$&*(,nmht]v_zcxa|e- ,  12435srvwyxz>@ t~!%-3:DHU]adq~37=BEKQTWY[cikru #(1?_';Io     " & 0 : D !"!&"""""""+"H"`"e%%,b1Bj $(09AGJX`dht&7=ADJQSVY[chkru#'1?^.>Lx     & 0 9 D !"!&"""""""+"H"`"d%%,`0BjT1hw8:"+ xjyޖދtq_/0TOn  2:>BDFFrxz|VFJNBBD8<:<>@B@bcdeYfgh<jikmlnoqprsutvwTxzy{}|~9.#F?G@IBHAUZQi}fl~g&!GAHBVMXOYPik}gq|f.0?u-"~hpmonq/1625/0o) $* + @>:DSRt{;7=9<8JCVNXPWOkjp#%(':7;8ICKELFJD[R\SWNZQ]Tc_ead`jl^43 "$&*(,nmht]v_zcxa|e- ,  12435srvwyxz>@*s[hc -,K PXYD _^-, EiD`-,*!-, F%FRX#Y Id F had%F hadRX#eY/ SXi TX!@Yi TX!@eYY:-, F%FRX#Y F jad%F jadRX#Y/-,K &PXQXD@DY!! EPXD!YY-, EiD` E}iD`-,*-,K &SX@Y &SX#!#Y &SX#!#Y &SX#!#Y &SX#!@#Y &SX%EPX#!#!%E#!#!Y!YD- ,KSXED!!Y-dL/ /ܹи/EX/ >Y+017!!'!!L2P223l+A&6FVfv]A]Aq/ +01%#"&5466632''66667'6"(,)6 %-!V*/0$A5"6-?4"4 Z // //01#66667#666671#! " %$! " $j      7 7//EX///>YEX6/6>YEX/>YEX*/*>YEX1/1>Y + и1 и иии #и %и&013#%3#3#'#'#'6673#'66736673667b_A _ q.yq-z _ l-sk-Ns33 H H21" " XV [-\/]/\KиK/A&6FVfv]A]Aq]%ܺK%9 A ]A qA  ) 9 I Y i y ]K%9K%95и5/0/V/*/2/5/0V90V90V9014&&&'6666'&&''7"#"&&&5466632'&&&&54666776666732 "<03/L6m'=,A0R=#"& F)7Ec?9dL  $`V< $ !3I3H?U40X|K  H~),0i#4>8430 .ARO'-'!:z$EJT3LtTy u'2 ,,!+60 DFH"8n]C rq4h)5IaŻ@ V+J 6+ + +A]A qA )9IYiy]A]A qA )9IYiy]A@@&@6@F@V@f@v@@@@@@@@]A@]A@qAJJ&J6JFJVJfJvJJJJJJJJ]AJ]AJqc///4/]/CQ+Qи/ C%и%/]9014&#"32666667#"&&&546666632'6674&#"32666667#"&&&5466666323=+.# :/."/VwI0L5(9HV01M4D #'% I5>-." <.."(9HW10K5(9IU10M4[W&=MNGTP$:JKDBxN*Id:+^ZQ=$)Ic^ #![W&=MNGTP$:JKD,^ZR>$*Id9,^ZQ=$)Ic7#q5D+V +pl+l,и,/D>01666676&&&#"267&&&&'&&&&'7#"&'#"&&&766667&&76666326676&&&#'667! CZ91%$8(KP9'OIA7O6 B\n2((( I:-M6UnS6NiTZ' 7YxI B_t<9K) sE>HN' ("0 )j9~B(IEB %B2%#l# - "  (+D53h+A&6FVfv]A]Aq +01%#"&5466632'6"(,)6 %-A5"6-?4"4? //01'667+ $# @ E/#34/5/ܹA]A qA )9IYiy]4%и%/ A  & 6 F V f v ]A ]A q +/+014&&&#"32666667#"&&&546667666632/?#8U>) ]Q:V=' 3&!KVe:FfC! 0#KXfY01!!76666766'&&'66667Ha;l 9V< 'mqg /TC5  (, 3 *11#  -+A]A qA )9IYiy]и/EX/ >Y)+01!2666767!'66654&#"'6666328w +3@ZT%=-ImE2XB'>!(4 J٢?H\&T0-L6>reGK'&=89#GkH%#8H$ :jQ0$9(#9)";&Cd|@Y+и и /01!#!7666677!'6673RP  @  /$H;L,D)*BflI   ++  !&#=(+A(]A (qA (()(9(I(Y(i(y((((((((]?;/=/#+8+-+ -901!6632#"&'73266654&&&#"'6666667!26767  %c.ElI'1H]rBSG)-I>4KjC 6N2fk% ) " ]`U 2Tn;l`E51FYl=AkM+-W}o6B*R*!8HLM G\5e`A26wrgN.9gWlϻ~Tub /+01'667!"'66667!b8rneWFBY!bDf  )% {lʳ50!=:EJD! %M:+0+A&6FVfv]A]Aq0 A]A qA )9IYiy]& +:&9?:&9DиD/0O!5+I +01666654&#"4&&&'32666#"&&&546667&&&&5466632"9J'!8(^N(C11#:J'1M62F-7T8&>Q*%C3FwUEhF#2Qi7 :,=fG=]?'<2,3=H,QU"8I7P>1!DP_=+J7 1Na7\L@7EX7UvF.Ne8EvcQ-:H/I]5#>VD#56/7/ܹA]A qA )9IYiy]и/6*и*/A&6FVfv]A]Aq"*9/1 +%+012676654&&&#"'66667#"&&&546666632Jm*0=.F5% #4>`v[4J6bI++L>#G%QpF!7IPR%AT03;hh;G*Kg=8xqeL,-\3.+A&6FVfv]A]Aq9/A]A qA )9IYiy]EX / >Y+ A]A(8HXhx ]01#"&5466632#"&5466632&6")+(6 %-d'6"(,)6 %-b@4"5-?4"3 A5"6-?4"4)ɻ+A]A qA )9IYiy]/EX / >YA]A(8HXhx ]01#"&5466632'654&&&'6666&6")+(6 %-P)6@F#" ) ;EAb@4"5-?4"3DLPJ>#l# - "  (2%//901%'667%  j  %  82c% + +01!'667!!'667!%  o   o   3  3// 901'66667%%'677667 F   9q6  %  );dk&AV0 @z`:&BY75l // ܹNAN]A NqA NN)N9NINYNiNyNNNNNNNN]XиX/~ A~~&~6~F~V~f~v~~~~~~~~]A~]A~q&1+=+S+bw+и/SI013266676&&&&&#"326667#"$&766666632#"&&&5#"&&&54666766663266766667&&&&#"32666   ML? 9[uJ]uY; EKh &saHQznޖ> 9P^b_'#BKQ*"K?)AeG@ED (F3 !&).N@2!%3=@AJ&Hf9H)BrUqxQ*7bZe#5<7&TF.v7pvBhܻWiJ'Eya-m_?&S^Oy0"& #c.d\L  -J_eb'AhJ(=`v/% 3/EX/ >YEX/ >Y +01!766766667!766''s;M?L  $&#  ) G>:^ ++ ^ J ++ M[  #MID+A]A qA )9IYiy] и /D$и$/DOEX?/?>YEX0/0 >Y)++?A]A qA(8HXhx]и/и/2и2/6и6/I901"3266654&&&"3266654&&&#"&&&'&'#76675666632'6A Fe<+BNC,J"I.10BkL*>cZL_8?C KRB?L-J$.`jxEQ[1!;Q03S  "A]:4bSA 8QhL 2ػ(+A&6FVfv]A]AqEX0/0>Y#+0 A ]A qA  ( 8 H X h x ]01'&&#"3267#"&&&5466676632#' )uR(d(?Y8;]o58pDwlb/Ki?%NzTI^c"*) -;&1?mg1GY  D\7A|t^ùC9B:5 +'+A']A 'qA '')'9'I'Y'i'y'''''''']EX/>YEX/ >Y иии/ A  ' 7 G W g w ]A ]A q01#!766756632#"326666654&&&5"Dda?Lsgu~B "?1J}fO56hXk=+!A?NQ8`Fk~F5CEX / >YEX/ >Y)+ /01%!76676&'7!#6&&&#!!&&&&##3326667X) ?L>A +;v  $5M8ED &H>8.=%N    #D;(9EX'/'>YEX/ >Y +' 01#6&&&#!!&&##!76676&'7!)<C  "YBFKDWJ?L>A>>8.=%N " ++! $+L' Bػ2+A22&262F2V2f2v22222222]A2]A2qEX/>Y7 +*A*]A *qA**(*8*H*X*h*x********]01#"&&&5466676632'&&&&#"32676&&&'7!!62 Lt^Q(SvG,QpDUwFFB$):CM-&m63Q:Af~>6h#( -XK+WZP=I' Cx_6CP&")( * 4,)Fkh2 -  ++QEX / >YEX*/*>YEX/ >YEX / >Y+01!6&'7!!7667!!76676&'7!B?L=,>>??L>Af?JFE??f?L>A"> $++" #++! #++! $+\%EX/>YEX/ >Y01!76676&'7!V?L>Af?K>?" #++! $+u*EX)/)>Y+01#"&&&54666323266676&&&'7!o?M4AL)??62&') *::3 %C6"JwnJ "  " "Xu +5A/*/EX / >YEX4/4>Y 9% 90166&&'7!67#"&&&'!76676&'7!B?LE-"/& ^ -A/&IA7 H??f?L>A"  ++$/ J #++! $+P+EX / >YEX/ >Y01%!76676&'7!3326667P) ?L>A?L %A2*;0,W+! $++" #D;.vEX / >YEX-/->YEX/ >YEX/ >YEX/ >Y 9 9' 901"!7667#!7667&!266663!F$yBAf?Ql)YEX'/'>YEX/ >Y 9 901&&&&'!7667&&'7326&&&'7!BC" {8DDC>+{ 0#l #^| & ++ &+ d+Z= 5E6/7/ܹA]A qA )9IYiy]6'и'/ A  & 6 F V f v ]A ]A qEX1/1>Y"+1A]A qA(8HXhx]014&&&#"32666667#"&&&546667666632$FfBLy^B+'He>M{]B*/E-*_kwC`_/+>(*ao~Gb]-ToA;b?]sA>gQG96]E(Ri?78cJ+R 3+A]A qA )9IYiy]EX///>YEX / >Y + /9/и/& /901#"&''3266654&&&##"!76675666632BsW&N "C BnO,6\zC  "YE+$+3A]A qA(8HXhx]@$9014&&&#"3266666#"&&&'#"&&&546667666632326667$FfBLy^B+'He>M{]B*83*8d`_2`_/+>(*ao~Gb]-/E-6{H'IGE" !,ToA;b?]sA>g,C-:T]"Ri?78cJ+RiG9Eo#;4#5 2C'?+A?]A ?qA ??)?9?I?Y?i?y????????]'E//EX"/">Y,"93и4и6и6/01%#"&&&'&&'!7667566663267#"3266654&&&5'L@3 "=D??f?L'M'-[drDXf8#O^!-B3$H9?tX57Ugm-! 3) #++!A <  )QwN'qo]c "##EiGJe>/l FaG/H/G?и?/A&6FVfv]A]AqHܸ!и!/5A5]A 5qA 55)595I5Y5i5y55555555]EXD/D>Y+!+D A ]A qA  ( 8 H X h x ]!001'&&&&#"#"&&&54666323266654&&&'&&&&5466632h*276T9,QCOnDNc&aW;!% "7Q;=iN-YEX/ >Yи 01!!7667!"'66667!#&&&&#!BVG &%   )>Q+' $E:AC?=@>2B' (.+и/и/A&6FVfv]A]Aqи/EX/>YEX-/->Y# +01#"&&&5476&'7!3266676&'7!?FZVcRb6 b>A?MX"CdAAnT7 \8Aw"}ˊG2`\5> $++"{:FrR-@jK $+3 /EX/>YEX/>Y 901&&'7!6&'7!?K (.+ 5;H<@H{    ++D +*\ //EX/>YEX / >YEX)/)>Y 9 9" 901&&'7!36&&&'7!4(#)) (,* I2A,6 ;7%$3      ++   +5kEX/>YEX'/'>YEX/ >YEX / >Yܺ9 ик 9501!!76666'!7667&&&&'7!64&&'7!=e(4 :SeYEX$/$>YEX/ >Y901!!7667&&&&'&&&'6666326&'7!BWG;:EI" !0# A@901-'5-Dh?Gz; !7)+'I2 + '!ZgkgZ! ++ U FEX/>YEX/>YEX / >Y017!26667!'!"'3!$%)1Z'+*4H((*<d1TA!QSM-b0N8 7 .?  + +01!!!! w%1 T1? //01&&&&'7 !z%< FV? + +01!'667!#'667!H 2 /^5V// /901''66667V1"   $&${X f  } +01!'667! ' ՜205 / //017666635 %)&  $5RS/T/S-и-/ A&6FVfv]A]AqTJܹ# A#]A #qA ##)#9#I#Y#i#y########](+7 +(0166667&&&&#"32666#"&&&5#"&&&5466676666326673267[ $)-.QE7&&4EKJGWk!KV[*!K?*%JkGAED(&"4    7(-aZK  -J_eb'AhJ(=aw94Eza.m_?&S^Oy0"   #*"Hf9N/ jvW+++A&6FVfv]A]AqA]A qA )9IYiy]4и4/GNG9YD/ $+S+01"326667666654&&&#"&&&&&54666666654&&&#"'666632666632\HW[% 4A#$>5*'1;H"Sbp?353'   ! *XM:   3mfY'D1#1YyHHq<3!)3$NNK"EgD!je-S?&&29>pv  '"$.eLVvI +Rz5-6(+A&6FVfv]A]Aq#+2 +22901#"&&&#"326667#"&&&546667666632-/,' +6 .L>/4H+8IZ6N|eQ$"TK3?eJDIG,E6)/""*",Jaij/AhI( !8, GW/"TiC|1""+'5_ ++Y!+A!]A !qA !!)!9!I!Y!i!y!!!!!!!!]!и/A&6FVfv]A]Aq8!Y9Y=и=/YPYaM/&+5+&к85901667&&&&#"32666#"&&&5#"&&&546667666632666654&&&#"'6666323267m '-2.OC4$,8BGKSUm" "OVZ+"NC,"EiGADD4    # *UI8 7#?/@/?0и0/A&6FVfv]A]Aq@ܹ A ]A qA  ) 9 I Y i y ]и/ :и:/++:+01666654&&&#"326667#"&&&546667666632!?3"N!>J )Iyy#/7BHMS-8\B% :0GW/%Q_N|3!2?>?%/: ++::9,и/01#"&&&#"!&&&&##'66667#'73666676666320.'  *&:,  "&# $6%H ?_|J2Q=( NeK 1Q?247,G8+{##*#-Kahf*/! nͱ-?xR;Ps/"!)& o^EX)/) >YV+G+):A::':7:G:W:g:w::::::::]A:]A:q01%26667667667&&&&#"#"&&&5466632326766667#"&&&546667666632667RHSU!  $+2.RE7'(6;"'  [}N*aR7"!#2F/ar  U_b,!KA*$InJ@DC*'#3`CpQ&S/& -J_eb'AhJ(%RHwīMhO$*+%#8c]Y/?nG&S^Oy1"   V]bA.+W+A]A qA )9IYiy] W9 /A ]A qA  ) 9 I Y i y ].и/AAA&A6AFAVAfAvAAAAAAAA]AA]AAqH.A9 P_>//$/M+H>901%#"&546666654&&&#"'6666666654&&&#"'6666326666323267]y*-   BU[&$  ')& ! !    % *UJ8  :rlc-?6   <%H6;?6]|~d&3 ?^@0Z7TA36ɩ   '"$.o$Hc<Oa#hxs_"F5/?)+A))&)6)F)V)f)v))))))))]A)]A)q)9/A]A qA )9IYiy]")0)8и8/"A/+=5+,01%#"&546666654&&&#"'6666323267#"&5466632/K>2"(  !*SH6  9)* ." -"H*?6oh %#&.qq7-$"7.% 5FUKGN+AN]A NqA NN)N9NINYNiNyNNNNNNNN]NG9/4A4]A 4qA 44)494I4Y4i4y44444444]GWD/EX/ >YSL+%A%%'%7%G%W%g%w%%%%%%%%]A%]A%q01#"&&&546663232676666766666654&&&#"'666632#"5466632w   ;FO)@>6>4"$% (<,Un    !*TH6 S!.@ -#f 7JUQE#Yhu>umI  *'μJrY5=A:-  %#^7-F7.%VWC+A&6FVfv]A]AqC9/C9S/(/9/ +9S9/9S9017666&'66323267#"&&&&&''6666666654&&&#"'666632  EX/C;m?0+SvKu6430,5&Bc&$DA>;8  )*( ##"!  $*VJ8 'uU4J-'"6IV.G [cbN1"83DjxaU}Yغ& '")f34/5/$ܸи/4и/$A]A qA )9IYiy]и/-A--&-6-F-V-f-v--------]A-]A-q!//01%#"&54666666654&&&#"'66663232671K=4,   #*UI8 ! =-H+ <;x̰#  '"$.'ɬ"")ٻZE+i(+}+A]A qA )9IYiy] }9 /A ]A qA  ) 9 I Y i y ]A(]A (qA (()(9(I(Y(i(y((((((((]AZZ&Z6ZFZVZfZvZZZZZZZZ]AZ]AZqZ_и_/ilиl/ v+q++иJиJ/qUиqd01%#"&546666654&#"'666654&#"'66666654&&&#"'6666326666326666323267]z+):FO'(*' )  6FO&  ')& !   # *TH6  ;g]U)%2 :bUM&0"  9)H6;?6Zyp!>59Y=#DIQ0[1fŚ%L:9X= DKT0[0F|a'" -# !'*Fc=2?!C^<*C0&kx~q]"/Y^=(+S+A]A qA )9IYiy] S9 /A ]A qA  ) 9 I Y i y ]A==&=6=F=V=f=v========]A=]A=q=BиB/ L[/ /8-+-и/8G01%#"&546666654&#"'66666654&&&#"'6666326666323267[&#'  , ARY%#  ')& !   #*UJ7 6njc-1   <*H6;?6]|~dL< @`@0aY;B` '" ,$ &+) Db?)C0#hyt_"5+/0/1/ܹA]A qA )9IYiy]0#и#/A&6FVfv]A]Aq +++014&#"32666667#"&&&5466676632pgHb<!;O..I9( :U8:<>FmK'6ZB3z>NnE S~@CwY4)EY```Cu.&;gRH|r5(88d uWX/Y/%ܹA]A qA )9IYiy]XEиE/A&6FVfv]A]AqE92E9E6и6/EX;/; >Y + -+ U01"3267666654&&&%666632#"&&&''6666666654&&&#"'666632bRZX 6FP)6S %. @p]L'D48?;FO+"A?:" (+' "#! !*VK9 %2[MI?*;6'USLFgC!AlXct<(OsKds(QB*)BT* zħ  %#5 J8+A&6FVfv]A]AqEX(/( >YB +3+01%26667&&&&#"'66667#"&&&546667666632667\FLM"6&+1.TI<+0;@ -(! (+( #%#QVU(!NC-)NoG@CC *($4`<`y>D -J_eb'AhJ(%*"z &{:mT2&S^Ox."  )D6!+A66&666F6V6f6v66666666]A6]A6q;!69/@ + @9@@101#"&&&#"'66666654&&&#"'666632666632  EE?   ')& !   "*UJ7  0VMF "R.+GsH&[`](G|b %# -# 4=<c|FEF/G/F<иby;3F5(L%#)#%5 #N%2AS5EyY3*1 *)%,%-=#'?3*24: ?hK) ;;5 + и/A55&565F5V5f5v55555555]A5]A5q50и0//8++"и.01%#"&&&546667#'7376666773&&&&##3267D1bXG-#MeH  e  #&# $5%G-#eKu&;(5J,&).;#)6$ H J>:! 5+'" >;"'`b3+Z +A]A qA )9IYiy]A33&363F3V3f3v33333333]A3]A3q39/,@Z9@/A@]A @qA @@)@9@I@Y@i@y@@@@@@@@]Sb)/P/]+ и]6и6/01%#"&&&7#"&&&546666654&&&#"'666632326667666654&&&#"'6666323267Qq&% )[bh63-    " *UH7   )%BTc5  *RE4   5$D67#P^J|Z2>\=\jrhU %#&.^pxp^`V'We+WQI %#&.]oyqb![W }A 3+A3]A 3qA 33)393I3Y3i3y33333333] /?6+6и/?"01'&&&&&&'&&&&#"'666632666654&#"'666632},f{    *N@0   JpK&")A>5#'T ~ 8h %# 1#n] x֩v# $n^+A^]A ^qA ^^)^9^I^Y^i^y^^^^^^^^] / /ja+j8иjL01'&&&&&&''&&&&&&'&&&&#"'66663266667666632666654&#"'666632 0MmK  ! (6>C"    *L>/  (TF/##  K_5(">1"\Fb܋ 8v`Ur9 8k" %#AHe|zbNF  !6G%J%/rxx4ҝm&%}KH+(+(;01%#"&&&''66667&&&&#"'666632666654&&&'66323267m+B5+=;;;CH#%\%.jmi-874 )*M@- 8:<)N<%' Bf0&/[X742=%H+ ?iJMXc5 #(jtw6LoC'#DqM1fW< ) Ek`Hf=K }Nܻ B+AB]A BqA BB)B9BIBYBiByBBBBBBBB]1/J/EX / >YA'7GWgw]A]Aq; 1901#"&&&5466673267&&&&&&'&&#"'66663266666654'&'6632}%SaCU5'!% QT #&&#  ! *L=.  -., A<3'5 ;Hm" \"k֙T  +/':m3%#1"6Z?ubG2%*$8-4//"/+8*+ 016632326667#"&&&#"'6666667!"'6673!R}O0CPS% $#*1@11XNG"+8&2kli^P!'%,#uYe,&>R1 !KX4:JAJ:*PG: -D[7QS5{K(:/).;)D+? //01'667<.E-?C/1/!1901'6666766&&&6766667&&&&7666676&&&'7332667-r  ;]{I(H8&"5F*-:  8-Je;  @E yM)9.(/:)DvfU"1=KY4'9/*/:()PF9 -D[8;TJJ18VE9=!K\qH;NGQ=QSZ#/ / и /01#"&&&#"'6666323267JU[,(QRQ(.Y1-JV[,+SQM&-Z31iW82<2NT1iW82<2NR/T"$@/"$@Le OcP/Q/ܸPи/9 A ]A qA  ) 9 I Y i y ]91и6A66&666F6V6f6v66666666]A6]A6q MиM/EX"/">Y.A.]A .qA..(.8.H.X.h.x........]01&''6654&666767&'&&&5466676632'&&#"3267L>c{<4R8./:6Ei?%NzTI^c,#' )uR(d(?Y8;]o58pDwl13@5X1#'.%92 !|t^ùC9B:*"*) -;&1?mg1GY  D\7 9"( @a"1D@Z=T"2@T"8X@5"D5"D95"D55"D5B"D5"D5e-Q;R/S/ܸRи/9A99&969F9V9f9v99999999]A9]A9q99 A ]A qA  ) 9 I Y i y ]2и2/2929IиI/ OиO/#2+>+#-(#-901&''6654&66677#"&&&546667666632#"&&&#"326667>c{<4R8./ "TK3?eJDIG,E6)/,' +6 .L>/4H+8IZ6N|3-&3@5X1#'.%92"TiC|1""+'""*",Jaij/AhI( !8, GW 9?W"H_?"H?!"H^?75"HaF"F"xF"F5"/B"Q5R"RZ5+"R 5+"RY525"R\5XB"R\'"X'"XX'"X'5"X7).//2+и2(и(/01&&''66766'667'66766'667666672:yK !- 0 " )NLN*29wJ  &S #!0*NMO*2"U&FEo`:=C=cnEE  +"U&[J+^_  f'(/)/ܹA]A qA )9IYiy](и/ A  & 6 F V f v ]A ]A q+#+01266654&#"%#"&&&5466632j#7%*&4) /=@>!7'9Wd, 6(S"5>0B0@$* /SF7'+:!BsV2,;h` J;+A&6FVfv]A]Aq1/I/1I91I9 1I91I9016632#"&&&'66667'7#"&&&&&546667667766667'6a:[> P ,E6))% !1$c1@Q47YK>   8;7,?fJE% 3XE2 ]}!*'% )&-"8+  2G2 !7PlFCw1 AGC;E+Eи/;I0>+F+%иF*01#62766667&&&'6666667#'6673666632#6&#"3 ,5>REG2*<3.% ,n{~u1 1&gaGsg+0;'+[[10,#E~o^$ "C;+OC2 1,3Db_"SJME|p #Cqt9f2$+LZ+016676&&&'&'%#"&'&666732676&&&'&&&&766667&&7666632'&&#", );^@&6Xb P. 2BLG=+s7  &4K/A\ 9K%7U7 +5 :Sd3A>5"!S-[;*2,HC95#966&EObBu //01#"&&&5466632#Cb@&8%$Ec>&8$8t];.>$8t^</?O <S!/EX;/;>YEX4/4>Y*+4 ии/'*901&&'667&#"3!7667#"&&&76666323% ;S8'8dM3 =lQ AL?BAK_(We/ S]9@E!MTA   N*Hb86t`>_" #++"/_^Ug9j\]/^/].и./A&6FVfv]A]Aq^ܸи/.и/7A7]A 7qA 77)797I7Y7i7y77777777]'и'/.SиS/G/X<+$+01#"&&&546663232654&&&&&54666666654&&&#"'66667666676666324O\O42KXK2/YT*SA) "  #1!PW1JWJ10>@>0/J48[E- X ?_|J2R=( X (B]@995@eE$YuO53;.$847EZ>>}e@+3 ('$,$[U=R=28H5;R;*%&2D2-WE+M}S oͱ-?xRRr/" 4Ti'M\(EX1/1>Y#++01#"&&&76666326&&&#"32666767766327#"&''#"'#"332676&&& >]u@AeB >]tA@fB?7U65_J28T56_K2q(-';%"1 :)H+ G %!  -1"YFT+!+0+`:A:]A :qA::(:8:H:X:h:x::::::::]01#"&&&#"326667#"&&&5466676666324&&&#"32666667#"&&&546666632&D &3"*H:.":M*+;N2HkTE"TL54VA>CC)@1%YEX!/!>YEXB/B>YEXK/K>Y ܸик0!93!9G!901#&&###7665#"'66667!"#7665##7665&˜6633/`0  2c   `%1'c,8  f\ k ! k   6_  t << //01&&'< "^ @Rv5 /!/ и/A&6FVfv]A]Aq!ܹA]A qA )9IYiy] +и 01#"&5466632#"&5466632 / $!/#u!/# .%7-$"7.%"7-$"7.%2k%-? / /-+++и-и+и"01!'7#'66737!'667!76673#!   &#  ҂ |E  ʃ:   "33!  KdEX#/#>YEX / >YEX/ >Y+#02и>и E016&3!7667#!76676&&&'7!#6&&&#!!&&&&##33266673 h' ?JJ3G>J+D+) <m  '6%F &G>8.=%N  #D;!-:HI/J/ܸIи/.A..&.6.F.V.f.v........]A.]A.q09;A;]A ;qA ;;);9;I;Y;i;y;;;;;;;;]>9)//EX"/">YEX*/*>YB +0)9"4A4]A 4qA44(484H4X4h4x44444444]>)901#"&''7&&5466676666327667&&#"%4&'3266666=/E-*_kwCNy.8 $%$+>(*ao~GO{-:A###A#`=Ly^B+#_9M{]B*G96]E(50H  !D]?78cJ+50K!Da08;b7e-27>gB:xF'O'<+2+2(и<F0126667&&&&#""3266676&&&%2666632#"&&&'#"&&&76666$/3?.$1@(*6" /26A*&2@(*7" .-I<.:]OF#">,>\q:-K=/A\J@$">,>\q0)8.).3. ) 0$8-).4- r3@ 2D*";Q/8mU53@!8F'";Q/8mU5)+/)#++и01#'#'66736673!'667!  &  ' ';(4   J   3  3L!/!+01667%!'667!Z   x  p 7  %   3*!/!+01'66667%%'677667!'667! G      p 3  %  -  38<)/EX/ >Y+ик2)9013#!7667#'737&&&&'&&&'6666326&'7!7>&5G}K=&3;@ *881**(! (;8  B '**%7@G1 * % XchcX  * p"dJ+(+A(]A (qA (()(9(I(Y(i(y((((((((]J2и2/J]Add&d6dFdVdfdvdddddddd]Ad]Adqr /Z/EX=/= >Y&+(= 9&-к2= 9i0166654&'66667326667#"#"&&&''6666667666654&&&#"'66663232667676   +..    &#*B:9U#GKN(!<2(260 !    "*UJ7    ,%SZ15cT B  dy|h!-63-Mc:?iK*5K.:g[ 9C'QLF %#&.PY^XL7\B%5oV$[D!+>4+*+01&&&&#"326667647#"&&&7666666326&&&#"'76632/CO'4WD. 9L&4cT>GXdaX!V~N /CT\b0A?6Ga318B,$}6^-1bVD(47^F(9cNNj>E~om@F|dA~p`E'0={6(! :f'V +A  & 6 F V f v ]A ]A q L>////6*+*и/*(и(/01%#"&&&546667&&'&&'&'6666667&##"'6666323267##"'326667*G?8(! .%FM*%.47,(%%&$1/0 ><<l@(I 678  !+I\4+K9TϘ\̻F  .]iyr#"?/  # =0>q'5<)& KEX"/" >YG +GG9"1A11'171G1W1g1w11111111]A1]A1q01#"&&&#"#"&&&54666323266676666667666676666320.'  *&:, 2BV8@=6>3""$ (;,,I<0  1O@247,G8+{##*#-Kahf*7(gnX. *'6e]62Qt0"!)&;J3 OP/Q/Iܹ'A']A 'qA '')'9'I'Y'i'y''''''''] и /P1и1/A&6FVfv]A]Aq'9и9/I<и  +%=,, $)'.;  '/1("OM#$%)   {/72( 4KTGS 3@ $A4:2!,F2W5    'RH7 >/ ;J34/5/ܹA]A qA )9IYiy]4)и)/A&6FVfv]A]Aqи/и/+/+ $+014&#"32666!'667!#"&&&54676632<6'6! +%5   qC.B &:(6GC +<%QY-EP"#@00HQw$JE> 8K-L86Jd 65-y+z/{/zgиg/{?ܺg?9 A ]A qA  ) 9 I Y i y ]g$ A$$&$6$F$V$f$v$$$$$$$$]A$]A$q3g?9 :и:/]g?9KX+:+иXbи:q01666654&&&#"666675&&&&#"3266667666632326667#"&&&'#"&&&546667666632667!?3"N!> $(-.OC4$$2FLJ$  $;?A+?* )Iyy#/7BHMS-8\B% :0GW/!JwU.l^?&S^Oy0"   m C"D/E/D0и0/A&6FVfv]A]AqEܺ09 A ]A qA  ) 9 I Y i y ] 09+/EX?/?>Y"+8++?9 +?901&#"%4'32666667#"&''7&&54666766327667 8gHb<xO/.I9( :U8:<><`$ #! u6ZB3z>?`"?n'I RS~)PA -4)EY```Cu.&,&!0uBH|r5(8%", /uw;߸4"3@z`:'BY3Xk\anH`tCpf_bk>cj&AU/ Xt+A]A qA )9IYiy]/ +01466632#"&'667o'6!(.(7'-/+//%A5"6-?4"5 3j / +01'7!'667!6 D0)& //EX/>Y 901##'66773% #&$ y ܿl1 ;.)s ]EX0/0 >YY ++YY90?A??'?7?G?W?g?w????????]A?]A?qKиN01#"&&&#"!&&&&###"&&&54666323266676666667#'73666676666320.'  *&:,  "&# $6%2BU8@=6>3""$ (;,,I<0  eL 1O?247,G8+{##*#-Kahf*/! >}f!gnX. *'6e]/|9;Ps/"!)&2:*1/E%//// / +*+ /9%01#"&&&#"'6323266677#"&&&#"'632326667p|%HGG$)I.'o|(NJB+*'Oq{%HGG$)I.(p|(NIB,*'۞$5.6$%$4/6$%2%.('/EX/ >Y'901%&'&&&&'3!266&!'676666766667&#! CEEJJ .+,0}rj-_YO   $*,15E9?C  3_ɿA Jd:$y%///%/01676673676673:t" %/1.%*?t" %/1.%* :JMJ:"b :JMJ:"b<&y////01'&&&&''7'&&&&''7&"!'" *@"!'" * "i=[k[l "i=[k[3O/0/(и(/A]A?]A]A]A?]A]( 1 +и и%и -01%#"&5466632#"&5466632#"&5466632O'6"(,)6 %-f'6"(,)6 %-f'6"(,)6 %-A5"6-?4"4*A5"6-?4"4*A5"6-?4"4/"$ @/a"$@Z=a"2@Z T  +A  & 6 F V f v ]A ]A qEX'/'>YEX,/,>YEX/ >Y;H+A'7GWgw]A]Aq'A]A qA(8HXhx]Nй9и9/:и:/NO01%267&#"%!#"&&&546666632!#6&&&#!!&&&&##3326667Io3~VLz]C+(He( 0E=='\_1,OoS%404# + <G  '6&[E<V NfgʸsA >>8.=%N  #D;5))k~T++* +A ]A qA  ) 9 I Y i y ]A]A qA )9IYiy]A&6FVfv]A]Aq1и1/H9_9*m#O+\+и/#6и6/OCи\g0166666654&&&#"4&#"3266666326667#"&&&'#"&&&5466676632667666632"@3!X}U3!?la*E6) 8K+)D6)  )Jyy#/7 R}bN$@>4(19<;AjJ(7X>1{9jD):?A+@*Vem5"<51/.- Й'AU]`*CwY4)EY``)>BHMS-8\B% :0GW/1N78,&;gRH{s5)7ym&F !2?+5 +01!'667! l /,+5 +01!'667!  @/,+-ٸ.///.и/A&6FVfv]A]Aq/ܹ(A(]A (qA (()(9(I(Y(i(y((((((((]/-/017'&&5466677'&&546667*$53 8A>-FT'*$52 8A=-ES&4;>%// 4#4j`R#4;>%// 4#4j`RN-.///. и /A&6FVfv]A]Aq/ܹ"A"]A "qA "")"9"I"Y"i"y""""""""]EX/>YEX/>Y01'666654&'6666'666654&'6666-EU'#)$53 8A=-ES'#)#51 8A>+4i`R#4<='/- 3%4i`R#4<='/- 3d+A&6FVfv]A]Aq/017'&&546667*$53 8A>-FT'4;>%// 4#4j`Rq +A ]A qA  ) 9 I Y i y ]EX/>Y01'666654&'6666-EU'#)$53 8A=+4i`R#4<='/- 3F *!+)!+ +01!'667!#"&7666632#"&7666632   J  !=8  !=T  3((%(((%(.,/EX / >Y 9 9013'66667E   ==A  K }5"\fT"<@B //017'667'Q. i<'//*/2/ +.+01"3266676&&&'#"&''&&'7&7667'7667663277#=."3!#;.!3OAM(Ao o" !V (P%L &@mn  3W.># <..="<0h-j jDQ0Mp! jiBi"N'VBq":y //01676673:t" %/1.%* :JMJ:"b<y //017&&&&''7<!'" *$=[k[l "imKn/o/ܹA]A qA )9IYiy]9nAиA/#и#/A2A22&262F2V2f2v22222222]A2]A2qAiиi/T/(/i+ +A>+#M+ii9A5иM[и#^01#"&5466632'#"&&&#"!2666323267#"&546666654'&&#!'66667#'7366667666632 ." -"0.'  *&:,!3!A8+  9)/K>2"( CGH ?_|J2Q=( NeL 2Q?247,G8+7-$"7.%k##*#-Kahf*/ &.qq%*?6oh nͱ-?xR;Ps/"!)&]W+A]A qA )9IYiy]Wи/W_2//E++++9и<01%#"&546666676&'&&&&#"!&&&&##'66667#'7366667663232671K=4, !  %06,D3$( "&# "3B%H ?_|J2Q=( NeK 6U?6t>O/%2 ""=-H+ <;NAm#&-Kahf*/! nͱ-?xR;Ps/'6," &17Rӷ%"HMC/D/ +H+"и .и4иH>и>/01&&'66667&&''667'66766'667'6676&'667666672;wK *NMO*39yK &R $#1)NMO*3:wJ ST29xK  &R $2*NLO*2"U&>uDB{:  +"U&`L+`e  +"U&:|AEt>+"U&_K+^d  iP d +A ]A qA  ) 9 I Y i y ]/01%'666654&#'6666-EU'#)$53 8A= q5i`R#4;=%/- 3 -ٸ.///. и /A&6FVfv]A]Aq/ܹ"A"]A "qA "")"9"I"Y"i"y""""""""]//01%'666654&#'6666'666654&#'6666-EU'#)$53 8A= -ES'%*#51 8A>q5i`R#4;=%/- 3%5i`R#4;=%/- 3h)=S_sj +t `+4 H+> *+ + +A]A qA )9IYiy]A]A qA )9IYiy]A*]A *qA **)*9*I*Y*i*y********]A44&464F4V4f4v44444444]A4]A4qAjj&j6jFjVjfjvjjjjjjjj]Aj]AjqAtt&t6tFtVtftvtttttttt]At]Atq/C/Y/^//m{+{и/ m%и%/{-и-/ 7иmOиO/c014&#"32666667#"&&&5466666324&#"32666667#"&&&546666632'6674&#"32666667#"&&&546666632=+.# :/."/VwI0L5(9HV01M4=+.# :/."/VwI0L5(9HV01M4D #'% I5>-." <.."(9HW10K5(9IU10M4[W&=MNGTP$:JKDBxN*Id:+^ZQ=$)Ic/[W&=MNGTP$:JKDBxN*Id:+^ZQ=$)Ic^ #![W&=MNGTP$:JKD,^ZR>$*Id9,^ZQ=$)Ic/"$ @"( @/"$ @T"(@"( b@", @", @T",@\", @Z="2 @Z="2 @K %4AN]kxsci+++++A]A qA )9IYiy] ииA]A qA )9IYiy]$и$/A&6FVfv]A]Aq,и,/2и:и:/A]A qA )9IYiy]@и@/Acc&c6cFcVcfcvcccccccc]Ac]AcqcGcMиM/Uи[и[/и/cиiиGиcи//EX/ >Y+!++y+)и)//и!7и7/!и/=и=/и/DиJиJ/7X`иfиf/ylиl/rиr/Xи!и014632#"&4632#"&6632#"&546632#"&546632#"&54632#"&546632#"&546632#"&546%2##"&546%2##"&546#"&54632#"&54632#"&54632#"&54632#"&54632#"&54632  6< d<757 E Z="2 @"8 V@"8 U@"8 @Ff // /01&&'3  \/ s{FB%/ /// и /01#"&&&#"'66663232675>G' 3//#H*+5?H&!70*#M&.(P?'")"?8(O?'")"?8B EX / >Y01!'66667!B  #   b //+01#"&&&'667326667%QTT(,M@2/9>HGD7QmBCmP7L//L7R_5j+A&6FVfv]A]Aq +01#"&5466632_ ." -"7-$"7.%!"/#/ܹA]A qA )9IYiy]"и/A&6FVfv]A]Aq ++014&#"326667#"&&&5466632*!)T#=Q., #=R/5@#7)2%7)3=)YK1",-\K0Ied +A ]A qA  ) 9 I Y i y ]/01&''6654&66667>c{<4R8./ D 3@5X1#'.%92/)19 /// /01&&'&&'   ) )B7n +A&6FVfv]A]Aq/+01#"&&&5466673267%799+!8Xl42%SE.&C*4) ,!:h[KEQW+$& ///01#667%\ _ 5e"D#5"D#5:"D5"D#5:"D5,"D#5"D5"D#5E"D5b"D5eb"D#5"D#@5g"D5"D#e@5g"D5"D#@5"D5"D#@5^"D5"D5"D5u"D#r55"D5d"D#a5W"D#5"D5e"D55RS/T/S-и-/ A&6FVfv]A]AqTJܹ# A#]A #qA ##)#9#I#Y#i#y########](+7 +(0166667&&&&#"32666#"&&&5#"&&&5466676666326673267[ $)-.QE7&&4EKJGWk!KV[*!K?*%JkGAED(&"4    7(-aZK  -J_eb'AhJ(=aw94Eza.m_?&S^Oy0"   #*"Hf9N/ 5"5"95"5e"#5"#5"#5,"#5"#5b"5eb"#5"#@5"#e@5"#@5"#@5"5B"5"55"5u"#r55"5d"#a5"5W"#5"5e"5"O5"[DK"$# @ T("$@/K"$# @ /1"$@M"$# @VV"$@/x"$# @[ig"$@e/"$# @/"$@5"$#@ E/F"$@/"$#@ /c"$@X"$@X"$@/"$#@L/"$@e/"$#@/"$@/""$%@:b"$#@Q/T"$@:b"$#@Q?P"$#@ O/"$ @e/%"$" ;@""G@jv"Ebv"Ejev"E{T)U/V/UDиD/ A  & 6 F V f v ]A ]A qи/V5ܹA]A qA )9IYiy]+D598и8/DGиG/=+P'+0+P"P"901"326667666654&&&#"&&&#"666632#"&&&&&5467666676632\FUZ%!4C#$>5*'1<0.( $,cy"/3kdX'D1;H"Sbp?353'T "7U@5x?+K<.#0VvF%1<3!)3$NNK"EgD!X##*#SsG+RzOje-S?&&29> ;/7Qt0'6!)&T"%@ "%e "%su !WN+A]A qA )9IYiy]и/N"и"/NYEXI/I>YEX./. >Y'+ +I и/0и0/4.I9S 9013266654&&&#"3266654&&&#"&&&'&'#7667&&7666632A Fe<"GnL%,J"I.10BkL*>cZL_8?C KRB?L)M?* +/! Oҋvf.!;Q03S+#2+#-(#-901&''6654&66677#"&&&546667666632#"&&&#"326667&&'>c{<4R8./ "TK3?eJDIG,E6)/,' +6 .L>/4H+8IZ6N|3-&3@ "5X1#'.%92"TiC|1""+'""*",Jaij/AhI( !8, GW 9  )89/:/ܸ9 и /A&6FVfv]A]Aq$ A$]A $qA $$)$9$I$Y$i$y$$$$$$$$]+4'+01#"&&&546766667326666654&#"&&'666632;Ti|F8^E'#152:D+9 +L?1"ta6AP2 ?fYQ*BhH&;Az\7"?Y60_%"[3 :-*F[``( 0$@P-1^ 4++++00901666632#"&&&'732666766&&&&#"#"&&&[3BO,CA;Y.A.]A .qA..(.8.H.X.h.x........]01&''6654&666767&'&&&5466676632'&&#"3267&&'L>c{<4R8./:6Ei?%NzTI^c,#' )uR(d(?Y8;]o58pDwl13@9 5X1#'.%92 !|t^ùC9B:*"*) -;&1?mg1GY  D\7 9 ; eRm+#и#/OиO/+1A+Q+,&+иQи,Fи&I01!326667#"&&&7#'7673565667#'7673666632'&&&&#"!!!  =T/7BP4?eYW2CpL$ T Q ; G"e~S-D6,!% #+8)IID> +hY('@.  D]8;z  ? qt:$&& 'NiD, F <I=/>/ܸ= и /A&6FVfv]A]Aq$A$]A $qA $$)$9$I$Y$i$y$$$$$$$$]EX8/8>Y+8)A)]A )qA))()8)H)X)h)x))))))))]01#"&&&546766667326666654&&&#"&&&&'666632$Ff_KsP)(EH@ TJ 7L,CpZD-7Xp9=Nb<Eug].Kf=W|H-La5.c1 2+eC%F7"@iY+*A*]A *qA**(*8*H*X*h*x********]016632#"&&&'6666732666766&&'&&#"&&&&6c^9BL oK/]]^1 V84}xg 11Y(R5  *:B9C^t|A7\D  YG1gm?1&;- )*"5"G*"Gn5e"Gl5kE ++e!+A!]A !qA !!)!9!I!Y!i!y!!!!!!!!]!и/A&6FVfv]A]Aq8!e9eAиA/eDиD/eWem&+A;+T^+5+&к859AZи;]01667&&&&#"32666#"&&&5#"&&&546667666632667!'667!6654&&&#"'6666323#3267m '-2.OC4$,8BGKSUm" "OVZ+"NC,"EiGADD3    # *UI8    7#A>]A >qA >>)>9>I>Y>i>y>>>>>>>>]и/gHиH/ A&6FVfv]A]AqU/>9C+b#+R+bb9C9кUR901667&&&&#"32666#"&&&#"3267#"&&&5#"&&&54666766663266766667666632k &+/.OC4$,8AGJ0-' &!4*#  7# Um" "OVZ+"NC,"EiGADD/$/:$347,D4(?U$-J_eb'AhJ(2Wv##*#"IrP;hti $94K|\?tZ5)W]Ou/!3Y$NpT="!)&5 Qkһa +F +<)+A ]A qA  ) 9 I Y i y ] F9F&и&/A)]A )qA ))))9)I)Y)i)y))))))))]Aaa&a6aFaVafavaaaaaaaa]Aa]Aaq<m9/EX/ >Yf+#Z+99&Z#9IAII'I7IGIWIgIwIIIIIIII]AI]AIq01#"&&&547#"&&&5466676666326654&&&#"'6666323266&'667677&&&&#"3266675FOR&!- !OUY+"NC,"EiGADD8  # *UI8 )'.&*(%R#(.4.OC4$,8DKM%"GE=-&Ge?g?sX4)W]Ou/!#P  '"$. =Uyr^^-9& 31b)#-J_eb'AhJ(6^~I5Mx"+A&6FVfv]A]Aq@/+) +01%26667&&&&#"#"&&&546666632&&'&&&&''7&&&&577HkK, 1BM&-J<-!7I&D\kv;JsO)!YEX/ >Y +ии!и!/"и %и,A,,',7,G,W,g,w,,,,,,,,]A,]A,q01#!7667#'767356632#"3#326666654&&&5"Dda?LF Csgu~BD@ "?1J}fO56hXk=+! ?NQ D%8`Fk~F5 73+A3]A 3qA 33)393I3Y3i3y33333333]EX/>YEX/ >Y +ии!и!/"и %и,A,,',7,G,W,g,w,,,,,,,,]A,]A,q01#!7667#'767356632#"3#326666654&&&5"Dda?LF Csgu~BD@ "?1J}fO56hXk=+! ?NQ D%8`Fk~F5 73+A3]A 3qA 33)393I3Y3i3y33333333]EX/>YEX/ >Y +ии!и!/"и %и,A,,',7,G,W,g,w,,,,,,,,]A,]A,q01#!7667#'767356632#"3#326666654&&&5"Dda?LF Csgu~BD@ "?1J}fO56hXk=+! ?NQ D%8`Fk~Fs  )GEX/>YEX/ >Y*и8A88'878G8W8g8w88888888]A8]A8q*G01666632#!7667&&3266676&&&'&&&&#{ b}IpWE9O- D^r|?6?Jon +/!) $>/>d 4'=PhGLmE! $-zP{d=+!DQ?$8Lߘc69ʒD}k'+?"H#^?:"H^?!"H#^Z?:"H^?,"H#^?"H^?!"H#^?E"H^?e!"H#N^?Lb"H`?M"Hc?]B"Ha?Y"Hk?%"H#kr?Y%"H#kEr?5"Hb?"Hf?e"HN?eGY`M+ +(R+AMM&M6MFMVMfMvMMMMMMMM]AM]AMqM9A ]A qA  ) 9 I Y i y ](9(9M/и//AR]A RqA RR)R9RIRYRiRyRRRRRRRR]([#W+4+01&''6654&66677#"&&&546667666632326667666654&&&#">c{<4R8./ !TJ3&GhB:?A+?* )Iyy#/75X1#'.%92%Q_N|3!2? >BHMS-8\B% :0GW 9Vdm53SJD#- ?eLbGYsrM+ +(R+A&6FVfv]A]AqAMM&M6MFMVMfMvMMMMMMMM]AM]AMqM9(9(9M/и//AR]A RqA RR)R9RIRYRiRyRRRRRRRR](ug/q/l_+4+#W+01&''6654&66677#"&&&546667666632326667666654&&&#"#"&&&'667326667>c{<4R8./ !TJ3&GhB:?A+?* )Iyy#/7%QTT(,M@2/9>HGD5X1#'.%92%Q_N|3!2? >BHMS-8\B% :0GW 9Vdm53SJD#- QmBCmP7L//L7?r?>?/@/ܹA]A qA )9IYiy]?!и!/ A  & 6 F V f v ]A ]A qи/(и(/+:-+01%66667326#"&&&54666667&&&&#"&&'666632$<,O!GS>bE;CI&+?* (Ixx#.5YEX/ >YEXJ/J >Y-:++@A01&''6654&666767!76676&'7!#6&&&#!!&&&&##3326667!>c{<4R8./?L>A +;v  $5M8ED &H>8.=%N    #D;W'19ePjǻ +A ]A qA  ) 9 I Y i y ]^/h/EX/>YEX/ >YEXJ/J >YcV+-:++@A01&''6654&666767!76676&'7!#6&&&#!!&&&&##3326667!#"&&&'667326667>c{<4R8./?L>A +;v  $5M8ED &HHGD5X1#'.%92+! $+>>8.=%N    #D;W'19#QmBCmP7L//L7)9CEX/>YEX/ >Y 3+ ,016666733#"'667!!'4666733326667#"  (;)<2-%( ?K=? )$:H)B20+ #E:W+! $+TWM.S>$5 @.+A&6FVfv]A]Aq8.98/EX>/>>Y)++> A ]A qA  ( 8 H X h x ]3901'&&&&#"326667#"&&&546667&&&&54676632>%17@(7bJ,"N] <~U$AY5-FNbHNxnqELpI$3Re3#>.f[@Ha"D($AZ5&L@+G#HqN:V: ">3-L_64Ul8CwdL+7D(l>+-2"IT")@ o"Jd o"Jc ob"Je o"Jh o"Jp o5"Jg o^EX)/) >YV+G+):A::':7:G:W:g:w::::::::]A:]A:q01%26667667667&&&&#"#"&&&5466632326766667#"&&&546667666632667RHSU!  $+2.RE7'(6;"'  [}N*aR7"!#2F/ar  U_b,!KA*$InJ@DC*'#3`CpQ&S/& -J_eb'AhJ(%RHwīMhO$*+%#8c]Y/?nG&S^Oy1"   o"d o"c ob"e o"h o"p o5"gL'"* 1@L'"* 0@L'"*2@L'"*5@L'""*=@L'T"*4@V"K DV"KDVX"KDVX"KDV"KVe"K"+ A@"+F@T"+D@T"+E@e"+)@EX / >YEX/ >YEX/ >Y 01176676&'7!!76676&&&##?K=?@K>?j?J,B-+B.=?+! $++" #++!     #+Fb"F"CB"F"F%"#rF&"Fe5"L5OI+AII&I6IFIVIfIvIIIIIIII]AI]AIqIIи/(I9(/A(]A (qA (()(9(I(Y(i(y((((((((];Q8/+ +#+#@иCиL01#"&5466632#"&546667#'6673666654&&&#"'6666323#3267 ." -"/K>2"(     !*SH6    9)7-$"7.%8*?6I[i5$*MB2 %#&.H[h6A/VK;F/)+A))&)6)F)V)f)v))))))))]A)]A)q)9/A]A qA )9IYiy]"1/+,01%#"&546666654&&&#"'6666323267/K>2"(  !*SH6  9)H*?6oh %#&.qq?9+A99&969F9V9f9v99999999]A9]A9q99/A]A qA )9IYiy]+A(/+ +0и 3и<01%#"&546667#'6673666654&&&#"'6666323#3267/K>2"(     !*SH6    9)H*?6I[i5$*MB2 %#&.H[h6A/VK;F 5"LM^//901%#"764&&'76666733266679O'P 8/GHG m   &.gx3=$'yCJ$ 1&",@",@a",@C",@",#@ #\T",@\",@e\",\?EX/>YEX/ >Y+ и01#!7667#'76736&'7!3 G>Af?KE =>??L?f #++!  $++">",-= w wFλ4+A4]A 4qA 44)494I4Y4i4y44444444]D/EX/ >Y%A%%'%7%G%W%g%w%%%%%%%%]A%]A%q01#"&&&546663232676666766666654&&&#"'666632w   ;FO)@>6>4"$% (<,Un    !*TH6 f 7JUQE#Yhu>umI  *'μJrY5=A:-  %#V"N DV"ND3"NwVe"NuVV3//$3+C?+$.)$.901%#"&&&&&''6666667666676632#"&&&#"7666&'66323267yBc&$DB=<8  )*( # "9T=5x?+K<.0.( $,bx$&EX/C;m?0+SvKu6430,5&D83Djx`I{g%'5M~n.'6!)&##*#ko4J-'"6IV.G [cbN1". (@".,@".e".@//EX/>YEX$/$>Y $9 $96A6]A 6qA66(686H6X6h6x66666666]01%#"&&&'!76676&'7!6632'676&#"675&IA7 H??f?L>A?LEGG*H2'48 +)&$P+] -A/o/ J #++! $++"ST6U<07T:=?A2f"O R"O$e"O$eb"O#(i? @/A/@и/A&6FVfv]A]AqA8ܸ и /8%A%]A %qA %%)%9%I%Y%i%y%%%%%%%%]0и0/5//>+и>01#3267#"&546667#'6673666654&&&#"'6666323J =- 1K=4,!   #*UI8 ! c{!"%+ <;n$Ui  '"$.$asM%N/O/Nи/A&6FVfv]A]AqOAܸ и /A.A.]A .qA ..).9.I.Y.i.y........]9и9/>//)#+L+иLи)Fи#I01#3267#"&546667#'6673667#'6673666654&&&#"'6666323#3@  =- 1K=4,      #*UI8 !  Uf"%+ <;ma$6$FmS  '"$. hSDn S T/U/Tи/ A  & 6 F V f v ]A ]A qUHܸи/H5A5]A 5qA 55)595I5Y5i5y55555555]@и@/E//P+P$и$/-01##"'3267#"&546667&&#"'666632666654&&&#"'66663232675>G'  =- 1K=4,! #H*+5?H&   #*UI8 !  #M&a(P?'Vg"%+ <;m ?8(O?'O|`  '"$.&f ?8+"EF/G/-ܸи/Fи/?A??&?6?F?V?f?v????????]A?]A?q ?9-A]A qA )9IYiy]%и%/2-9*// *92*901%#"&546667'666677666654&&&#"'666632732671K=4,    #*UI8 !  =-H+ <;nab d`y!  '"$.#v\cieæ!"~"/ @P"/eP"/e""/#@P*IEX/>YEX/ >Y +и и$01%!7667#'66736&'7!3#3326667P) ?L<J>A?LH!4 %A2*;0,W+!" $++"C #D;P6cEX./.>YEX/ >Y+5+ и!и#и5(01#3#3326667!7667#'66737#'66736&'7!3V!& %A2*;0,') ?L.<>A?L;}nC #D;W+!M"n" $++"TP=KEX/>YEX/ >Y%.+% и /701%!7667&#"'66663326&'7!3267##"'3326667P) ?LA#H*+5?H&=>A?LH #M&-5>G' - %A2*;0,W+!?8(O?' $++" ?8(P?' #D;P0?EX)/)>YEX/ >Y )9/)9013326667!7667'6666776&'7!% = %A2*;0,') ?L9x kI>A?L=7 B #D;W+!? 8 $++"E)"P)5"P)e"P"0 @T"0@e"0/"Q/"Q[/"Q/5"Q/"Q/e"Qs n̻a +R=+A ]A qA  ) 9 I Y i y ]h a9h/A]A qA )9IYiy]ARR&R6RFRVRfRvRRRRRRRR]AR]ARqRWиW/ap/6/EX"/" >YMB+Bи/"1A11'171G1W1g1w11111111]A1]A1qM\01%#"&546666654&#"#"&&&546663232666766666654&&&#"'6666326666323267[&#'  , ASX%$3 &3""$ (;,,B2&    #*UJ7 6njc-1   <*H6;?6]|~dL< @`@>K~l`. *'5^N-n '" ,$ &+) Db?)C0#hyt_") qhai/j/ܹ*A*]A *qA **)*9*I*Y*i*y********]iEиE/ZAZZ&Z6ZFZVZfZvZZZZZZZZ]AZ]AZq_и_/EX/ >YUJ+ A  ' 7 G W g w ]A ]A qJ-и-/Ud01#"&&&5466632326667666654&#"'66666654&&&#"'666632666632q 4AO-@>6KE0#% #2@%3K9+' , ARY%#  ')& !   #*UJ7 6njc-1 WsCblS& *' & *[d}辇L< @`@0aY;B` '" ,$ &+) Db?)C"1 B@"1 @"1F@T"1E@"1e"1?/EX0/0>YEX>/>>Y&+01&&&&'#"&&&766667326667&&'7326&&&'7!BC" g "(.?=63'&' #994=+{ 0#l #^ YhMYEXD/D>YEX,/, >Y+',69=,6901#"&&&766667326667&'!7667&&'7326&&&'7!BC{ #+?=63'&' #**( {8DDC>+z 0#l #tYhMlS| & ++ &+ \+ j߻aR+ + $A]A qA )9IYiy]RPиP/Aaa&a6aFaVafavaaaaaaaa]Aa]AaqfRa9 l/EX/>YEX^/^>YJ;+'A']A 'qA''('8'H'X'h'x'''''''']^UAU]A UqAUU(U8UHUXUhUxUUUUUUUU]f90123267#"&&&546666654&#"#"&&&766667326667654&#"'6666326666"E8$  <*[& =4UafWA - &),-?=63'&' #20,}8& 4]J6 ?| 9`HA~/$*%6;'>.+9bN!@_~\ajK6("   !Wx+$ 7 :0>JN!fX' \.&K+&*и*/KHиH/&^EX/>YEX / >YEX/ >YB3+ A ]A qA  ( 8 H X h x ]9 PAP]A PqAPP(P8PHPXPhPxPPPPPPPP]011766766&&#"'666632666632#"&&&5466633266676654&&&#"?L 9& 4]J6 ?-)TC+GQ519C):- -1'#0 ?*9"Vfl^E A1$+!'4! 7 :0>JN!`Z,9`H  s2%(+#$%"6Q\&&V1C*"Ba~Y&+DR V&#+и/A#]A #qA ##)#9#I#Y#i#y########]# и /EXE/E>YEXR/R>YEX4/4 >Y + R(A(]A (qA((((8(H(X(h(x((((((((]M4E901#"&&&546663232676654&&&#"!76676&&'666632666632Re9N^3BIK!,N;",0 &9)"\+UafWA A??f?L $F9 4]J6 ?|0"E8$(4hm("%(%%#+#&B1C*!@_~\ #++!:? 7 :0>JN!fX'9`(D SA+!и!/AA]A AqA AA)A9AIAYAiAyAAAAAAAA]A?и?/UEX/>YEX/>Y4*+*;FAF]A FqAFF(F8FHFXFhFxFFFFFFFF]0176676&&'6632666632#"&&&54666323267654&&&#"(ER4!G> e#C}v6&J9#65GS*;^EqR-*-*5C)3 $3U^^O9 HF+!9B7%-<-8h7`|H9\A#vb`$13'6802(+2+fB0/@&:RfxB0 "+5"Rb5"R#Y5:"RY5+"R#YU5+:"RY5,"R#Y5"RY5+"R#Y5E"RY5e+"R#XY5Gb"R[5H"R^5"R#\a5d"R#\a5c"R#\a5T"Rf5%"R#fr5T%"R#f@r5u"R#\r5+5"R]5d"R#]a5+"Ra5e+"RX5+/Y0/1/ܹ0#и#/ и/и/+++ +01&&#"3266677#"&&&5466676632of:U<$ !;Q.:W<$:U8:<>FmK'6ZB3z>NnE 6Xo8ZJ^5@e|;Cu.&;gRH|r5(88dm"\S ;7/$/EX/>Y.+7$9A]A qA(8HXhx]7$901&&#"6&'32666%6666327667#"&''7&&(-qCW]6)i-rCW]VunU9q@=4uoU9o $# =3ujJ3/4WqjJ03Wrh=7!Zh=6 ![5^>D*++8+A]A qA )9IYiy]A&6FVfv]A]AqA8]A 8qA 88)898I8Y8i8y88888888];*9@FmK'6ZB3z>Us"32S~@CwY4)EY``?,OF=VrCu.&;gRH|r5(8B:J3*P+5"Z5" 5B"\5"a5e^"XZk"2%@ZHK"2# @ XZ("2@Z=K"2# @  Z=1"2@ZQ"2# @ZZZ"2@Z=x"2# @_Zmg"2@Ze="2# @Z="2@Z="2!@Z="2#@ IZ>b"2#@UZ="2#@KZ=""2)@Z="2#)@ IZ="2#)@ Z>b"2#@UZ=T"2 @Z>b"2# @UZ="2$@Ze= "2Z= ;kY(+ +7A]A qA(8HXhx]016654&&&#"326666677#"&&&546667666632$FfBZc> 'He>@iUB1 /E-*_kwC`_/+>(*ao~Gb]-  ToAQT[(]sA+JdszY*+9A]A qA(8HXhx]B9C9014&&&#"3266666#"&&&5466676666326654&'7$FfBLy^B+'He>M{]B*9'D]5-,/E-*_kwC`_/+>(*ao~GR~.?=ToA;b?]sA>g-QG>FhG96]E(Ri?78cJ+:3N9*P+Z"0 @Z"0 @Za"0@Z"0$@Ze"0d JEX/>YEX/ >YEX-/- >Y !и"и;A;]A ;qA;;(;8;H;X;h;x;;;;;;;;]013'66667333&&&&766666632326667!766666676&&&#"0  )&Uh5 1Ke{Qcd' Alb'r?cL7' BsR>gUA/0F2>DD UUUQA{jO-JzYX)B0GHDoLxfYY_9[L.K`d`%Y 1+$+A[96A[901"326667666654&&&666632#"&&&''666654&&&#"'666632bOXV!7FO'0'  %.'@m]K'D48??KW/ <84 # (+' (-.,' ! *XL: %/WyJMC.%0'USLFgC!n~`p:(OsKds(QB*$YEX-/- >Y7#A##]A##(#8#H#X#h#x###### ]и/-A'7GWgw ]A]-7901632#"&''3266654&&&#"!76676&'7!\/?&@KYb4BsY*G $D DoO+.Qn@,p ":-P?K>?  /VzKWtDJ 0SqAEkI& ++! $+5 UcG+A&6FVfv]A]AqEX6/6 >Y_#+B+Q+__9TQ901667&&&&#"32666#"&&&#"'66667#"&&&546667666632766667666632z &+2.TI<+0;FMN0-' &!5+" "(*& )-) #!% #QVV(!NC-)NoG@CC 3 $0;$347,D4(RK -J_eb'AhJ(=b|##*#"IrP@ 8x3;mT3&S^Ox." NpT="!)&5 b[P+#C+A&6FVfv]A]Aq#-AC]A CqA CC)C9CICYCiCyCCCCCCCC]FC#9#dEX>/> >YZ +K+>(A(('(7(G(W(g(w((((((((]A(]A(q01%26667&&&&#"3266654&'66667#"&&&5467#"&&&546667666632667\FLN"6&,1.TI<+0;@ !  %,-.26CK&'/ #QUV(!NC-)NoG@CC +)%2`B -J_eb'AhJ(%*"˿1*I6$#0X2*%1Nc2l;mT2&S^Ox."    *KEX7/7 >Y+G+7%A%%'%7%G%W%g%w%%%%%%%%]A%]A%qDG90126667&#"'66663232666'&6667#"&&&767667"&&&c-$ T,& "=]r:5F% 6823#1 %55 5IPR&ui2.">52DZ  `ڻ K+"<+A  & 6 F V f v ]A ]A q"(A<]A <qA <<)<9<I<Y<i<y<<<<<<<<]A<"9"bEXU/U>YEX]/]>YEX9/9 >YF+UA]A qA(8HXhx]9%A%%'%7%G%W%g%w%%%%%%%%]A%]A%qA9U901&&#"3266676666732654&'66667#"&546667#"&&&546667666632667*sG?tfS@ugmӿ@/$"0&b)%"U-)"U1)5"U0X"U)e"U)e'"U#95"5 @5"5@5T"5@5 "5e5 "5e5""5#@"V"V#Lr"V"Vu"V#E@5"Ve"Ve5"V# aBEX/ >Y> +>>9,A,,',7,G,W,g,w,,,,,,,,]A,]A,q01#"&&&#"#"&&&5466632326667667666676632a0-' &!D>4 Y}E#I<&"!",*K;*  .=F$5x>,D4({##*#$LtPMM{̒Q$* *'$8h\uCq[G'6!)&/"6 @/"6# @/l"6 @/"6@/"6#@/lT"6@/el "6r/elT"6#r@;"W ;"W "W;e"Wq^"7@q^T"7@q^"7qe^"7'"X'b"X'"X'B"X'"X#a'"X'"X#r'%"X#r'%"X#r'"X#r'u"X#r'"X'"X'e"X m +g +A]A qA )9IYiy]g9A  & 6 F V f v ]A ]A q, 9,/?Hg9H/AH]A HqA HH)H9HIHYHiHyHHHHHHHH][o\=#rB$'KB5 %#&.L[d2H0 %#&.LZe2A3V[W '^pD#+ +j+A]A qA )9IYiy]Q 9Q/AQ]A QqA QQ)Q9QIQYQiQyQQQQQQQQ]dи/ADD&D6DFDVDfDvDDDDDDDD]AD]ADq*#D9*/=dgиg/Aj]A jqA jj)j9jIjYjijyjjjjjjjj]m#9n/:/a/G+G и /кm:n9013267#"&&&7#"&&&546666654&&&#"'666632326667666654&&&#"'6666326654&'73WtA 5$Qq&% )[bh63-    " *UH7   )%BTc5  *RE4 IG4[OB:q$[W $67#P^J|Z2>\=\jrhU %#&.^pxp^`V'We+WQI %#&. S<*P+'"u'"uX'B"u'"u'e^"u1m78/9/ ܸ8и/&A&&&&6&F&V&f&v&&&&&&&&]A&]A&qи/ 2 A2]A 2qA 22)292I2Y2i2y22222222]!/6/++01#"&&&54676&&&'&'7!326666654&'7!f;*!638Obr>OuN'kl &51o~!;O.1M8& C<4`>Q9{uiP.5]{G^)ZU9fM.)DX^^'l+Z"8^@"8W@"8Z@a"8X@"8#X@ ""8b@"8#b@"8#X@ "8#X@ 4"8#X@b"8#X@"8W@"8]@e"8)@>ڻ #+A  & 6 F V f v ]A ]A q и/#%и%/#1и1/EX1/1>YEX=/=>Y+8+8ии&и8+01%266677!3##"&&&5477#'66736&'7!!6&'7!`AnT7 "Cd?F?~!~VcRb6 @>A?M>M=8AwV@jKh:FrR-k">DfˊG2`\5># $++"> $+8 +и/и/A  & 6 F V f v ]A ]A q и/6/EX/>YEX///>Y% +56901#"&&&5476&'7!3266676&'7!654&'70Rm>TVcRb6 b>A?MX"CdAAnT7 \8AU2YLAˊG2`\5> $++"{:FrR-@jK $+#0*P+" V@" @a"X@"]@e")}B"Yqe}"Ym{?1 +A11&161F1V1f1v11111111]A1]A1q / /4=+4и/= 0174667667767327#"&&&'&&&&&&53267#"&,f{     *D1J8)  JpK&(*D?5#'+ ~#  8i$!* 1#o] x֩w #"$ \ (++ O+A]A qA )9IYiy](9A  & 6 F V f v ]A ]A qJ9AO]A OqA OO)O9OIOYOiOyOOOOOOOO]^B/X/EX#/# >Y#B9A'7GWgw]A]AqJ#B9014&'32666#"&&&54666667&&&&'&&#"'666632666654'&'6632#1"   +" .IhF 8Sa)/=$"6L4@DC  *L>. 6r./S=#5+Jl# 7~E;V>+ $'An1E]v[`>w]80;#1B]|RS;1!%#0"Rb2 %*$a"9@@e"9k @EX / >YEX/ >YEX/ >Y 901'66766667!766'!?J (.+ 5;rH<H@H+ D  b ++D +"ZR"Z"ZQ5"ZT5"ZU"ZSe"ZP": @": @": @T":@T":@e":}5"[}5"[T";,@T";-@K "\K }"\HK }"\K B"\K "\K }5"\K }"\K }"\K }"\bK S>/EXO/O>YEX/ >YO A ]A qA  ( 8 H X h x ]O 9$A$$'$7$G$W$g$w$$$$$$$$]A$]A$qHO901#"&&&#"#"&&&546667326667&&&&&&'&&#"'66663266666632 05.&:omkmqwT6)!($ -SPM& "%$"  ! *L=.  ***4ls~Z B:- "!!\ѝ\  -.' 1WzHYEX1/1>YEX/ >Y,!901!!7667&&&&'&&#&&766663326&'7!BWG;7AF!)%C  +/! BXc)*<-!!@6* 5-Dh?Gz; !7)+'F4 BH$8L7DdB!  22 ++ U T(DCEX/>YEX/ >Y97й!01!7667&&&&'&&&𒭘666632'6676&&&#"UG:=A@0%6l%" ;:5>HU6*D- .65 "  ; 6)+'Q0  + 0H.P<"$YEX / >YEX/ >Yи01!'7!2667&&&&#!!26667.0&4 ZP'##Y(b`T+PF+ JP$1TB E5/EX/ >Y9++A+A'7GWgw]A]AqA'7GWgw]A]Aq?A901#"&&&54666323266676&&&&&'66667!"'6673!63j[5ZB% # !2J79cO:=gF.32 J{i\,!%%-#=ub!=+HtKty>'' '+"'/'#LwTM_5Zv4 3*Z+A|*6h &t"D?BMEX4/4>YEX8/8>Y+>+8,;>901#"&&&7666673266676&&&#"'&&''527!"'3!6632]VNY/+06DM&9dN5 8Z=&G*3 %'&1D #2:hH!ju?-<="% %@/DlLY$+01376766666676&&&#"'467666632S2 9;?&y56EX/ >Y%+0137667766666676&&&#"'4676666325NO/=D<,+>$ 5'A'Jl@7U7.23:'-G1+8=hJ*%AX25K906C0* 4EX#/#>YEX/ >Y#A]A qA(8HXhx]013766766666676&#"'547666632PK( ;OYM7vy.R?) J) VYOvI ;NVM80FH+$HmXIIO2|'>N'%F^8+OqFJhN@DS; +Y`K //01!76666766'&&'66667Z2A%I$9*PSKc8/`*  )  -`\F)t+A]A qA )9IYiy] +%+01326767!'666654&#"'666676\'c ($: e*83)63Nc5'B0&Wzr6m~^&,- * C8%"4%M8E@| -+A-]A -qA --)-9-I-Y-i-y--------] B+<0+01#"&'73266654&&&##"'666654&#"'6666328)9"3$+PrF2h5-)'.G1$/  (H4.1*9=0H[0.C+!8.# ,78eK,*033E'"1. (8(&52) 90 '2B'NV/&/!/EX/ >YEX"/" >YEX(/( >Y?2+(G0176666564'&&'66667'667!'666676&#"'666632326767(4 2-"?A;E-&'Q. gK#4"%. ,BQ*BL $LyY }" n!  " I#cZ<-4!7-BF BWwWB=Gj5/F/A/EX/ >YEX>/> >YEXB/B >Yиии013##7666657#'667376666564'&&'66667'667qٵ   !)0/06(4 2-"?A;E-&'Q.j e " n!  " B &ehn/EX/ >YEX/ >YEX/ >YWK+3,+%и%gиh017'667##7666657#'6673#"&'732676&&&##"'666676&&&#"'6666323'Q.^   !)0/06C'>S1-X)*B&;P)  .: &-/ 'Y+ A]A(8HXhx ]01#"&5466632#"&5466632t&6")+(6 %-d'6"(,)6 %-b@4"5-?4"3 A5"6-?4"4-/01'676&&&#'6666#6F+T.# 4=9-2!IE>JV/#(  [g,/01'6676&&&#'666666$ .H`<*G' 3//#H*+5?H&!70*#M&X  \(P?'")"?8(O?'")"?8q sVE 1 2/3/2и/A&6FVfv]A]Aqи/3 ܺ 9и/ A]A qA )9IYiy]/////901&&'3%&&5466654&#"#'466632@  \M-5-" $-5-!3;43/  sV".# $(   +,b //+01#"&&&'667326667%QTT(,M@2/9>HGD7QmBCmP7L//L7g $/+01&&'#"&&&'6673266678 3%QTT(,M@2/9>HGDR QmBCmP7L//L7g $/ /+01766663#"&&&'667326667 "%# E%QTT(,M@2/9>HGD F$QmBCmP7L//L7=5-//.!+ и/01#"&&&#"'6666323267#"&&&'667326667=5>G' 3//#H*+5?H&!70*#M&$%QTT(,M@2/9>HGDx(P?'")"?8(O?'")"?8QmBCmP7L//L7^*DG#+#9/#9=0+(+01&&546666654&#"'&66632#"&&&'6673266674?4' %,%  )HGD)9.)  &'% 6):QmBCmP7L//L7 ///01#667%\ _ {FB%/ /// и /01#"&&&#"'66663232675>G' 3//#H*+5?H&!70*#M&.(P?'")"?8(O?'")"?8<i////01%'76677'&&'A1+2.:"  ""A/f#. +01!'66667!  #   B EX / >Y01!'66667!B  #    EX / >Y01!'66667!  #    EX / >Y01!'66667!N S    7+ +01!'66667! y    rT +01!'66667!T y     (EX/>Y +01!'66667!!'66667! _  _      Mr  + +01!'667!!'667!M 0  0 Y2020R5 /!/ и/A&6FVfv]A]Aq!ܹA]A qA )9IYiy] +и 01#"&5466632#"&5466632a / $!/#u!/# .%7-$"7.%"7-$"7.%eLHj+A&6FVfv]A]Aq +01#"&5466632L ." -"7-$"7.%\R5j+A&6FVfv]A]Aq +01#"&5466632 ." -"7-$"7.%I^r +A ]A qA  ) 9 I Y i y ]/ /  90176654&'78_~E[W6`QCDWC*P+f!"/#/ܹA]A qA )9IYiy]"и/A&6FVfv]A]Aq ++014&#"326667#"&&&5466632*!)T#=Q., #=R/5@#7)2%7)3=)YK1",-\K0IY*A#+#9/#9 /(+01&&546666654&#"'&666324?4' %,%!  *>G996*=1+  &")% 3%::ed +A ]A qA  ) 9 I Y i y ]/01&''6654&66667>c{<4R8./ D 3@5X1#'.%92/)193Bn +A&6FVfv]A]Aq/+01#"&&&5466673267799+!8Xl42)TD*&C*4) ,!:h[KGOV+$&-T//EX/ >Y01!#!5T'''//EX/ >Y01!!#''''l //01&&'9   ; l / /01%766667|o $" 1 /\ // /01'&&'3@  \/-/ //01&&''&&'3<0 P  \ -{ -!/ //01'7667'&&'3 (6  \*( >->&+///!/ и/01#"&&&#"'6666323267'&&'3>5>G' 3//#H*+5?H&!70*#M&Q  \{(P?'")"?8(O?'")"?8-Q'&12/3/ܸ2 и /A&6FVfv]A]Aq 9 и /A]A qA )9IYiy]'и'/$/*/,/*$901&&5466654&#"#'466632'&&'3Q-5-" $-5-!3;43  \".# $(   +,4-"/+01&&'%#"&&&'6673266670 v%QTT(,M@2/9>HGD - QmBCmP7L//L7#"/+01%7667#"&&&'667326667 (%QTT(,M@2/9>HGD*( QmBCmP7L//L7>5-//.!+ и/01#"&&&#"'6666323267#"&&&'667326667>5>G' 3//#H*+5?H&!70*#M&%%QTT(,M@2/9>HGD{(P?'")"?8(O?'")"?8QmBCmP7L//L7^*DG#+#9/#9=0+(+01&&546666654&#"'&66632#"&&&'6673266674?4' %,%  )HGD)9.)  &'% 6):QmBCmP7L//L7t ///01#667%\ - v%!/// /EX/>Y A ]A qA  ( 8 H X h x ]и/01#"&&&#"'6666323267 5>G' 3//#H*+5?H&!70*#M& (P?'")"?8(O?'")"?8d +01!'66667!  #   1T /!/ и/A&6FVfv]A]Aq!ܹA]A qA )9IYiy]EX / >YEX/>Y A]A qA(8HXhx]01#"&5466632#"&5466632\ / $!/#u!/# .%7-$"7.%"7-$"7.%W1ʻ+A&6FVfv]A]AqEX / >YA]A qA(8HXhx]01#"&5466632 ." -"7-$"7.%  3/EX/ >YEX/ >Y+013'!#3dY::LR͎B+_<ee+x { xd3777#h7\f\K+337/7?777 77D7u7!7D3H2H2H-97{L^{1L=BN\ZoZ5/+q#yfJ3J^}^5j55%?/VFVf)/`55)u;' Kf+Z{{L\Z5555555%?%?%?%?FFFF/`5`5`5`5`5''''ot7h7aBOjP^^@H2\BHH7nJ;;-5`-73*)/sH2X2r:r<3{{\ZZ^57++BBFd.KyfV7i*:*<'oHi B {h{{====\Z\Z*K\ZF-f{!pPDy-555555555555555555555555555555555555555555555555555555-5-5{{{{{{{{{{{{{{{{{{{{{{{{{{{jbj{s55555;) LLLLL7F%5*5555u5^^^^^^^s%?%?%?%?%?%?%?%?%?%?%?%?%?%?%?%?%?%?%?%?%?%?!)5/{1L1L1L1L1L1LVVVVVVFFCFFFFFF^=========VV3VVNNNNyfR$$i(i  +)))//////s)zL(`5`5`5`5`5`5`5`5`5`5`5`5`5`5`5`5`5`5`5`5`5`5`5`5`555555\Z\Z\Z\Z\Z\Z\Z\Z\Z\Z\Z\Z\Z\Z\Z\Z\Z\Z\Z\Z\Z\Z\Z\Z\ZZZZZZ55zZ)))X))555555////////u;u;uu;+q+q+q+q''''''''''''''T'T'T'T'T'T'w1@UUUUUU       ######KKKKKKKKKKyfyfyfyfyfyfyfyf7g00??S(w5@Y@-@%VVV~2~(,i'++F+h8_{<!A!7u\XI:3T-T!vW) ::::" >p( F J  V V (  B`V0z br\vv@ H !!L!j!"~#$%2&&'f()*+z,@-//012V3835 567488899999;;";.;:;F;R;^;j;v;;<<<<<<<<====*=6=B=N=Z=f=r=~=>?`?@@AVBlCDEcJcVcbcnczcccfffffffg gJgghzhiiviiiiiiiij jj$j4jDjPj`jlj|jjjjjjjjjkkkl ll$l0l@lPl`lplllllllllmm mm(m8mDmTm`mlmxmmmmmmmmmnnn n,n&dp|Ȑ ,4*6ԑʒ֒֔ڕږΖږn~ ,8DP\htR^jvDdzĢԢ $4DT`pĤ>J*6BR^nz¦Φڦ &6FRbnzĩЩܩƪҫ 8ṯر (4@P\hx&2BNZjv³γڳ "2>N^n~ʷַƸҸ޸&6FVbnzL*6BNZjvֽ*6BNZfr~ƾҾ޾ ,8DP\h~pT`bĸŴnǸfB`~ɜɺX"N˂:Xb̸̂4dͼΐ\TvжDnјѼ(\VӨԦ j ,Jl֜$<؂zٜ 0ې sY2+ +8,>j ~, "   f0 8$ 8? 8D 8H 8[ 8` 8d 8| 8  8  8  8  8  9 9  9   9, 95  9M  9V 9` 9~ 9 9 9 9 9 9 9 9 V   4 X@ (  & X "X Dz . 8 "$ 47 69  : :  &:  :9 :C 0:K 0:{ *:  2:  &;  :;-  ;g  ;  ; ; ; 0; < ?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      uni00A0uni25CCuni1EADuni1EA5 uni1EA5.VNuni1EA7 uni1EA7.VNuni1EAB uni1EAB.VNuni1EA9 uni1EA9.VNabreveuni1EB7uni1EAF uni1EAF.VNuni1EB1 uni1EB1.VNuni1EB5 uni1EB5.VNuni1EB3 uni1EB3.VNuni01CEamacronuni01DFuni0227uni01E1 aringacuteuni1EA3uni1EA1uni0251 a.SngStoryaacute.SngStoryagrave.SngStoryacircumflex.SngStoryuni1EAD.SngStoryuni1EA5.SngStoryuni1EA7.SngStoryuni1EAB.SngStoryuni1EA9.SngStoryabreve.SngStoryuni1EB7.SngStoryuni1EAF.SngStoryuni1EB1.SngStoryuni1EB5.SngStoryuni1EB3.SngStoryuni01CE.SngStoryatilde.SngStoryamacron.SngStoryadieresis.SngStoryuni01DF.SngStoryuni0227.SngStoryuni01E1.SngStoryaring.SngStoryaringacute.SngStoryuni1EA3.SngStoryuni1EA1.SngStoryaeacuteuni01E3uni1EA4 uni1EA4.VNuni1EA6 uni1EA6.VNuni1EAA uni1EAA.VNuni1EA8 uni1EA8.VNuni1EACAbreveuni1EAE uni1EAE.VNuni1EB0 uni1EB0.VNuni1EB4 uni1EB4.VNuni1EB2 uni1EB2.VNuni1EB6uni01CDAmacronuni01DEuni0226uni01E0 Aringacuteuni1EA2uni1EA0AEacuteuni01E2uni1E03uni1E07uni1E05uni0253uni1E02uni1E06uni1E04uni0181 ccircumflex cdotaccentuni1E09uni0254uni0254.TopSerif Ccircumflex Cdotaccentuni1E08Eurouni0186uni0186.TopSerifuni1E0Buni1E0Funi1E0Duni0257uni0256Dcaronuni1E0Auni1E0Euni1E0CDcroatuni0189uni018Auni1EBF uni1EBF.VNuni1EC1 uni1EC1.VNuni1EC5 uni1EC5.VNuni1EC3 uni1EC3.VNuni1EC7ebreveecaronuni1EBDemacronuni1E17uni1E15 edotaccentuni1EBBuni1EB9uni0229uni1E1Duni01DDuni0259uni025Buni1EBE uni1EBE.VNuni1EC0 uni1EC0.VNuni1EC4 uni1EC4.VNuni1EC2 uni1EC2.VNuni1EC6EbreveEcaronuni1EBCEmacronuni1E16uni1E14 Edotaccentuni1EBAuni1EB8uni0228uni1E1Cuni018Euni0190uni1E1Funi1E1Euni01F5 gcircumflexgcaronuni1E21 gdotaccent g.SngBowluni01F5.SngBowlgcircumflex.SngBowlgbreve.SngBowlgcaron.SngBowluni1E21.SngBowlgdotaccent.SngBowluni01F4 GcircumflexGcaronuni1E20 Gdotaccent hcircumflexuni021Funi1E27uni1E23uni1E96uni1E25 Hcircumflexuni021Euni1E26uni1E22uni1E24Piibreveuni01D0itildeimacronuni1E2Funi1EC9uni1ECBuni0268 i.Dotlessuni0268.Dotlessijuni0269Ibreveuni01CFItildeImacronuni1E2Euni1EC8uni1ECAuni0197IJ j.Dotlessuni0237uni1E31uni01E9uni1E35uni1E33uni0199uni1E30uni01E8uni1E34uni1E32uni0198lacuteuni1E3Buni1E37uni1E39uni019Auni2C61uni026BLacuteuni1E3Auni1E36uni1E38uni023Duni2C60uni2C62uni1E3Funi1E41uni1E43uni1E3Euni1E40uni1E42nacuteuni01F9ncaronuni1E45uni1E49uni1E47uni0272engNacuteuni01F8Ncaronuni1E44uni1E48uni1E46uni019D Eng.UCStyleuni019D.LCStyleEng.BaselineHookEngEng.Kom ohungarumlautuni1ED1 uni1ED1.VNuni1ED3 uni1ED3.VNuni1ED7 uni1ED7.VNuni1ED5 uni1ED5.VNuni1ED9obreveuni01D2uni1E4Duni022Duni1E4Fomacronuni1E53uni1E51uni022Buni022Funi0231uni1ECFuni1ECDuni0275 oslashacuteemptysetohornuni1EDBuni1EDDuni1EE1uni1EDFuni1EE3 Ohungarumlautuni1ED0 uni1ED0.VNuni1ED2 uni1ED2.VNuni1ED6 uni1ED6.VNuni1ED4 uni1ED4.VNuni1ED8Obreveuni01D1uni1E4Cuni022Cuni1E4EOmacronuni1E52uni1E50uni022Auni022Euni0230uni1ECEuni1ECCuni019F OslashacuteOhornuni1EDAuni1EDCuni1EE0uni1EDEuni1EE2uni03A9uni1E55uni1E57uni1E54uni1E56uni02A0uni024Buni01AAuni024Aracutercaronuni1E59uni1E5Funi1E5Buni1E5DRacuteRcaronuni1E58uni1E5Euni1E5Auni1E5Csacuteuni1E65 scircumflexuni1E67uni1E61uni1E63uni1E69uni0283Sacuteuni1E64 Scircumflexuni1E66uni1E60uni1E62uni1E68uni1E97uni1E6Buni1E6Funi1E6DTcaronuni1E6Auni1E6Euni1E6C uhungarumlautubreveuni01D4utildeuni1E79umacronuni1E7Buni01D8uni01DCuni01DAuni01D6uringuni1EE7uni1EE5uni0289uhornuni1EE9uni1EEBuni1EEFuni1EEDuni1EF1uni028A UhungarumlautUbreveuni01D3Utildeuni1E78Umacronuni1E7Auni01D7uni01DBuni01D9uni01D5Uringuni1EE6uni1EE4uni0244Uhornuni1EE8uni1EEAuni1EEEuni1EECuni1EF0uni1E7Duni1E7Funi028Cuni0263uni1E7Cuni1E7Euni0245wacutewgrave wcircumflex wdieresisuni1E87uni1E98uni1E89WacuteWgrave Wcircumflex Wdieresisuni1E86uni1E88uni1E8Duni1E8Buni1E8Cuni1E8Aygrave ycircumflexuni1EF9uni0233uni1E8Funi1E99uni1EF7uni1EF5uni01B4Ygrave Ycircumflexuni1EF8uni0232uni1E8Euni1EF6uni1EF4uni01B3uni01B3.RtHookzacuteuni1E91 zdotaccentuni1E95uni1E93Zacuteuni1E90 Zdotaccentuni1E94uni1E92uni01A9uni0292uni01EFuni01B7uni01EEuni0294uni02C0uni0242uni0241uniA78B uniA78B.LrguniA78C uniA78C.LrguniA789 uniA789.Wideuni02BC uni02BC.Lrguni2219uni2011uni00ADuni02D7uni02CA acutecombuni030Buni02CB gravecombuni0302uni0302_acutecomb.VNuni0302_gravecomb.VNuni0302_tildecomb.VNuni0302_hookabovecomb.VNuni0306uni0306_acutecomb.VNuni0306_gravecomb.VNuni0306_tildecomb.VNuni0306_hookabovecomb.VNuni030C tildecombuni02CDuni0331uni02C9uni0304 uni0304.Shortuni035Funi035Euni033FuniA78Auni0308 dotbelowcombuni0307uni031Buni030A hookabovecombuni0327uni0328uniF130uniF131 acutecomb.LP gravecomb.LP uni0302.LPuni0302_acutecomb.VNLPuni0302_gravecomb.VNLPuni0302_tildecomb.VNLPuni0302_hookabovecomb.VNLPuni0306_acutecomb.VNLPuni0306_gravecomb.VNLPuni0306_tildecomb.VNLPuni0306_hookabovecomb.VNLP uni030C.LP tildecomb.LP uni0304.LP uni0308.LP uni0307.LP .notAccess+++UC4'+OC4'+KC4'+r\G5+ SC4'+b\G'+|\G5+m\G5+VC4'+ + E}iD  lf(m @ ( @ ( @ ( @ ( @ ( @ ( @ ( @ ( @ ( @ (p  N(  `(  (  (  mO( u 7(  (  (  (  (  (  f-(  s(  (  0f(  P(  (  j( | >(  (D 3 f(0  (  (  (  (  (phZ  #f&  p,a | ff v fbf X f> 6 )h6> ( fz  ,  fe l G  fi,  fh  -  Hv b \f W f2 1 f   "    f  fE w  Z   & l 'f5| [ p  Nhp  N  (  m{h   h  0fhD 3 f!h  #fYZ  #fXZ  #fo  #fZZ  #fWI  #f\  fff X f1Zf X f0Zf X fGf X f2Z  fZ  fZ  f  fZ  yIv b \f,Zv b \f+Zv b \fBv b \f-Zv b \f*I  fxZ  fwZ  f  fyZ ~ lh2ZlI  (  (  c(  H> W fh d f g h  lf(p  Nhp  Nh  0fh  i( l f l 'fiZ  hp  N  mp  Nh  m{h  mzh  h    h  h  0fh  0f  GV  0fhD 3 f!hD 3 f0D 3 f h  fe(   T Z K #fo  #fD  #f  #fD  #f  #f3  #f  #f  #f  #fR( K #fR(  #f  #f  #f  #f  #f  #f  #f~h  #f  #fR(  #fZZ  #f  #fWI  #f  #f  #fR( K #f&  #f&  #f&  #fYZ  #fXZ  #fo K #fo  #fD  #fD  #f3  #f  #fR( K #fR(  #f  #f  #f  #f~h  #fR(  #fWI  #fZZ  #fZZ  #f  #fWI  #f  #f\  #f  #fR( K #f&> W f!Z> W f"Zp  Np  Nhp  Np  Nhp  Np  Nhp  Np  Nhp  Np  Nhp  Np  Nhp  Np  Nhp  N4*p  Nhp  Np  N"p  Nhp  Nhp  Nhp  Np  Nhp  Np  N p  Nhp  N(  h  h  pI j p, i p,  h  h Y `( X `( G  (a | ffEZa | ff[a | ff>(a | ffCI  ffEZ^ T fv b \f  h    h  h  h  }(  `( v fI 2 fb 1 fb v dh v   f m [h  h  h  (  (  (  (  (  a(f X fzDf X ff X fyDf X ff X fx3f X fxf X fsf X ff  fGf X f*(f X f*(f X f/If X f2Zf X fef X fdf X f/If X f*(f  f  f  f*(, ? Y, ? YO C f  m  m{h  m  m{h  m  m{h  m  m{h H m  m{h  m{h  m{h  m{h  m  m  m{h  m{h H mO( I mO( I m{h  M(  P(> 6 U u ch6> ( f6Z6> ( fL6> ( f/(6> ( f/(6> ( f7Z6> ( f4I ( f ( f6Z ( fL ( f/( ( f/( ( f7Z ( f4I  h     h  h  h  hz  z  lz  lz  lz d ,z c ,     h   h   h  (  f(  f(  fI  fZ  f  f(  fe  fd  fe  fd d   "fp  h  h  h      h  h  (  (  ( l G l G  fl  fl ; fi, : fi,  h  h  h  (  (  (  f Z fh Y fh Y f  h  h  h  h  fYh E f-( D f-( D fYh  -(  -(  -(  I(  `Z  ^I Q -  h  h k s(  {Z  zZ  t(  yI m H l H r N a <   h   h   h   h  (  (  (  (  ( > (  (  (v b \f%(v b \fuDv b \fv b \ftDv b \fv b \fs3v b \fsv b \fnv b \fv  \fBv b \f%(v b \f%(v b \f]v b \f^v b \f^v b \f-Zv b \f`v b \f_v b \fav b \f*Iv b \f^v b \f%(v  \f b fh d f.Z b  b ,Z b +Z b *I b %(    0fh  0f#  0fh  0f"  0fh  0f#  0fh  0f#  0fh  0f  0fh  0fh  0f  0f  0f  0fh  0f  0f  0f  0fh  0f  0fh  0f(  (  h  (  h  h  h  h  ( W feZ W fcI x h  |h  |h  ^( 1 h 1   E xh  (  Z  (  I `  _  _ Z  h  h  h c j( b j( b h"  Z"  "  "  ("  "  I"  "  I e h | jh |  | y | jh |  | jh 7 >( 7 jh  f@  f/  f  f  h  h { ( z (  fq(  fq(  fq(  fvI  f  fyZ  f  f  f  f  f  f{  fq( i fE  fE  E  xZ  wZ  vI  q( i Er j fD 3 f!hD 3 f!hD 3 f!hD 3 f!hD 3 fMD 3 f!hD 3 fMD 3 fMD 3 fLD 3 fMD 3 fMD 3 f+D 3 f!hD  f( 3 ( 3 ( 3 !h 3  h 3 !h 3 !h  ( w ?I 2   f Z 50   h0  (  ( Z $Z Z #Z Z : Z %Z Z "I Z '    h  h    h  h  (  ZZ  WI  h  h l 'fhZ l 'fgZ l 'f~ l 'ffI l 'fiZ l 'ffI l 'fk l 'fa( ' 'f5 l 9  h  h    h  h  h  h  (  r(  (| [ %Z| [ ;| [ (| [ #I|  |    h    h  h  (  (  c(     (  m(  h * pfh  Y    H(  &x  &x  &xm mm2ZZ ( ZZ      (     ( I   14IZ Z   D Z   1 4I %f (   '  ,8( (  ( ( ( ( ( (   ( ( ( ( ( 69 ; 48<BHNRVZ^bhn| "8Ndjnrvz~,D`v,D\t $(4Xx4Xn"@Xp48<@D`|2Vz  ( L p $ < `  8 \     $ * . B F \ ` f l r  4 P t "&*.28Tp4X|*NRVZfjnz~:^ .Rv*Nr&Jn"FjBf>b:^z">Zv:Vr6Rh~  6Ll2Jbz8Nd2Hl  4 P t !!(!L!p!!!!""4"X"t""""###8#T#p#####$$4$P$j$$$$$%%*%N%r%%%%&&*&F&b&~&&&&&''&'>'V'n''''''(((@(d(((())<)`)))))**4*P*l*****++ +@+`++++++, ,,,L,l,,,,,--$-D-d-z-----...0.F.^.v.....///2/J/b/z//////0020V0r000011:1^11112262Z2~2223323P3t3x3333334 4D4h444455@5d555566<6`66667787N7d7z77777788.8D8Z8p88888899&9>9V9n999999::.:F:^:v:::::;;;4;L;d;|;;;;<<,>$>H>l>>>>>>??8?\????@@4@X@|@@@A A0AFA\ArAAAAABB&BETEjEEEEEEFFF2FJFbFzFFFFFGGG,GBGXGnGGGGGGHH&H*H.H2H8H)y@9&&((2288DDFFHHJJRRXXin  !!$$%%&&''(())**++DDEE LLMM ]]^^__``aabbccddttuuvvwwxxyyzz{{       !"#$%&'()*+,-./012  !"#$%&'(     '.5<CJQX_gnu|#3BP`o}#/5;?CGKQW]ciosw{ !$%"#&'()*+,- . / 0 1 23456789:;<=+"+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0"! 0"! 0"! 0"! 0"! 0$! 1$! 1$! 1$! 1$! 1!* 1# ! 1# ! 1# ! 1# ! 1# ! 1# ! 1# ! 1# ! 1# ! 1# ! 10 1)11111-1.1&1'1/ 1(11111,1%111     LM    )4?JU     +1+1+1+1+1!1!1!1!1!12LLOiVC$$%%&&''(())*+,,--..//00112234578899:=AACCDDEGHHIIJJKKLLMMNOPQRRSSTTUVWWXXYYZZ[[\\]]bcddeeffgnoopwxxy45679:>?BCCDEFIJJLMNPQRSST\]]^^__``acddenop qrsst  -.5789@ATUUV]^abeftuz{                 "$&(*,.02468:<>@BDFHJLNPRTVXZ\^`bdfhjlnprtvxz|~   "$&(*,.02468:<>@BDFHJLNPRTVXZ\^`bdfhjlnprtvxz|~,' ", "1' "1 "1,' "1,'1 "'1 ",1 "1 " &!+!+ &+ &!+ &!!0+!0+0+0! &!0 &0 &0!0!0!+ &0+ &0!+0 & &!0 &+!0+ %   % % * ** * %* %* % * %  /// * /*/*/ /* % / %/ %/ / %* %/*/ %* %/ $ $ $)))) $) $) $) $...).).)..) $. $. $.. $) $.). $) $. "1!0/ %* $). #(-.=L[jy-<KZix(8HXhxIc,m4v;~A KWY  P F  @ | 9 v     ljmkpponq ttsurwwvx||{y}zV~WW''X'''&!!$$$YY----...2225557788899<==>>@AABB:::////;;;G I FF\FFF  H H 1111CCC4444???[[EEEELLLMMMMNNNNOOOOSSSTTTTUUUUWWW "!XXX#X$YY'%(&ZZ+)Z*--,[.[//\\0\112]]]55436_88`79`;;<:aa==b>bbAA@cB?DDddECGGHeeFIIfffJPPPP_Pcc^^^^QQ`QQQMMgKNLPPhOhQTTURiSRRRRRaVVVbVVedfeddfefdefegdgdegggdfgfegdfegfhhedhhdhhehhhhegdhhgdhheghhghhedfhhdfhhefhhfhhgdfhhgfhhegf !!"""####$$$%&!!(($$$))))***$$$++++,,,''X'''YY----////00001111////33334444000033336666...222555888111144446666:::////;;;99==222>>:::3333???;;;4444???99AA...BB:::0000CCC;;;1111CCCDDDDZD[[EEEEDDDDZDFF\FFFFF\FFF[[EEEEAA==555  H H BB>>888  H H CCC6666???FF\FFFJJJJJ]JJJJJ][[EEEEFF\FFFKKK^KKKKK^KK[[EEEEMMMMNNNNOOOOPPPP_PQQ`QQQPPPP_PRRRRRaQQ`QQQRRRRRaMMMMTTTTUUUUPPPP_PVVVbVVQQ`QQQVVVbVVNNNNTTTTXXX#X$RRRRRaVVVbVVLLLSSSWWW "!ZZ+)Z*OOOOUUUUXXX#X$//\\0\MMMM112]]]PPPP_Pcc^^^^QQ`QQQcc^^^^--,[.[88`79`SSS;;<:aa//\\0\TTTT==b>bb112]]]UUUU==b>bbVVVbVVcc^^^^--,[.[DDddECLLLGGHeeF//\\0\NNNNIIfffJ112]]]OOOOIIfffJRRRRRacc^^^^DDddEC88`79`WWW "!PPhOhQIIfffJXXX#X$==b>bbGGHeeF;;<:aaZZ+)Z*PPhOhQeddfefdedfefedgdegdgegdfgdgffgedfegdgdfegfefeggfhhdedhhedhhehhhhedhhgdegdhheghhdgdhhghheeghhgghhhhedhhdfedfhhefhhddfhhfhheefhhffhhhhdfhhgdgdfhhgfhhfgfhhghhefhhegegfhhgf+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70&&,#,#,#,#,#)'#)*# ))# )+# #370&&,#,#,#,#,#)'#)*# ))# )+# #370&&,#,#,#,#,#)'#)*# ))# )+# #370&&,#,#,#,#,#)'#)*# ))# )+# #370&&,#,#,#,#,#)'#)*# ))# )+# #370&&, #, #, #, #)1#)4# )3# )5# #370&&, #, #, #, #)1#)4# )3# )5# #370&&, #, #, #, #)1#)4# )3# )5# #370&&, #, #, #, #)1#)4# )3# )5# #370&&, #, #, #, #)1#)4# )3# )5# #370&&,#,#,#,#),#)/# ).# )0# #370&&,#,#,#,#),#)/# ).# )0# #370&&,#,#,#,#),#)/# ).# )0# #370&&,#,#,#,#),#)/# ).# )0# #370&&,#,#,#,#),#)/# ).# )0# #370&&,#,#,#,#)"#)%# )$# )&# #370&&,#,#,#,#)"#)%# )$# )&# #370&&,#,#,#,#)"#)%# )$# )&# #370&&,#,#,#,#)"#)%# )$# )&# #370&&,#,#,#,#)"#)%# )$# )&# #370&&,#,#,#,#)#) # )# )!# #370&&,#,#,#,#)#) # )# )!# #370&&,#,#,#,#)#) # )# )!# #370&&,#,#,#,#)#) # )# )!# #370&&,#,#,#,#)#) # )# )!# #370&&,#,#,#,#,#)'#)*# ))# )+# 370&&,#,#,#,#,#)'#)*# ))# )+# 370&&,#,#,#,#,#)'#)*# ))# )+# 370&&,#,#,#,#,#)'#)*# ))# )+# 370&&,#,#,#,#,#)'#)*# ))# )+# 370&&, #, #, #, #)1#)4# )3# )5# 370&&, #, #, #, #)1#)4# )3# )5# 370&&, #, #, #, #)1#)4# )3# )5# 370&&, #, #, #, #)1#)4# )3# )5# 370&&, #, #, #, #)1#)4# )3# )5# 370&&,#,#,#,#),#)/# ).# )0# 370&&,#,#,#,#),#)/# ).# )0# 370&&,#,#,#,#),#)/# ).# )0# 370&&,#,#,#,#),#)/# ).# )0# 370&&,#,#,#,#),#)/# ).# )0# 370&&,#,#,#,#)"#)%# )$# )&# 370&&,#,#,#,#)"#)%# )$# )&# 370&&,#,#,#,#)"#)%# )$# )&# 370&&,#,#,#,#)"#)%# )$# )&# 370&&,#,#,#,#)"#)%# )$# )&# 370&&,#,#,#,#)#) # )# )!# 370&&,#,#,#,#)#) # )# )!# 370&&,#,#,#,#)#) # )# )!# 370&&,#,#,#,#)#) # )# )!# 370&&,#,#,#,#)#) # )# )!# 370 8r\@#$=>@AABBC]^abJKKL]^^__``acdde5667    "#%&(+-.012469<?CFIMQUZ[]^`cegjlosvwyz|  !"$'),/369=AEJMPTX\aeinsx~  !%).036:<?ABEIMRV[`fimqvy} &-5=DLU]fkqv|&/7@HOW`hqz '1:DNYajs} $)*17>AEHKMOV\ckrz~  '-3:@GOV[aflsy~ #,4=GPZ`gnv|    ' 0 : B K S Z [ \ ^ _ a b d e g j l o p r t u w z |          $+#+#$*#)#)+))+*+$(#'#'+$(*#')#')+''+(')')+(*))+*+$&#%#%+$&*#%)#%)+$&(#%'#%'+$&(*#%')#%')+%%+&%)%)+&*%'%'+&(%')%')+&(*''+(')')+(*))+*+$#+#$*#)#)+$(#'#'+$(*#')#')+$&#%#%+$&*#%)#%)+$&(#%'#%'+$&(*#%')#%')+%%+&%)%)+&*%'%'+&(%')%')+&(*''+(')')+(*))+*+%%+&%)%)+&*%'%'+&(%')%')+&(*''+(')')+(*))+*+"$!#+!#"$*!#)!#)+"$(!#'!#'+"$(*!#')!#')+"$&!#%!#%+"$&*!#%)!#%)+"$&(!#%'!#%'+"$&(*!#%')!#%')+!%!%+"&!%)!%)+"&*!%'!%'+"&(!%')!%')+"&(*!'!'+"(!')!')+"(*!)!)+"*!+!"$$*$($(*$&$&*$&($&(*&&*&(&(*((**"$"$*"$("$(*"$&"$&*"$&("$&(*"&"&*"&("&(*"("(*"*" $ $* $( $(* $& $&* $&( $&(* & &* &( &(* ( (* * "$ "$* "$( "$(* "$& "$&* "$&( "$&(* "& "&* "&( "&(* "( "(* "* "024677/135702467/1357/13575757675702467/1357/135702467/1357/1357357357467357357467357357467357 02467/1357/1357 02467/1357/1357 02467/1357/1357 02467/1357/135713571357 246713571357 246713571357 246713571357 24671357135724671357135724671357135724671357.02467-/1357.02467-/1357-/1357.02467-/1357-/1357.02467-/1357-/1357 .02467-/1357-/1357 .02467-/1357-/1357 .02467-/1357-/1357 .02467-/1357-/1357 02467 02467 02467 02467024670246702467 .02467 .02467 .02467 .02467.02467.02467.02467,.02467,.02467,.02467,.02467 ,.02467 ,.02467 ,.02467 ,.02467 ,.02467 ,.02467 ,.02467 ,.02467,.02467,.02467,.02467                     024677/1357/1357702467/1357/135757576757576702467/1357/135702467/1357/1357357357467357357467357357467357357467 02467/1357/1357 02467/1357/1357 02467/1357/1357 02467/1357/135713571357 246713571357 246713571357 246713571357 2467135713572467135713572467135713572467135713572467.02467-/1357-/1357.02467-/1357-/1357.02467-/1357-/1357.02467-/1357-/1357 .02467-/1357-/1357 .02467-/1357-/1357 .02467-/1357-/1357 .02467-/1357-/1357 02467 02467 02467 0246702467024670246702467 .02467 .02467 .02467 .02467.02467.02467.02467.02467,.02467,.02467,.02467,.02467 ,.02467 ,.02467 ,.02467 ,.02467 ,.02467 ,.02467 ,.02467 ,.02467,.02467,.02467,.02467,.02467                             8v   *T~&Pz"LvHrDn@Wn&=Tk #:Qh       "!#$&%'(*)+,.-/0213487658787:98787<;8787=>8787@?8787BA8787DC8787EF8787HG8787IJ8787LK8787NM8787PO8787QR8787TS8787VU8787XW8787YZ8787\[8787^]8787`_8787ab8787dc8787fe8787hg8787ij8787lk8787nm8787po8787qr87vutsvuvuxwvuvuzyvuvu{|vuvu~}vuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvu     ! "$#%'&(*)+-,.0/1234675:98=<;@?>CBAFEDIHGLKJNMOQPRTSUWVXZY[]\^`_abcdfegihjlkmonprqsutvxwyz{|~}67:9=<@?CBFEIHLKfeihlkonrqutxwz{~67:9=<@?CBFEIHLKfeihlkonrqutxwz{~STUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@AB   111222RRR555ZZZjjjkkllmmnnooppqqrrsst tu!uv"vw#wx$xy%yz&z{'{|(|})}~*~+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRCDEFGHIJKLMNOPQRSTUVWX Y  Z  [  \ ]^_ ` a b c defghij kl m  n !o!"p"#q#$r$%s%&t&'u'(v()w)*x*+y+,z,-{-.|./}/0~0334455566778899::;;<<==>>??@@A AB BC CD DE EFFGGHHIIJJ^_ KKa b LLdeMMghNNNOOOQRPPGHQQE@@A ASSC CD DTTFFGGUUIIJJVV::;;WW==>>XX7788YY44334455566778899::;;<<==>>??@@A AB BC CD DE EFFGGHHIIJJ@@A ASSC CD DTTFFGGUUIIJJVV::;;WW==>>XX7788YY44555ZZZ[[\\]]^^__` `a abbccddeeffgghhii[[\\]]^^__` `a abbccddeeffgghhiijjj[[\\]]^^__` `a abbccddeeffgghhiijjj[[\\]]^^__` `a abbccddeeffgghhiijjjkkllllmmnnoonnooppllqqrrsst tu!uv"vrrssw#wu!uv"vx$xnnoopplly%yz&z{'{|(|})}~*~+,-./0z&z{'{1})}~*~2,-3/04rrssw#wu!uv"vx$xnnooppllkkllmmnnooqqrrsst tu!uv"vy%yz&z{'{|(|})}~*~+,-./01234w#wx$xpp1234w#wx$xppkkmmqqt ty%y|(|+.1234w#wx$xpp56789:;<=>?@ABCDEFGHIJKLMNOPQR** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1 D= #$=>CD]^abJKKL5667     $'),.135789;<=>AEHJMOQTVWYZ^adgiknprtuvy{}    "%),/246:=@CEGJLNPQRV[_bfilpsuxz} "%(*-/127;?BFILNRUXZ]_abfilnqsuvy{}~ #*16;BGLQ\gpy "*2:@FNTZ`n| &0:BJR]fov&/<GR[fox   $(,02468>DHLPTVX^dhlptvx   $(,02468>DHLPTVX^dhlptvx    ! , 7 @ F J N R T V a l u ~    # * 1 6 B L V ` h p z  " . 8 D P Z d n v    ! & 2 < F N X ` h n }     & . 6 > D J L N T Z ` f j n r v x z | ~  &.6>DJLNTZ`fjnrvxz|~ &.6>DJLNTZ`fjnrvxz|~ &.46<BFJLNTZ^bfhnrvz|~'7GWgw/?O_o/O_o?'GWg?O_O_/?o/o7w?7GWw/?Oo/OoGW?OO/?_o/_o'7gw?__'g/?o/o7w?'7Ggw/?_o/_o'Gg?__7Gw/?o/oG?/?O_o/O_o'7Wgw?O_O_'Wg/?Oo/Oo7Ww?OOW/?_o/_o'7gw?__'g/?o/o7w?'7GWgw/?O_o/O_o'GWg?O_O_7GWw/?Oo/OoGW?OO'7Ggw/?_o/_o'Gg?__7Gw/?o/oG?'7Wgw'Wg7WwW'7gw'g7w'7Wgw'Wg7WwW'7gw'g7w'7GWgw'GWg7GWwGW'7Ggw'Gg7GwG'7Wgw'Wg7WwW'7gw'g7w#3CScs&6FVfv +;K[k{.>N^n~ +K[k.N^n;{>~#CSc&FVf ;K[{>N^~ K[N^+;k{.>n~+k.n3s6v3CSs6FVv +;Kk{.>Nn~ +Kk.NnCSFV ;K{>N~ KN+;[k{.>^n~+[k.^n#3cs&6fv;[{>^~[^#c&f#3Ccs&6Ffv#Cc&Ff3Cs6FvCF#3Scs&6Vfv#Sc&Vf3Ss6VvSV"2BRbr%5EUeu *:JZjz -=M]m} *JZj -M]m:z=}"BRb%EUe :JZz =M]} JZ M]*:jz-=m}*j-m2r5u2BRr5EUu *:Jjz -=Mm} *Jj -MmBREU :Jz =M} J M*:Zjz-=]m}*Zj-]m"2br%5eu:Zz=]}Z]"b%e"2Bbr%5Eeu"Bb%Ee2Br5EuBE"2Rbr%5Ueu"Rb%Ue2Rr5UuRU!1AQaq )9IYiy )IYi9y!AQa 9IYy IY)9iy)i1q1AQq )9Iiy )IiAQ 9Iy I)9Yiy)Yi!1aq9YyY!a!1Aaq!Aa1AqA!1Qaq!Qa1QqQ'7GWgw/?O_o/O_o?'GWg?O_O_/?o/o7w?7GWw/?Oo/OoGW?OO/?_o/_o'7gw?__'g/?o/o7w?'7Ggw/?_o/_o'Gg?__7Gw/?o/oG?/?O_o/O_o'7Wgw?O_O_'Wg/?Oo/Oo7Ww?OOW/?_o/_o'7gw?__'g/?o/o7w?'7GWgw/?O_o/O_o'GWg?O_O_7GWw/?Oo/OoGW?OO'7Ggw/?_o/_o'Gg?__7Gw/?o/oG?'7Wgw'Wg7WwW'7gw'g7w'7Wgw'Wg7WwW'7gw'g7w'7GWgw'GWg7GWwGW'7Ggw'Gg7GwG'7Wgw'Wg7WwW'7gw'g7w#3CScs&6FVfv +;K[k{.>N^n~ +K[k.N^n;{>~#CSc&FVf ;K[{>N^~ K[N^+;k{.>n~+k.n3s6v3CSs6FVv +;Kk{.>Nn~ +Kk.NnCSFV ;K{>N~ KN+;[k{.>^n~+[k.^n#3cs&6fv;[{>^~[^#c&f#3Ccs&6Ffv#Cc&Ff3Cs6FvCF#3Scs&6Vfv#Sc&Vf3Ss6VvSV"2BRbr%5EUeu *:JZjz -=M]m} *JZj -M]m:z=}"BRb%EUe :JZz =M]} JZ M]*:jz-=m}*j-m2r5u2BRr5EUu *:Jjz -=Mm} *Jj -MmBREU :Jz =M} J M*:Zjz-=]m}*Zj-]m"2br%5eu:Zz=]}Z]"b%e"2Bbr%5Eeu"Bb%Ee2Br5EuBE"2Rbr%5Ueu"Rb%Ue2Rr5UuRU!1AQaq$4DTdt )9IYiy ,<L\l| )IYi ,L\l9y<|!AQa$DTd 9IYy <L\| IY L\)9iy,<l|)i,l1q4t1AQq4DTt )9Iiy ,<Ll| )Ii ,LlAQDT 9Iy <L| I L)9Yiy,<\l|)Yi,\l!1aq$4dt9Yy<\|Y\!a$d!1Aaq$4Ddt!Aa$Dd1Aq4DtAD!1Qaq$4Tdt!Qa$Td1Qq4TtQT 0@P`p(8HXhx(HXh8x @P`8HXxHX(8hx(h0p0@Pp(8Hhx(Hh@P8HxH(8Xhx(Xh 0`p8XxX ` 0@`p @`0@p@ 0P`p P`0PpP 8v                *T~&Pz"LvHrDn@j<f8b 4^  0 Z  , V ( R | $ N x J t FpBl>h:d 6`2\.X.E\s+BYp(       "!$#&%('*),+.-0/214387657887:97887<;7887>=7887@?7887BA7887DC7887FE7887HG7887JI7887LK7887NM7887PO7887RQ7887TS7887VU7887XW7887ZY7887\[7887^]7887`_7887ba7887dc7887fe7887hg7887ji7887lk7887nm7887po7887rq78vutsuvvuxwuvvuzyuvvu|{uvvu~}uvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuv     ! "$#%'&(*)+-,.0/1324765:98=<;@?>CBAFEDIHGLKJNMOQPRTSUWVXZY[]\^`_acbdfegihjlkmonprqsutvxwy{z|~}76:9=<@?CBFEIHLKfeihlkonrqutxw{z~76:9=<@?CBFEIHLKfeihlkonrqutxw{z~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~$$$,,,<<<=5+=>6,>?7-?@8.@A9/AB:0BC;1CD<2DE=3EF>4FG?5GH@6HIA7IJB8JKC9KLD:LME;MNF<NOG=OPH>PQI?QRJ@RSKASTLBTUMCUVNDVWOEWXPFXYQGYZRHZ[SI[\TJ\]UK]^VL^_WM_`XN`aYOabZPbc[Qcd\Rde]Sef^Tfg_Ugh`VhiaWijbXjkcYkldZlme[mnf\nog]oph^pqi_qrj`rskastlbtumcuvndvwoewxpfxyqgyzrhz{si{|tj|}uk}~vl~wmxnyozp{q|r}s~tuvwxyz{|}~                     "!$#    &%(' "!*)  ,+.-0/2143 "!$#               &%(' "!* )    , + .-0/21 4 3!! "!"$"#  # # %.%-    &0&/    '2'1(4(3)) "!*$*#    + +  ,,,           &%(' "!* )    , + %.%-    &0&/    '2'1(4(3)) "!*$*#    + +  --..// 001& 1%2(!2'3*"3)4,#4+5.$5-60%6/72&7184'839(9:$):#; *; <<<--..// 001& 1%2(!2'3*"3)4,#4+5.$5-60%6/72&7184'839(9:$):#; *; <<<--..// 001& 1%2(!2'3*"3)4,#4+5.$5-60%6/72&7184'839(9:$):#; *; <<<--..// 001& 1%2(!2'3*"3)4,#4+5.$5-60%6/72&7184'839(9:$):#; *; >6,>@8.@B:0BD<2DF>4FH@6HJB8JLD:LNF<NPH>PD<2DRJ@RTLBTVNDVXPFXZRHZ\TJ\^VL^`XN`bZPbd\Rdf^Tfh`VhLD:LNF<NPH>PD<2DjbXj^VL^`XN`ldZld\Rdf^Tfnf\nLD:LNF<Nph^pD<2D@8.@B:0Brj`rH@6HJB8JtlbtTLBTVNDVvndvZRHZ\TJ\xpfx^VL^`XN`bZPbd\Rdf^Tfh`VhLD:LNF<NPH>PD<2D>6,>@8.@B:0BF>4FH@6HJB8JRJ@RTLBTVNDVXPFXZRHZ\TJ\jbXj^VL^`XN`ldZld\Rdf^Tfnf\nLD:LNF<Nph^pD<2Drj`rtlbtvndvxpfxbZPbh`VhPH>Prj`rtlbtvndvxpfxbZPbh`VhPH>P>6,>F>4FRJ@RXPFXjbXjldZlnf\nph^prj`rtlbtvndvxpfxbZPbh`VhPH>Pyqgyzrhz{si{|tj|}uk}~vl~wmxnyozp{q|r}s~tuvwxyz{|}~               ** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1   # Ā̀Ԁ    vie$swift-im-2.0+dev6/BuildTools/DocBook/Fonts/Gentium Basic/GenBasBI.ttf0000644000175000017500000075145412227051773025076 0ustar kismithkismith`Feat$x0GDEFٷl8GPOSfMvCGSUB GH$GlatQhLPGlocӔWFOS/24gMl`Silfas..Sill$cmap>tMTcvt 9 ^ &fpgmY7^Hsgasp_glyf\_headp1#l6hhea , ?$hmtxjN9 plocaLJ$8:maxp?t nameApost}S>6 gprep>f P #$=D]bJL]__ace57 **"(%$&' #       !  )    &Dlatn IPA markmkmk "?\C g L/0 +        2 h n t z     n   " ( . . 4 : @ F F F L R n t z X n t z ^ d j p v | n   " F F F F n   " . . 4 :   d j p v$*0666<BHN 4 :T d j p vZ`flrx n   "~ n t z   T d j p v n   " n   "  n & , X n t z 2 n8>>>DJ nP`flrV\\\bT d j p v  nx nh`flrnHN 4 :tz n n   " z  t z    j p v |  n"( F F . n4::: @666<F n t z L nR F F X^f^djpvp| d j p v d j p v n   " | n  d j p v  n"" 8>>>D""$*0006< d j p vBHN 4 :4 F F HNNNTZ`flrxHN 4 :~ n   "$*  n   " n t z J n   " &,2B 8 8 >DDD X n t z J d j p vPVVV\ n t z  n   "bhnhtzHN 4 : HN 4 :\\\bT d j p v n t z HN 4 : d j p v n   " t z >DDD d j p v nZ`flr" &,$* (HN 4 :  n t z  d j p v$HN 4 :. n   """"T d j p v(((.8>>>D4:::@~ nF\\\bT d j p vL n   " LLLRX &,^ d`jpv z |   "| n   " d j p v  HN 4 :$* n n t z (::: 8>>>D n   " n8>>>DHN 4 :  &, nHN 4 : X n t z " | n T d j p v&$*0 d j p v n   "6<<<BH`flrNTZ`flrxr~ n   " n   "B| nHN 4 :`jpv8>>>D ( n   "`jpvT d j p v d j p v`jpv8>>>D`jpv ( d j p v8 8  d j p v$ n t z X n t z T d j p vBT d j p v2 n   " HN 4 :`flrHN 4 : n   "| & ,2 n t z n8 n   "8>>>D > > BDJJJ"P(`V\|   "bh d j p v (nN 4 :t &, n   "z n t z 8>>>D666 X n t z  n   " n   "BnN 4 : HHHHN 4 :8>>>Dz n t z  n   " j p v X n t z  n   "z>DDD d j p v n d j p v8 n j p v &, n t z  n $*T d j p v F F  nHN 4 :"(".4:::@ d j p vFLRLX^HN 4 :djjjpv d j p v|`flr D ff,phf Dmf$Gf(Ffg(ff2fhbfh  efFsf(Af((f,pfKW>fJ?FJ=f(GbfR(fZvhZfVn>f(yfl8fxlA0fr(cf"9fXfMK9(qff  r|f{q(Uflg4fkhq((%`ffQKJG~hzZ{IPu>fX(fPOzfK/"Zt?(~fB(Ef((f"lfRs(f"#K(Bf(fa5PZM>6XfsKK()fj(f(fk(6fNyf(hvO(fMI.hV.fwI=f.5hyfKK@q*f(f(fdKl:xf, (JfhL5]qfhJwafK{-fwI}('fVG*fsfH((((fjILKimf(VqpKDHf|s((f(1Bl fqdhNl!E#fihfJq(;f<5Dff,4fWH&fah(  +          $ 4 |         $ * 0 0 0 6 < B B B H N T T T Z ` f l f r x ~ ~  &&&,28>DJPV\bhntttz   ~ ~ ~    "(" .444:@ FL R ~ X^d^jpvvv|(        T T T Z       6$*444:06 <BHNTZ`ffflr ~ | x~dj     |   T T T Z >J <28>DJ& , 2 ~ 8vvv> ~ DJJJzPVVV\bhhhnt z, T T TVVV\  \bh<      " (. T T T Z4: :@F ~ $ L T T T Z|"(" R X ~ ~ ~^djpjv|NTZ | vv  444: p     < $ *^d^j06(6 | <B 0 0 0 6HNNNTZ```\f lrx~x l r  ~  Zzx~x T T T Zjpjv>  l rx~x ~ ~ ~ 6(6 &VVV\,22286 >DJJJP ,V  \\ | bhhhnt z  6  ~ VVV &444: ~       " ~ (     .4:>DJ $ <@F  LRXXX^d,jpppvvJJJz|  8   \ T T T Z:>DJ ` l r < ~  0 0 0 6 $ :>DJ ~ ~ ~ ~|$&* 0666<BHjpjvR ~ NT TZ ` f f |VVV\l   rx ~444:  ~ ~ ~: |  0 0 0 6, jpjv |   JJJz &~&, 2  8 >6(6 D444:J    P  V\: :@bhhhn ,VVV\tJJJzzT T:>DJ:>DJ:>DJ  :>DJ T T T Z  l z JJJzx~x444:    B D fhZf$(_f<Gbfh:xffxF&f`lf.hh(f( 3fzfsfZ Dmf(+fO(fg4f!h9fdDf+uyf4f6f*Gf(;fK8f6k(lf3(fKfIyfhf7Zf(fIfs ,opflvKK{-f{(|H\qf(Ff(j(f~/f`lfZ(fsVDHfX|)fSI*IC(:fK(; ff2?hfhfhf?(Fs>?K[f-X((zX(X*jpswqf)DZWKu>fZ9sKh=Jf.R(UfMf 1fhf;f(f(L(-_K4IY~f(-z4fW(OTKh'f5hlf8/(flKK KY/Kfhzfhgfh fh(fhqKjh<hKfV{hm4\f(5{(fdK (hKhVKZ%`fnIt"f Iyf,N\f hCzU5bfV(n<(BlAI0=s(Of%s'fIhOfBv 9s)K IhI(<fhsl"F?l{f**"(%$&' #       !  ) 1Z+$$/%%&&]''X((o))**?++,,--T..l//|001122033445566J7778899::!;;_<<n==DD EEuFFbGGHHII.JJKK+LLMM9NNOOPPiQQRRSSITTUU(VVWWXXYYZZY[[\\O]](bbccxddVeeff'gghhhiijjkkkll6mm$nnfooDppaqqrrssttuuvvswwxx#yyzz{{r||}}~~j< m-ydc z2-#p{"MqN*"1S  5    &  0  G@C,&>RQ  !!""##*$$L%%&&''((/))!**`++,,--..$//0011 22K3344=55.6677889:;;;<<== >>U??B@@%AABBHCCDD4EEFF[GGWHHIIJJALLMMNNOOPPPQQ RRSS)TTUUVVWWXX\YYZZ[[v\\]]e__'aabbccee ff~gghh iijj,kk3ll}mmnnooppqqrrssttuu)vv wwxxyywzz{{||:}}~~  F8Etg%^ 12)1-w&"!HNkG|h,B[X+ V{`$(ALv5i )0Wl(gJ%?cZY 1m  9        /\+ty>  !!""##$$%%&&''&((2))P*+D,,--..//K0011U2233^445577o8899R::;;<< == >>}??@@AABBCCDD*EEsFF#GGHH2IIJJ!KKLLMMINN@OOPPQQRRSSTTUUVVWWXXYYZZ[["\\d]]^^__`` aabbccddzeeffggQhh=iiSjjkk ll'mmnnooppqqprrss-ttuuvvuwwxxyyzz{{||q}}~~*f3M:b;x a.3C% ,Tj4 'rFn0]~8$. _ 67Oe</ &   $   $ *0# &,,28>DAK?l(q(??7?? Z(dI((s(K&%# !"        $ #"         ! 44   JzcyrllatnIPA VIT aaltccmpccmp$0Tpbtb &6H &6HLM   LM"(FdnxJJdCCo7CCJJppopp0u&&(( 2288EEFFHHRR XXddoo??FFffoo }}  N ,  F ')uy^+bw%d!{`    $$&&((**]] __aaccttvvxx zz  f0     EM   DD"JJ(in DDLL!#$%&')*,./XXXXd@ JSIL ! D  4 t~!%-3:DHU]adq~37=BEKQTWY[cikru #(1?_';Io     " & 0 : D !"!&"""""""+"H"`"e%%,b1Bj $(09AGJX`dht&7=ADJQSVY[chkru#'1?^.>Lx     & 0 9 D !"!&"""""""+"H"`"d%%,`0BjT1hw8:"+ xjyޖދtq_/0TOn  2:>BDFFrxz|VFJNBBD8<:<>@B@bcdeYfgh<jikmlnoqprsutvwTxzy{}|~9.#F?G@IBHAUZQi}fl~g&!GAHBVMXOYPik}gq|f.0?u-"~hpmonq/1625/0o) $* + @>:DSRt{;7=9<8JCVNXPWOkjp#%(':7;8ICKELFJD[R\SWNZQ]Tc_ead`jl^43 "$&*(,nmht]v_zcxa|e- ,  12435srvwyxz>@ t~!%-3:DHU]adq~37=BEKQTWY[cikru #(1?_';Io     " & 0 : D !"!&"""""""+"H"`"e%%,b1Bj $(09AGJX`dht&7=ADJQSVY[chkru#'1?^.>Lx     & 0 9 D !"!&"""""""+"H"`"d%%,`0BjT1hw8:"+ xjyޖދtq_/0TOn  2:>BDFFrxz|VFJNBBD8<:<>@B@bcdeYfgh<jikmlnoqprsutvwTxzy{}|~9.#F?G@IBHAUZQi}fl~g&!GAHBVMXOYPik}gq|f.0?u-"~hpmonq/1625/0o) $* + @>:DSRt{;7=9<8JCVNXPWOkjp#%(':7;8ICKELFJD[R\SWNZQ]Tc_ead`jl^43 "$&*(,nmht]v_zcxa|e- ,  12435srvwyxz>@*s[hg -,K PXYD _^-, EiD`-,*!-, F%FRX#Y Id F had%F hadRX#eY/ SXi TX!@Yi TX!@eYY:-, F%FRX#Y F jad%F jadRX#Y/-,K &PXQXD@DY!! EPXD!YY-, EiD` E}iD`-,*-,K &SX@Y &SX#!#Y &SX#!#Y &SX#!#Y &SX#!@#Y &SX%EPX#!#!%E#!#!Y!YD- ,KSXED!!Y-dH/ /ܸܸи/EX/ >Y+017!!'!!L2P22,0 +A &6]/ +01%#"&5466632''66667_0E-?:1E+<<3;7AFA*!G9%?3 D9$= !" D[ // //01#66667#66667M  >F> ,  >F> ,W* EI// /"/EX;/;>YEXD/D>Y ++и$и +и-и4и=и FиH013#3#'#'#'6666737#'6666736666736666737#HYr ! {r ! {Ym   tm   բY "#  !#   9  9 #!  !#   gPm [\/]/\KиK/ A &6]]#ܺK#9K#9 A  ) 9 ]K#9K#9-и-/.и./1и1/K=и=/ UиU/T/,/(/./1/,T9,T9,T9,T9014&'66'&&''7"#"&&&546666632'&&&&5466677667325 1'-&@-O=E8Xb :H=2HoL(4fd1.n`@   ,8%A$?aA"-^e$*QKA)''A+57S(mu}-5.!7X@K\;NuSou09 '/0'+41( :FT47p_EsP'5K_B V+L 6+ +A )9]A LL&L6L]L Lи/A BB&B6B]a +[;+EQ+014&#"32666667#"&&&5466632'666674&&&#"32666667#"&&&5466632\."-! )+) 1_Y6YA#3aZ3V?$ '*'  &*'  -! )+)!1^Z6Z@#3aZ3V?#VJ*DTULOI$F> ,WUD / /01&&&76667CzeN!K<;cVkt7KK<()97D //01'66676&&'7 kt&CyfM!K<;cV{9K7 KK< /G.//.9.9.9.9 .9(.9017''&&&&'7%'76667766667" 4 ,0, #[ "  3 +0, $ĸ!8j` 04. #1 8j`$4. $<%/ /+и01#'!'666673666673$   &% &\ !      w* +A  ) 9 ]//01%'6654&&&'6666663w2CIL#9LM * *4:7. u)]_\RB2Q;%N  !,yD,_, +A )9] +01%#"&5466632_0E-?:1E+<Y01!!76666766'&&'6666667B Jc=d 7V?O[aYK#0VDS  (, Q#" *  w/G +A )9]EX/ >Y++01!2666767!'66654&#"'666632w7\h J?F@=%5!6=:O|V7gO/*Wi$-9J٢?HF$9G$ D4n[;3QFDX + + A )9] 9 F+@.+01#"&&&'73266654&&&##"'6654&#"'66666632F#>T03M3Le#UXT#6IE3X?$/?$05-!7;:#8KYd6PlB6]M;0J\2^}I*>'aD5+LkA4L1Z"y?F*;% =@?;,'AUh!</EX/ >Y + к901!#!7666677!'666673F"=; 0% =O-K!(9:7#d <  II  )*(@Z *+A **)*9*]B>/@/%+;+/+ /901!6632#"&&&'73266654&&&#"'6666667!26767 (,( t%o9SzQ(6PkO,[XR#8-ND?7\B%1G,Js6)  ) 11( KKD7Zr;#-@F= 6)1B$#:*(7,F2*DU*'I7!O[\V*4Un:">.EuVNrI#$6-$)lKFJ3@2J8*CLV1+C/,FZ4WI>6H]>UvF.Ne8Aq`O.VR23/4/ܹ A )9]и/3'и'/ A &6]'и/9/. +"+"901267654&&&#"'66667#"&&&5466666329`) $19L.%.h yW4IGiE!!=Vj{CJzW0:<.R]Kc;@cx79L/$YWy`1?1Sn=Y+ A (8]01#"&5466632#"&5466632/E-?:1E+<;^0E-?:1E+<<T!F9%>3 D9$=#!G9%?3 D9$=,p +A )9] и/./EX / >YA (8]01#"&5466632'6654&&&'6666663/E-?;1E+<3 D9$=)]_\RB2Q;%N  !,:T//901%'66667T27 x,3( " +TG + +01!'66667!!'66667!G^^ !   !   4// 901'66667%%'6674 2 47H{ ! ! $'# ,3|=u>/?/>и/ A &6]?ܹ$ A $$)$9$])и)/ +9)+01%#"&5466632'766666654&&&#"'4666320E->;1E+<<v9Wh]D =/CMB+!0 !2 <@<BygEkI%!G9%?3 D9$=Tm^ajAX|?mc]_f;)G3$>S0  '@hA&BY0o5h -+A &6]cn+|U+K+7+Kи/(кhK90166667&&#"326#"&&&'#"&&&5466676666326673266676&&&#"326667#"&&&&&7666666326(B4'  d C_pqh((' CJL#"OE.&IlGHLL )M! @  "50( 8oSpW; HoQQm &|nrϭUU{Kc*jka  (AV\]'8T7e$WiJ'2YF+R@'&S^Oy0"$ A*"HqVKA-XUT3[~ZyɡxP(*>C]&[N55fpvBhm% 3/EX/ >YEX/ >Y +01!766766667!766''s :K k @G 9<: & 3 H96Z II (#h II! y( #6E A+и/A AA)A9A]A2и2/GEX/>YEX / >Y-+<$+-и/ 9$<9$'и'/7:и:/01#"&&&'&'#766756632%"3266654&&&#"3266654&&&So;EL$U_;@D)E ]́z9^D&$+1D!O)<[? 6X/ >!5hS3'Y)+8 A ]A  ( 8 H X h x ]01'&&&&#"326667#"&&&5466676666324  <'5C)(J(?T38Ud-AQd> Dyn/WrB(Q|T$Y_c/1`UD+2-% ;.1?mX& !8-D[7A|t^ùC. $ + +A )9]-EX'/'>YEX/ >Y'A]A(8HXhx]и/A'7GWgw]A] '901"326666654&&&#!76675666632<6LEu`I2(W*Pth ?D9~~x3֋D5[yFkv?QXl?I!]K5CEX/>YEX/ >Y)+/01!76676&'7!#6&#!!&&&&##3326667  AE8A 1 F#8q  +>+t< 'I=\*?400+_UBI! $I NRI]ak#&%! G 1P;*9EX/>YEX/ >Y) +'01&&&&##!76676&'7!#6&#!!/  *8%`C ":, AE8A :G #8O'$  II! $I NRI]ak=e F6 +A 66&666]EX/>Y; +.A..]A..(.8.H.X.h.x.......]01#"&&&546667666632'&&&&#"32676&&&'7!_60 Lo_(\L+OnB*_l}H#XWN  :-YEX*/*>YEX/ >YEX / >Y+01!6&'7!!7667!!76676&'7!AF787A  AE8A  AD@?7B  AE8A "k $II"! #II!. #II! $I %EX/>YEX/ >Y01!76676&'7!@F7B  ?F7A "! #II! $It,EX+/+>Y+01#"&&&5466666323266676&&&'7!?G|?PX)FLK%G8#!('  )341, $A6 "hpqO ' $%!Lu I3A/(/EX / >YEX2/2>Y 9# 90166&&'7!267#"&'!76676&'7!AF?&$  +!2a )38XXN'C7B  AE8A "/  II ?' G2(N #II! $I +EX/>YEX/ >Y01!76676&'7!3326667  AE8A  AF %A3h*>3/0+_UBI! $II"[ 1P;i.vEX / >YEX-/->YEX/ >YEX/ >YEX/ >Y 9 9' 901"!7667#!7667&!266663!`A#?B  @NeWx8A } BDA A   9 #II!5E #II!I!@!F'N/EX/>YEX&/&>YEX/ >Y9901&&'!7667&'7326&&&'7!=E@WeAl .# y E?3D m /$  %}9-JII &< IIK 56/7/ܹ A )9]6'и'/ A   & 6 ]EX1/1>Y"+1A]A(8HXhx]014&&&#"32666667#"&&&546667666632 >]<=dN:& ?Z;@fO9%2I-*ivCfm9.A(*fyOki3Tm>:a?]q>=eQG96]E(RiJ|78cJ+R@ 3t +A )9]EX///>YEX / >Y + /9/и/& /901#"&''3266654&&&##"!76675666632@I~^&N"7#;^B#2Rl9 "YH+'+6A]A(8HXhx]C'9014&&&#"3266666#"&&&&&'#"&&&546667666632326667 >]<=dN:& ?Z;@fO9%P=90+SPOOO)fm9.A(*fyOki32I-0vD2[UQ* *Tm>:a?]q>=e,I62AB@RiJ|78cJ+RiG9YA]A(8HXhx]и/*3901"#3266654&&&#"&&&'&&'!76677666632672  A.4aK,/JX,'\UC !!!?>??  ?E&H -nzDiq;+RxN)8)!!@]Y/!+J A ]A  ( 8 H X h x ]!601'&&&&#"#"&&&&&5466666323266654&&&'&&&&5466632 ; *3;3K1,QCOrJ#Rn"POI8"  #,9E+0U?%AiLBV1C}l_)0**>)2?!04;")LR\8^K #& $*,$-5- :T5+JEB$EKR-Bf@7^(7EX/>YEX/ >Y!и"01!!766667#"'66667!#&&&&## ,:$!*@  E  6)I 2P:SVO JQN/K3 7.r +и/и/A &6]и/EX/>YEX-/->Y! +01#"&&&5476&'7!3266676&&&'7!-?>W`cRpB ^6A  ?FXy~AbF+ \ , "ˊG2`\5> $II":6_KI|H3 /EX/>YEX/>Y 901&&'7!6&'7!>BJ  /9;3:  J5 0J  $ IIE Ir(\ //EX/>YEX/>YEX'/'>Y 9 9 901&&'7!36&&&'7!OB%-, '..S4@  .49^$4   { II %j  I7mEX/>YEX)/)>YEX/ >YEX/ >Y9ик"9701!!7666&'!7667&&&&'7!66&&'7!^ *4  0* ` =X` )  P5 1(  "6+# &I Z II'( II!+0 II U w3@EX/>YEX%/%>YEX/ >Y 901!!766667&&&&'&&&&76666326&'7!" +:$5;@A0&IH@0 >;4*F  0"4 6*I N0  I 2B II   9FEX/>YEX/>YEX / >Y01%!26667!'!"'3!?$%*M*.,NT('+n>]>!ag^57">W5l328?++01!!!!8$ $ ! %   ? //01&&&&'7 /3,},,/+ 0 ?+ +01!'66667!#'66667!,$ %  $ p" ^5V// /901''66667V1"   $&${X f } +01!'66677!    "  A / /01766663A.( :B;  k3 &LM/N/Fܹ! A !!)!9!]и/M+и+/ A &6]&и&/I+5+Iи/&0166667&&#326#"&&&'#"&&&5466676666326673267G@*F7*# t*SH9+' IOS($PC-(LnGBJQ**Q#"D /(z*`]U*(BU\[(8T7r5)7\F,VD)&S^Oy0"&A*"HqVEY [Q '+ +A &6]A )9]0'90/C H0C9S@/ "+M+01"67666654&&&#"&&&'4666666654&&&#"'666632666636o1@L( -42H'=VK#Wht?"`Y>   -phT"$2\SG0S>$<`CEj7,5(INN"4T< jc-S?&/Nf6io  E,$$.+iBS0+SzG4J &+A &6]!+0 + 09001#"&&&#"3267#"&&&546667666632:<6!'/6U9'4 6l No`-*cU8?eJJTX*,F;5750 '/'JuFAeG%OXGa:"TiCx1' &%&[ʻ '+U +A )9]и/A   & 6 ]4U99U99/A 99)999]CиC/9L ]I/X+1+Xи/"к41901667&&#"32666#"&&&'#"&&&546667666632666654&&&#"'6666323267M @%*E7) >**8D*SH<*'QE'RC+'KmGDJM$     !3k`K"$/#}B`0(AV\]'po5V4(9]Du})W]Ou/! ;o`N  E."$.'{s"US 09<o=/>/=.и./ A &6]>ܹ A )9]и/8/)+01666654&#"%3267#"&&&5466676666322?cu<) 'Ixy'.+y Rp_-)dW;&Gd?BLR'9S6:a,H@DKOT+0M5NaGc>%Q_Nu5'!2?8?!/4 ++ 494(и+01#"&&&#"3&&##'66667#'73666676632CD: #)*:' '+( ?@ D W{J)2Q=( K- %1^O<t63&%-%Ox@/#(' nͱ-UrR'QPs/'6#,( >U( S+A ((&(6(](DиD/EX/ >Y8L+?-+ A  ' 7 G W g w ]A ]01#"&&&546666632326667667#"&&&54766326772666766767&&#""'    ;Tenq42hT5! "8S;%?3( (SUV+!PF/8[&T&" : ,>O.  E *F8*;gRHwMEp[A$"' "%&"*"@lSTBYPH+!A!!'!7!G!W!g!w!!!!!!!]A!!]01#"&&&546663232666766667666654&&&#"'666632#"&5466632 AQZ)7:*P>&$0. $0<$(6'    *keR$Q-A+51-B*51f 7JUQEE}dyY +0 ! 88+%%Q^JrYHI@ C,&^<2!3"<3"4GYj E+A &6]E91E9U/*/;/ +;U9*!1;U9017666&'66323267#"&&&&&''6666666654&&&#"'666632  ES.:Q?;'+SvK}0.,*)D& !LJB"?=:85  BJA +!!  "*ojW'tR~3E(E #"4ES.MMTQA( @6+<^usdKlN!&}˱& E+&)X3W- +A --&-6-]-9/A )9]$ 5!//01%#"&54666666654&&&#"'66663232671d^R,  !*niV! 6,n7)<;s#  E+&$.'ý~""\ G+i ,+} +A  ) 9 ]}A ,,),9,]A \\&\6\]\_и_/ilиl/v }+q+/иqWиqd01%#"&&&546666654&#"'666654&#"'666654&&&#"'6666326666326666323267.c\P   &19 AIA ('#1:   AH@ (&  *keR ;f\S)%4!:aTK&4&  2)n6++Ql{ug!-( !7+;HZ9[p &f̱%0) 7*R1.=! :O.*C0&ailbS"  YA ,+S +A )9]SA AA&A6A]ADиD/L S[/$/<1+<G01%#"&546666654&#"'66666654&&&#"'66663266323267 -d]O#.   0:?!  AH@ (  *ohS mZ2!  -)n6+?6Un|q\;/'?/0a-C7/ &CpU E,% ,$6mh $>0#`mshW"&g3q4/5/ܹ A )9]4%и%/ A   & 6 ]и/ +/+014&&&#"32666667#"&&&546667666632~+: :O1/< %:, "@Z8@DCN|V.<^C?DG!NyR*LvP*Fm@CvY4";MVYwCu.&;gRH|s3#8d Zm$ +A )9]$\EX>/> >YXM+ .+Mи/Xк3. 901"32676654&&&'666632#"&&&''6666666654&&&#"'666632q;EI  4:<+C$7]QG!,P<#$1:DL)=@B BIA (!" ,ngT!?[;1oA/$2(6K5W="n)d@Q.(OsK;|yp-&J:%1E*ga8$ty  C-$& FG 6+A &6]EX(/( >Y@ +1+01%267667&&#"'66667#"&&&546667666632667spOC *G;- )Y+% %/3.$+ VA$QE-)NoGINM *S$"CmrXS,(AV\]'8T7*"ҥp $"}lkd&S^Ox."(|BV4 +A 44&464]949/> +>>/к9>901#"&&&#"'666654&&&#"'666632666632|"@@:BF> (&  *lgS 0TJC +/0R '/0(2ZzHKP &kԼ- C,& & %-.Oe9CD/E/D:и:/ A   & 6 ]Eܹ0 A 00)090]и:&и&/0?и?/&+? + ?9?-01#"&&&#"#"&&&546663232654&&&'&&&&5466632++$ '1;$8:RQ)O?&'BW_b+@nQ.&' %1A*BL"9H%5F)Jq>6RE;L6. *1*90/F)2@R5.UI<+ /6=?/ /7/ I:":3+79= ?hK),9b3 + и/A 33&363]3.и.//6++"и,01%#"&&&546667#'7376666773&&##32677neS=1Hx9    '*& F@9%#^K,H35J,&).'Q#)/ ^ J>:#(' +'" >4"-b5 +\ +A  ) 9 ]A 55&565]5. \BU \d+/R/_+и_8и8/01%#"&&&'#"&&&546666654&&&#"'666632326667666654&&&#"'6666323267-(QKA-&&Y_a->4"    *niU  !4CO-  *keS  -$j5)>^?6[B&>\=S]b[K C,&&.]nvn]9:<_C)TND C,&&.VckeZ![H <8 /+A //)/9/] / /:/* 901'&&&&'&&&&#"'666632666654#"'6666.h{ .40 ( "$$ *aXF% EU07!YYO#/T r&Uǿ. C/'9FaxnQ myK1A)##ogT W+A WW)W9W]4/H/c/ // 49> 49T 4901'&&&&''&&&&&&'&&&&#"'666632666676666326654&#"'666632o!8TtL .4/ )  ':M. 3;5 '  +bXF  (G5!883  il0%PJ@\@Yx֋&U.#mR &9qU C0&AHShrjXN-)"!6GJJ/`[U$<C%IF+(+(;01%#"&&&''66667&&&&#"'666632666654&'6666323267+RJ@875(_6EKD+kol.530 **\SB679!8(0@!MKA;120,D%f4'3Ws?6Q +&bko4Fd<E-%YA'7GWgw]A]7 /901#"&&&5466673267&&&&&&'&&#"'666632666654'&'6666325_NEX'L<&!-/0`%9xD $'&$  !-`UD*()()I7 5%$RPG \"֚U :>3-+vn;g3C-$:E2SRe2 C'<78//$/ !+;-+! 901676632326667#"&&&#"'6666667#"'666673!7WLEKT5  =BA !;29G1,UOD ^L$2c`ZRG$A B#j;kfflwC,$#>cE$(&2 A >>&>6>]>9 A  ) 9 ]9и/ и/W 9EX$/$>Y6A66]A66(686H6X6h6x6666666]01&''6654&666767&'&&&546667666632'&&&&#"326667}DoK*F1);5LrB(Q|T$Y_c/1`UD  <'5C)(J(?T38Ud-AQd> Dy7?>5X2$E 84 !|t^ùC. $+2-% ;.1?mX& !8-D[7 H"( @F}"1X@Kt"2D@7t"8t@&"D&"DY&"D&["D&\"D&"DGdOP/Q/ܸPи/8 A 88&868]89 A  ) 9 ]89и/ и/ и/HиH/M 9$3+)3$901&''6654&666767&'&&&546667666632#"&&&#"3267.DoK*F1)'.1U8?eJJTX*,F;5:<6!'/6U9'4 6l N85.?>5X2$E 84TiCx1' &% 50 '/'JuFAeG%OXGa H0"H}09"H/0l"H0["H7"7"7"7["  \"Q&"Rq&g"R#&g"R&v["R&\"Rs-"X-"X-"X-["XA/:/$:9>:901&&&&''666676&'667'666676&'66667667 %,+47;# 0=  .51  &3UU &,+35:"BE?(-AUVXAHCDCo`:u="JKHcnCD*%3@HCPG ## [\)%%*&/'/ܹA]A )9IYiy]&и/A&6FVfv]A]EX!/!>Y+! A ]A  ( 8 H X h x ]01266654&&&#"%#"&&&5466632+ *".$=]n1-?);[n4(?,j*4$'6/5I{Z2 3A!E{]73CYGr :+A &6]F/1/ +1F9 9  и / 9 91F9012632#"&&&'66667'7"&&&&&5466676677667>:)V+E0> /TH; "&)% )Y8?J. 9]OG#8CD?1%KoJC'*\pqUn|!*' #()!%24}+;&4O:& y"6PkFCw1 KE? I+Iи/?M2B+J+%иJ,01#66766667&&&'6666667#'666673666632#6&#"!s M=*=/'*1"+@41@ -|22'k  ]XhB;/ |"1<(?5/AUlE"D?7"M/>bC# 9GEY_2-!3.0?VpBu //01#"&&&5466632#Cb@&8%$Ec>&8$8t];.>$8t^</?@x "1>W/EX!/!>YEX/>Y1+!19$и$/&и'01!7667#"&&&76666323&##"3&&'667o@D6A @EY'al1 Th@NV*9L,YK52XCH$  "( #II"2a^Ug9 >X86mW7  [3\]/^/ܸи/]2и2/ A &6]2и/; A ;;);9;]+и+/XиX/K/X@+&+01#"&&&5466666323266654&&&&&54666666654&&&#"'6666767663230HTH00HSH0AqT*ZI0 %.1'/GRG/,9:9,0> >Z@& X W{J*2R=( X9IO{T,SqP:6>,$:78EX:>}e@/5!),%/7/"8*9P=3;J51I8-+,8G0-P=$FuS oͱ-UrR ]+24Tio'Q\(EX3/3>Y#++01#"&&&76666326&&&#"32666767766327#"&''#"'332676&# @`|FGmF A`|FFmGR4W;;`G.5U;;aH.j%+ B%'6! :*LE%  (/".YHV+#+2 +229b<A<<]A<<(<8<H<X<h<x<<<<<<<]01#"&&&#"3267#"&&&5466676666324&&&#"32666667#"&&&54666663200+ %-$>2' 4D%-zW  Bj\S**bU82SA>HP).I9*YEX/>YEX@/@>YEXI/I>Y ик.919E901#&&###7667#"'66667!"#7665##7667&˜6633U Z.##.\  Z,"%l'2   _  ! !( $$ S") !# $  $$ I $$ \$ $ _ //01&&'_%&<5 W <[#o$/%/$и/ A &6]%ܹ A )9] +и 01#"&5466632#"&5466632)<%0-):%$ )<%/.):%$ B6#<(B7%%B6#<(B7%%+eG1? /%/1+/+и1и/и'01!'7#'6666737!'66667!7666673#! '.* nv ')'n0 ! *      ) !IdEX%/%>YEX / >YEX/ >Y+%02и<и C016&3!7667#!76676&&&'7!#6&#!!&&##3326667.    ?DC)B 9F :,  F$8o  H0= 'I=W+@51] Q+_UBI! II  I NRI]`l#&% G 1P;! JK/L/ܹ A )9]и/K3и3/ A   & 6 ]и/F/./EX=/=>YEXG/G>Y%+.F9.F9=A]A(8HXhx]014'3266666&&#"%#"&''7&&546667666632766667 Z:@fO9%_@=dN:&G2I-*ivCL~30 ,1. )*.A(*fyOR25+-*%$>8u7<=e+N$[I@$-I0Ff}) :.$+2* )9-$*3+ 3? 1D*#B^;>y`;3@ 6E($B^;>y_;o - -+*/ +-+и-#01%!'66667!#'#'666673666673o^%   %%   & !    !      u / +01!'66667!667%_d 27 !   N@!,3D / +01!'66667!'66667%%'667^B 2 56 !   )A $'# +4;?@1/>/EX/ >Y+ик919013#!7666677!'6666737&&&&'&&&&76666326&'7!1>?.  3)  *8# 49:.%HIA. 63.)D wp  ` II L/  I 2t~B H IhC '+b +A  ) 9 ]A CC&C6C]'C9'< bj9/X/EX/ >YF + к 99 F9Feиe/h9901%#"&&&'#"&''66666676654&&&#"'66666632326667666654&'666673267I-VNE,!EJ5[! %& )5:3()   GJJ=,  0#/:E*  GPJ ;,Lc:'CY2vF=d{O $8C@y6 C  &.S^d\PSU9aJ+XPB B  _p{sc!?1*< D++>4+*+/*901&&&&#"326667647#"&&&7666666326&&&#"'76632'5=*D4$ )>#1O>-H[gg`&[V -BS`i654-!@O"<>C(*6_,1f\K.47W>!2[NNd:@yom@F|dA~p`E'!.e' /#(:f ITP +A   & 6 ] J>///6/OT>901%#"&&&546667&&'&&'&'66667&##"'6666323267##"'3266670TJ@1% *?~E  &174. 'C;8 /,. EECl@(A *>A@   #)I\4+K9YtSF  A٘:"OC,  #7 MB,jN/;" '! GEX / >YC + C9C /A//'/7/G/W/g/w///////]A//]01#"&&&#"#"&&&54666323266676666667666676632CD: #)*:&5K_8?BB%L>'#.- &1;!)8*"  $1^O<t63&%-%Ox@7(Ote."  ! 88+%%S]62Qt0'6#,(,J` J.+A&6FVfv]A] +4+G!+Gи/!)01!'667!666675&&#"326#"&&&'#"&&&54676632673267R !%5";.S &K#2)ZML1.&     =7+  /FO<@?^) , 3A,F2W5 # 'MB3 #, J$12/3/ܹA]A )9IYiy]2'и'/A&6FVfv]A]и/*и*/+-+ "+014&#"32666!'667!#"&&&546766327$( "& dJ?FG!M&1F-KP$9G"!:,' C *F8+$y  ?EF4O6 'Ixz *.+z Sr](PM> $RVV("NC-'KnHAJP*(B2 <[+IB@#)-#z&UTK.(AV\]';T6rp   !2? ?DJOU-3L3NaGc>=aE4\F)&S^Oy0"   GH/I/H1и1/ A &6]и/Iܹ A  ) 9 ]и/ и/-/EXD/D>Y$+;+-D9-D901&&#"4&'32666667#"&''7&5466676666327666678: :O1n= %:, "@Z8@DC9`' ).+ o;<^C?DG!7[% (,( k8-}**Fm,-5";MVYwCu.&%aH|s3#'_=q>/?/ܹ A )9]>и/. A ..&.6.]3и3/3+ +01466632#"&#"&&&54666667766732666'66667W0E-?:1E+<<sBygEjI%9Wh]D =.CMB+!0 !1 =@<!F9%?3 C9$=@hB&BY3Tm^`jAY{@lc^_f;)F4$>T/  8 +A )9]/ +01466632#"&'667b1E+<=0E,?;~BF@*= E9$>0!F9%? !" .Hy //01&&&7%'6666766666672&    ~H% EJD%)"4@ /+01##'6666773< :B; s #%# S( 1" " @z WEX,/, >YS ++ S9S,;A;;';7;G;W;g;w;;;;;;;]A;;]GиJ01#"&&&#"3&&###"&&&54666323266676666667#'73666676632CD: #)*:' '+( ?@  5K_8?BB%L>'#.- &1;!)8*"  . $1^O<t63&%-%Ox@/#(' >va Ote."  ! 88+%%S]-v:'QPs/'6#,(4SL7=-/7// / +-&2!01#"&&&#"'66323266677#"&&&#"'6632326667*ADE&GEF$+I.38?(MH@++'cADE&GEE$+I/29@(MG@,*((A.$50MR_%%)A-%40MQ`%%%,(%/EX/ >Y%901%&'&&&&'3!266&!'6766667667  A@@CA .+i,0, me+\WO2l8',.36ĕ?9?=  ;[ºA"5Kc3%-//EX/ >YEX%/% >Y01676673676673. ",-+"YEX/ >Y01'6766677'&&&'&'7'766666677'&&&&''7Y/C6,"  $YEX/>YEX>/> >Y,+>A'7GWgw]A]P A ]A  ( 8 H X h x ]2и3и2D01%26667&&#"#6&#!!&&&&##3326667!"#"&&&7666666323-"4,'{#mfyA.6->"4M41;8O1 )Jz{)1 Rpb/>A<AEDMzU.;]ABFEj#")@JR*6S88\.H?;!)4$LvP)Em@CwY4KrT=EJOU,.L6*C0Gc>$;,&;gRH{s5#_S" (!2?YD +01!'66667!Y%    D +01!'66667!h%   {\1]2/3/2и/ A &6]3ܹ, A ,,),9,]/1/017'&&5466677'&&546667.$C3 )6>9. -5Tg2-$C3 )6>9. -5Tg2x@EG!30M J:@tb#2@EG!30M J:@tb#T1]2/3/2 и / A &6]3ܹ$ A $$)$9$]//01'666654&'666666'66665&&'6666665Tg39.$D3 )6>9. -5Tg2:.$C2 )6>9. - ?tc#2AEG 31L J;?tc#2AEG 31L J{\& +A &6]/017'&&546667.$C3 )6>9. -5Tg2x@EG!30M J:@tb#T& +A  ) 9 ]/01'666654&'6666665Tg39.$D3 )6>9. - ?tc#2AEG 31L J7 +!+*"+ +01!'66667!#"&7666632#"&7666632r#**#*P9#**"*O\ !   3'404&3'403&.9EX / >YEX/ >Y 9 9013'66667D +0+ +0+ =GjA  < ["\wt"<>@e //017'66667 #"  $!   1{?'//,/4/ +0+01"3266676&&&'#"&''&&'7&&7667'7667663277+N<(+C,+M<'+Ch(\34V &, l +i*^32S '*!k4$=O+'N='$=O*(M=&!++b46d,,  "#U-e34d+-3/EX/ >Y016766733/ "+.,!<-:KNJ:#]-./EX/ >Y017766666677'&&&&''7. ",-+" ! ;4X,:KNK;]l]-VWm2 C+A CC)C9C]C292oV/(/i+5@+#O+ и /i9 O]и#`01#"&5466632'#"&&&#"!266632326667#"&546666654'&&#!'66667#'73666676632,B*61-B*51CD: #),# ^!LF7  /b[N"/ CGD W{J)2Q=( J. %=Z?6r>/]P><2!3"<3"4d63&%-%$>QZ^*/ &.g}}g C6+?6j}c nͱ-UrR'QPs/'6#,([VU +A )9]U]0//C+)+)7и:01%#"&546666676&'&&#"!&&&&##'66667#'7366667663232671d^R, ! a6,A0  ( $'$ "3B%D W{J)2Q=( J- %=[?5iL~/ #  7,n7)<;NAP"//'AU]_+/**" nͱ-UrR'QPq/'=.&/ &07R}%"9X3%/Q/%Q9)%Q9;%Q9U%Q901&&'67&&&&''667'666676&'667'666676&&&'66667667 %,+6gE#0 %,+46<# AE>)0AUU %,+5gD%/ UU &,+6gD  BE>)+BUUXAHC$*9. .k@tb#2@EG!21L I01]2/3/2 и / A &6]3ܹ$ A $$)$9$]//01%'666654&'666666'66665&&'666666u5Tg39.$E2 )6>9. .5Tg2:.$C2 )6>9. -k@tb#2@EG!21L I;@tb#2@EG!30L IP';O]sj ~+t ^+< (+ + +A )9]A )9]A (()(9(]A tt&t6t]t2 tFиF/A jj&j6j] +c+my+ 5иA014&#"32666667#"&&&54666324&#"32666667#"&&&5466632'666674&&&#"32666667#"&&&5466632."-! (+* 1_Y6ZA#3bZ3V?#."-! )+) 1_Y6YA#3aZ3V?$ '*'  &*'  -! )+)!1^Z6Z@#3aZ3V?#VJ*DTULOI$Y+!++y+)и)//и!7и7/!и/=и=/и/DиJиJ/7X`иfиf/ylиl/rиr/Xи!и014632#"&4632#"&6632#"&546632#"&546632#"&54632#"&546632#"&546632#"&546%2##"&546%2##"&546#"&54632#"&54632#"&54632#"&54632#"&54632#"&54632  6< d<757 E K"2 @7"8 c@7"8 j@7"8 @7n // /01'&&'3& $ +7 "zkd\'//// 01#"&&&#"'6666323267d$+058(C??###%A=HR++G?:*E'>>>9, '!*)bU9!'!9:! +01!'66667!= #%! "$" _//+01#"&&&'66667326667%]b`(,YN=4?AMNI7QoEFoP 2C))C2<[, +A &6] +01#"&5466632);%0-):%# B6#<(B7%%'#ɸ$/%/ܹA]A )9IYiy]$и/A&6FVfv]A]/+014&#"726667#"&&&5466632   'D\6#4$&BZ5#7%&!+'!,=,aQ6(50dR5*7[d< +A  ) 9 ] и/ 9/01&''6654&66667DpK*E2)b ?>5X2$E 843:H / ///01&&'&&'( */() */( 8 8Bj0 +A &6]/+01#"&&&5466673267jBKQ'7/ 8Zo7f(H6&J*6, 2(4bXK?BA $ &q ///01#667%ĉ#  u &G"D#&"D#&Fg"D&"D#&o"D&F"D#&<"D&"D#&-w"D&"D&G"D#&"D#W&"D&"D#W&"D& "D#W&="D&"D#W&"D&"D&!"D&>"D#&["D&5"D#a&"D#&"D&G"D&&LM/N/Fܹ! A !!)!9!]и/M+и+/ A &6]&и&/I+5+Iи/&0166667&&#326#"&&&'#"&&&5466676666326673267G@*F7*# t*SH9+' IOS($PC-(LnGBJQ**Q#"D /(z*`]U*(BU\[(8T7r5)7\F,VD)&S^Oy0"&A*"HqVEY &"&"Y&"&G"#&"#&"#&F"#&"#&"&G"#&"#W&"#W& "#W&"#W&"&\"&!"&["&>"#&["&5"#a&"&"#&"&G"#"O#!"[mb"$# 0@ c?"$(@mb"$# 0@ mH"$(@"$# 0@e "$(@m"$# 0@j"$(@Gm"$# 0@m"$D@m"$#D@ Wm"$*@m"$#D@  m"$*@*"$*@*"$*@m?"$#D@^m"$D@Gm"$#D@m"$5@@"$5@"$#:@imt"$,@"$#,@am"$#*@ cm"$0@Gm%"$" c@@"o@[+c + 9+A )9]A &6]'9'/A '')'9'], B99B/U ZBU9,eR/4+ +_+01#"&5466632"67666654&&&#"&&&'4666666654&&&#"'666632666636x);%/.):%# 1@L( -42H'=VK#Wht?"`Y>   -phT"$2\SG0S>$B6#<(B7%%<`CEj7,5(INN"4T< jc-S?&/Nf6io  E,$$.+iBS0+Sz*"E[G"Er0TV5 +A )9]5V0/=+P%+%P9P 01"36676654&&&#"&&&#"666632#"&&&&&546767666632p1@L(-42J, %CD; $+0&E;0#0ZQH3U="SN#Vfs?>@;.Q@DD 4_P><_Ce$7..,JDEY3~63&%-%']u?R1+RzOje,S@&(5?D" ;/  `"#,(t"%@ "%G "%h TM +A )9]и/Mи/MVEXH/H>YEX+/+ >Y$+ +H и/-и-/1+H9P 9014&&&'32666"3266654&&&#"&&&'&'#7667&&&&7666632>cH=3kX8&4C#V,`A"5J/D6` .Oj=2]H,btA I! (6$$8"%09HwU0"A]:i, 7SiG"FG"FG"FG["FGdOZ[/\/ܸ[и/8 A 88&868]89 A  ) 9 ]89и/ и/ и/HиH/M 9U/$3+)3$901&''6654&666767&'&&&546667666632#"&&&#"3267&&'.DoK*F1)'.1U8?eJJTX*,F;5:<6!'/6U9'4 6l N85.?>%&<5 5X2$E 84TiCx1' &% 50 '/'JuFAeG%OXGa H O8i9/:/ܸ9 и / A &6]" A "")"9"]+4'+01#"&&&5467666673266654&&&#"&&&&'666632O'E`sDFpO*#IPJ%:D,>X7->"3_ ?qjf5KsO);@{]7%B[60WC"S30$IsDLmF IY@[;1^4+!++++9001666632#"&&&'6667732666766&&#"#"&&&F?FN,*SJ? A >>&>6>]>9 A  ) 9 ]9и/ и/W 9_/EX$/$>Y6A66]A66(686H6X6h6x6666666]01&''6654&666767&'&&&546667666632'&&&&#"326667&&'}DoK*F1);5LrB(Q|T$Y_c/1`UD  <'5C)(J(?T38Ud-AQd> Dy7?>) b/.( 5X2$E 84 !|t^ùC. $+2-% ;.1?mX& !8-D[7 H (C  Rm +MиM/OиO/+3A+Q+-'+иQи-Fи'I01!326667#"&&&5#'666673667667#'666673666632'&&#"!!!O$9I%5BQ6 AmbX+H_8N E+ :$la]* : VE?>;N)\|K 8.%DV0;ut! +!pn6:*'.*!JNCzb[% 7( >ĸ?/@/ܸ? и / A &6]& A &&)&9&]EX:/:>Y+:+A++]A++(+8+H+X+h+x+++++++]01#"&&&546766667326666654&&&#"&&&&'666632('Koi[X+#OSI%1 3C$AgP9%2Sk9?Qd< Kyi+]q?W|H-Pk>0l- !P389)C/=fY#+2A22]A22(282H2X2h2x2222222]01666632#"&&&'66667326667666&&&'&&#"&&&&N\e1/^WLBN rW/hjk21TH=-om`  )!C()KB7A  $ .C^t|A7[D-8! &Xm*elnfX!.;  CI;&"G"G&G"G&Sh '+b +A )9]и/A   & 6 ]4b9Ab9A/A AA)A9A]?и?/KиK/AT jQ/e+?7+1+eи/"к419?Wи7Z01667&&#"32666#"&&&'#"&&&546667666632667!'66667!654&&&#"'6666323#3267M @%*E7) >**8D*SH<*'QE'RC+'KmGDJM$      !3k`K"$ 0  /#}B`0(AV\]'po5V4(9]Du})W]Ou/! ,S&3"  E."$.'{K XIzaUS &_`/a/+ܹ< A <<)<9<]и/`DиD/ A   & 6 ]?и?/P+<9.7+[+N+.и/[9[7?кPN901667&&#"32666#"&&&#"3267#"&&&'#"&&&546667666632667666676632L=#*E7) >* &6GpCD9  %$%$  /#*TH:,)X@'RC+'KmGBHN'  .:E$5x9.VH9yDa+(AV\]'po2U=63&%-%3_P;fXP B5(3^M{u)W]Ou/!*KBnYF'6#,(& gi /+\ $+A   & 6 ]A $$)$9$]'$\9<$\9?$\9?/A ??)?9?]R kO/EX/ >Y*+9+'O9<99_A__'_7_G_W_g_w_______]A__]01667&&#"32666766#"&&&5467#"&&&5466676666326654&&&#"'6666323266&'66667eA%*E7) >*+7E( +F[ce-$:)K@'RC+'KmGDJM$  !3k`K"$'0!,$!9>? ._11(AV\]'po6V>#G|,VLA/&Ge?Rpfl)W]Ou/! v9  E."$.8z޺*NX-9&AP +A &6]4и4/6/+% +( %901%26667&&&&#"#"&&&5466632&&'&&''7&&5%7=X<"+VkS|S*Lc.Q6& 7/jK0P"(5Q<[TK:`E'#YEX/ >Y+5A]A(8HXhx]и/ A  ' 7 G W g w ]A ]%и,к.5901"3#326666654&&&#!7667#'6666735666632<?1;6LEu`I2(W*Pth ?D@s  m=9~~x3֋D4 XJ5[yFkv?QXl?I!]K 9  +A )9];EX5/5>YEX/ >Y+5A]A(8HXhx]и/ A  ' 7 G W g w ]A ]%и,к.5901"3#326666654&&&#!7667#'6666735666632<?1;6LEu`I2(W*Pth ?D@s  m=9~~x3֋D4 XJ5[yFkv?QXl?I!]K 9  +A )9];EX5/5>YEX/ >Y+5A]A(8HXhx]и/ A  ' 7 G W g w ]A ]%и,к.5901"3#326666654&&&#!7667#'6666735666632<?1;6LEu`I2(W*Pth ?D@s  m=9~~x3֋D4 XJ5[yFkv?QXl?I!]Kh -LEX/>YEX/ >Y9.;A;;';7;G;W;g;w;;;;;;;]A;;]01666$32#!7667&&&&%3266666766&&'&&&&#l b WdK9K' Gd|I ?Gb] $-65, ( 7/ NSRF5 -(ENS+LvQ* $-zPyd?I!W?$=)7=ؑZ.3Pq^D~j&$ 0"H#0g"H|0l"H#m0\o"H|0F"H#0"H0l"H#0w"H|0Gl"H#l0"H0"H0\"H0!"H0-"H#r0-"H#cr09["H09"H0G9"Hl0d9GWK + +) P+A &6]A KK&K6K]K9K9K0и0/E 9A PP)P9P])Y$/01&''6654&666767&'&&&5466676666323267666654&#"DoK*F1)%,2W;&Gd?BLR'9S6 'Ixy'.+y R87/?>2?cu<)5X2$E 84Q_Nu5'!2? >DKOT+0M5NaGcH:a,H@~%]b`(,YN=4>BMNH2?cu<)5X2$E 84Q_Nu5'!2? >DKOT+0M5NaGcHQoEFoP 2C))C2:a,H@2#":^y>KU.R-BH2@"@IN%3QE=541$+3+%7%0(S-I74AJYI@]=7N/0UH8R="C=6#( b"(# @ l?"(@b"(# @ H"(@:"(# @U "(@"(# @ Q"(@G"(# @"(@"(@}"(@&@"(@&"(#@ &"(#@ t"(@"(@G"(dQ +A &6] 9и9/O 9EX/>YEX/ >YEXJ/J >Y+8+)>?01&''6654&666767!76676&'7!#6&#!!&&&&##3326667!SDoK*F1)o AE8A 1 F#8q  +>+t< 'I=\*?40A ?>5X2$E 84I! $I NRI]ak#&%! G 1P;+_UBHdQo +A &6] 9и9/O 9a 9a/k/EX/>YEX/ >YEXJ/J >YfW++8+)>?01&''6654&666767!76676&'7!#6&#!!&&&&##3326667!#"&&&'66667326667SDoK*F1)o AE8A 1 F#8q  +>+t< 'I=\*?40A ?>%]b`(,YN=4>BMNH5X2$E 84I! $I NRI]ak#&%! G 1P;+_UBH#QoEFoP 2C))C2")>CEX/>YEX"/" >Y 8+ "1016666733#"'66667!!'466666733326667#" "&# (6&8):/,C  ?C8? " G":H)<20+y++%  &G:+VI9I!! $I!6?C<0 .UA'& D 0+A &6]: 5:9EX@/@>Y +++@ A ]A  ( 8 H X h x ]5901'&&&&#"326667#"&&&546667&&&&54676632 &%@)4A(5X@$ K}]DvL .s]Ba*YVR<@9%=- ;U5&G:'e"BdD3K2 8,KM_5(OwNU( S+A ((&(6(](DиD/EX/ >Y8L+?-+ A  ' 7 G W g w ]A ]01#"&&&546666632326667667#"&&&54766326772666766767&&#""'    ;Tenq42hT5! "8S;%?3( (SUV+!PF/8[&T&" : ,>O.  E *F8*;gRHwMEp[A$"' "%&"*"@lSTBYEX/ >YEX/ >Y 01#76676&'7!!76676&&&## ?D6? b @E7@ & ?B#6%#6%6@ I! $II"! #II!    N #I7"7""\"7!"76D"# 7k"7G1D"LEDOG +A GG&G6G](G9(/A (()(9(]; Q8/J+ +%+%@иC01#"&5466632#"&546667#'6666736654&&&#"'6666323#3266671-A+51-A*52/b[O"/      *jeR .  <2!3"<3"4^6+?6HYc29Z C,&&.EVa1!WBm 71]) +A ))&)6)])9/A )9]" 3/,+01%#"&546666654&&&#"'666632326667/b[O"/  *jeR n6+?6fzw^ C,&&.g}}g E?w7 +A 77&767]79/A )9]+ A(/:+ +0и 301%#"&546667#'6666736654&&&#"'6666323#326667/b[O"/      *jeR .  n6+?6HYc29Z C,&&.EVa1!WBm 7 OD"LM8*4 /+01%#"&764&&'76666733266674KT[0ULE 8/ %[]W!=c  #0 3_H+ru3=$E!$;F% /& '",(@ !",@ A}",@ a",@ H",#@ A t",@ ",@ G", !?EX/>YEX/ >Y + и 01#!7667#'6666736&'7!3R@7B  ?F?  77A  @F8X. #II!  $II"k t1",-xt t Bh@/EX/ >Y!A!!'!7!G!W!g!w!!!!!!!]A!!]01#"&&&546663232666766667666654&&&#"'666632 AQZ)7:*P>&$0. $0<$(6'    *keR$f 7JUQEE}dyY +0 ! 88+%%Q^JrYHI@ C,&G"N DG"N D"NGG"NG;`=//(9+MG+-9(9(4]01%#"&&&&&''666666766667666632#"&&&#"7666&'6666323267!OKA"?=;85  BJA + 1Kc=@FH"1_Q@)./*! #(.&D;1$ER-:OSP ;'+SvK|0-,+(D&q6+<_vseD|kW!&'5Mt."#,( ##%-%'^ugi4E(E #"4FT.KNSR@( ". E@".Q@".!G".D//EX/>YEX"/">Y "9"9:A::]A::(:8:H:X:h:x:::::::]01%#"&'!76676&'7!6632'666676&#"6670[QA'C7B  AE8A  AF?vGU+H0 3CF  0c (19~#5#N #II! $II"0TSp,  E+&$. eN X >  QW +A   & 6 ]4 94/A 44)494]G SD//01#"'3267#"&546667&&#"'666632666654&&&#"'6666323267 2AN-  6,1d^R,  #G#B3BO,    !*njV!#L (eX=EjS"C7)<;|h?8(dX=@ucM  E+&$.'a ?8C= +A ==&=6=] =9=9/A )9]- 2-9E*// *92*901%#"&546667'666677666654&&&#"'666632732671d^R,     !*niV!    6,n7)<;dY_ "bWn  E+&$."mT`"4"d[r ""/ @"/G"/G @"/#@.IEX/>YEX/ >Y +и !и(01!7667#'6666736&'7!3#3326667  AE6  B8A  AFB/. %A3h*>3/0+_UBI! $II"# X 1P;>cEX6/6>YEX/ >Y+=+ и&и(и=001#3#3326667!7667#'67766737#'67766736&'7!3/ %A3h*>3/@  AE'    /8A  AF/} X 1P;+_UBI!  }  S $II" A+EX / >YEX/ >Y;01!7667&#"'6666326&'7!326667#"'3326667  AE>###B3BO, 28A  AF>&%$$C2AN-* %A3h*>3/0+_UBI! $1(_S8k $II"A$1(`S8 1P;2?EX+/+>YEX/ >Y +91+9013326667!7667'6666776&'7!%7 %A3h*>3/@  AE-jG8A  AF5`&&# o 1P;+_UBI!ID)(" 7 $II""P["PG"Pi"0 @it"0@Gi"0  "Q  "Q}  "Q  ["Q  "Q G "Qh n޻a +V A+A )9]A VV&V6V]VYиY/hap/:/EX&/& >YQF+&5A55'575G5W5g5w5555555]A55]Q\01%#"&546666654&&&#"#"&&&546663232666766666654&&&#"'66663266323267-h_M#/    /:@pAB?%OB*$/. $0<#$1%   !*nhR yT2#  .)n6+?6Un|q\ ) '@.ei\" " 88+%EuZ.f E,% ,$6ta $>0#`mshW" hi/j/ܸiGиG/\ A \\&\6\] и /* A **)*9*]\_и_/EX/ >YWL+ A  ' 7 G W g w ]A ]Wd01#"&&&546666632326667666654&#"'66666654&&&#"'666632666632  8IV-6E'WI0  *5= $7-%%  /:?"  @H@ )  !0ofO 7oia*2"YvCQlX&-5#$ && !' F|d}ڪs;/&@00a-E8/ &CpU E,$ ,$67Q4$>F"1 V@F"1 @F"1b@Ft"1Y@F"11GF"1/tF?/EX0/0>YEX>/>>Y&+01&&'&&&&'#"&&&54666667326667&&'7326&&&'7!=E@We0puv6[0:F(:9<0&%" "-31.?" m /$  %}9-Xda{P /.!#%U#IItFEeEX6/6>YEXD/D>YEX"/" >YEX-/- >Y +&-69=-6901#"&&&54666667326667&&'!7667&'7326&&&'7!=E@}+8E)6|=%G9"!(&! !)1))%# Al .# y E?3D m /$  %styT +1! #&3]L $JII &< IIu l» "+A "")"9"]" n/EX/>YEX`/`>YL?+%A%%]A%%(%8%H%X%h%x%%%%%%%]h90123267#"&546666654&#"#"&&&546667326667654&#"'6666326666"L@+?!/^VL';81OWXK4   &/676EJJ $H8"#3<-M#3.)x1&3maP Go 9`HAx/**= 7(UO+9>>7Of{G*:E#RdUjR>.'! ,0, %2LxJ+& U'!:0 )4<VsE \ݻ% K+%(и(/A KK)K9K]KHиH/%^EX/>YEX / >YEX/ >YB1+9 NANN]ANN(N8NHNXNhNxNNNNNNN]011766766&&#"'666632666632#"&&&546666633266676654&#" ?Eu8#4ocN =0$OC,DaW5>F((G4%.,& !< <8R]`P9I I!L)6 U(!.43=@LtP)9`H3fN'#4<06,%GgB&GB:7N_o=  I< X߻ )+и/A ))))9)])$и$/EXI/I>YEXT/T>YEX8/8 >Y + T,A,,]A,,(,8,H,X,h,x,,,,,,,]O8I901#"&&&546666632326667666654&#"!76676&&'666632666632cG`t;CLP&,\L0"*)# +8F)+QD4\94MUVH3 I>?  ?Eu A94ocN =y0"L@+..9fw)#(+&)"(0(6nq'% B:6N_p= #II!L:? U(!19"j7MrJ$9`, \" L+"%и%/A LL)L9L]"^EX/>YEX/>Y<0+0COAOO]AOO(O8OHOXOhOxOOOOOOO]01766766&'6666326632#"&&&5466666323267666654&#", ER/C>2qeP k*P?&5 ;O]0#IP[4N^4%--&  +7D)n*7<OURE0HF I!K9@ V(!&*(*<^A%}Z~b#*'68 $*,$.7.2 /1* 4D8MZe40 "I&"Ry&"R#&g"Rp&g"R#a&go"Rp&F"R#&"Rz&g"R#&w"Rp&Gg"R#o&"R&"R&?"R#s&"R#s&"R#s&!"R}&-"R#}r&-"R#}Wr&"R#&g["Rt&"R#ta&g"Rx&Gg"Ro&g 3q4/5/ܹ 4%и%/ и /и/и/ *и*/ +/+  +01&&&&#"3266677#"&&&546667666632{*5-D0/:.D."@Z8@DCN|V.<^C?DG!NyR*CeC!*Ib7xEtS.5WrY0+9&9A]A(8HXhx]9&901&&#"6&'32666%666632766667#"&''7&&  ]'`7P|W  '`7P}VtunN8_--*9.uoN8U %069-u[B#(Qi[B #'Qjh50v  (Y焉h40k(Z&J|D *+ +A )9]A   & 6 ] :и:/?9F@/%+4+014&&&#"3266666#"&&&5466676666326654&&&'7~+: :O1/< %:, ̈}""@Z8@DCN|V.<^C?DG!Px*(*   LvP*Fm@CvY4";MVYe[Y& +;+6A66]A66(686H6X6h6x6666666]01#"&&&5466676666323266677464654&&&#"2I-*ivCfm9.A(*fyOki3 ?Z;LtT6  >]WYx Tm>K{R" 8@K>G -+ +A )9]A   & 6 ]=9=/A ==)=9=] B9IC/EX7/7>Y(+7A]A(8HXhx]B7C9014&&&#"3266666#"&&&5466676666326654&&&'7 >]<=dN:& ?Z;@fO9%**2I-*ivCfm9.A(*fyOY324   Tm>:a?]q>=e\=D_G96]E(RiJ|78cJ+93@* nK>"0 3@K>"0 @K>}"05@K>"0:@KG>"0 JEX/>YEX/ >YEX-/- >Y !и"и;A;;]A;;(;8;H;X;h;x;;;;;;;]013'66667333&&&&766666632326667!766666676&&&#"& D&Mh= 0Ni[rk( Bm^B5:XB/  'AUZY%4VRSavL "S ["S ]^/_/'ܹ A )9]^KиK/ A &6]K96K9K<иY 1+"+A[961 901"3267666654&&&666632#"&&&''6666654&&&#"'666632u7BH"(39<#H # #6]OE,P<#!3"YEX./. >Y8$A $$($8$]8$9 .A '7].89016632#"&''3266654&&&#"!76676&'7!/?& F&`m;D{f*G $D 3XA%'BW10h ":- ?C6?  l1Y}KWvFh (KjAEe@ II! $I& Ze @+A   & 6 ]EX2/2 >YV+;+J+V9V01&&#"3267#"&&&#"'66667#"&&&5466676666327666676632D *G;-!(qPN(-.)   %'&! "')% CLC +WB$QE-)NoGINM  .U4os ##%-%6^I@z$0ckf&S^Ox."EoXC'6%-'& q^ֻ L+! ?+A &6]!'A ??)?9?]D?!9!`EX:/: >YV +G+:$A$$'$7$G$W$g$w$$$$$$$]A$$]01%26667&&#"32654&'66667#"&&&546667#"&&&546667666632667s*7E+7"@ *G;-)\')( :A@!.FXe1*<'  NE!PG/)NoGEKO%+)%"E7W>S((AV\]'2S: *"1TV.&( :368/+1Nc26bfm@w}&S^Ox."  "  HEX6/6 >Y+D+6$A$$'$7$G$W$g$w$$$$$$$]A$$]AD9012676&#"'6666323266&'&6667#"&&&7677#"&&&1&#8aSCX/ 451%%2JP!;NY_-Ga5 340,X'.E,KN#&4+]M29^y@{u]&-;"?KKG6!A}u'($:KA : hL S+" D+A   & 6 ]"*A DD)D9D]ID"9"jEX]/]>YEXe/e>YEX?/? >YN+]A]A(8HXhx]?%A%%'%7%G%W%g%w%%%%%%%]A%%]I?]901&&&&#"3266676673266654&&&'66667#"&&&546667#"&&&546667666632667z.6>#4^QC/ 6E%MW\*" #-   6BE$-KW].7, 3fc`,>u[70^Z$U_g5?=7L#--2#7_~FMxQ*?q`rkq3Xb !:96/1)ChH?u~ZQY/@ugmӿ@/$*?&T|"Ud"U|["Ug7|"UG|"UG!"U#p"5 @"5@t"5@ "5G "5G@"5#@R"V<R"V#<Fc+"VJX"VWX"V#Wu["V?G"V:G["V#:? FEX/ >YB + B9B.A..'.7.G.W.g.w.......]A..]01#"&&&#"#"&&&5466666323266676&766667666632FF: !)/-)  9Thnl./\J-#! +?,#B7(   3BK$>AB1[N?{92#%-%5aPMMR{bD%*1 #() 0;0%R\uCq[G"('"6 @"6# @"6 @"6@"6#@t"6@G "6Gt"6#@,.A"WB,A"W,k"W,G"W^"7 @^t"7@8"7^G"7="X-"X-"X-\"X-?"X#-!"X-"X#r-D"X#-D"X#-M"X#"d"X#-"X-"XG-"X; oܻ +i +A )9]i9A   & 6 ]. 9./A Ji9J/A JJ)J9J]] q>/Z/l+G+lи/и#иG*иGbиe01%26667!#"&&&'#"&&&5467#'6666736654&&&#"'666632!6654&&&#"'6666323#32674AN*!(QKA-&&Z_`->4"     *niU ,  *keS--$=aD+H9:15)@`B6^E(>\=%k<5Z C,&&.HU_/3Y C,&&.IU].!W5[H |sF %+ + k+A )9]A FF&F6F]F,? S 9S/A SS)S9S]f hиh/A kk)k9k]p%9q/4"    *niU  !4CO-  *keS^?6[B&>\=S]b[K C,&&.]nvn]9:<_C)TND C,&&.F. n:"u"u\"u"uG|"u";o,"2$$@[nDWZ-9S6 (7 V]g2A#hK)Ls;6^O>p7"8k@7"8~@7"8o@7}"8e@7"8#e@ 7@"8o@7"8#o@7"8#t@ 7"8#t@ I7)"8#t@7"8#t@7"8d@7"8j@G7"8=AB3@? +и/#и#/A ??&?6?]?<иYEX2/2>Y4++ии*и:013##"&&&5477#'67766736&'7!!6&&&'7!266677!8?>9v.s `cRqB |  y:6A  ?E:9 , ZAcF+  "k XXˊG2`\5>   $II"kI6_KZ:: +и/и/A   & 6 ] и/8/EX/>YEX///>Y# +78901#"&&&5476&'7!3266676&&&'7!654&&&'70ZRO`cRpB ^6A  ?FXy~AbF+ \ , p  5aXLσˊG2`\5> $II":6_KI n:" c@" @}"e@"j@G"=\"YG"Y?8/ +A //&/6/] / /;/, 901'4667666673267#"&&&'&&&&&&53267#"&&&.g{ -5/ ( 3#1aTA  ub*Y\Q+ r&8q\ ?!6' 1#bwnQ 3 A,%    V."+ + G+A )9]"9A&6FVfv]A]B9A GG)G9G]X:/R/EX/ >Y:9 A  ' 7 G W g w ]A ]B:9014&'326#"&&&546667&&&&'&&#"'666632666654'&'666632',)&/ 0KkH*3Bfz7:L,1]M@CA4 *dZF .c.%=,5!(WSI *n?@R7%")</@Y~ssN>w]80;>euK61!C,&0!ep>p]F2 C'|H}"9H@|GH"9  @EX / >YEX/ >YEX/ >Y 901'66766667!766'!AJ /9<3; ( J4z1J qI $ IIG Io"Zo"Z5o"Zo["Zo["Zo"ZGo"Zr": @r": @r":  @rt":@rt": @rG":["[["[t";@@t";2@< "\< "\b< "\< \"\<  !"\< ["\< "\< "\< "\< UB/EXQ/Q>YEX/ >YQ A ]A  ( 8 H X h x ]Q 9(A(('(7(G(W(g(w(((((((]A((]JQ901#"&&&#"#"&&&54666667326667&&&&&&'&&#"'66663266666632'*' $+7gedhluY)M>% .2."A?= $'&#  !*aYE*'''2ipzU1YH3$% ' Y˙X  )+&  ;S3;h3C-$:E2QqȖW'#w"< -@w"< @w"< 4@w}"</@w@"<9@wt"<0@w"<4@wG"<\I@EX+/+>YEX;/;>YEX/ >Y6+901!!766667&&&&'&&&&&&766663326&'7! +<%4/7= %   )0/)  Ibo55O9(91% -D  ."5 7)I F3,'$%09NrK%(3~}2 II   |(HEEX/>YEX/ >Y%=+=и/901!766667&&&&'&&&�666632'666676&#" +:$3;@@0&?#E=, ;:5@MZ74M0 ;FC  %#=4 6* I zQ0  I 0H/P;"$YEX / >YEX/ >Yи01!'7!2667&&&&#!!26667.0+Q (#$(rte32U2JW- 9%1TB I9/EX/ >Y<.+E +A '7]A'7GWgw]A]C E901#"&&&5466666323266676&&&#&&&&'66667#"'666673!63n^5_H* %4E/#E<03N-$V* JkWL,$A C# :vk\ M{NjG'' %)""*"6]HMwQ)Zpa4 5*-bZK(>v+4e _"W:CMEX7/7>YEX:/:>Y+?+:/=?901#"&&&76666732676&&&#"'&&&&''527!"'3!632 8Tit{:P]1"292>H'u4P2%&(- *+)NT&T;"BiEG{fQ8&59 2:4 /L5Y'+01766766666676&&&#"'&67666632S NG2  W_W{K ;NXN: ;BH HqLucVZc>2Z|KIkXOXlIO Hw-/  +01766666676&#"'467666632(4:4% :5& (.,:Zs>9S3'3:4&m.H<58>'HH(  /R>#6I),B817?*98EX/ >Y'+017667766666676&&&#"'&676666329 NH.=D<,.("8=< MuQHhA.?F>-CFHy8M;.3>--B+"1$   CpP,#>U25K906C0 Hr 6yEX%/%>YEX/ >Y%A]A(8HXhx]013766766666676&'&67666632 PC% 9KSI6X[&@0 >GC Sm_N 9KUJ6/@H IHgN?BN6|| 5F'- &Fh>/UvFJcH8>N; IrifT //01!76666766'&&'66667]3C'<#9+ ag\W :0i<  ;  HiO'6 +A )9] +%+01326767!'666654&#"'666636&^z 4) a&*0+ +2.9Yq>eh,Wpl$! /xzY) +B- )B6$IKWND ++ и/A++]A ++)+9+I+Y+i+y+++++++]+ 9+и/ F+@0+01#"&&&'732666'4&&&#"'666654&&&#"'666632)8 &6"5[yD=?=$6Z/8*&4E( &**0Oh;;M. 9/$ ,59fL, %M)%)<&!+ @ $-3 # %>6%)2G)TH1+A11]A 11)191I1Y1i1y1111111]HV/(/!/EX/ >YEX"/" >YEX*/* >YC3+*M0176666564'&"'66667'66667!'666676#"'666632326767*8!79GMQJM 0' #"  $! Y fV&F #" .G[2!;-Pj ,}* ^ ) *  I$ZeIH"  5, *@Y}Z G;Ij3/H/A/EX / >YEXYEXB/B >Y иии013#!7666677#'66667376666564'&"'66667'66667 # *#, 8 !"! 7GP*8!79GMQJM 0' #"  $! ^ %% Y} * ^ ) *  G-f.I+A..&.6.F.V.f.v.......]A..]._и_/bI.9,/Z/%/B/E/EX / >YEX / >YEX&/& >Y<3+ ииииZLb%,9013#!7666677#'666673'66667#"&&&'732676&#"'6676&#"#'666632 # *#, 8 !"! 7G #"  $! ,Jb8231*N'6K9) SI' !#" (AS//>%I7,^ %% Y}   ,N:" .!E>7/-G2 "(# /)!*0H%. / /01#66667Y  "F9%?3E8%> +A &6]9/A )9] EX / >Y+ A (8]01#"&5466632#"&54666320E-?:1E+<<_0E-?:1E+<<T!F9%>3 D9$=#!G9%?3 D9$=# /016&&&76666'6&&� '5?  *  >B<8DI"97121-,"  M/01'6676&&&#'666666D 8VsH1HM 'K; -;B?5%-8~p'5?K+R?'D  @MVZ, +A )9] +01#"&5466632/E-?;1E+<<!F9%?3D9$=#?  //01'66667'66667e %)&q#%#& $)' $o #$#)       yDyD7{ +01!'66667!q[ !   yD +01!'66667!y%    oY //01&&'%'<5 W h / ///01&&'&&'$( */(( */( 8 8;X / /01766663.( :B;  k3 g // /01'&&'3& $ +7 "zfg ///012&&''&&'3 %($  Z' $ +g/ "z0o  //01'76666'&&'3 ( %'% ' $ +U( "zg&)///!/ 01#"&&&#"'6666323267'&&'3#)B*A=IR++G?:*E'i& $ +)aT8 '!;7)bU9!'!9:A "zfw 34/5/ ܸ4и/ A&6FVfv]A]  A]A )9IYiy]и/!и!////!+01'&&'3%&&5466654&#"#'466632 ' $ +m/9/+ $.7. $!4H*#17 "zn(4'"   ,& * 3) r//+01#"&&&'66667326667%]b`(,YN=4>BMNH7QoEFoP 2C))C2r (/+01&&'#"&&&'66667326667=% /3- R%]b`(,YN=4>BMNH8R QoEFoP 2C))C2r ( /+01766663#"&&&'66667326667/( 383 >%]b`(,YN=4>BMNH? 03 QoEFoP 2C))C2r{;+//2#+ 01#"&&&#"'6666323267#"&&&'66667326667{=HS+(C??###%A=HR++G?:*E(:%]b`(,YN=4>BMNH(aT9!'!+(bU9!' 8:NQoEFoP 2C))C2r0N '++ '9A]A )9IYiy]E6+,+01&&546666654&#"'&66632#"&&&'66667326667^)0)' )1)%+' 'AV/+;%]%]b`(,YN=4>BMNH8*%"   &!&    $C4 )QoEFoP 2C))C2 ///01#667%ى# u :3\'//// 01#"&&&#"'66663232673%+058(C??###%@=IR+*H?:)E(>>>9, '!*)bU9!'!9:-z/ ///01%'7666677'&&&''2 !  " ;"  ""    .' +01!'66667!'=V #%! "$" dL +01!'66667!> #$! "%" ! +01!'66667!> #%! "$" 6Q! +01!'66667!Q= #%! "$" ! +01!'66667!   #%! "$" #> +01!'66667!> #%! "$" Mg +01!'66667! #$! "%"  (EX/>Y +01!'66667!!'66667!f f !" ! !" ! &z + +01!'66667!!'66667!z$$d !   !   l<[#o$/%/$и/ A &6]%ܹ A )9] +и 01#"&5466632#"&5466632i);%0.);%# )<%/.):%$ B6#<(B7%%B6#<(B7%%pGlf, +A &6] +01#"&5466632l);%/.):%# B6#<(B7%%<<8[, +A &6] +01#"&5466632);%/.):%# B6#<(B7%%I|4 +A  ) 9 ]//90176654&&&'73aXHL   6fZMbJ2 n#ո$/%/ܹA]A )9IYiy]$и/A&6FVfv]A]и//+014&#"726667#"&&&5466632   'D]5#5$&CZ5"7&&!+'!,=,aQ6(50dR5*7 ~0 '++ '9A]A )9IYiy]/,+01&&546666654&#"'&66632(0(' )/) %+' (DW/*:$6*&"!   &!'   $B3 )d< +A  ) 9 ] и/ 9/01&''6654&66667DoK*F1)b ?>5X2$E 843:H B0 +A &6]/+01#"&&&5466673267BKQ'7/ 8Zo7f)H5&J*6, 2(4bXK?BA $ &-T//EX/ >Y01!#!5T'''//EX/ >Y01!!#'''' //01&&') b/.(  (C   / /01%766667m X471@( > sk // /01'&&'3"  "z7 "4s ///01&&''&&'3 %'#  n  "z-  "4  ///01'766667'&&'3   %)& '  "z2 // "4s*)//#/%/ 01#"&&&#"'6666323267'&&'3%+058(C??###%@=IR+*H?:)F'j  "z>>9, '!*(bU9!'!9:k "4sC)45/6/ܸ5и/ A&6FVfv]A] A]A )9IYiy]и/и/-///%+01&&546666654#"#'466632'&&'3/9/* $ ' . $!4H)#1   "z(4'"  +/* 3) < "4Y */!+01&&&&'#"&&&'66667326667 )-& %]b_(,YN>4>BMNHY- QoEFoP 2C))C2_ * /!+01%766667#"&&&'66667326667  ,1- %]b_(,YN>4>BMNHH (QoEFoP 2C))C2r{=+//4%+ 01#"&&&#"'6666323267#"&&&'66667326667{%+058(C??###%@=IR+*H?:)F':%]b`(,YN=4>BMNH>>9, '!*(bU9!'!9:MQoEFoP 2C))C2r0N '++ '9A]A )9IYiy]E6+,+01&&546666654&#"'&66632#"&&&'66667326667^)0)' )1)%+' 'AV/+;%]%]b`(,YN=4>BMNH8*%"   &!&    $C4 )QoEFoP 2C))C2 ///01#667%' ! <$ %82='//// 01#"&&&#"'66663232672%+058(C??###%A=HR++G?:*E'>>9, '!*(bU9!'!9:2dN EX / >Y01!'66667!N= #%! "$" k4o /!/ и/ A &6]!ܹ A )9] +и 01#"&5466632#"&5466632h)<%0-):%0/)<%/.):%0/B7#='A8$=(B7#='A8$=844, +A &6] +01#"&5466632);%0-):%# B7#='A8$$  3/EX/ >YEX/ >Y+013'!#3dY::LR͎Go_<fjfkd# dr xd,z*kP 7xUx3<,x E9x!R,u:u+un0A=`=x ~FK'KyU^|;rw(2Y^^&[iG&a04G874tG#X8 &&D,Ml<fK=K&&&&&&iGa0a0a0a0878787878 &&&&&MMMMYqBU@b[oPO^u+=3uuW  &h,-n.k@zu4r3.,KBD&{{37.<we1S3R.9ZPx x x x KK*KK87nkp_kD[y-q&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&--[*[rAAAEhiGiGiGiGiGw>=====87&&&&&&ha0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0E"&`=`=`=`=`=`=4G4G4G4G4$4G87878"878787878878l7*x x x x x x x x x  4t4tGGGG#X##%#%KFFF8 8 8 8 8 8 8h ,&&&&&&&&&&&&&&&&&&&&&&&& &&&&&&KKKKKKKKKKKKKKKKKKKKKKKKKKKKKK''+&&ADDDD7DDyyyyyyg,,k,U^U^U8U^MMMMMMMMMMMMMM`"A||lllllll;r;r;r;r;r;r<<<<<<<<<<wwwwwwwwO\3|(((((( DD::JSKw9rrHKGGG0(>Z#37Ytgf0gfrrrrr:3-! d!6M&lp<gI T-Tssssrr82k8) 8888vb(J|$pJ: l n  ~ , l :.N:V^*&fL L 24Zx p !"#"#$%&.&'|(<()`**+v+,-:-.r.//j///0000001111*16222 2,282D2P2\2h2t2222222223\4.45t6 6H678T9:|:; ;~<(==> >`>?,@ @@@AVBBCCDEdEFHFFGHHdHI*IIIIIJKLL6LM8MxMNNZNfNrNOOBO|PdQQQR RSSSSSTTTT(T4T@TLWW&W2W>WJWRWtWWXXTXY>YtYYYYZZZ$Z4Z@ZPZ\ZhZxZZZZZZZZZ[[[[,[<[H[T[\\\"\.\:\J\Z\j\z\\\\\\\\\] ]]&]2]B]N]^]j]v]]]]]]]]]]^^^*^6^F^R^^^j^z^^^^^^^^^___` ``"````aaaabbctcccdd eefg,g8gDgPh`iLjrkkkk&k2lm mnnnnnoo oo(o8oDoPo\ohoxoooop~qqr,rrrrssss.s:sJsVsbsnszssssstuv vvvvwwww&w2xxx x,x8xDxPx\xhxtxxxxxxxxxxxyyyyyyyyyyyz{ {{{{| ||"|2|>|J|V|||}`}l}x}}~B~N~Z~f~r,8DP`\&6<0 " D . 8* "b 48. 6:   :? :I &:Q  :w : 0: 0: *:  2;  &;E  :;k  ;  ;  ; < < 0<+ <[ ?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      uni00A0uni25CCuni1EADuni1EA5 uni1EA5.VNuni1EA7 uni1EA7.VNuni1EAB uni1EAB.VNuni1EA9 uni1EA9.VNabreveuni1EB7uni1EAF uni1EAF.VNuni1EB1 uni1EB1.VNuni1EB5 uni1EB5.VNuni1EB3 uni1EB3.VNuni01CEamacronuni01DFuni0227uni01E1 aringacuteuni1EA3uni1EA1uni0251 a.SngStoryaacute.SngStoryagrave.SngStoryacircumflex.SngStoryuni1EAD.SngStoryuni1EA5.SngStoryuni1EA7.SngStoryuni1EAB.SngStoryuni1EA9.SngStoryabreve.SngStoryuni1EB7.SngStoryuni1EAF.SngStoryuni1EB1.SngStoryuni1EB5.SngStoryuni1EB3.SngStoryuni01CE.SngStoryatilde.SngStoryamacron.SngStoryadieresis.SngStoryuni01DF.SngStoryuni0227.SngStoryuni01E1.SngStoryaring.SngStoryaringacute.SngStoryuni1EA3.SngStoryuni1EA1.SngStoryaeacuteuni01E3uni1EA4 uni1EA4.VNuni1EA6 uni1EA6.VNuni1EAA uni1EAA.VNuni1EA8 uni1EA8.VNuni1EACAbreveuni1EAE uni1EAE.VNuni1EB0 uni1EB0.VNuni1EB4 uni1EB4.VNuni1EB2 uni1EB2.VNuni1EB6uni01CDAmacronuni01DEuni0226uni01E0 Aringacuteuni1EA2uni1EA0AEacuteuni01E2uni1E03uni1E07uni1E05uni0253uni1E02uni1E06uni1E04uni0181 ccircumflex cdotaccentuni1E09uni0254uni0254.TopSerif Ccircumflex Cdotaccentuni1E08Eurouni0186uni0186.TopSerifuni1E0Buni1E0Funi1E0Duni0257uni0256Dcaronuni1E0Auni1E0Euni1E0CDcroatuni0189uni018Auni1EBF uni1EBF.VNuni1EC1 uni1EC1.VNuni1EC5 uni1EC5.VNuni1EC3 uni1EC3.VNuni1EC7ebreveecaronuni1EBDemacronuni1E17uni1E15 edotaccentuni1EBBuni1EB9uni0229uni1E1Duni01DDuni0259uni025Buni1EBE uni1EBE.VNuni1EC0 uni1EC0.VNuni1EC4 uni1EC4.VNuni1EC2 uni1EC2.VNuni1EC6EbreveEcaronuni1EBCEmacronuni1E16uni1E14 Edotaccentuni1EBAuni1EB8uni0228uni1E1Cuni018Euni0190uni1E1Funi1E1Euni01F5 gcircumflexgcaronuni1E21 gdotaccent g.SngBowluni01F5.SngBowlgcircumflex.SngBowlgbreve.SngBowlgcaron.SngBowluni1E21.SngBowlgdotaccent.SngBowluni01F4 GcircumflexGcaronuni1E20 Gdotaccent hcircumflexuni021Funi1E27uni1E23uni1E96uni1E25 Hcircumflexuni021Euni1E26uni1E22uni1E24Piibreveuni01D0itildeimacronuni1E2Funi1EC9uni1ECBuni0268 i.Dotlessuni0268.Dotlessijuni0269Ibreveuni01CFItildeImacronuni1E2Euni1EC8uni1ECAuni0197IJ j.Dotlessuni0237uni1E31uni01E9uni1E35uni1E33uni0199uni1E30uni01E8uni1E34uni1E32uni0198lacuteuni1E3Buni1E37uni1E39uni019Auni2C61uni026BLacuteuni1E3Auni1E36uni1E38uni023Duni2C60uni2C62uni1E3Funi1E41uni1E43uni1E3Euni1E40uni1E42nacuteuni01F9ncaronuni1E45uni1E49uni1E47uni0272engNacuteuni01F8Ncaronuni1E44uni1E48uni1E46uni019D Eng.UCStyleuni019D.LCStyleEng.BaselineHookEngEng.Kom ohungarumlautuni1ED1 uni1ED1.VNuni1ED3 uni1ED3.VNuni1ED7 uni1ED7.VNuni1ED5 uni1ED5.VNuni1ED9obreveuni01D2uni1E4Duni022Duni1E4Fomacronuni1E53uni1E51uni022Buni022Funi0231uni1ECFuni1ECDuni0275 oslashacuteemptysetohornuni1EDBuni1EDDuni1EE1uni1EDFuni1EE3 Ohungarumlautuni1ED0 uni1ED0.VNuni1ED2 uni1ED2.VNuni1ED6 uni1ED6.VNuni1ED4 uni1ED4.VNuni1ED8Obreveuni01D1uni1E4Cuni022Cuni1E4EOmacronuni1E52uni1E50uni022Auni022Euni0230uni1ECEuni1ECCuni019F OslashacuteOhornuni1EDAuni1EDCuni1EE0uni1EDEuni1EE2uni03A9uni1E55uni1E57uni1E54uni1E56uni02A0uni024Buni01AAuni024Aracutercaronuni1E59uni1E5Funi1E5Buni1E5DRacuteRcaronuni1E58uni1E5Euni1E5Auni1E5Csacuteuni1E65 scircumflexuni1E67uni1E61uni1E63uni1E69uni0283Sacuteuni1E64 Scircumflexuni1E66uni1E60uni1E62uni1E68uni1E97uni1E6Buni1E6Funi1E6DTcaronuni1E6Auni1E6Euni1E6C uhungarumlautubreveuni01D4utildeuni1E79umacronuni1E7Buni01D8uni01DCuni01DAuni01D6uringuni1EE7uni1EE5uni0289uhornuni1EE9uni1EEBuni1EEFuni1EEDuni1EF1uni028A UhungarumlautUbreveuni01D3Utildeuni1E78Umacronuni1E7Auni01D7uni01DBuni01D9uni01D5Uringuni1EE6uni1EE4uni0244Uhornuni1EE8uni1EEAuni1EEEuni1EECuni1EF0uni1E7Duni1E7Funi028Cuni0263uni1E7Cuni1E7Euni0245wacutewgrave wcircumflex wdieresisuni1E87uni1E98uni1E89WacuteWgrave Wcircumflex Wdieresisuni1E86uni1E88uni1E8Duni1E8Buni1E8Cuni1E8Aygrave ycircumflexuni1EF9uni0233uni1E8Funi1E99uni1EF7uni1EF5uni01B4Ygrave Ycircumflexuni1EF8uni0232uni1E8Euni1EF6uni1EF4uni01B3uni01B3.RtHookzacuteuni1E91 zdotaccentuni1E95uni1E93Zacuteuni1E90 Zdotaccentuni1E94uni1E92uni01A9uni0292uni01EFuni01B7uni01EEuni0294uni02C0uni0242uni0241uniA78B uniA78B.LrguniA78C uniA78C.LrguniA789 uniA789.Wideuni02BC uni02BC.Lrguni2219uni2011uni00ADuni02D7uni02CA acutecombuni030Buni02CB gravecombuni0302uni0302_acutecomb.VNuni0302_gravecomb.VNuni0302_tildecomb.VNuni0302_hookabovecomb.VNuni0306uni0306_acutecomb.VNuni0306_gravecomb.VNuni0306_tildecomb.VNuni0306_hookabovecomb.VNuni030C tildecombuni02CDuni0331uni02C9uni0304 uni0304.Shortuni035Funi035Euni033FuniA78Auni0308 dotbelowcombuni0307uni031Buni030A hookabovecombuni0327uni0328uniF130uniF131 acutecomb.LP gravecomb.LP uni0302.LPuni0302_acutecomb.VNLPuni0302_gravecomb.VNLPuni0302_tildecomb.VNLPuni0302_hookabovecomb.VNLPuni0306_acutecomb.VNLPuni0306_gravecomb.VNLPuni0306_tildecomb.VNLPuni0306_hookabovecomb.VNLP uni030C.LP tildecomb.LP uni0304.LP uni0308.LP uni0307.LP .notAccess+++UC4'+OC4'+LC4'+ nWC4+ 3*! +bWC'+|fO4"+mWC4+VC4'+)" + + E}iD  f( q 9( q 9( q 9( q 9( q 9( q 9( q 9( q 9( q 9( q 9(  e(  (  (  (  k(  R( % ( J  (  :(  ( ) (  fO(  ( 9 (  Zf(  }( * (  (  X(  (b G f(G * (  (  (  (  (phZ  fF  p,  fJ  =fF v f> n ahM> A 0f  ,  f  g  pf,  yfh  X  j y f u >fP E #f!  H D   sf  Dmfl  .  "  =  zfO X   e  e  (  i 9 "  Zfb G f6  fMK  fK  f  fq  fxl  f G fJ v f#K v fpK v fZ v fVq  6fK  6fK  6f  6fq  l y fK y fdK y fN y fJq y fBl  DmfsK  DmfK  Dmf  Dmfq  h2ZlI  (  (  j(  H> W f { -f v h  f(  e  e  Zf  (  Gf$  zfq    e    e      :  :  :  :  Zf  Zf   GV  Zfb G fb G f<b G fL  6f(   T Z k f  f5  f  f5  f  fV  f  f  f  ft? k ft?  f{  f  f  f  f  f  f  f  f|s  fzZ  f  fwI  f  f  fr( k fF  cfF  cfF  cfMK  cfK  cf k cf  cf5  cf5  cfV  cf  cft? k cft?  cf{  cf  cf  cf  cf|s  cfxl  cfzZ  cfq  cf  cfwI  cf  cf  cf  cfr( k cfF> W fK> W f"Z  e  eh  eL  eh  e/  eh  e.  eh  e  e  e  eh  e@  eh  eh  eh  e"  e,  e  e  eh  e(  eh  e   eP  eh  e(    .h  pI  p,  p,  h  h  (  (  B(  fQK  f  fs  f{I G fQK| r f q ] f        h    (  (  =fwI K =fF J =fF v dh     f  h    h  (  (  (  (  (  ( v fa5 v f v f5 v f v fV v f v f v f 1 fZ v fJ? v fRs v fNl v fPZ v fW v f v fMI v fH( 1 f 2 f 2 fJ?J ] wJ ] wm i f    h    h    h    h k         h      h  h k k( l k( l   g(  g(> n   ~hM> A 0f#KM> A 0fZM> A 0fJ?M> A 0fRsM> A 0fPZM> A 0fMI A 0f A 0f#K A 0fZ A 0fJ? A 0fRs A 0fPZ A 0fMI %  %  %  %  %  h %  h        l  ,  , J C J = J = J 5h   (  6f?  6fs  6fl  6fZ  6f  6f(  f  6f  6f  6f    "ft  :  :  :  :  :  :h  :h  :(  ( : :C(  g  g  pf  pf p pf, o pf,  h )  )   (  ( - (  yf { yfh z yfh z yf  h  h  h  h  fM n fO( m fO( m f{h  O(  O(  O(  k(  _K  I | X    h  (  qK  K  s  I  j  j  s ~ Y 9  9 ? 9 ) 9 !h  (  (  (  ( ; ( _ $(  ( 9 ( y f<( y fU5 y f y f5 y f y fV y fz y fz y f 4 fN y f>? y fFs y fI y fv y f| y fDZ y fK y f y f~ y fAI y fu y f<( 4 f y f { -fK y  y K y dK y Bl y <( 4   Zfh  Zf   Zfh  ZfV  Zfh  Zf9  Zfh  Zf8  Zfh  Zf   Zf  Zf  Zf  Zf+  Zf3  Zfh  Zf  ZfH  Zf2  Zfh  Zf*  Zfh  Zf(  (    (        h  ( u >fWK u >fI  h  {  h  ( O h 1   z h  (   K  9s  4I      7Z      h  (  (  hH D KH D H D H D sH D BH D  IH  H   I  h  V          h X X( X h  sfW  sf/  sf  sf    h  (  (  Dmf(  Dmf?  Dmfs  Dmfl  Dmf  DmfZ  Dmf  Dmf  Dmf  Dmf  Dmf  Dmf  Dmf(  Dmfl  ;fs  l  sK  K  l  (  l  Kfb G f.hb G f0b G f6b G f/b G f-b G f.hb G fbb G f4b G fb G fjb G fbb G f<b G f.hb  f( G ( G ( G  G L G / G .h  (  `l R .  'f  \G * G  ( < (  )K  vK  `  \q  SI  \ F "          h  (  wq  nI    h  zfVK  zfK  zf  zfl  zfZ  zfI  zf  zf{( T zfO  [          h  h  h  (  (  ( X K X - X %s X  I            h  (  (  j(    d  (   K f<h / Y *   Z( F &x F &x F &x 5K ( 5AK      ?     s l   14IZ Z   D q   1 4I %f (   '  ,D? 7?  ( ( ( ( ( (    d ? ( ? ( 69 ; 48<BHNRVZ^bhn| "8Ndjnrvz~,D`v,D\t $(4Xx4Xn"@Xp48<@D`|2Vz  ( L p $ < `  8 \     $ * . B F \ ` f l r  4 P t "&*.28Tp4X|*NRVZfjnz~:^ .Rv*Nr&Jn"FjBf>b:^z">Zv:Vr6Rh~  6Ll2Jbz8Nd2Hl  4 P t !!(!L!p!!!!""4"X"t""""###8#T#p#####$$4$P$j$$$$$%%*%N%r%%%%&&*&F&b&~&&&&&''&'>'V'n''''''(((@(d(((())<)`)))))**4*P*l*****++ +@+`++++++, ,,,L,l,,,,,--$-D-d-z-----...0.F.^.v.....///2/J/b/z//////0020V0r000011:1^11112262Z2~2223323P3t3x3333334 4D4h444455@5d555566<6`66667787N7d7z77777788.8D8Z8p88888899&9>9V9n999999::.:F:^:v:::::;;;4;L;d;|;;;;<<,>$>H>l>>>>>>??8?\????@@4@X@|@@@A A0AFA\ArAAAAABB&BETEjEEEEEEFFF2FJFbFzFFFFFGGG,GBGXGnGGGGGGHH&H*H.H2H8H)y@9&&((2288DDFFHHJJRRXXin  !!$$%%&&''(())**++DDEE LLMM ]]^^__``aabbccddttuuvvwwxxyyzz{{       !"#$%&'()*+,-./012  !"#$%&'(     '.5<CJQX_gnu|#3BP`o}#/5;?CGKQW]ciosw{ !$%"#&'()*+,- . / 0 1 23456789:;<=+"+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0"! 0"! 0"! 0"! 0"! 0$! 1$! 1$! 1$! 1$! 1!* 1# ! 1# ! 1# ! 1# ! 1# ! 1# ! 1# ! 1# ! 1# ! 1# ! 10 1)11111-1.1&1'1/ 1(11111,1%111     LM    )4?JU     +1+1+1+1+1!1!1!1!1!12LLOiVC$$%%&&''(())*+,,--..//00112234578899:=AACCDDEGHHIIJJKKLLMMNOPQRRSSTTUVWWXXYYZZ[[\\]]bcddeeffgnoopwxxy45679:>?BCCDEFIJJLMNPQRSST\]]^^__``acddenop qrsst  -.5789@ATUUV]^abeftuz{                 "$&(*,.02468:<>@BDFHJLNPRTVXZ\^`bdfhjlnprtvxz|~   "$&(*,.02468:<>@BDFHJLNPRTVXZ\^`bdfhjlnprtvxz|~,' ", "1' "1 "1,' "1,'1 "'1 ",1 "1 " &!+!+ &+ &!+ &!!0+!0+0+0! &!0 &0 &0!0!0!+ &0+ &0!+0 & &!0 &+!0+ %   % % * ** * %* %* % * %  /// * /*/*/ /* % / %/ %/ / %* %/*/ %* %/ $ $ $)))) $) $) $) $...).).)..) $. $. $.. $) $.). $) $. "1!0/ %* $). #(-.=L[jy-<KZix(8HXhxIc,m4v;~A KWY  P F  @ | 9 v     ljmkpponq ttsurwwvx||{y}zV~WW''X'''&!!$$$YY----...2225557788899<==>>@AABB:::////;;;G I FF\FFF  H H 1111CCC4444???[[EEEELLLMMMMNNNNOOOOSSSTTTTUUUUWWW "!XXX#X$YY'%(&ZZ+)Z*--,[.[//\\0\112]]]55436_88`79`;;<:aa==b>bbAA@cB?DDddECGGHeeFIIfffJPPPP_Pcc^^^^QQ`QQQMMgKNLPPhOhQTTURiSRRRRRaVVVbVVedfeddfefdefegdgdegggdfgfegdfegfhhedhhdhhehhhhegdhhgdhheghhghhedfhhdfhhefhhfhhgdfhhgfhhegf !!"""####$$$%&!!(($$$))))***$$$++++,,,''X'''YY----////00001111////33334444000033336666...222555888111144446666:::////;;;99==222>>:::3333???;;;4444???99AA...BB:::0000CCC;;;1111CCCDDDDZD[[EEEEDDDDZDFF\FFFFF\FFF[[EEEEAA==555  H H BB>>888  H H CCC6666???FF\FFFJJJJJ]JJJJJ][[EEEEFF\FFFKKK^KKKKK^KK[[EEEEMMMMNNNNOOOOPPPP_PQQ`QQQPPPP_PRRRRRaQQ`QQQRRRRRaMMMMTTTTUUUUPPPP_PVVVbVVQQ`QQQVVVbVVNNNNTTTTXXX#X$RRRRRaVVVbVVLLLSSSWWW "!ZZ+)Z*OOOOUUUUXXX#X$//\\0\MMMM112]]]PPPP_Pcc^^^^QQ`QQQcc^^^^--,[.[88`79`SSS;;<:aa//\\0\TTTT==b>bb112]]]UUUU==b>bbVVVbVVcc^^^^--,[.[DDddECLLLGGHeeF//\\0\NNNNIIfffJ112]]]OOOOIIfffJRRRRRacc^^^^DDddEC88`79`WWW "!PPhOhQIIfffJXXX#X$==b>bbGGHeeF;;<:aaZZ+)Z*PPhOhQeddfefdedfefedgdegdgegdfgdgffgedfegdgdfegfefeggfhhdedhhedhhehhhhedhhgdegdhheghhdgdhhghheeghhgghhhhedhhdfedfhhefhhddfhhfhheefhhffhhhhdfhhgdgdfhhgfhhfgfhhghhefhhegegfhhgf+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70&&,#,#,#,#,#)'#)*# ))# )+# #370&&,#,#,#,#,#)'#)*# ))# )+# #370&&,#,#,#,#,#)'#)*# ))# )+# #370&&,#,#,#,#,#)'#)*# ))# )+# #370&&,#,#,#,#,#)'#)*# ))# )+# #370&&, #, #, #, #)1#)4# )3# )5# #370&&, #, #, #, #)1#)4# )3# )5# #370&&, #, #, #, #)1#)4# )3# )5# #370&&, #, #, #, #)1#)4# )3# )5# #370&&, #, #, #, #)1#)4# )3# )5# #370&&,#,#,#,#),#)/# ).# )0# #370&&,#,#,#,#),#)/# ).# )0# #370&&,#,#,#,#),#)/# ).# )0# #370&&,#,#,#,#),#)/# ).# )0# #370&&,#,#,#,#),#)/# ).# )0# #370&&,#,#,#,#)"#)%# )$# )&# #370&&,#,#,#,#)"#)%# )$# )&# #370&&,#,#,#,#)"#)%# )$# )&# #370&&,#,#,#,#)"#)%# )$# )&# #370&&,#,#,#,#)"#)%# )$# )&# #370&&,#,#,#,#)#) # )# )!# #370&&,#,#,#,#)#) # )# )!# #370&&,#,#,#,#)#) # )# )!# #370&&,#,#,#,#)#) # )# )!# #370&&,#,#,#,#)#) # )# )!# #370&&,#,#,#,#,#)'#)*# ))# )+# 370&&,#,#,#,#,#)'#)*# ))# )+# 370&&,#,#,#,#,#)'#)*# ))# )+# 370&&,#,#,#,#,#)'#)*# ))# )+# 370&&,#,#,#,#,#)'#)*# ))# )+# 370&&, #, #, #, #)1#)4# )3# )5# 370&&, #, #, #, #)1#)4# )3# )5# 370&&, #, #, #, #)1#)4# )3# )5# 370&&, #, #, #, #)1#)4# )3# )5# 370&&, #, #, #, #)1#)4# )3# )5# 370&&,#,#,#,#),#)/# ).# )0# 370&&,#,#,#,#),#)/# ).# )0# 370&&,#,#,#,#),#)/# ).# )0# 370&&,#,#,#,#),#)/# ).# )0# 370&&,#,#,#,#),#)/# ).# )0# 370&&,#,#,#,#)"#)%# )$# )&# 370&&,#,#,#,#)"#)%# )$# )&# 370&&,#,#,#,#)"#)%# )$# )&# 370&&,#,#,#,#)"#)%# )$# )&# 370&&,#,#,#,#)"#)%# )$# )&# 370&&,#,#,#,#)#) # )# )!# 370&&,#,#,#,#)#) # )# )!# 370&&,#,#,#,#)#) # )# )!# 370&&,#,#,#,#)#) # )# )!# 370&&,#,#,#,#)#) # )# )!# 370 8r\@#$=>@AABBC]^abJKKL]^^__``acdde5667    "#%&(+-.012469<?CFIMQUZ[]^`cegjlosvwyz|  !"$'),/369=AEJMPTX\aeinsx~  !%).036:<?ABEIMRV[`fimqvy} &-5=DLU]fkqv|&/7@HOW`hqz '1:DNYajs} $)*17>AEHKMOV\ckrz~  '-3:@GOV[aflsy~ #,4=GPZ`gnv|    ' 0 : B K S Z [ \ ^ _ a b d e g j l o p r t u w z |          $+#+#$*#)#)+))+*+$(#'#'+$(*#')#')+''+(')')+(*))+*+$&#%#%+$&*#%)#%)+$&(#%'#%'+$&(*#%')#%')+%%+&%)%)+&*%'%'+&(%')%')+&(*''+(')')+(*))+*+$#+#$*#)#)+$(#'#'+$(*#')#')+$&#%#%+$&*#%)#%)+$&(#%'#%'+$&(*#%')#%')+%%+&%)%)+&*%'%'+&(%')%')+&(*''+(')')+(*))+*+%%+&%)%)+&*%'%'+&(%')%')+&(*''+(')')+(*))+*+"$!#+!#"$*!#)!#)+"$(!#'!#'+"$(*!#')!#')+"$&!#%!#%+"$&*!#%)!#%)+"$&(!#%'!#%'+"$&(*!#%')!#%')+!%!%+"&!%)!%)+"&*!%'!%'+"&(!%')!%')+"&(*!'!'+"(!')!')+"(*!)!)+"*!+!"$$*$($(*$&$&*$&($&(*&&*&(&(*((**"$"$*"$("$(*"$&"$&*"$&("$&(*"&"&*"&("&(*"("(*"*" $ $* $( $(* $& $&* $&( $&(* & &* &( &(* ( (* * "$ "$* "$( "$(* "$& "$&* "$&( "$&(* "& "&* "&( "&(* "( "(* "* "024677/135702467/1357/13575757675702467/1357/135702467/1357/1357357357467357357467357357467357 02467/1357/1357 02467/1357/1357 02467/1357/1357 02467/1357/135713571357 246713571357 246713571357 246713571357 24671357135724671357135724671357135724671357.02467-/1357.02467-/1357-/1357.02467-/1357-/1357.02467-/1357-/1357 .02467-/1357-/1357 .02467-/1357-/1357 .02467-/1357-/1357 .02467-/1357-/1357 02467 02467 02467 02467024670246702467 .02467 .02467 .02467 .02467.02467.02467.02467,.02467,.02467,.02467,.02467 ,.02467 ,.02467 ,.02467 ,.02467 ,.02467 ,.02467 ,.02467 ,.02467,.02467,.02467,.02467                     024677/1357/1357702467/1357/135757576757576702467/1357/135702467/1357/1357357357467357357467357357467357357467 02467/1357/1357 02467/1357/1357 02467/1357/1357 02467/1357/135713571357 246713571357 246713571357 246713571357 2467135713572467135713572467135713572467135713572467.02467-/1357-/1357.02467-/1357-/1357.02467-/1357-/1357.02467-/1357-/1357 .02467-/1357-/1357 .02467-/1357-/1357 .02467-/1357-/1357 .02467-/1357-/1357 02467 02467 02467 0246702467024670246702467 .02467 .02467 .02467 .02467.02467.02467.02467.02467,.02467,.02467,.02467,.02467 ,.02467 ,.02467 ,.02467 ,.02467 ,.02467 ,.02467 ,.02467 ,.02467,.02467,.02467,.02467,.02467                             8v   *T~&Pz"LvHrDn@Wn&=Tk #:Qh       "!#$&%'(*)+,.-/0213487658787:98787<;8787=>8787@?8787BA8787DC8787EF8787HG8787IJ8787LK8787NM8787PO8787QR8787TS8787VU8787XW8787YZ8787\[8787^]8787`_8787ab8787dc8787fe8787hg8787ij8787lk8787nm8787po8787qr87vutsvuvuxwvuvuzyvuvu{|vuvu~}vuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvu     ! "$#%'&(*)+-,.0/1234675:98=<;@?>CBAFEDIHGLKJNMOQPRTSUWVXZY[]\^`_abcdfegihjlkmonprqsutvxwyz{|~}67:9=<@?CBFEIHLKfeihlkonrqutxwz{~67:9=<@?CBFEIHLKfeihlkonrqutxwz{~STUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@AB   111222RRR555ZZZjjjkkllmmnnooppqqrrsst tu!uv"vw#wx$xy%yz&z{'{|(|})}~*~+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRCDEFGHIJKLMNOPQRSTUVWX Y  Z  [  \ ]^_ ` a b c defghij kl m  n !o!"p"#q#$r$%s%&t&'u'(v()w)*x*+y+,z,-{-.|./}/0~0334455566778899::;;<<==>>??@@A AB BC CD DE EFFGGHHIIJJ^_ KKa b LLdeMMghNNNOOOQRPPGHQQE@@A ASSC CD DTTFFGGUUIIJJVV::;;WW==>>XX7788YY44334455566778899::;;<<==>>??@@A AB BC CD DE EFFGGHHIIJJ@@A ASSC CD DTTFFGGUUIIJJVV::;;WW==>>XX7788YY44555ZZZ[[\\]]^^__` `a abbccddeeffgghhii[[\\]]^^__` `a abbccddeeffgghhiijjj[[\\]]^^__` `a abbccddeeffgghhiijjj[[\\]]^^__` `a abbccddeeffgghhiijjjkkllllmmnnoonnooppllqqrrsst tu!uv"vrrssw#wu!uv"vx$xnnoopplly%yz&z{'{|(|})}~*~+,-./0z&z{'{1})}~*~2,-3/04rrssw#wu!uv"vx$xnnooppllkkllmmnnooqqrrsst tu!uv"vy%yz&z{'{|(|})}~*~+,-./01234w#wx$xpp1234w#wx$xppkkmmqqt ty%y|(|+.1234w#wx$xpp56789:;<=>?@ABCDEFGHIJKLMNOPQR** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1 D= #$=>CD]^abJKKL5667     $'),.135789;<=>AEHJMOQTVWYZ^adgiknprtuvy{}    "%),/246:=@CEGJLNPQRV[_bfilpsuxz} "%(*-/127;?BFILNRUXZ]_abfilnqsuvy{}~ #*16;BGLQ\gpy "*2:@FNTZ`n| &0:BJR]fov&/<GR[fox   $(,02468>DHLPTVX^dhlptvx   $(,02468>DHLPTVX^dhlptvx    ! , 7 @ F J N R T V a l u ~    # * 1 6 B L V ` h p z  " . 8 D P Z d n v    ! & 2 < F N X ` h n }     & . 6 > D J L N T Z ` f j n r v x z | ~  &.6>DJLNTZ`fjnrvxz|~ &.6>DJLNTZ`fjnrvxz|~ &.46<BFJLNTZ^bfhnrvz|~'7GWgw/?O_o/O_o?'GWg?O_O_/?o/o7w?7GWw/?Oo/OoGW?OO/?_o/_o'7gw?__'g/?o/o7w?'7Ggw/?_o/_o'Gg?__7Gw/?o/oG?/?O_o/O_o'7Wgw?O_O_'Wg/?Oo/Oo7Ww?OOW/?_o/_o'7gw?__'g/?o/o7w?'7GWgw/?O_o/O_o'GWg?O_O_7GWw/?Oo/OoGW?OO'7Ggw/?_o/_o'Gg?__7Gw/?o/oG?'7Wgw'Wg7WwW'7gw'g7w'7Wgw'Wg7WwW'7gw'g7w'7GWgw'GWg7GWwGW'7Ggw'Gg7GwG'7Wgw'Wg7WwW'7gw'g7w#3CScs&6FVfv +;K[k{.>N^n~ +K[k.N^n;{>~#CSc&FVf ;K[{>N^~ K[N^+;k{.>n~+k.n3s6v3CSs6FVv +;Kk{.>Nn~ +Kk.NnCSFV ;K{>N~ KN+;[k{.>^n~+[k.^n#3cs&6fv;[{>^~[^#c&f#3Ccs&6Ffv#Cc&Ff3Cs6FvCF#3Scs&6Vfv#Sc&Vf3Ss6VvSV"2BRbr%5EUeu *:JZjz -=M]m} *JZj -M]m:z=}"BRb%EUe :JZz =M]} JZ M]*:jz-=m}*j-m2r5u2BRr5EUu *:Jjz -=Mm} *Jj -MmBREU :Jz =M} J M*:Zjz-=]m}*Zj-]m"2br%5eu:Zz=]}Z]"b%e"2Bbr%5Eeu"Bb%Ee2Br5EuBE"2Rbr%5Ueu"Rb%Ue2Rr5UuRU!1AQaq )9IYiy )IYi9y!AQa 9IYy IY)9iy)i1q1AQq )9Iiy )IiAQ 9Iy I)9Yiy)Yi!1aq9YyY!a!1Aaq!Aa1AqA!1Qaq!Qa1QqQ'7GWgw/?O_o/O_o?'GWg?O_O_/?o/o7w?7GWw/?Oo/OoGW?OO/?_o/_o'7gw?__'g/?o/o7w?'7Ggw/?_o/_o'Gg?__7Gw/?o/oG?/?O_o/O_o'7Wgw?O_O_'Wg/?Oo/Oo7Ww?OOW/?_o/_o'7gw?__'g/?o/o7w?'7GWgw/?O_o/O_o'GWg?O_O_7GWw/?Oo/OoGW?OO'7Ggw/?_o/_o'Gg?__7Gw/?o/oG?'7Wgw'Wg7WwW'7gw'g7w'7Wgw'Wg7WwW'7gw'g7w'7GWgw'GWg7GWwGW'7Ggw'Gg7GwG'7Wgw'Wg7WwW'7gw'g7w#3CScs&6FVfv +;K[k{.>N^n~ +K[k.N^n;{>~#CSc&FVf ;K[{>N^~ K[N^+;k{.>n~+k.n3s6v3CSs6FVv +;Kk{.>Nn~ +Kk.NnCSFV ;K{>N~ KN+;[k{.>^n~+[k.^n#3cs&6fv;[{>^~[^#c&f#3Ccs&6Ffv#Cc&Ff3Cs6FvCF#3Scs&6Vfv#Sc&Vf3Ss6VvSV"2BRbr%5EUeu *:JZjz -=M]m} *JZj -M]m:z=}"BRb%EUe :JZz =M]} JZ M]*:jz-=m}*j-m2r5u2BRr5EUu *:Jjz -=Mm} *Jj -MmBREU :Jz =M} J M*:Zjz-=]m}*Zj-]m"2br%5eu:Zz=]}Z]"b%e"2Bbr%5Eeu"Bb%Ee2Br5EuBE"2Rbr%5Ueu"Rb%Ue2Rr5UuRU!1AQaq$4DTdt )9IYiy ,<L\l| )IYi ,L\l9y<|!AQa$DTd 9IYy <L\| IY L\)9iy,<l|)i,l1q4t1AQq4DTt )9Iiy ,<Ll| )Ii ,LlAQDT 9Iy <L| I L)9Yiy,<\l|)Yi,\l!1aq$4dt9Yy<\|Y\!a$d!1Aaq$4Ddt!Aa$Dd1Aq4DtAD!1Qaq$4Tdt!Qa$Td1Qq4TtQT 0@P`p(8HXhx(HXh8x @P`8HXxHX(8hx(h0p0@Pp(8Hhx(Hh@P8HxH(8Xhx(Xh 0`p8XxX ` 0@`p @`0@p@ 0P`p P`0PpP 8v                *T~&Pz"LvHrDn@j<f8b 4^  0 Z  , V ( R | $ N x J t FpBl>h:d 6`2\.X.E\s+BYp(       "!$#&%('*),+.-0/214387657887:97887<;7887>=7887@?7887BA7887DC7887FE7887HG7887JI7887LK7887NM7887PO7887RQ7887TS7887VU7887XW7887ZY7887\[7887^]7887`_7887ba7887dc7887fe7887hg7887ji7887lk7887nm7887po7887rq78vutsuvvuxwuvvuzyuvvu|{uvvu~}uvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuv     ! "$#%'&(*)+-,.0/1324765:98=<;@?>CBAFEDIHGLKJNMOQPRTSUWVXZY[]\^`_acbdfegihjlkmonprqsutvxwy{z|~}76:9=<@?CBFEIHLKfeihlkonrqutxw{z~76:9=<@?CBFEIHLKfeihlkonrqutxw{z~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~$$$,,,<<<=5+=>6,>?7-?@8.@A9/AB:0BC;1CD<2DE=3EF>4FG?5GH@6HIA7IJB8JKC9KLD:LME;MNF<NOG=OPH>PQI?QRJ@RSKASTLBTUMCUVNDVWOEWXPFXYQGYZRHZ[SI[\TJ\]UK]^VL^_WM_`XN`aYOabZPbc[Qcd\Rde]Sef^Tfg_Ugh`VhiaWijbXjkcYkldZlme[mnf\nog]oph^pqi_qrj`rskastlbtumcuvndvwoewxpfxyqgyzrhz{si{|tj|}uk}~vl~wmxnyozp{q|r}s~tuvwxyz{|}~                     "!$#    &%(' "!*)  ,+.-0/2143 "!$#               &%(' "!* )    , + .-0/21 4 3!! "!"$"#  # # %.%-    &0&/    '2'1(4(3)) "!*$*#    + +  ,,,           &%(' "!* )    , + %.%-    &0&/    '2'1(4(3)) "!*$*#    + +  --..// 001& 1%2(!2'3*"3)4,#4+5.$5-60%6/72&7184'839(9:$):#; *; <<<--..// 001& 1%2(!2'3*"3)4,#4+5.$5-60%6/72&7184'839(9:$):#; *; <<<--..// 001& 1%2(!2'3*"3)4,#4+5.$5-60%6/72&7184'839(9:$):#; *; <<<--..// 001& 1%2(!2'3*"3)4,#4+5.$5-60%6/72&7184'839(9:$):#; *; >6,>@8.@B:0BD<2DF>4FH@6HJB8JLD:LNF<NPH>PD<2DRJ@RTLBTVNDVXPFXZRHZ\TJ\^VL^`XN`bZPbd\Rdf^Tfh`VhLD:LNF<NPH>PD<2DjbXj^VL^`XN`ldZld\Rdf^Tfnf\nLD:LNF<Nph^pD<2D@8.@B:0Brj`rH@6HJB8JtlbtTLBTVNDVvndvZRHZ\TJ\xpfx^VL^`XN`bZPbd\Rdf^Tfh`VhLD:LNF<NPH>PD<2D>6,>@8.@B:0BF>4FH@6HJB8JRJ@RTLBTVNDVXPFXZRHZ\TJ\jbXj^VL^`XN`ldZld\Rdf^Tfnf\nLD:LNF<Nph^pD<2Drj`rtlbtvndvxpfxbZPbh`VhPH>Prj`rtlbtvndvxpfxbZPbh`VhPH>P>6,>F>4FRJ@RXPFXjbXjldZlnf\nph^prj`rtlbtvndvxpfxbZPbh`VhPH>Pyqgyzrhz{si{|tj|}uk}~vl~wmxnyozp{q|r}s~tuvwxyz{|}~               ** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1   # Ā̀Ԁ    vie$swift-im-2.0+dev6/BuildTools/DocBook/Fonts/Gentium Basic/OFL.txt0000644000175000017500000001062612227051773024153 0ustar kismithkismithCopyright (c) 2003-2008 SIL International (http://www.sil.org/), with Reserved Font Names "Gentium" and "SIL". This Font Software is licensed under the SIL Open Font License, Version 1.1. This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL ----------------------------------------------------------- SIL OPEN FONT LICENSE Version 1.1 - 1 February 2007 ----------------------------------------------------------- PREAMBLE The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others. The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that the font names of derivative works are changed. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives. DEFINITIONS "Font Software" refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation. "Reserved Font Name" refers to any names specified as such after the copyright statement(s). "Original Version" refers to the collection of Font Software components as distributed by the Copyright Holder(s). "Modified Version" refers to any derivative made by adding to, deleting, or substituting -- in part or in whole -- any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment. "Author" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software. PERMISSION & CONDITIONS Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions: 1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself. 2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user. 3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users. 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission. 5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software. TERMINATION This license becomes null and void if any of the above conditions are not met. DISCLAIMER THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. swift-im-2.0+dev6/BuildTools/DocBook/Fonts/Gentium Basic/GenBasR.ttf0000644000175000017500000101537012227051773024774 0ustar kismithkismith`Feat$xGDEFٷl8GPOS/ =`GSUBAGlat3.8LLGloc3FOS/2gG`Silfas..Sill$cmap>tGxTcvt GWfpgmY7WsgaspY\glyfcdYlhead--*,6hhea g*d$hmtx:h* plocaOhH6:maxp?N=4 namea>i=TApost~S>~\gprepǸr #$=D]bJL]__ace57 **"(%$&' #       !  )    &Dlatn IPA markmkmk "9< F N)* +      2 D J P V \ b h n h t z t       " ( J P V \ D J P V \ . 4 : @ F L R X ^ X d j p p p v |  4 : @ F  $*  4 : @ F06<BHN D J P V \TZ Z`fl lr j p p p v 4 : @ Fx ~   L V J P V \  06<BH 4 : @ F z & ,6<BH2$* 8>DJPV  \bhbn Dtt V \zt P V \ v  : @ F  X ^ X d  d ( J P V \  X ^ X d $$" 4 : @ F . 4 : @ F(  .444:  4 : @ F  @FFFLRXXX^djpvp|pvp|dpvp . 4 : @ F$*  X ^ X d $*        $* *06<<<BHNNNTZ J P V \`flrx~  flrx   b n t J P V \ 4 : @ F D J P V \x $*  $*  4 : @ F J P V \$* : 4 : @ F Zt P V \ 4 : @ F&  flf pvp&   $* ,flrx J P V \ . 4 : @ F,flrx $* ~ |2 4 : @ FH888>HNNN>D J2 4 : @ F PVVV \& \ bhntzZtt V \ L   4 : @ F j v$*    D J P V \ dN & $*    $*  J P V \    dpvp  ^  4 : @ F  4 : @ F   06<BH"(.4:@F@L x   R  $* Xhntz^ dV jpppv bhntz|  ^2 4 : @ F 4 : @ Fbhntzhntz   4 : @ F n t 4 : @ F   J P V \ D J P V \2 4 : @ F,flrx 4 : @ F   $* 06<BHNNNT$* ( R D J P V \   b h n h t$$$ `flrx . 4 : @ F  * & (  J P V \   J P V \(  444: *    $*  D J P V \N  : @ F D J P V \(   $***0 4 : @ F 2 4 : @ F 2 : @ F&  J P V \6<<<B H  4 : @ FN X ^ X dV $* TZZ`  4 : @ Ffl2$* |rxZ Z` 4 : @ F~6<BHO0 T  !!H\,,X/Z/s Z 11WWZZf+Z/T:ZO 4)WZ2ZggI~DE}Z}AAujZ Z 31 Z V;W* Dg "Z"'h!WZW  ZlfHJq*=ZWRJZJq~Z~LfZffH&Z&&vZv6Z6bZD*CCxmNZdZ(\Z\ZO+??Z?WPZ* W\ ZZ!'''YbWZ;&   OZG*\(\\ 'ZZZA'NjZ\ :b3;;--]Z]+AT6TgZ +      4 X ^ d j p v | | |       $ * 0 0 0 6 < B H B N T Z ` f l r x x x  ~ Z ` f l | | ^ d j p   &,2888>DJPJV \bbb T Z Z Zh ^ d j p vnnn tzDJPJV     ^ d j p Z ` f l " ^ d j p(..4:@ ` f lF LR X 0 0 0 6 ^ d j pF ^dddjpvvv|      6 Z ` f l ^ d j p   F X ^ d j p ^ d j p 0 0 0 6$ *06< v | | |B   HNTTT|  Z ^ d j p Z ` f l`fffl T Z ` f lrxxx> X ^ ^ ^H~ | |       0 0 0 6 X ^ ^ ^H  ~ v &,   vn n (..4JPJV | |   0 0 0 6 l& Z ` f l  0 0 0 6" ^ d j p T Z Z ZhNTTT|,228>D 6 ^ d j pJPVP\b h ntzz4    X ^ d j p ` fl     ^ d j p    JJV H N* Z ` f l 6"JJV 0 0 0 6(..4::: ,228@l" ^ d j p H N FFFHJJV T Z Z Zh  X ^ ^ ^HLRRRX ^dVd\jpppv  JPVP\ X ^ d j p| zzz4   Z ` f l    H    DJPJVPV^dddj Z ` f l bhn X ^ d j p tzz4  Z ` f l  8   4:::  vnnn  8rxxx>(..   ~hn d j pRRR H 0 0 0 6$ < B H B N*060< BHNT < Z ` f lZ  0 Z Z Zh`f:@@@hlV(<r ^ d j pxppp 6*060<~228 Z ` f l`66f  X ^ ^ ^H  " ^ d j p    PV : Z Z Zh FFF ^ d j p ~hn     ,228 X ^ d j p  x 6 xxx> lV   d j p    F   ^dVd\ l v,   ^ ^ ^Hrxxx>66f$ ~   0 0 0 6 vn n *060< 66frxxx>JJV    | | *060<(.. O0 ggI~Z2'''YbQQ&+/\Z\k\(\\!!H\ZZ!31Zg?LO+b3]Z]AAujZZ}+\{Z{CCxm77`g*T!!  Zvv/Z/s+ZZ(\xZ!ZgDCCNdZ'//=]]UUNZdZZZ4AZ q*!;"Z"'h--GG++*(Z('Z'GFZA\\ZA+\8Y8o""'AAz!Z&&?gZg l  TB+AT'hh&Z8Z8Z]-Z**"(%$&' #       !  ) 1Z+$$/%%&&]''X((o))**?++,,--T..l//|001122033445566J7778899::!;;_<<n==DD EEuFFbGGHHII.JJKK+LLMM9NNOOPPiQQRRSSITTUU(VVWWXXYYZZY[[\\O]](bbccxddVeeff'gghhhiijjkkkll6mm$nnfooDppaqqrrssttuuvvswwxx#yyzz{{r||}}~~j< m-ydc z2-#p{"MqN*"1S  5    &  0  G@C,&>RQ  !!""##*$$L%%&&''((/))!**`++,,--..$//0011 22K3344=55.6677889:;;;<<== >>U??B@@%AABBHCCDD4EEFF[GGWHHIIJJALLMMNNOOPPPQQ RRSS)TTUUVVWWXX\YYZZ[[v\\]]e__'aabbccee ff~gghh iijj,kk3ll}mmnnooppqqrrssttuu)vv wwxxyywzz{{||:}}~~  F8Etg%^ 12)1-w&"!HNkG|h,B[X+ V{`$(ALv5i )0Wl(gJ%?cZY 1m  9        /\+ty>  !!""##$$%%&&''&((2))P*+D,,--..//K0011U2233^445577o8899R::;;<< == >>}??@@AABBCCDD*EEsFF#GGHH2IIJJ!KKLLMMINN@OOPPQQRRSSTTUUVVWWXXYYZZ[["\\d]]^^__`` aabbccddzeeffggQhh=iiSjjkk ll'mmnnooppqqprrss-ttuuvvuwwxxyyzz{{||q}}~~*f3M:b;x a.3C% ,Tj4 'rFn0]~8$. _ 67Oe</~ & #~O OZZOZZ ZZZ&%# !"        $ #"         ! ((   JzcyrllatnIPA VIT aaltccmpccmp$0TpPbb &6H &6HLM   LM&Dblv JJdCCoCCJJppopp0u&&(( 22 88FFHHRR XXddoo??FFffoo}}  N ,  F ')uy^+bw%d!{`    $$&&((**]] __aaccttvvxx zz  f0     EM   DD"JJ(in DDLL!#$%&')*,./XXXXd@ JSIL @ D  4 t~!%-3:DHU]adq~37=BEKQTWY[cikru #(1?_';Io     " & 0 : D !"!&"""""""+"H"`"e%%,b1Bj $(09AGJX`dht&7=ADJQSVY[chkru#'1?^.>Lx     & 0 9 D !"!&"""""""+"H"`"d%%,`0BjT1hw8:"+ xjyޖދtq_/0TOn  2:>BDFFrxz|VFJNBBD8<:<>@B@bcdeYfgh<jikmlnoqprsutvwTxzy{}|~9.#F?G@IBHAUZQi}fl~g&!GAHBVMXOYPik}gq|f.0?u-"~hpmonq/1625/0o) $* + @>:DSRt{;7=9<8JCVNXPWOkjp#%(':7;8ICKELFJD[R\SWNZQ]Tc_ead`jl^43 "$&*(,nmht]v_zcxa|e- ,  12435srvwyxz>@ t~!%-3:DHU]adq~37=BEKQTWY[cikru #(1?_';Io     " & 0 : D !"!&"""""""+"H"`"e%%,b1Bj $(09AGJX`dht&7=ADJQSVY[chkru#'1?^.>Lx     & 0 9 D !"!&"""""""+"H"`"d%%,`0BjT1hw8:"+ xjyޖދtq_/0TOn  2:>BDFFrxz|VFJNBBD8<:<>@B@bcdeYfgh<jikmlnoqprsutvwTxzy{}|~9.#F?G@IBHAUZQi}fl~g&!GAHBVMXOYPik}gq|f.0?u-"~hpmonq/1625/0o) $* + @>:DSRt{;7=9<8JCVNXPWOkjp#%(':7;8ICKELFJD[R\SWNZQ]Tc_ead`jl^43 "$&*(,nmht]v_zcxa|e- ,  12435srvwyxz>@*+Pn 9,K PXYD _^-, EiD`-,*!-, F%FRX#Y Id F had%F hadRX#eY/ SXi TX!@Yi TX!@eYY:-, F%FRX#Y F jad%F jadRX#Y/-,K &PXQXD@DY!! EPXD!YY-, EiD` E}iD`-,*-,K &SX@Y &SX#!#Y &SX#!#Y &SX#!#Y &SX#!@#Y &SX%EPX#!#!%E#!#!Y!YD- ,KSXED!!Y-dH/ /ܸܸи/EX/>Y+017!!'!!L2P22O+A&6FVfv ]A]/ +01%#"&5466632''667%36.&321@/&T"+{#;,:6";-;+-  ////01#66667#666675 5 *-( +< 5 ).) +Be e W 7//EX/// >YEX6/6 >YEX/ >YEX*/* >YEX1/1 >Y + ии и иии #и %и&013#%3#3#'#'#'6673#'66736673667IMIW0]W.] IS,YS-Ns33 H H21" " XR^h W1+M+R +A&6FVfv ]A]A ]A  ) 9 I Y i y ]MиMии+и6иM=иRY/w_:!N_ .WL<" '(T*>z`<+VT&9.%`,7k/H7*C#7IX  '9KOgk 2GdJ*aU@  !%+* .8 8QoO<|jN L9'3G[Ļ>R+H4+ ++A]A )9IYiy ]A]A )9IYiy ]A>>&>6>F>V>f>v>> ]A>>]AHH&H6HFHVHfHvHH ]AHH]]2/W//-/#M+Mи/A!'7GWgw]A'7GWgqAvqW9#CиC/014&&&#"326667#"&&&5466632'6674&&&#"326667#"&&&5466632*6+"'6 ,"0Pk::bF(1Qj9YEXC/C>YU +qm+CA!'7GWgw]A'7GWgqAvqm,и,/01666654&&&#"267&&&&'&&&&'7#"&'#"&&&546667&&54666326654&&&#'667!"DV1#5#$6$TU92je]%3F+0Pgi1./, >9E:&X: WwnHNm]n=(LpH"6Zu?@W5w"#Wag3 !-: +j:B'LJF!#>/%-3FN+G/*/*9*9 *9*9*9$*901%%'&&&&''%%666677666679-%&  '* F#'  $$)D `+   F = ?+ и// + и01%'!'667!667!!0 2 .1XG +A ]A  ) 9 I Y i y ]/01%'666654&'6666:S4-' :@ :EC0!F'bfa'# <@G+,4/ O=u:qK+A&6FVfv ]A] +01%#"&5466632q%36.&321{#;,:6";-;1@ //01'667 $# %B% ELu'+(/)/ܹA]A )9IYiy ](и/ A  & 6 F V f v ]A ]EX/>Y#+A!'7GWgw]A'7GWgqAvq014&&&#"326667#"&&&5466632+H^33R9(E_84R9=o__a1>o__a15ЋG7s}ҍH7uee恁edH"+/EX/>Y0135666654&'&&'66667Nh> ">^B*svj!%8aK5  (, 3 *11#  5bD3# +#и/A ]A  ) 9 I Y i y ]#3и3/#5EX/>Y+*01!!'66666654&&&#"'466632!26667675Jw~R00M6)E1!FtM+>hLKkF!7O19cH*}TpA#9+C", 'Hg@KiB<BGG"I<&0>"*_P50Oc3&JD:7Um7j^+ии!/EX/>Y+9и01!#!5666655!'6673It!#F 6)AR.!*B%{   ++  !&#ND>+A]A )9IYiy ])/+/EX/>Y&1+:+A!'7GWgw]A'7GWgqAvq7:901#"&'73266654&&&#"'6666667!26767!6632D5e^]W#5WJ>Aa@ !CgF6;;/ ,  } (l3[^1WzHDQC%- 2Sk9DpO, !$_joi^##  [_T =fnq2I3/4/3и/A&6FVfv ]A]4ܹ A ]A  ) 9 I Y i y ])и)/#/EX/>Y.+A!'7GWgw]A'7GWgqAvq)#901"3266654&&&#"&&&546676666323{8'Hf>6G+-DQX+?Sh=Rl>QvQ#IE>Q~W.9Cwr73Oa.^yF/fbXC(IqЏ;mY". 2]huEX / >Y+01'667!"'66667!u.^YRE8#M2)Wvj7:#. lʲ5$ !<:FIE^c%M0+D+A]A )9IYiy ]D9/A]A )9IYiy ]A&6FVfv ]A]&50&9:I0&9&OEX+/+>Y? ++!A!!!'!7!G!W!g!w!!!!!!!!!]A!!'!7!G!W!g!qAv!!q016654&&&#"4&&&'326667#"&&&546667&&&&5466632+H]2I=8P1-B+/Lb40D+"@[97O2BrUV\/%Ea<+M:"7`KKuQ*6K,2\F*,A3+5j64M2"7D]B^D2#CFN-4ZB%*DUWNi=4Xq>6e[P!0@S8EvU0(Fb:*KE@;Phdf23/4/ܹA]A )9IYiy ]3'и'/ A  & 6 F V f v ]A ]и//.+"+01267&&&&#"%'66667#"&&&546666632Eu*/FU(3P7-DQRE @EI"P}W.3IZj;Ff=+M>}e*)Ie;\uCɇ;!le1"5^K2ibV@%:{q"X"=F//901%'465747776677777F d7B       =cF  + +01!'667!!'667!F- - -+0.=F// 901'66667%%'667F /'+q;  ' P&/?&+08++A00&060F0V0f0v00 ]A00] 809 /A]A )9IYiy ]=5+++01''&6666654&#"'&55466632#"&5466632&/HTK6(@MB,qi$A2 F-EuTIrM(%36.&321Kxh_erG`t9qmkfc0|!6H'FxX1.St#;,:6";-;F/5uKSj+)+>+G+A&6FVfv ]A]и/AGG]A GG)G9GIGYGiGyGG ]ASS&S6SFSVSfSvSS ]ASS]_j9wEX0/0 >YEX8/8 >YEX/>YEX$/$>YZe+qL+0AyqA!(8HXhx]A(8HXhq$ A!  ' 7 G W g w ]A  ' 7 G W g qAv q09B01&&#"326667#"&&&'#"&&&5466666326673266654&&#"326667#"$&546666632D<7[B$)?L#$*6&9'CW^`+,$)?<>(3iT64L_rA&%*6  <0B;)VfsP*3[ZTk$slt=pu^8?/^^Ub5 ":-+WfG&.I49J,Azo9zrfM,$-!J*JZV?mT]4]Zyѭ]0#4<7&TF.v7quBe%@EX/ >YEX/>YEX/>Y+01!5667667!566'o JR`DP tD. ZN< rZ ++ f)J ++!N) #K 5+D +A ]A  ) 9 I Y i y ] D9/A]A )9IYiy ]$G5$9MEX?/? >YEX)/)>YEX./.>YEX0/0>Y+?AyqA!(8HXhx]A(8HXhqи/)A!'7GWgw]A'7GWgqAvqGиG/01"3266654&&&"3266654&&&#"&&&'&'#5665'666632"rN!N 0Q$ ,..HtS,'Q}Cye=FJ#S[IDM&I" 'grv6dq=fX@nQ.!-FX+2V?$ %B]87t`=U`4+!> >  %Fd?l" Ba{F ._" +A""&"6"F"V"f"v"" ]A""]EX/ >YEX/>YAyqA!(8HXhx]A(8HXhq'A!''''7'G'W'g'w''''''''']A''''7'G'W'g'qAv''q01%#"&&&546632'&&#"3267@umi4]Wa{l4# #3["QRL;#MyG6n D]8T呠d:*&& /<6W|hȅCJ\  )c ,-/./-и/и/.ܹ A ]A  ) 9 I Y i y ]EX(/( >YEX/>Y(AyqA!(8HXhx]A(8HXhqи/A!'7GWgw]A'7GWgqAvq01"3266654&&&#!5665'66663220 PNEzNB&3Vq~;DM(H! ,s;UCЍӓN}ŗiC+!< >  S25U*+*EX / >YEX/>Y)+ /01%!56654&'5!#&&&&#!!&&&&##3326667DMIH&! -  #-=*,PB.A2)V+! $+>>8.>%M  #D;2*K&+&EX / >YEX/>Y%+ 01356654&'5!#&&&&#!!&&&&##2DMIH0 / v  "-=*pMb+! $+>>8.>%M  +Fy :;/YEX,/,>Y6 Ay qA!  ( 8 H X h x ]A  ( 8 H X h q,A!'7GWgw]A'7GWgqAvq01'&&&&#"32674&&&'5!#"&&&546632!' !!FMV1KSRC)JxLI{1/O=90JubX-eě`i!ORO$# ) 3Syh·CS  ++-=H' K囦`&2+,/-/,и/'и-ܹ$EX / >YEX/ >YEX/>YEX/>Y%+01356654&'5!!4&'5!!5665!2DMIHDMbIHDMIH>DMHI+! $++"> $++" #++! #+F/+EX / >YEX/>Y01356654&'5!FDMIHDMIH+! $++" #+B+*("+EX)/) >Y+01#"&&&5466673266654&&&'5!+DM!7H'AB;<.$(*$!;1 +L<"JwnI "   "Wu +21m-+-EX / >YEX/ >YEX/>YEX(/(>Y( 9,( 901356654&'5!66&&'5!67#"&'2DMIHDM 2' 2(!J)/2;w0- HI+! $++"  ++  + b #+25+EX / >YEX/>Y01%!56654&'5!3326667DMIHDM*H7.?0'W+! $++"! #D;<".vEX / >YEX-/- >YEX/>YEX/>YEX/>Y 9 9' 901"!5665#!5665&!266663!G# IH9DW J1C IHkEL &M   ~   #++! _ #++!1+!z"2$%/&/%и/&ܹи/ EX / >YEX/ >YEX/>YEX/>Y 9 90135665&&'5324&'5!&&'2JG!I'APHI29 CCN+ &+vo ' ++ &_ & +Fr )*/+/ܹA]A )9IYiy ]* и / A  & 6 F V f v ]A ]EX%/% >YEX/>Y%AyqA!(8HXhx]A(8HXhqA!'7GWgw]A'7GWgqAvq014&&&&&#"326667#"&&&5466632.CTd9Ya4=gMSe8ZrxDXv|AuEygK+KzpȗXEʖnj~nm) 4*5/6/5и/6ܺ9%A%%]A %%)%9%I%Y%i%y%% ].EX/ >YEX/>Y +9*Ay**qA!**(*8*H*X*h*x*********]A**(*8*H*X*h*q-и-/0135665'666632#"''3266654&&&#"2DM%I# 0qz}=l{C'AT[['^C*M#.dT6;fM1'D4+!> >  .ZVErYA+K $HjGRvK$ +F @޸A/B/1ܹA]A )9IYiy ]A'и'/ A  & 6 F V f v ]A ]6'19EX,/, >YEX / >YEX"/">Y;+,AyqA!(8HXhx]A(8HXhq A!'7GWgw]A'7GWgqAvq6 9014&&&&&#"32666#"&&&'#"&&&5466632326667.CTd9Ya4=gMSe8+20+>yxw<nKXv|A8dP.[WR% ",uEygK+KzpȗXE,E1=W_#fꄈnmzjƧ$<4$)s 1?A@/A/@и/Aܺ9"и"/-4и;A;;]A ;;);9;I;Y;i;y;; ]EX/ >YEX/>YEX"/">Y"92Ay22qA!22(282H2X2h2x222222222]A22(282H2X2h2q4и4/0135665'6666327#"&'##"&'"32654&&&2DM#I% /`hsBtr8)Kh>/#+5" Bw'7 4HID&'('S+!= >  .Ro@HuZ@ + 1 #+7\B%uu IϸJ/K/ܸJ%и%/ и /%и/A]A )9IYiy ]6и6/%AAAA&A6AFAVAfAvAA ]AAA]EX,/, >YEX/>YA!'7GWgw]A'7GWgqAvq,<Ay<<qA!<<(<8<H<X<h<x<<<<<<<<<]A<<(<8<H<X<h<q01#"&&&'&&4673266654&&&&&&&546666632'&&&&#"u1Ic}K!NRQ#)ETa3-[H.6WpupW6(?YtI-[O@ ! $=?>8O36XqvqX6y-^[Q<$$YEX/>Yи01!566665!"'667!#&&&&#!3.?& +  - H\+  $F:;53y?.A) (+2)׸*/+/ܸ*и/#EX/ >YEX(/( >YEX / >YA!'7GWgw]A'7GWgqAvq01#"&&&54&'5!3266654&'5!DM@xlg}FIHDM-YVGnK'IH"ΎK9uz $++"k`i8EpJ $+ @EX/ >YEX/ >YEX / >Y 901&&'5!6&'5! DM '., H E?P; aP GR   ++J +*vEX/ >YEX / >YEX)/) >YEX / >YEX/>Y 9 9" 901&&'5!36&&&'5!,:"!*,"+/>G18D7b*:"    ++   +5EX/ >YEX&/& >YEX/>YEX / >Y9 иик9%и(и401!5666&'!5667&&&&'5!6&&&'5! ,6A\>A\=#-!U4">-$9,! m%.+ h,"++%* ++!+k ++ ) +,&+&9EX/ >YEX/ >YEX/ >YEX/>Y9и/!и!/01!5665&&&&'&&&&#'66326&'5!h[I%^b\# $6*;v*. %VVR# 1MEI'?/+'Q0  + '0H ++ V +;JEX/ >YEX/ >YEX/>Y01!'!"'3!!266675&$ 9-+? " % NOK-[0K4 -+.P>N@ ! + + +01!!!!N[!14 W7@ //01&&&&'7f  C%:7 F")@ ) + + +01'667!!'667!L!#2 2qd5// /901''66667/"%  B #$${X f = } +01!'667! 20 / /01766667  '+* 6y)PIJ/K/CܹиJ!и!/ A  & 6 F V f v ]A ])EX?/? >YEX/>YEX/>YA!'7GWgw]A'7GWgqAvq?9?/Ay//qA!//(/8/H/X/h/x/////////]A//(/8/H/X/h/qF01%267#"&'#"&&&54676666754&&&''6666323267`.+;9 ^y?nw,(d=BE/(2 ;5\Q-B*4R:Lf%+($";+$1  '2\F*sg*$  %:;/YEX/>Y9!+Ay++qA!++(+8+H+X+h+x+++++++++]A++(+8+H+X+h+q6A!66'676G6W6g6w666666666]A66'676G6W6g6qAv66q01#"&&&'4&&&'56676666324&&&#"32666:TkJCWf6 3)Bx9 2c[NDsS/)EZ0;JT*(SK==\? :zthM- /.2(! i( 5WnPH._$ +A$$&$6$F$V$f$v$$ ]A$$]EX/ >YEX/>YAyqA!(8HXhx]A(8HXhq)A!))')7)G)W)g)w)))))))))]A))')7)G)W)g)qAv))q01%#"&&&5466632'&&&&#"326667HAcTP/IlAOl!E?5  %$9O35aK-3Uo<09K8MW- AzolQ  ,1- *&/^^Ub540P0CD/E/'ܹи/Dи/1и:A::&:6:F:V:f:v:: ]A::]&/EX/ >YEX/>YEX / >Y&95Ay55qA!55(585H5X5h5x555555555]A55(585H5X5h5q59 ?A!??'?7?G?W?g?w?????????]A??'?7?G?W?g?qAv??q01%#"&'#"&&&5466666324&&&'566767%&&#"32666.G5&!*&KOX38vc?:Th|F/]6:5R4 3YEX$/$>Y+3AyqA!(8HXhx]A(8HXhq$A!'7GWgw]A'7GWgqAvq01"!2654&&&!326667#"&&&5466676666324WC- -Q1< )MmD;DS8 CeYW3Mj?9R47<;AfL4!W(Ie=QM6" Nk@6.IY0ByjCtb$ $=QZ]-1:l&+и&+EX/ >YEX*/* >YEX!/!>Y6 +'и(01&&&&#"!&&#!5665#'735466676666321(+1-' ;3"  VP2O; EGNM 7H(@?9B7$ " #\|V  #  ++ # CvoK "   *t4L+++A]A )9IYiy ]A&6FVfv ]A]^L9^/ +&и&/+>QL>9TиT/YL>9r+9+vEXc/c >YEXk/k >YEXE/E>Y0+cAyqA!(8HXhx]A(8HXhqE!A!!!'!7!G!W!g!w!!!!!!!!!]A!!'!7!G!W!g!qAv!!qQEc9Y09rEc9014&&&#"32666&&'3266654&&&###"&&&&&546667&&546667&&&&546663266667#EgC=6$!CgG?3!&@HS* 8^|DCnN+DuEpI0$9dNmT!*Hbpx;/dbWB':hVA2 9.3S;!BlI1(*K7!$?W4/&M[3(5  0?H#7cTD0 +=M19BL.:'2 5K_8Ia9"/&V7L67/8/2ܹ7и/&к'29%/EX,/, >YEX/>YEX/>Y, Ay qA!  ( 8 H X h x ]A  ( 8 H X h q'%901!56654&&&#"!56654&&&'566667666632HD 0!#QXZ+KARBJ80*E;6%+ije'+P=%=O+=L,$FiFC ++*/ ("@fH&6S8+FL%A+EX/ >YEX/>Y$+01356654&&&'5666673#"&5466632FDH95ED>"CIn*-' )U+!63?#( #+2%2.2%$ L)8+*EX(/( >YEX / >Y7/+ A!'7GWgw]A'7GWgqAvq01%#"&&&5466673266654&&&'5666673#"&5466632c3F(<<5;/(+ D&9.94*A;7 %*-(*UcsqN % "%\ys3>#( -2%2.2%74`.+./EX/ >YEX/>YEX)/)>Y)9-)901356654&&&'566766&!7#"&'7BJ 7+E3%4!-{,J*x$-91' *3,%+.2(%"& #++"G + ^  +<"+/EX/>Y0135666654&&&'5667<+:" 6*Hx>$DR+-2 (" "| +7PTU/,и,/A]A]A]A]A]A]A]A],#:и:/DкE9PVEX8/8 >YEX?/? >YEXJ/J >YEX/>YEX/>YEX'/'>YJ Ay qA!  ( 8 H X h x ]A  ( 8 H X h qи2к:89E8901!56654&&&#"!56654&&&#"!56654&&&'566667666632666632HC +!JNO&>ORHC +DUKARBJ80&@:5# -\ZT$0Q;"*ZYU%0Q;">O+=Q.%Ec>+++=Q.+ ++_(. ( #BcB!7ZC?_A!6S8+7L45/6/0ܹ5и/%и%/EX#/# >YEX*/* >YEX/>YEX/>Y* Ay qA!  ( 8 H X h x ]A  ( 8 H X h qи/%#901!56654&&&#"!56654&&&'5667666632HD1#LW_0KARBJ83Dt8# ,jle'+P=%=O+=L,BkNC ++_'. ( )#CiI'6S8+P+,/-/ܹA]A )9IYiy ], и / A  & 6 F V f v ]A ]EX'/' >YEX/>Y'AyqA!(8HXhx]A(8HXhqA!'7GWgw]A'7GWgqAvq014&&&#"326667#"&&&5466666324Rh3Lh?8Uf.GfB "^m;Om@:ePOl?5btCraF'H~fBsaF(H7 @˸A/B/ܹA]A )9IYiy ]A)и)/  и 7и7/EX5/5 >YEXYEX$/$>YEX/>Y<AyqA!(8HXhx]A(8HXhqA!'7GWgw]A'7GWgqAvq9/и//7$59014&&&#"326667#"&'!56654&&&'566667666632f)DZ0:JT,-MC<6[B&4HU^1;GK^5BJ70">:9# 2e\ODtT/Wn>4W@s", *Rz:zthM-B>' ++>%0 (#YEX4/4 >YEX/>YEX/>YA!'7GWgw]A'7GWgqAvq* Ay qA!  ( 8 H X h x ]A  ( 8 H X h q*901%26667&&#"5665#"&&&546667663266667"B@?!xH8hQ11L[^L&JOW38vc?8Q\#9p&68<"    >Md-:9?/^^Ub5+.H3AzoXsP"$  !N- +7 2ӻ++и+/EX)/) >YEX0/0 >YEX/>Y0 Ay qA!  ( 8 H X h x ]A  ( 8 H X h q+)901#&&&&#"!56654&&&'&&&&'5667666632  +#;>=K^5BJ &A}2# ?HO+ J:LP(8!%O{Ux ++F!- (##7`G(^EϸF/G/ܸF$и$/ и /$и/A]A )9IYiy ]4и4/$<A<<&<6<F<V<f<v<< ]A<<]EX)/) >YEX/>YA!'7GWgw]A'7GWgqAvq)7Ay77qA!77(787H7X7h7x777777777]A77(787H7X7h7q01#"&'&&6673266654&&&'&&&&5466632'&&#"(@NLC0C +(CY4$<,+FY.*N=%2Sk9KI@ '0g1!4%&>O*+XF,GeF*$%;MN *J6 ):#(>3-0aB# *2,H9#* 3-)2AS' + иEX/ >YEX/ >YEX/ >YEX/>Y ии$A!$$'$7$G$W$g$w$$$$$$$$$]A$$'$7$G$W$g$qAv$$q01%#"&&&5#'7357!&&##32671`WJ#?0NHwG  bQ4 )#jNu$7%;`FfCh  

/6ܹ*и=и/ EX/ >YEX4/4 >YEX/>YEX / >Y9%A!%%'%7%G%W%g%w%%%%%%%%%]A%%'%7%G%W%g%qAv%%q01%#"&'#"&&&54&&&'5666673266674&&&'566767-%D:, #,>gWI /VB'4.$?;M*Ex^05 (  'F^8)A/-6 ( &'e>N   @EX/ >YEX/ >YEX / >Y 901&&'5!6&&&'5!&  "(' 42'1-%Cw  Q ++  +'vEX/ >YEX/ >YEX&/& >YEX / >YEX/>Y 9 9! 901&'5!36&'5!$ #*) !(' d16E6EEw    W'++  { +9EX/ >YEX,/, >YEX/>YEX/>Y9 иик%9+и.и801!56664'!566667&&&&'5!766&&'5!f,ƽ"3(8' %6(-,}QY %2!+! !++RB++   ++/%+ 3@EX#/# >YEX2/2 >YEX / >Y+ #901#"&&&5466677666677&&'5!6&&&'5!& *gop2&@.$)0_%01/ 42'11%Nw {oe0  $+( ,@Q+EJ ++ } +L=JEX/ >YEX/ >YEX/>Y01%!'!"'73!!267=5+! 1$'$* HD:- !;. +M[f@AB/$и$/))ܸ$)Ap)]A)]A ) )0)@)P)]A)))]A)]$и) и)и/)5и$<иCA//A901&&&&5466654&##"'6654&&&&&546667'C2  .C*2E*  'C5TxM$  QK vx -TxK>KX4:JAJ:*QG: -D[7QS5|K(:/).;)CweT"k@ +//01'667@>#/$'L@AǸB/и/ܸAP]A0]A]A]A p]A]  9$и)и 4и9и C////901'666654&&&546667&&&&5466654&&&'7332677Lww -TxJ'C2  /C+3E+  'D4TyL$  QK yM)9.(/:)DvfU"1=KY4:IAK;)PF9 -E[8;TJJ18VE9=!K\qH;NGQ=QT1/ /ܸ и /01#"&&&#"'6666323267@O[0*^a_+1S*5AOZ0.a_[(0V*1iW82<2NT1iW82<2NRd"$Z@"$Z@FD JAK/L/ܹ A ]A  ) 9 I Y i y ]Kи/92A22&262F2V2f2v22 ]A22] FиF//EX/ >Y9+Ay++qA!++(+8+H+X+h+x+++++++++]A++(+8+H+X+h+q01'666654&'676767&'&&&546632'&&#"3267#JuR4I.4> USYWa{l4# #3["QRL;#MyG6n @um53'%D8* 1  #"-!4&*呠d:*&& /<6W|hȅCJ\  D]8 O*2"( @2q"1@Frd"2j@2d"8@P"DP"DP"DPL"DPY"DP"DPDHJAK/L/ܹ A ]A  ) 9 I Y i y ]Kи/94A44&464F4V4f4v44 ]A44] FиF//EX/ >Y9/Ay//qA!//(/8/H/X/h/x/////////]A//(/8/H/X/h/q01'666654&'676767&'&&&5466632'&&&&#"326667#JuR4I.4> HDElAOl!E?5  %$9O35aK-3Uo<09K8'AcT(3'%D8* 1  #"-!4!zolQ  ,1- *&/^^Ub540)MW-N*Pb"HPb"HPb"HPbL"HFK"<"Y"IL" 7LY"QDP"R6P"RP"RPL"RPY"R)-"X@)-"X)-"X#)-L"X$F72(+A22&262F2V2f2v22 ]A22]2и/2 и /2и/(и/(и/(.//2+и2(и(/01&&''66'&&'667'667&&'66766667+DT 2 .USS-+CR&U"+.WTU-2"U&FEo`:=C=cnEE  +"U&[J+^_  Q'(/)/ܹA]A )9IYiy ](и/ A  & 6 F V f v ]A ]+#+014&&&#"326667#"&&&5466632(.$'.%m0L^.)F2/L^/'E35)&5!5)%5U>hM+4D'>iM+4Eh` C+8+A&6FVfv ]A]8 иии8#/"/"901'5&&&&5466675666676632'&&&&'26667";P.-O<#5   Gh?7bQ   !E@5  # /@*09J6'3RG? hFrV9 2V{> >smX{W   ,0- &">4/)YQ_+'7A!77'777G7W7g7w777777777]A77'777G7W7g7qAv77q016654&&&'&&'%#"&&&'&&66732654&&&'&&&&546667&&5466632'&&#"5 %%LsN* "Fm{!*(@NLB;AE! +(CY4H\+GY-BjJ('3#&2Sk9KI@ '0g1BK7S7CoQ-?&.KGG* &(.GADT,L?0F/GbA% ;MN *M<#DG(:/*$ISd@%E=1(^9>[;*2,HC95#966&EObFu //01#"&&&5466632#?W50G/%@W3-G0=dH(!/EX/ >YEX/ >Y1+%и%/'015665#"&&&54666323&#"3&''665EM*]}HCze@KQ'\fHDNII0>)%  7+Y7<+Q%+J,+A,,]A ,,),9,I,Y,i,y,, ],J9/A]A )9IYiy ]AQQ&Q6QFQVQfQvQQ ]AQQ]EX7/7>YEX/>YE1+A!'7GWgw]A'7GWgqAvq01#"&&&'&&466673266654&&&&&&&546666654&&&#"!5665466676632+&RZRP?'2FQ&#>.'@QVQ@'1JVJ1<_C0Q; DH!6H'6HVxK!3LZL3&?OTO?&;pX6 #062% 6L0'6!4M;0,/;K3CU7&*:03kX9#\|+ # [xnI ,0Gl~7RhB*&0(!1*',5G^#'JW +G++4U++A]A )9IYiy ]A&6FVfv ]A]7 9GNAUU]A UU)U9UIUYUiUyUU ]EX1/1 >Y#++01#"&&&54666324&&&#"32666565'66327#"&''#"'#"332654&2XzHHxX11XxHHzX2;)Hd<;cH))Hc;YEXH/H>Y%++HR9R2Ay22qA!22(282H2X2h2x222222222]A22(282H2X2h2qH<A!<<'<7<G<W<g<w<<<<<<<<<]A<<'<7<G<W<g<qAv<<q01#"&&&5466632'&&#"3266674&&&#"326667#"&&&5466632-QLI$A}b=CtVLp&  $Z= TK3;Ye**2;&QwvǑQQvwȑQV+Oq[^^[qO+q+:$4c[ds?% 0 MaPxQ)$vќZZvwҜZZw[yV/hh/Vx;'="L˻+4=+%K+K/и//0%9G%9%N/*/1/8/EX/ >YEX/ >YEXB/B >YEXK/K >Yик0939G9015665#"'46667!#&&##"#5665##5665&Ȕ6633#n   i& s r' i  !  3`  t 8 k //01&&' (+'  'ddL//ܹA]A )9IYiy ]и/A&6FVfv ]A] +и 01#"&5466632#"&5466632*-' )Up*-' )U2%2.2%_2%2.2%=cF'?/&/ ++ иии!013#!!'7#'66737!'667!7667x^a #('  w J0- #+.!K~1+и1?EX#/# >YEX / >YEX/>Y+#02и>и E014&3!5665!!56676&&&'5!#&&&&#!!&&&&##3326667 DK >R`DM 2M1! -   ".>*-QB.A1(| tW+!# ++  +>>8.>%M  #D;Fr! ;:YEX3/3 >YEX;/; >YEX / >YEX*/*>Y):9 A!'7GWgw]A'7GWgqAvq):93AyqA!(8HXhx]A(8HXhq014&'32666%&&#"#"&''7&&54666327667(&0t?Se8 *&.tDYa4 CDZrW;5 !&% BDXvW:6DuYH 6;Eʐ^F4L./: &: 8:A*4@M-/: &:3XK>:_TL('K;$6\wB3YM@@^NG('K;$6\w2):.*/4/ / 1%9.*/4/ t 5A!4F+#Y +&!9(и(/ -и00135665!'667!5&&&&'&&&&#'66326&'5!!![I #LNL" $7*;v*. %D@A#1MDJ'?/+'A#5Iu+  + '0fp}H{ ++ 3D += =L +F8+8 кF9.>F9FNEX(/( >YEXA/A >YEX/>YEX/>YEX / >Y (93A33'373G3W3g3w333333 ]A33] 39(9>(901%#"&&&'#"&''4&&&'5666673266674&&&'6673267=@<3 Q:;r,'3371 '4/'A:5 /D*9?F( ,[) 3!R(!":L+ijQHo_', (  #RF0 !=4-,& 'I?]L P@ǸA/B/ܹ+A++]A ++)+9+I+Y+i+y++ ]иA!и!/ A  & 6 F V f v ]A ]EX&/& >YEX/>Y:0+&AyqA!(8HXhx]A(8HXhqA!'7GWgw]A'7GWgqAvq+&901&&&&#"3266657#"&&&5466632&&&&#"'76632DW`+;[=5Q`+:fL+-K_d_&bk9HwQMOFBcx97;E--v:g37sj]F(J8`G):ePOl?FqoBH~fcW0> ~7)"!и>/EX9/9 >YEX5/5 >YEX:/: >YEX/>YEX/>Y9AyqA!(8HXhx]A(8HXhqи/)и)/*и*/=и>иFA!FF'F7FGFWFgFwFFFFFFFFF]AFF'F7FGFWFgFqAvFFq01%#"&&&546667&&'&&'&'6666667#"'66663!267##3266673L;/#4# GR 9DC ( 3 2//89: y*H4i0A "$0"n+7 *MnE [~irH{7  &DPiώ $#<,HMQN[T'  2 E%9+A&6FVfv ]A]2992/A22]A 22)292I2Y2i2y22 ]EX / >YA + -A!--'-7-G-W-g-w---------]A--'-7-G-W-g-qAv--q01&&&&#"#"&&&5466673266654&&&&&5466676632(+)'" - $9H#=;6;/(+ D&:. '<'3o'+I5!", K{[>ƱCWhL% "!Sl7ū;U{]H"-/!+*1J48GH/I/-ܹи/Hи/-1и1/5и5/9и@A@@&@6@F@V@f@v@@ ]A@@]65+(+01#"&'#"&&&5467754&&&#"''666632675!'5326.=0]")"_f\"  $#7HL$27+,9" F(+-& + >_/ # :-.L7 II !&1J+ɸ,/-/ܹA]A )9IYiy ],"и"/ A  & 6 F V f v ]A ]"и/и/+'++014&&&#"326665!#"&&&5466632,;!/"-:/# -H\/2R; (F]50RYEXP/P >YEX/>YEX#/#>Yq+ A!  ' 7 G W g w ]A  ' 7 G W g qAv qH9q0и0/H6Ay66qA!66(686H6X6h6x666666666]A66(686H6X6h6qKH9[и[/ fи6k01!326667#"&&&'#"&&&54666766754&&&#"''66666632666632&&5532666"!2654&&&9< ?YEX8/8 >YEX#/#>YEX-/->Y,A9#A!'7GWgw]A'7GWgqAvq,A98AyqA!(8HXhx]A(8HXhq014&'32666%&&#"#"&''7&&546666632766667 (X(GgC !'Y,LhAf37"Ew1  Y#+=5A55]A55(585H5X5h5x555555 ]016673266654&'667#"&&&54666667#"&5466632'@KB,pi$A2"F-EuTJrM(/HTL6'33/%250u9pnjgc0|!7H'GwW1.SsEKyh`eqF";-;8":,:  +A]A )9IYiy ]/EX/ >YA]A(8HXhx ]01'667#"&5466632s'S"+/E&131%34.- 1)";-;8";,;={j +/ +01%'!'667!{)J +0EX / >Y+01##'66773 "(' r a\2 L0 M9+и9>EX/ >YEX=/= >YEX%/%>YI +%4A!44'474G4W4g4w444444444]A44'474G4W4g4qAv44q:и;01&&&&#"!&&##"&&&546667326665#'73546667666632(+2-& <3!  UN 6I(<93:.(+" !9.NL!7I(?>8C8% " $\|V usN#  " #\|BvoK "  8@K#/;%//// / +*+%01#"&&&#"'6323266677#"&&&#"'632326667Ke*RQQ(/K,5d-YSK /+'6e*RQQ(/K,5d-YSK /+'$507$%ʢ$507$%;F%,5EX%/% >YEX/>Y%901%&'&&&&'3!266&!'6766667667q@>872465:;3014ZS#JF>CENQ'[bE9?C  3_ɿA)JdPV#///#/015467354673Pg* *,) + g* *,) +#;KNK:#\#;KNK:#\ /// /01'7'7+*g+*gcfIcf"#"$ @q"$Z@Frq"2j@F' S!+H+A&6FVfv ]A]H3ܸH9EX+/+ >YEX&/& >YEX/>YEX/>Y:G+A!'7GWgw]A'7GWgqAvq+ Ay qA!  ( 8 H X h x ]A  ( 8 H X h q8и8/9и9/MиN01%26667&&#"%!#"&&&5466632!#&&&&#!!&&&&##3326667\(F=65~GYa4=gr5LBB,xDXv(;6;(P / j  #->*dDB.A1)Z )$LzpȗXW j~n >>8.=%N  #D;P !aG+&+" +&и/A ]A  ) 9 I Y i y ]A&6FVfv ]A]=&9S&9"cEXN/N >YEX[/[ >YEX8/8>YEXB/B>Y%+[AyqA!(8HXhx]A(8HXhqи8+A!++'+7+G+W+g+w+++++++++]A++'+7+G+W+g+qAv++q=8N9S8N901"!2654&&&4&&&#"32666%!326667#"&&&'#"&&&5466666326676666320SA+ *L+3Ul96Y@#6Vl66X@#< )IhA6AR8 BeWR/2]TF Q[d4Ui<"=Tes<6cVE?&9:9>aJ4 X)Ie=RL7oPl@:ePOl?9e# Nj?6.IY07O3/O9H~fBsaF(6M0&A $=QZ]=: +01!'667! "41=: +01!'667! p"41\%/0/1/0и/A&6FVfv ]A]1,ܹ!A!!]A !!)!9!I!Y!i!y!! ] /&/01'&&546667'&&&&546667 ;EB"$4L0+  6; ;EB 4L0+ 6: !E;'ZZR#785-A- !%.'ZZR#785-AX-.///. и /A&6FVfv ]A]/ܹ"A""]A "")"9"I"Y"i"y"" ]//01'666654&''6666'666654&''6666y4J0-  69 :EC $4J0+ 89:FC $'[ZS"885-C- F9'[ZS"885-C- F\G+A&6FVfv ]A] /01'&&546667 ;EB"$4L0+  6; !E;'ZZR#785-AXyG +A ]A  ) 9 I Y i y ]/01'666654&''6666y4J0-  69 :EC $'[ZS"885-C- F= 'm+A]A )9IYiy ]и ++'!+01#"&5466632#"&5466632!'667!"# "C"# "Cj T((%(((%(1.,/EX / >Y 9 9013'66667  -  ==A   L"\d"<_@2 /EX/>Y01%'667<+/ $CD/E/D(и(/A&6FVfv ]A]E>ܹA]A )9IYiy ]>и/( и /(0и0/>8и8/0/8// / +4+0132676654&'&&#"'#"&''&&'7&&5467'7667663277k> >= >^i"M((N"l!kk k"N((L"j"jj? => >.ik k!M((N"l!kjBj"N((M"i"P //01%54673g* *,) #;KNK:#\  //01'7 +*gcf-q"IL}-{QR/S/MܹR2и2/'и27иIиI/EX/ >YEX6/6 >YEX/>YEX-/->YB+&3и401!5666654&'&'&&&&#"!&&#!5665#'735466676666326767+:"   +1-' ;3"  VP2O; EGNM 7H(@?9B<>$DR+-2   #\|V  #  ++ # CvoK "   "| +FK .+A  & 6 F V f v ]A ] и/ и/."и.4и.>и H/D/ +H+"и .и4иH>и>/01&&'66667&&''667'667&&'667'667&&'667667+ET.UTU-+CT&U"+.UTT-+CS\Z+CS&U"+\Z2"U&>uDB{:  +"U&`L+`e  +"U&:|AEt>+"U&_K+^d2H0XyG +A ]A  ) 9 I Y i y ]/01%'666654&''6666y4J0-  69 :EC $='[ZR"885-C- FX-.///. и /A&6FVfv ]A]/ܹ"A""]A "")"9"I"Y"i"y"" ]//01%'666654&''6666'666654&''6666y4J0-  69 :EC $4J0+ 89:FC $='[ZR"885-C- F9'[ZR"885-C- FL';O[odfz+p\+2F+<(+ ++A]A )9IYiy ]A]A )9IYiy ]A((]A (()(9(I(Y(i(y(( ]A22&262F2V2f2v22 ]A22]Aff&f6fFfVfffvff ]Aff]App&p6pFpVpfpvpp ]App]/A/U/Z//Ku+uи/A!'7GWgw]A'7GWgqAvqK#иu-и-/7иaKkиk/014&&&#"326667#"&&&54666324&&&#"326667#"&&&5466632'6674&&&#"326667#"&&&5466632=(6,#'7 -!0Pk:;cG'1Qj9Y+!++y+)и)//и!7и7/!и/=и=/и/DиJиJ/7Xܸ`иfиf/ylиl/rиr/Xи!и014632#"&4632#"&6632#"&546632#"&546632#"&54632#"&546632#"&546632#"&546%2##"&546%2##"&546#"&54632#"&54632#"&54632#"&54632#"&54632#"&54632  6< d<757 E Fr"2 @2"8 @2"8 @2"8 V@F1 // /01&&&&'3  kD   {FYY%/ /// и /01#"&&&#"'66663232672=H'#?<;(B%51>G'&D<6&I"B)P@(#+#A8)Q@(#+#@;K EX / >Y01!'66667!K  e    (.} //+01#"&&&'667326667KSZ-1\SI AHK!#MIAPQnEEnQ9N//N9dd,LK+A&6FVfv ]A] +01#"&5466632,*-' )U2%2.2%F'(/)/ܹA]A )9IYiy ](и/ A  & 6 F V f v ]A ]+#+014&&&#"326667#"&&&5466632`  &  &Z'?M&"9)'>M' 9*,!,+!+F3U?#*8 3W?#+9ND_ +A ]A  ) 9 I Y i y ] 9//901'666654&'67667#JuR4I.4>N'3'%D8* 1  #"ZTt*P / ///01&&'&&' !!  !  ) )dD+Q +A&6FVfv ]A] /+01#"&&&546732679<<8,-HX00&I*4) 8-ZQ,TJ>%#$&2/ // /01#66667%k  /e   P`"D#P"D#Pb"DP"D#b"DP"D#P"DPe"D#P"DP}"DP`}"D#P"D#@P"DP"D#@P"DP"D#@P"DP"D#@Pr"DP"DP"DP"D#hPL"DP"D#hP"D#P"DP`"DPP0AɸB/C/&ܹ1и/Bи/&и/&"и"/:A::&:6:F:V:f:v:: ]A::]EX/ >YEX/ >YEX/>YEX / >Y95Ay55qA!55(585H5X5h5x555555555]A55(585H5X5h5q ?A!??'?7?G?W?g?w?????????]A??'?7?G?W?g?qAv??q01%#"&'#"&&&546666632667674%&&#"326Wx*$*GGJ+7s_<8QfzE..3 8   A3 \G@gI(.HW*0hT;7Wi5J-Azo9zrfM,%-!!J*+к?!9EXD/D >YEX/>Y,9+DAyqA!(8HXhx]A(8HXhqA!'7GWgw]A'7GWgqAvq?D9014&&&#"326667#"&&&'46667666632&&#"666632/)EZ0;JT*(SK==\? :TkJCWf6"?X6CC;-UB((+*r; NC-2c[NDsS/dj81S>i( 5Wn:zthM- /|b+" !,+  !4>.pYEX>/>>YEXC/C>YEXE/E>Y+)и/>A!'7GWgw]A'7GWgqAvq4и4/KиK/01&&&&'3266654&"3266654&&&466632#"&&&'&'#5665&&8JbC"rN'0Q$ ,..HtS,'Q}MςUlT$-5fX@nQ.Cye=FJ#S[IDM4U=!&,1%0p-FX+1W6%B]87t_=bAjK) !`9l" Ba{EU`4+!G(7#$8LPH"F PH"FPH"FPHL"FPDHJUEV/W/ܹ A ]A  ) 9 I Y i y ]Vи/94A44&464F4V4f4v44 ]A44] FиF/O//EX/ >YO9/Ay//qA!//(/8/H/X/h/x/////////]A//(/8/H/X/h/q01'666654&'676767&'&&&5466632'&&&&#"326667&&'#JuR4I.4> HDElAOl!E?5  %$9O35aK-3Uo<09K8'AcT(3' '+( %D8* 1  #"-!4!zolQ  ,1- *&/^^Ub540)MW-N* 'BM67/8/ܸ7 и /A&6FVfv ]A] A ]A  ) 9 I Y i y ] -и-/EX2/2 >YEX/>YA!'7GWgw]A'7GWgqAvq2%Ay%%qA!%%(%8%H%X%h%x%%%%%%%%%]A%%(%8%H%X%h%q01#"&&&54676673266654&&&#"&&'666632MFx]GzY3&V& "9J'8^C&1Tm<8AO3 =g]X.Ij@kP,OnA 9 /  #C4!0`^U`4-%@Q-Az7/.c$ +A ]A  ) 9 I Y i y ]$0EX/ >YEX)/)>YA!'7GWgw]A'7GWgqAvqAyqA!(8HXhx]A(8HXhq0173266654&&&#"&&&&7666632#"&&&'^7L90YO9+Ay++qA!++(+8+H+X+h+x+++++++++]A++(+8+H+X+h+q01'666654&'676767&'&&&546632'&&#"3267&&'#JuR4I.4> USYWa{l4# #3["QRL;#MyG6n @um53'  "$ %D8* 1  #"-!4&*呠d:*&& /<6W|hȅCJ\  D]8 O* =-MEX/>Y.>+L+)#+A!'7GWgw]A'7GWgqAvqиLи)Cи#F01!326667#"&&&'#'6673547#'6673666632'&&&&#"!!!q :Qc5;CP3 YEX/>YA!'7GWgw]A'7GWgqAvq6'Ay''qA!''('8'H'X'h'x''''''''']A''('8'H'X'h'q01#"&&&5467666673266654&&&#"&&&&'666632U{bb0"11,#%Da;Ff>MzHCSe< Cvld3^Wg8Yl3*N& 1!%+$L>(9՜ȆC 6*  CR-R< .c% +A ]A  ) 9 I Y i y ]%0EX / >YEX*/*>YA!'7GWgw]A'7GWgqAvq AyqA!(8HXhx]A(8HXhq017666673266654&&&&&#"&&&&76632#"&&&< n6GyM#;LRQ"[3# #4l{ܦaW]4imu  \JCȅh|W6YEX/>YEX / >Y)"+-9AAyAAqA!AA(A8AHAXAhAxAAAAAAAAA]AAA(A8AHAXAhAqA9"ܸ"/и#0и2и3и KA!KK'K7KGKWKgKwKKKKKKKKK]AKK'K7KGKWKgKqAvKKq01%#"&'#"&&&5466666325!'667!54&&&'56673#67%&&#"32666.G5&!*&KOX38vc?:Th|F/]6 :5R4 3YEX/>YEX&/&>Y> +!292GAyGGqA!GG(G8GHGXGhGxGGGGGGGGG]AGG(G8GHGXGhGq52G9&QA!QQ'Q7QGQWQgQwQQQQQQQQQ]AQQ'Q7QGQWQgQqAvQQq01&&&&#"67#"&'#"&&&5466666325466676632&&#"32666 (+)'" - 3< .G5&!*&KOX38vc?:Th|F/]6$8'3o'+I5!xH@jM+1L[)"B@?!"+ Jz[#2" ++ [m.J3Azo9zrfM,(U{]H"-/!+*m9?/^^Ub5-:P K'L/M/ܹ+иL6и6/A&6FVfv ]A]+@J/EX=/= >YEX&/&>YEX1/1>YA!'7GWgw]A'7GWgqAvq= Ay qA!  ( 8 H X h x ]A  ( 8 H X h q&A!'7GWgw]A'7GWgqAvq,&J9@= 901%26667&&#"32666'&666#"&&&5#"&&&5466666324&&&'5667"B@?!xH@jM+1L[7H/ (8:7bI=N.&JOW38vc?:Th|F/]6:5R4d-:9?/^^Ub5s|*2'&\Q6-Kc6.H3Azo9zrfM,(y7A# ' %POP/Q/ܹA]A )9IYiy ]P#и#/ A  & 6 F V f v ]A ]к/#9 8и8/:#9K#9A/EX*/* >YEX/>Y*AyqA!(8HXhx]A(8HXhqA!'7GWgw]A'7GWgqAvq/A9:A9KA901&&&&#"326665467#"&&&546666632&&'&&&&''7&&&&'77EU^+;Z<4P`+7eM.,H]b^&bk9!:Q^j6@@<dG ;BI*qT Ed?J9aG':ePOl?7s{ ta=H~fBsaF($0eHb%o' )HKN[#gK)c"'(@)cd"')@)c "'))`c "')c !89/:/ܸ9 и /и *$и*,и,/4A44]A 44)494I4Y4i4y44 ]EX/ >YEX/>Y +"Ay""qA!""("8"H"X"h"x"""""""""]A""("8"H"X"h"q$и$/%и (и/A!//'/7/G/W/g/w/////////]A//'/7/G/W/g/qAv//q01#!5665#'6673'666632"!!3266654&&&c3Vq~;DM (H! ,s;Ub20 PNEzNB}ŗiC+!" >  SC3CЍӓNc !89/:/ܸ9 и /и *$и*,и,/4A44]A 44)494I4Y4i4y44 ]EX/ >YEX/>Y +"Ay""qA!""("8"H"X"h"x"""""""""]A""("8"H"X"h"q$и$/%и (и/A!//'/7/G/W/g/w/////////]A//'/7/G/W/g/qAv//q01#!5665#'6673'666632"!!3266654&&&c3Vq~;DM (H! ,s;Ub20 PNEzNB}ŗiC+!" >  SC3CЍӓNc !89/:/ܸ9 и /и *$и*,и,/4A44]A 44)494I4Y4i4y44 ]EX/ >YEX/>Y +"Ay""qA!""("8"H"X"h"x"""""""""]A""("8"H"X"h"q$и$/%и (и/A!//'/7/G/W/g/w/////////]A//'/7/G/W/g/qAv//q01#!5665#'6673'666632"!!3266654&&&c3Vq~;DM (H! ,s;Ub20 PNEzNB}ŗiC+!" >  SC3CЍӓNI >J4+0+$+A]A )9IYiy ]A44&464F4V4f4v44 ]A44]$@EX/ >YEX+/+>Y+ A!  ' 7 G W g w ]A  ' 7 G W g qAv q1и1/01&&&&#3266654&&&466632#!5665&&$Ma}S PNEzN,D\QiU&Gb=3Vq~;DMwp&,1%0O' CЍDyi=LmE! !-tP}ŗiC+!FS@$8LPb"H#Pb"HPb"H#:bb"HPb"H#Pb"HPbe"H#P"HP`b"H#Pb}"HPb"HPbY"HPb"H Pb9"H# hPb9"H# hPbL"HPb"HP`b"HPDbGU/+ ++Q+A ]A  ) 9 I Y i y ]+9/MиM/AQQ]A QQ)Q9QIQYQiQyQQ ]+W/EX$/$ >YEX/>YEX@/@>YEXB/B>YN.+B4A!44'474G4W4g4w444444444]A44'474G4W4g4qAv44q$HAyHHqA!HH(H8HHHXHhHxHHHHHHHHH]AHH(H8HHHXHhHq01'666654&'676767#"&&&546667666632!326667"!2654&&&#JuR4I.4> Mj?9R47<;AfL4!< )MmD;DS8 CeY,3'4WC- -Q%D8* 1  #"-!4ByjCtb$ $=QZ]*" Nk@6.IY0P* (Ie=QM6PDb}GUo /+ ++Q+A ]A  ) 9 I Y i y ]+9/MиM/AQQ]A QQ)Q9QIQYQiQyQQ ]+qc/m//EX$/$ >YEX/>YEX@/@>YEXB/B>Yh[+N.+B4A!44'474G4W4g4w444444444]A44'474G4W4g4qAv44q$HAyHHqA!HH(H8HHHXHhHxHHHHHHHHH]AHH(H8HHHXHhHq01'666654&'676767#"&&&546667666632!326667"!2654&&&#"&&&'667326667#JuR4I.4> Mj?9R47<;AfL4!< )MmD;DS8 CeY,3'4WC- -QKSZ-1\SI AHK!#MIA%D8* 1  #"-!4ByjCtb$ $=QZ]*" Nk@6.IY0P* (Ie=QM6QnEEnQ9N//N9FRrFR 45/6/ ܹи/5и/A&6FVfv ]A]и/и/+и+/EX0/0 >YEX/>Y+A!'7GWgw]A'7GWgqAvq0#Ay##qA!##(#8#H#X#h#x#########]A##(#8#H#X#h#q01%267!"#"&&&5467667!&&&&#"&&'666632r~ p$&=MOE9ZH~^6F#1Qm=8BP3 =g]X.Ik@J/(/P;!vJ>:/SqB(B!R\1-%@Q-AzP=F< +A<<&<6<F<V<f<v<< ]A<<]<<1и1/EX/ >YEX/>Y67+9,Ay,,qA!,,(,8,H,X,h,x,,,,,,,,,]A,,(,8,H,X,h,qAA!AA'A7AGAWAgAwAAAAAAAAA]AAA'A7AGAWAgAqAvAAq01%#"&&&546667&&546667666632'&&&&#"326667=AnaX+TX-!6D#KR)9!82  ,1- .(*7<3%5(:D" <0702Q"(# @ 2T"(@2Q"(# @ T"(@2"(# @2"(@2s"(# @2"(@2`"(# @2"(@2"(@2q"(@21"(@2"(#@ 2"(#@ 2d"(@2"(@2`"(2DPܸQ/R/ܸQи/;и/ A ]A  ) 9 I Y i y ]9;,и LиL//EX/ >YEX/>YEXJ/J>Y-:++@01'666654&'676767!56654&'5!#&&&&#!!&&&&##3326667!#JuR4I.4>ODMIH&! -  #-=*,PB.A2)+#3'%D8* 1  #"-)J+! $+>>8.>%M  #D;Vh*2DPjk/l/ܸkи/;и/ A ]A  ) 9 I Y i y ]9;,и LиL/^/h//EX/ >YEX/>YEXJ/J>YcV+-:++@01'666654&'676767!56654&'5!#&&&&#!!&&&&##3326667!#"&&&'667326667#JuR4I.4>ODMIH&! -  #-=*,PB.A2)+#3'KSZ-1\SI AHK!#MIA%D8* 1  #"-)J+! $+>>8.>%M  #D;Vh*YQnEEnQ9N//N9A9]5+$и5;EX/// >YEX/>Y$+ /%013'6666733326665#"'6666733#"'667!b! - @O,750 #-.A2)+DMIHTWM.S>$  #E:V+! $+P >4 +A44&464F4V4f4v44 ]A44]44)и)/EX/ >YEX/>Y./+/.9$Ay$$qA!$$($8$H$X$h$x$$$$$$$$$]A$$($8$H$X$h$q9A!99'979G9W9g9w999999999]A99'979G9W9g9qAv99q01%#"&&&546667&&54676632'&&&&#"326667=tzL\_1'BU/`l`ZAQlA/+ YEXN/N >YEX/>YEX8/8>YA!'7GWgw]A'7GWgqAvqDAyqA!(8HXhx]A(8HXhq-A!--'-7-G-W-g-w---------]A--'-7-G-W-g-qAv--q3N901%267&&#"#"&&&54666732666''#"&&&546666632666671uOfI@jM+1L[  0Qgi`!@vZ6"02$F?61ZC&,MKM,8vc?;Si|E025!  dNT;A!N-rX4)/ *2 -f{{8L/:ph9{pU2$  P ";P "P }"P "P ")P L"Fy"* @Fy"* @Fy"*@Fy"*@Fy1"*@Fyd"*@7L"K C:7L"KC:7L^"KD:7L^"KD:7L"KD7`L"KD2"+ @2"+@2d"+@2d"+@2`"+1)l*/+/*и/+ܹ%EX / >YEX/>YEX/>Y 01356654&'5!!56654&&&##1DNJHEMII=DM4M1/G/HI+! $++" #++!     #+b}" Y"rY" %)"*K9"# <hF"#F`L"L / L1k-+и-'EX&/& >YEX/>Y ++(и+01#"&54666325665#'667354&&&'56666733#*-' )UDH 95ED>"CI2%2.2%+!7$3?#( 9A #+F/+EX/ >YEX/>Y01356654&&&'5666673FDH95ED>"CI+!63?#( #+/ "Y+ иEX/ >YEX/>Y + и0135665#'667354&&&'56666733#FDH 95ED>"CI+!7$3?#( 9A #+F L"LM+1V  +EX/ >YEX/>YA!'7GWgw]A'7GWgqAvq 901%#"&&&54&&&'56666733267V3WH8&/ :6FD?# \A0@&9X<3?#( oDN( ".l",*@c",)@|q",*@/3Y",4@x",#*@ FFd",*@F",-@F`",*0!Y + иEX/ >YEX/>Y+ и01#!5665#'66734&'5!3IH>DM IHDMg #++!# $++">F",-b$ c$ c)+EX(/( >YEX / >YA!'7GWgw]A'7GWgqAvq01%#"&&&5466673266654&&&'5666673c3F(<<5;/(+ D&9.94*A;7 %csqN % "%\ys3>#( 7"N ::7"N:7"N7`"N7Fw + 2EX2/2 >YEX:/: >YEX/>YEX/>Y -+ 2932901%#"&'!566566667666632&&#"66&!791' *3,%pBJ#>X7DA;)SB*(+.k> MD-4!-{,J*x$- ^  ++x`," !,+ #4>.pj #++"G 2". m@2".P@2".g2`".g2?P@/A/@и/;иAܺ9и/#A##]A ##)#9#I#Y#i#y## ]EX / >YEX/ >YEX/>YEX6/6>Y69&Ay&&qA!&&(&8&H&X&h&x&&&&&&&&&]A&&(&8&H&X&h&q:6901356654&'5!6632'66'&&#"67#"&'2DMIHDMjBN/T@& '6;7*)'%)/2;w0- HI+! $++"ST6U<.9+F=?' + b #+<n"O <o"O <`"O `yq"O# *FH"L + и/EX/>Y!+и!01#!566665#'66734&&&'566732DR>+:"  6*Hx>$ ++|$-2 (" "RFH. + и и&и+%/EX/ >YEX'/' >YEX/>Y-+и-и'и*и+01#!566665#'66735#'66734&&&'56673#32DR>+:"   6*Hx>$ ++$n$n-2 (" "An4N+"и-,/EX / >Y1+1и/ 01#"'!566665&&#"'6666324&&&'566732672=H' DR>+:"#(B%51>G'  6*Hx>$!&I"{)P@( ++ A8)Q@(-2 (" "F @;6(F$+и$/EX/>Y99013566665'6666774&&&'56677F+:"  6*Hx>$DR+=d `-2 (" "gdZ +2"/ @2"/2`"/2`1"/#@,*c+иEX/ >YEX/>Y +и и$01%!5665#'66734&'5!3#3326667DM IHDM*H7.?0'W+!" $++"C #D;,6++ии+ и+%EX/ >YEX/>Y ++!и$и&и )и001%!5665#'66735#'66734&'5!3#3#3326667DM  IHDM*H7.?0'W+!M"n" $++"TCnC #D;=m2+и2"EX/ >YEX/>Y&/+& и /и/701%!5665&&#"'66663234&'5!3267#"'3326667DM(B%51>G'IHDM"&I"62=H' *H7.?0'W+! A8)Q@( $++" @;)P@( #D;).Y#+и#EX/ >YEX/>Y 99(01%!5665'6666774&'5!%3326667DM gIHDM*H7.?0'W+!@ 4 $++"E4> #D;7P"Pb7PL"PF7`P"PF<""0 N@<"d"02@<`""027L"Q`7L"Q7L"QC7LL"QD7L"QD7`L"QD BIJ/K/EܹJ.и./:и:/EX8/8 >YEX?/? >YEX/>YEX/>Y? Ay qA!  ( 8 H X h x ]A  ( 8 H X h q4и4/:8901!56654&&&#"#"&&&5466673266654&&&'5667666632IC 1$MX^0 7I(??83-(,&! ;083Dv6" +kmf'+P=%=O+=L,BkN3uoK "   "  X|'. ( )#CiI'6S8+7 IJ/K/ܹJ3и3/*>и>/?3*9EX=/= >YEXD/D >YEX./.>YEX / >YA!'7GWgw]A'7GWgqAvqD$Ay$$qA!$$($8$H$X$h$x$$$$$$$$$]A$$($8$H$X$h$q9и9/? =901%#"&&&5466673266654&&&#"!56654&&&'5667666632%=M(DD="OE.),=90E<(1#MW_0LARBJ83Dt8# ,jlf'+P=%XuoK " " ""%^|V=L+BkNC ++_'. ) )#CiI'6S82"1 @2"1 J@2"1@2d"1@2"12`"1 9[:/;/ܹ/и/:#и#/EX(/( >YEX4/4 >Y+01&&'#"&&&546667326665&&'5324&'5!o29 C);'BA;<.$(*$!;1 "H'APHIwnI "   "Wu+vo ' ++ &2BC/D/Cи/Dܹ9и9 и />EX / >YEX/ >YEX/>Y4%+ 9= 90135665&&'5324&'5!#"&&&546667326667'&'2JG!I'APHI1$AB;<.$(*$!.& uCN+ &+vo ' ++ &DjTB"   AsZ  & + IJ/K/EܹJ.и./9к:.E9EX8/8 >YEX?/? >YEX/>Y)+? Ay qA!  ( 8 H X h x ]A  ( 8 H X h q:8901!56654&&&#"#"&&&546667326665&&&&'56676666323DN->%([kM/B'BA;<.$(*$!4* 7/D6#M~r6+\M1II+!Rk?"ZzwnI "   "Wu ( + '#jS"Ex^ "+1e IyJ/K/ܹJ3и3/*>к?39EX=/= >YEXD/D >YEX./.>YEX / >YA!'7GWgw]A'7GWgqAvqD$Ay$$qA!$$($8$H$X$h$x$$$$$$$$$]A$$($8$H$X$h$q? =901#"&&&546663266654&&&#"!56654&&&'5667666632e '=FJ"%E4 *+ #3"". ->%([kM63eDN83D6#M~r6+\M1KhlO#4)%# '$!&cRRk?"Zz| #++!'1+ '#jS"Ex^1e IJ/K/ܹJ3и3/*>к?39EX=/= >YEXD/D >YEX./.>Y +D$Ay$$qA!$$($8$H$X$h$x$$$$$$$$$]A$$($8$H$X$h$q?.=901#"&&&5466673266654&&&#"!56654&&&'5667666632e+H]3FMN$,XG- ,.>></^J.->%([kMHI=DN83D6#M~r6+\M1hm'" !% "' YEX@/@ >YEX/>YA!'7GWgw]A'7GWgqAvq@"Ay""qA!""("8"H"X"h"x"""""""""]A""("8"H"X"h"q;9901#"&&&5466673266654&&&#"!56654&&&'5667666632)YKAQd=Td7 ,.%QmHI=DN83D6#6rrq6+\M17H/%0DF "*J8!/dlzRk? #++!x'1+ '#;jP/Ex^P"R P"R#6Pb"RP"R#Pb"RP"R#P"RPe"R#P"RP`"R#P}"RP"RP9"R#6hP"R#$hP"R#hP"R$P9"R#$6hP9"R#$hP"R#$hPL"RP"R#$hP"RP`"RP -a.///."и"/и//ܹ  и /EX)/) >YEX/>Y+A!'7GWgw]A'7GWgqAvq)AyqA!(8HXhx]A(8HXhq01326667'&&&&#"#"&&&546666632 ^m;Fz[4/W|MZG}^74]~JCraF'H~fBsaF(HP",PS =>/?/+ܹA]A )9IYiy ]>и/+9 A  & 6 F V f v ]A ]+9+'и:&/9/EX/ >YEX0/0>Y9&9A!'7GWgw]A'7GWgqAvq9&9AyqA!(8HXhx]A(8HXhq014&'32666%&&#"466666327667#"&''7&&t?89K`~I^?8c9K`~I+MmR^HiDQ^^{^Hg !&% Q^ujJ03WqjJ204Wp[yV/=7![h=6 ![PLy; (++A]A )9IYiy ]A  & 6 F V f v ]A ]595/A55]A 55)595I5Y5i5y55 ]89=9/EX/// >YEX5/5 >YEX#/#>Y/AyqA!(8HXhx]A(8HXhq#A!'7GWgw]A'7GWgqAvq8#99014&&&#"32666#"&&&5466666326654&'74Rh3Lh?8Uf.GfB 14YB&)"[64/Om@:ePOl?5b=EK$YEX/>Y  +%AyqA!(8HXhx]A(8HXhqA!'7GWgw]A'7GWgqAvq01&&&&#"3266677#"&&&5466632 >aMS`:?gKRd9ZrxDXv|A]MBzlZlSCā nj~nmFr" {@F9 (++A]A )9IYiy ]A  & 6 F V f v ]A ]393/A33]A 33)393I3Y3i3y33 ]69;7/EX-/- >YEX#/#>Y-AyqA!(8HXhx]A(8HXhq#A!'7GWgw]A'7GWgqAvq6#79014&&&&&#"32666#"&&&54666326654&'7.CTd9Ya4=gMSe86[ELNZrxDXv\<<5uEygK+KzpȗXE>GK%]nj~n>6%J4P4F"0 @F"0 @Fq"0j@F"0m@F`"0jP LM/N/Mи/и/и/Nܸ*и*/ *98A88]A 88)898I8Y8i8y88 ]$и$/DADD&D6DFDVDfDvDD ]ADD]EX/ >YEX/>YEX///>Y #и$и=Ay==qA!==(=8=H=X=h=x=========]A==(=8=H=X=h=q013'66667333&&&&546666632326667!566666654&&&#"o -kR!!A_}ZnJ0`_- F:[E0 /^\DlR9$*DaC>CD *@+UQA{jO-JzYX)B0GGDnLxfZY_9[L.K`d`%?/@/ܹA]A )9IYiy ]?)и)/  и 4к5)93/EX:/: >YEX$/$>YEX/>Y:AyqA!(8HXhx]A(8HXhqA!'7GWgw]A'7GWgqAvq95$39014&&&#"326667#"&&&'!56654&&&'5667666632f)DZ0:JT,ME9X> 8N`o<1YEX/>Y 0A00]A00(080H0X0h0x000000 ]и/&A&&'&7&G&W&g&w&&&&&& ]A&&]# 902и2/01356654&'5!632#"&''3266654&&&#"1DNJH3D(?\l{C'AT[['.Q"*M#.dT6;fM20'D4+! $++ .ZVErYA+G $GkGSvK# +P  OʸP/Q/"ܹCиP7и7/A&6FVfv ]A]C,к-7"9"HиH/EX?/? >YEX'/'>YEX2/2>YK+?AyqA!(8HXhx]A(8HXhq2 A!  ' 7 G W g w ]A  ' 7 G W g qAv q-'?9B?901&&#"326667&&&&#"!5665#"&&&54666766325466676632!xH8hQ11L[)"B@?#(+)'" ->M5^L&JOW38vc?8Q\#9p&/]6$8'3o'+I59?/^^Ub5-:!", K{[7 ++.H3AzoXsP"(U{]H"-/!+*P P(+6P+F>+PA&6FVfv ]A]FREX%/% >YEX/// >YEXK/K>YEX/>YA!'7GWgw]A'7GWgqAvq% Ay qA!  ( 8 H X h x ]A  ( 8 H X h qK%9K9A!99'979G9W9g9w999999999]A99'979G9W9g9qAv99q01%26667&&#"%#"&&&5466676632666673266654'6666#"&&&5"B@?!xH8hQ11L[+&JOW38vc?8Q\#9p&68<"    7H' +329bH=N.d-:9?/^^Ub5B.H3AzoXsP !$  !N-s|$  ))\N4-Kc6 IW + :+A  & 6 F V f v ]A ] ?и/A  & 6 F V f v ]A ]??BиB/EX5/5>Y+E+5%A!%%'%7%G%W%g%w%%%%%%%%%]A%%'%7%G%W%g%qAv%%qBE90126667&#"'46663232666'&666#"&&&5454&5#"&&& '  i' )/RpB=X9-?(/ (8:7`EDeB!-d3+D/ ",-E,_N3:`|A}xd+*2'&\Q6@wk50%  1,3FB  Z4!+>Z+PH+ZA&6FVfv ]A]HJиJ/P\EX-/- >YEX7/7 >YEXU/U>YEX/>YA!'7GWgw]A'7GWgqAvq- Ay qA!  ( 8 H X h x ]A  ( 8 H X h qU-9UCA!CC'C7CGCWCgCwCCCCCCCCC]ACC'C7CGCWCgCqAvCCq01%26667&&#"%#"&&&&&54666667666632666673266654'46663#"&&&5 (Y\Y('V-`]T?%9]v/\bk=,a^UA&$:JMI"HD==>C&! *  .)' $49@e?AO,i(@R)NV;WsTrĎQx<_B"%IkdN{gS;  .%  &%-&i=MqJ##'0_K/.V{M7 "U7 "U7 L"U "U7` "U7`"U#)s"5 '@)s"5 @)sd"5 @)s "5 )`s "5 )`s1"5# @^"V^"V#hP"VP"VP"V#@^L"V^`"V^`L"V# IJ/%и%/ܸ%A0]A]A]AP]Ap]Cܸ%C*и*/KEX / >Y/>+ A!'7GWgw]A'7GWgqAvq01%#"&&&5466673266654&&&&&546667666632&&&&#" 6H(AGFB7$(+(+*,J6-@(@?9B7$(+1-' 1%BaoQ % " 'OvOO̼:\bI "   " %FiCMͽuu"6 @uu"6# @uu"6 @uu"6@uu"6#@uud"6@u`u "6u`ud"6#@Z"W_Z"W_ "W_`"W_ ;"7$@ ;d"7%@ ;"7% `;"7%)-"X)-}"X$)-"X#)-Y"X$)-9"X#$@h)-"X.)-"X#.$h)-9"X#$@h)-9"X#$h)-+"X#$#h)-"X#$.h)-"X$)-"X')`-"X$)- LdM/N/ܹM(и(/и(/и<и>иIEX;/; >YEXH/H >YEX/>YEX#/#>Y>+#A!'7GWgw]A'7GWgqAvqܸ ик9)и*и>.и5ܸDи>Jи.K01%266675!#67#"&'#"&&&55#'667354&&&'566667!54&&&'56673 CGN,C'9Hj  :3 %D:, #,>gWI /VB'j j4.$?;N  -)]g>M*Ex^$05 (  '`-6 ( &'`)yND-+7+H+AHH]A HH)H9HIHYHiHyHH ]H и /7иBкC9K9L/EX/>YEX/>YL92A!22'272G2W2g2w222222222]A22'272G2W2g2qAv22qCL9KL90167#"&'#"&&&54&&&'5666673266674&&&'5667666654&'7Md  :3 %D:, #,>gWI /VB'4.$?;N  -)]g>M*Ex^05 (  'F^8)A/-6 ( &'.,(4P4)"u@)"u)Y"u$)"u')`y"u$<;CYEX:/: >YEX/>Y/A!//'/7/G/W/g/w/////////]A//'/7/G/W/g/qAv//q01#"&&&54676&&&'&'5!3266654&&&'7!?-# ,<$/F]sEg^-I\ "-?Z0J3/Oi::X;4K0Yw'TZ_3/ig_G+GvTpS+\$RbwKC}a;9Xk3Kw\\2"8@2"8@2"8@2q"8@2"8#@ 21"8@2"8#@2"8#@ 2"8#@ V2"8#@2q"8#@2"8@2"8@2`"8, 9!:/;/ܹ: и /и 'и1и3EX,/, >YEX8/8 >YEX/>Y3+A!'7GWgw]A'7GWgqAvq3ии!и3&01%266655!3##"&&&55#'66734&'5!!4&'5!GnK'{-YDM@xlg}F IHDMIHUEpJy`i8l">C[ΎK9uz" $++"> $+2x34/5/ܸ4и/%1/EX/ >YEX*/* >YEX / >Y A!  ' 7 G W g w ]A  ' 7 G W g qAv q0 1901#"&&&54&'5!3266654&'5!654&'7x>hO@xlg}FIHDM-YVGnK'IHcpCLQ&ΎK9uz $++"k`i8EpJ $+4P42x" @2x" V@2xq"@2x"@2`x"Y"Y`"Y @EX / >YEX/>YEX/>Y 90176666766667!56666'!&  "(' J 42~'1-%+   ++  + FsG/H/G+и+/H!ܺ+!9+A&6FVfv ]A]!A]A )9IYiy ]>+!9EX6/6 >YEXE/E >YEX&/&>Y&69 A!  ' 7 G W g w ]A  ' 7 G W g qAv q>&6901%3266654&&&'#"&&&5466677&&'5!6&&&'5!", ")&!% 6( ?aC,O:"%J 22'//%N*DaC-*8! )0;O; q<\MH)%\Q79O16CX; ++ ! + q"9@` "9  @EX / >YEX/>YEX/>Y 901766766667!566'!DM '.,  E?MP; GR`+ D c ++L +"Z "Z"ZL"ZL"Z"Z`"Z": |@": @": _@d":`@d":`@`":`L"[ L"[ d";y@d";y@ "\1 "\ "\ Y"\ "\ L"\ "\ "\ "\ e ?EX>/> >YEX / >YEX&/&>Y& 9 &3A!33'373G3W3g3w333333333]A33'373G3W3g3qAv33q01666632&&&##"#"&&&5466673266677&&'5!'1,krp1;-"*#A>9,krp1;-")'?BA: 42w w]%  #)( "@]<1w]&  #)( #A^<>P +"< {@"<  @"< ^@q"<_@1"<i@d"<_@"<b@`"<_t>!?/@/9ܹ? и /A&6FVfv ]A]-99EX%/% >YEX2/2 >YEX/>Y%AyqA!(8HXhx]A(8HXhq-%901!!5665&&&&'&&&&#"&&54666326&'5!.[I$MNL"%""&&,1%0:Yi/7I3#HF 1MEI'?/+'N}3$"4$$8L7DdB!%_ ++ V k(AB/C/Bи/;;9C%ܸ(и(/%1A11]A 11)191I1Y1i1y11 ]EX/ >YEX/>Y96й 01!5665&&&&'&&&&#'6632666632'6654&&&#"h[I%^b\# $6*;v*. %VVR#9GZYEX / >YEX/>Yи01!'5!267&&&&#!!26667`8a<8$" +Vs()Y(b0Z*+PF+ JP%0TB& R9+A]A )9IYiy ]EX+/+ >YEX/// >YEX/>Y5+A!'7GWgw]A'7GWgqAvq/#25901#"&&&5466673266654&&&"&&'!"'73!663RSTG~]6*0:wCYEX6/6 >YEX/>Y<+A!'7GWgw]A'7GWgqAvq6*9<901#"&&&5466673266654&&&#"'&&''57!"'3!6632Q_Vi:*0BOV*?nP.(LlD-P01%$ 9%'q Az^9jyB-<="% %@/"HoLY$+01356546666654&&&#"'&55466632Ȫ1IVI1)Ha8(K;$ F-L~WM}X01IVI1XR+Mpdbf9=iL+'?N'F^84[zFJvd\anE= + ,].m+.+&+A]A )9IYiy ]&0/! +01546666654&&&#"'&55466632"4;4"0@#1'D 5ZvA:\?""4;4",j.MC<;<#%;*!,   *L9!7I*-F<7;B)F5+1+) +A ]A  ) 9 I Y i y ])7EX$/$ >YEX/>Y$AyqA!(8HXhx]A(8HXhq013565546666654&&&#"'&55466632(gI((YEX/>Y#AyqA!(8HXhx]A(8HXhq013566546666654&#"'&&5466632WN2JWJ23U<" P-McX]12JWJ2SR+$HmXIIO2|~&J' - 56@-Lc76N3&7"(E2Q3YD'/4)OM-=&.&(*' 2) 90 /<.*# !3BP`'Q+E;+E(иE/J;E9ES/&/EX!/!>YEX/>YEX"/">YEX(/(>Y@2+(J015666654&'&&'66667'667!'666654&#"'466632326767a,8 2% CE? 4*+/&kd|F/9$+(BV.%=-GtV$}" n!  " I#cZ<-4! 7-"3# BWwWP`=GɸH/I/ܹииH&и&/*и*/&7CиC/FиF/5/F/EXA/A>YEX/>YEX>/>>YEXB/B>Yܸиии013#!5666655!'66735666654&'&&'66667'667ɪ  $,3=,8 2% CE? 4*+/j e " n!  " P` &cf%\F++$A\\&\6\F\V\f\v\\ ]A\\]\6'dкeF9hEX;/; >YEX@/@ >YEX/>YEX/>YEX/>YEX/>YWK+& +3,+ и%ܺd;9eиf01%'667#!5666655!'6673#"&'732654&&&##"'666654&&&#"'4666323<+/.  $,3= =W72h33N*BO'0  2; %*,2$=O,+@(?7 7) j e W)G6%*!!@=$1 % !  )  .'&0$H(5e, //01#66667+ D 04/ 5Fy, //01#66667: I 382 5ye //01#666670 ? -1+ 5y //01#66667? D 04/ 5dPRIc+A&6FVfv ]A]и+ +01#"&5466632#"&5466632R%36.&321%36.&321#;,:6";-;#;,:6";-;dR+A&6FVfv ]A]иEX / >Y+ A]A(8HXhx ]01#"&5466632#"&5466632R%36.&321%36.&321P#;,:6";-;#;,:6";-;P$vG+A]A )9IYiy ]/01'6676&&&#'6666tYa'*,"7' 5A=$0E=!&P.0$*   $-3FJ/01'6676&&&''6666;]E8G'&D<6&I"  k)P@(#+#A8)Q@(#+#@;e   {(5c+A]A )9IYiy ]9&/,/./,&901&&666654&#"'5466632&&&&'3|&%   / &*! 0<68J  k$# !%'" !! '4   {.B} //+01#"&&&'667326667KSZ-1\SI AHK!#MIAPQnEEnQ9N//N9.B $/+01&&'#"&&&'667326667 $'% VKSZ-1\SI AHK!#MIA b'QnEEnQ9N//N9.B%"/%/+01#"&&&'6673266677677667KSZ-1\SI AHK!#MIA  %' PQnEEnQ9N//N9< =).R5-//.!+ и/01#"&&&#"'6666323267#"&&&'6673266672=H'#?<;(B%51>G'&D<6&I"&KSZ-1\SI AHK!#MIA)P@(#+#A8)Q@(#+#@;QnEEnQ9N//N9.Br,F3%+%9?2+*+01&&&6666654&#"'5466632#"&&&'667326667%2/* -+ "*$&  &;H#?DKSZ-1\SI AHK!#MIA,'#%( % &&    .$G'&D<6&I"B)P@(#+#A8)Q@(#+#@;]i////017''6677'&&']3366:"  ""4KO3 +01!'66667!  e    K EX / >Y01!'66667!K  e    O EX / >Y01!'66667!  e     EX / >Y01!'66667! 9     s+ +01!'66667! #     sm +01!'66667! #     @' (EX/ >Y +01!'66667!!'66667!@           <i  + +01!'667!7!'667!i     l20*1d)L//ܹA]A )9IYiy ]и/A&6FVfv ]A] +и 01#"&5466632#"&5466632*-' )Up*-' )U2%2.2%_2%2.2%`aHK+A&6FVfv ]A] +01#"&5466632a*-' )U2%2.2%daLK+A&6FVfv ]A] +01#"&5466632a*-' )U2%2.2%FyU +A ]A  ) 9 I Y i y ]//901'666654&'7F"Up;T6PY\*>33/4P4C'(/)/ܹA]A )9IYiy ](и/ A  & 6 F V f v ]A ]+#+014&&&#"326667#"&&&5466632]  &  &Z'?M&"9)'>M' 9*,!,+!+F3U?#*8 3W?#+9S.,-%+%9 /*+01&&&6666654&#"'5466632%2/* -+ "*$&  &;H#?D5,'#%( % &&    .$<ZD_ +A ]A  ) 9 I Y i y ] 9//901'666654&'67667#JuR4I.4>N'3'%D8* 1  #"ZTt*@D+Q +A&6FVfv ]A] /+01#"&&&546732679<<8,-HX00&I*4) 8-ZQ,TJ>%#$&-T//EX/>Y01!#!5T'''//EX/>Y01!!#''''02 //01&&']  "$ =- / /01%766667+$" D-:y // /01%&&&&'3  kD  5/ //01&&'%&&&&'3n-  r  k /   5:/ //01%7667%&&&&'3= *  kK / 0  5S(+/!/// и/01#"&&&#"'6666323267%&&&&'32=H'#?<;(B%51>G'&D<6&I"  k)P@(#+#A8)Q@(#+#@;  5F(5c+A]A )9IYiy ]9&/,/./,&901&&666654&#"'5466632%&&&&'3|&%   / &*! 0<68J  k# !%'" !! '42  5.B("/+01&&'%#"&&&'667326667-  KSZ-1\SI AHK!#MIA( / QnEEnQ9N//N9.B("/+01%7667#"&&&'667326667_ *KSZ-1\SI AHK!#MIAK / QnEEnQ9N//N9.R5-//.!+ и/01#"&&&#"'6666323267#"&&&'6673266672=H'#?<;(B%51>G'&D<6&I"&KSZ-1\SI AHK!#MIA)P@(#+#A8)Q@(#+#@;QnEEnQ9N//N9.Br,F3%+%9?2+*+01&&&6666654&#"'5466632#"&&&'667326667%2/* -+ "*$&  &;H#?DKSZ-1\SI AHK!#MIA,'#%( % &&    .$Y Ay qA!  ( 8 H X h x ]A  ( 8 H X h qи/01#"&&&#"'66663232672=H'#?<;(B%51>G'&D<6&I")P@(#+#A8)Q@(#+#@;oO EX / >Y01!'66667!  e    <)$//ܹA]A )9IYiy ]и/A&6FVfv ]A]EX / >YEX/ >Y A]A(8HXhx ]01#"&5466632#"&5466632*-' )Up*-' )U2%2.2%_2%2.2%YA]A(8HXhx ]01#"&5466632a*-' )U2%2.2%  3/EX/>YEX/>Y+013'!#3dY::LR͎@SP_<dd+y sxd-fWXLVPy)FH=X=1LbD7Nnh^dX===wPuFf)JF)22F;2bFqB22s<12F/)F})uh ;23/;w7w)d=P P)PP}-j7+F$7+<m7j7P=7P+7^=)Lf'1JF212F;2PPPPPPPPPPP+F+++j7PPPPP=)=)=)=)Fh@w9Fo17#dy;!d=;FJPH===Q=P(; 16+2 1B1PPPwP-=P8;PZFbFXP=!=}\}X\XH=pPp--F,2XXL222bFbbbFF*KF;2;2;2+F16F(dFNPd2PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP;;   f)f)f)LPPPPPB7JFJFJFJFJFJ=J<)P)P)P)P)PPP))))PPP:PPPPPPPPPPPPPPPPFFjP2222222222222222222AP}-2PPPPPPPFFFFFFj7j7j7j7j7j7;2;2;2;2;2 1++++%++F+F+/+F+/=FB1bbbb/bbFbFbFb0F$$7777722222+<++<+FF?62222,,)m7m7m7s<s<s<j7j7j7j7j7j7``71212121212121 12'111PPPPPPPPPPPPPPPPPPPPPPPPPFPLPLPLPLPLPLPFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFP=7=7=7/)/)/1PPB+7+7+7++7+7})})})})})})^^PPP^^^uuuuuuuu h h h h =)=)=)=)=)=)=)=)=)=)=)=)=)=)=)))))))<;2;2;2;2;2;2;2;2;2;2;2;2;2;2;,x2x2x2x2x2x2333kLLLLLL/;/;/;/;/;/;(;&&*^*^z<o 4F(a;&PPPddPF,2==H==}0):H]ss<FCSZ@T-T0) 8888||BNV b ^ $XBnjp\.0 !!"j# ##$$<$n$$$&"'J(B)*+>--.,./h/01245Z67f889N9:t:;B<<$<=(=4=@>T>`>l>x>>>>>>>???@@@@&@2@>@J@V@b@n@z@@@@@AjABCDDDEFGI\J6JVJK>KMrNJNNO0OPRR RSpTjU$UUWY(ZZZZ[\H\\]&]6]6]B]N]Z^`V`t`a0abbfbc c,c8c\d:ddddeTf,f4fgiii*i6iBiNiZifiri~iil2l>lJlVlbljlllm4mvnnfnnoo&o6oBoRo^onozoooooooooppp"p.p:pJpVpfpvpppqqqrrr"r2rBrRr^rnr~rrrrrrrrrs ss&s2s>sJsVsfsrssssssssssttt&t2tBtNt^tjtvttttttttttuvBvNvZvfwwwxxy:zl{f{r{~{{|}~j΄TЅ܅4hΊڊ".:FRbr~Žʏ$4@P\lxȑԑ ؕ̕ ʗ֗*6BNZfr~ƙ:FR^jzRNZfr~&ȜԜ읖ƞҞޞdnҠޠz&2>JVbnzȥԥD.XBhtȫث ,JVfr¾ҿȿԿ $4@LXdp| 0JVbnzʆʒʞʪr~ˊ˖ˢˮ˺̈́͐ͨ͜ʹvZfT`n:(fvBLnאײ8Hو>t~ڞ.ۊLܔL8lvޚ<`ߨb2"0Pt8L>d,` Y2+ +8%? d q ~, "   f  7 7 8 8 8 8 8! 89 8Q  8f  8  8  8  8 8  8  8 8  9   9 9 9; 9@ 9E 9] 9b 9f 9y 9~ 9 V   J' q   X " D7 .{ 8 " 47 69  9 9 &9  9 : 0: 0:8 *:h  2:  &:  ::  ;$  ;B  ;` ; ; 0; ; ; <<  <<  ?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      uni00A0uni25CCuni1EADuni1EA5 uni1EA5.VNuni1EA7 uni1EA7.VNuni1EAB uni1EAB.VNuni1EA9 uni1EA9.VNabreveuni1EB7uni1EAF uni1EAF.VNuni1EB1 uni1EB1.VNuni1EB5 uni1EB5.VNuni1EB3 uni1EB3.VNuni01CEamacronuni01DFuni0227uni01E1 aringacuteuni1EA3uni1EA1uni0251 a.SngStoryaacute.SngStoryagrave.SngStoryacircumflex.SngStoryuni1EAD.SngStoryuni1EA5.SngStoryuni1EA7.SngStoryuni1EAB.SngStoryuni1EA9.SngStoryabreve.SngStoryuni1EB7.SngStoryuni1EAF.SngStoryuni1EB1.SngStoryuni1EB5.SngStoryuni1EB3.SngStoryuni01CE.SngStoryatilde.SngStoryamacron.SngStoryadieresis.SngStoryuni01DF.SngStoryuni0227.SngStoryuni01E1.SngStoryaring.SngStoryaringacute.SngStoryuni1EA3.SngStoryuni1EA1.SngStoryaeacuteuni01E3uni1EA4 uni1EA4.VNuni1EA6 uni1EA6.VNuni1EAA uni1EAA.VNuni1EA8 uni1EA8.VNuni1EACAbreveuni1EAE uni1EAE.VNuni1EB0 uni1EB0.VNuni1EB4 uni1EB4.VNuni1EB2 uni1EB2.VNuni1EB6uni01CDAmacronuni01DEuni0226uni01E0 Aringacuteuni1EA2uni1EA0AEacuteuni01E2uni1E03uni1E07uni1E05uni0253uni1E02uni1E06uni1E04uni0181 ccircumflex cdotaccentuni1E09uni0254uni0254.TopSerif Ccircumflex Cdotaccentuni1E08Eurouni0186uni0186.TopSerifuni1E0Buni1E0Funi1E0Duni0257uni0256Dcaronuni1E0Auni1E0Euni1E0CDcroatuni0189uni018Auni1EBF uni1EBF.VNuni1EC1 uni1EC1.VNuni1EC5 uni1EC5.VNuni1EC3 uni1EC3.VNuni1EC7ebreveecaronuni1EBDemacronuni1E17uni1E15 edotaccentuni1EBBuni1EB9uni0229uni1E1Duni01DDuni0259uni025Buni1EBE uni1EBE.VNuni1EC0 uni1EC0.VNuni1EC4 uni1EC4.VNuni1EC2 uni1EC2.VNuni1EC6EbreveEcaronuni1EBCEmacronuni1E16uni1E14 Edotaccentuni1EBAuni1EB8uni0228uni1E1Cuni018Euni0190uni1E1Funi1E1Euni01F5 gcircumflexgcaronuni1E21 gdotaccent g.SngBowluni01F5.SngBowlgcircumflex.SngBowlgbreve.SngBowlgcaron.SngBowluni1E21.SngBowlgdotaccent.SngBowluni01F4 GcircumflexGcaronuni1E20 Gdotaccent hcircumflexuni021Funi1E27uni1E23uni1E96uni1E25 Hcircumflexuni021Euni1E26uni1E22uni1E24Piibreveuni01D0itildeimacronuni1E2Funi1EC9uni1ECBuni0268 i.Dotlessuni0268.Dotlessijuni0269Ibreveuni01CFItildeImacronuni1E2Euni1EC8uni1ECAuni0197IJ j.Dotlessuni0237uni1E31uni01E9uni1E35uni1E33uni0199uni1E30uni01E8uni1E34uni1E32uni0198lacuteuni1E3Buni1E37uni1E39uni019Auni2C61uni026BLacuteuni1E3Auni1E36uni1E38uni023Duni2C60uni2C62uni1E3Funi1E41uni1E43uni1E3Euni1E40uni1E42nacuteuni01F9ncaronuni1E45uni1E49uni1E47uni0272engNacuteuni01F8Ncaronuni1E44uni1E48uni1E46uni019D Eng.UCStyleuni019D.LCStyleEng.BaselineHookEngEng.Kom ohungarumlautuni1ED1 uni1ED1.VNuni1ED3 uni1ED3.VNuni1ED7 uni1ED7.VNuni1ED5 uni1ED5.VNuni1ED9obreveuni01D2uni1E4Duni022Duni1E4Fomacronuni1E53uni1E51uni022Buni022Funi0231uni1ECFuni1ECDuni0275 oslashacuteemptysetohornuni1EDBuni1EDDuni1EE1uni1EDFuni1EE3 Ohungarumlautuni1ED0 uni1ED0.VNuni1ED2 uni1ED2.VNuni1ED6 uni1ED6.VNuni1ED4 uni1ED4.VNuni1ED8Obreveuni01D1uni1E4Cuni022Cuni1E4EOmacronuni1E52uni1E50uni022Auni022Euni0230uni1ECEuni1ECCuni019F OslashacuteOhornuni1EDAuni1EDCuni1EE0uni1EDEuni1EE2uni03A9uni1E55uni1E57uni1E54uni1E56uni02A0uni024Buni01AAuni024Aracutercaronuni1E59uni1E5Funi1E5Buni1E5DRacuteRcaronuni1E58uni1E5Euni1E5Auni1E5Csacuteuni1E65 scircumflexuni1E67uni1E61uni1E63uni1E69uni0283Sacuteuni1E64 Scircumflexuni1E66uni1E60uni1E62uni1E68uni1E97uni1E6Buni1E6Funi1E6DTcaronuni1E6Auni1E6Euni1E6C uhungarumlautubreveuni01D4utildeuni1E79umacronuni1E7Buni01D8uni01DCuni01DAuni01D6uringuni1EE7uni1EE5uni0289uhornuni1EE9uni1EEBuni1EEFuni1EEDuni1EF1uni028A UhungarumlautUbreveuni01D3Utildeuni1E78Umacronuni1E7Auni01D7uni01DBuni01D9uni01D5Uringuni1EE6uni1EE4uni0244Uhornuni1EE8uni1EEAuni1EEEuni1EECuni1EF0uni1E7Duni1E7Funi028Cuni0263uni1E7Cuni1E7Euni0245wacutewgrave wcircumflex wdieresisuni1E87uni1E98uni1E89WacuteWgrave Wcircumflex Wdieresisuni1E86uni1E88uni1E8Duni1E8Buni1E8Cuni1E8Aygrave ycircumflexuni1EF9uni0233uni1E8Funi1E99uni1EF7uni1EF5uni01B4Ygrave Ycircumflexuni1EF8uni0232uni1E8Euni1EF6uni1EF4uni01B3uni01B3.RtHookzacuteuni1E91 zdotaccentuni1E95uni1E93Zacuteuni1E90 Zdotaccentuni1E94uni1E92uni01A9uni0292uni01EFuni01B7uni01EEuni0294uni02C0uni0242uni0241uniA78B uniA78B.LrguniA78C uniA78C.LrguniA789 uniA789.Wideuni02BC uni02BC.Lrguni2219uni2011uni00ADuni02D7uni02CA acutecombuni030Buni02CB gravecombuni0302uni0302_acutecomb.VNuni0302_gravecomb.VNuni0302_tildecomb.VNuni0302_hookabovecomb.VNuni0306uni0306_acutecomb.VNuni0306_gravecomb.VNuni0306_tildecomb.VNuni0306_hookabovecomb.VNuni030C tildecombuni02CDuni0331uni02C9uni0304 uni0304.Shortuni035Funi035Euni033FuniA78Auni0308 dotbelowcombuni0307uni031Buni030A hookabovecombuni0327uni0328uniF130uniF131 acutecomb.LP gravecomb.LP uni0302.LPuni0302_acutecomb.VNLPuni0302_gravecomb.VNLPuni0302_tildecomb.VNLPuni0302_hookabovecomb.VNLPuni0306_acutecomb.VNLPuni0306_gravecomb.VNLPuni0306_tildecomb.VNLPuni0306_hookabovecomb.VNLP uni030C.LP tildecomb.LP uni0304.LP uni0308.LP uni0307.LP .notAccess+++L</"+G</"+fP9"+xfP9"++ E}iD  ZY  Z  Z  Z  Z  Z  Z  Z  Z  Z  Z W WZ  ZH f fZ & &Z  Z  Zl  Z  Z' ' Y'Z J JZ d NZ  Z / /Z3  ZI g ~gZ  Z \ \Z  Z  Z' " "Z  2Z  bZ ] ]Z v vZ \ \Z  +ZG    T  4  )O   E T*   A AT  O+    T  x C Cu A A  ?q*  = - -    \ \ \(H ! \!           W W W W H fZ  3  I g ~g  2                        O+  O+  O+  O+u A A  ?  ?  ?  ?  ?H ! \!H ! \!H ! \!H ! \! 1 1G V Z \ \Z  Z A A    ?     Z W W W WI g ~g 6 6Z , X,   \ \ W W   W W    ' ' Y'' ' Y'' ' Y'' ' Y'I g ~gI g ~g  GI g ~g  2  2  2  O+mZ   ! o    D    D    D        Z  Z                  Z          :  Z                       D   D   D      Z   Z               Z                        :   Z        W W* W W W W* W W W W* W W W W* W W W W W W W W W W W W W W W W* W W W W W W W W W W W W W W W W W W W WR W W W WZ V  V   N  T  T       Z  Z ~ ~Z      Z        H f fH f fH f fH f f H f  Z  Z4  )O4  )O4  )O ; ;  O  )O   & & & & & &Z & &Z ? ?Z ? ?Z ? ?Z  Z  D    D    D          Z  Z            Z      Z      j  *    *    *    *                      Z  Z    Z   Z E   *  *  *  Z*  Z*  *          Z  Z    l  l  l  l  l  l   A A A A A A A A A AT A AT          Z  O+Z  O+Z  O+  O+  O+  O+Z  O+  O+  O+  O+ C O+  TB ' ' Y'' ' Y'' ' Y'' ' Y'' ' Y'' ' Y'' ' Y'' ' Y'Z ( (Z'  YZ          T  T   d N d N d NZ d NZ  Z         G G G G U U & &    Z  Z    Z  Z  Z  Zx C Cx C Cx C C / / / / / /Zu A Au A Au A AZu A Au A Au A A 7 7 F G3  3  3  3  3  Z3  Z  Z  Z  Z  Z  Z { {Z  ?Z  ?D  ?  ?D  ?  ?D  ?  ?  ?  ?  ?Z  ?Z  ?  ?  ?  ?  ?  ?  ?  ?  ?  ?Z  ?     ?           Z  I g ~gI g ~g*I g ~gI g ~g*I g ~gI g ~g*I g ~gI g ~g*I g ~gI g ~gI g ~gI g ~gI g ~gI g ~gI g ~gI g ~gI g ~gI g ~gI g ~gI g ~gI g ~gI g ~gI g ~gZ \ \Z \ \ g gZ g g g g g g g g g gZq*  =q*  =        Z - - - -   \ \Z    Z                Z  Z          Z                        Z  \ \ \\ \ \\ \ \(\ \ \(' " "' " "' " "Z' " "ZH ! \!ZH ! \!ZH ! \!ZH ! \!H ! \!H ! \!H ! \!H ! \!H ! \!H ! \!H ! \!H ! \!H ! \!ZH ! \!H + \=+ ! ! ! ! ! ! ! ! ! !Z ! !    2  2  2  2  2  2  2  2  2  2  2  2  2  2Z  Z  Z          Z          b  bZ  Z               ] ] ] ] ] ] ] ] ] ] ] ]Z     v v v v                Z     \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \Z 8 8Z \ \Z      Z        +  +  +  +  +Z  +Z  Z    Z  Z    z 8 8Y    Z h &h Q &Q Q &QY YYd  Z &OO      Z     Z    14   D    14 % Z   '  ,0Z OZO      Z Z   Z Z Z Z Z 69 ; 48<BHNRVZ^bhn| "8Ndjnrvz~,D`v,D\t $(4Xx0Tj"@Xp48<@D`|2Vz  ( L p $ < `  8 \     $ * . B F \ ` f l r  4 P t "&*.28Tp4X|*NRVZfjnz~:^ .Rv*Nr&Jn"FjBf>b:^z">Zv:Vr6Rh~  6Ll2Jbz8Nd2Hl  4 P t !!(!L!p!!!!""4"X"t""""###8#T#p#####$$4$P$j$$$$$%%&%J%n%%%%& &&&B&^&z&&&&&' '"':'R'j''''''(((<(`(((())8)\)))))**0*L*h******++<+\+|+++++,,(,H,h,,,,,-- -@-`-v-----...,.B.Z.r......//./F/^/v//////0 0.0R0n00001161Z1~1112222V2z2223 3.3L3p3t33333344@4d444455<5`55556686\66667747J7`7v7777778828H8^8t88888899*9B9Z9r99999:::2:J:b:z:::::;; ;8;P;h;;;;;<<0>(>L>p>>>>>???CTCjCCCCCCDD&DDDbDDDDDEE,EBEXEnEEEEEEFFF6FNFfF~FFFFFGGG0GFG\GrGGGGGGHH*H.H2H6H)y@9&&((2288DDFFHHJJRRXXin  !!$$%%&&''(())**++DDEE LLMM ]]^^__``aabbccddttuuvvwwxxyyzz{{       !"#$%&'()*+,-./012  !"#$%&'(     '.5<CJQX_gnu|#3BP`o}#/5;?CGKQW]ciosw{ !$%"#&'()*+,- . / 0 1 23456789:;<=+"+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0"! 0"! 0"! 0"! 0"! 0$! 1$! 1$! 1$! 1$! 1!* 1# ! 1# ! 1# ! 1# ! 1# ! 1# ! 1# ! 1# ! 1# ! 1# ! 10 1)11111-1.1&1'1/ 1(11111,1%111     LM    )4?JU     +1+1+1+1+1!1!1!1!1!12LLOiVC$$%%&&''(())*+,,--..//00112234578899:=AACCDDEGHHIIJJKKLLMMNOPQRRSSTTUVWWXXYYZZ[[\\]]bcddeeffgnoopwxxy45679:>?BCCDEFIJJLMNPQRSST\]]^^__``acddenop qrsst  -.5789@ATUUV]^abeftuz{                 "$&(*,.02468:<>@BDFHJLNPRTVXZ\^`bdfhjlnprtvxz|~   "$&(*,.02468:<>@BDFHJLNPRTVXZ\^`bdfhjlnprtvxz|~,' ", "1' "1 "1,' "1,'1 "'1 ",1 "1 " &!+!+ &+ &!+ &!!0+!0+0+0! &!0 &0 &0!0!0!+ &0+ &0!+0 & &!0 &+!0+ %   % % * ** * %* %* % * %  /// * /*/*/ /* % / %/ %/ / %* %/*/ %* %/ $ $ $)))) $) $) $) $...).).)..) $. $. $.. $) $.). $) $. "1!0/ %* $). #(-.=L[jy-<KZix(8HXhxIc,m4v;~A KWY  P F  @ | 9 v     ljmkpponq ttsurwwvx||{y}zV~WW''X'''&!!$$$YY----...2225557788899<==>>@AABB:::////;;;G I FF\FFF  H H 1111CCC4444???[[EEEELLLMMMMNNNNOOOOSSSTTTTUUUUWWW "!XXX#X$YY'%(&ZZ+)Z*--,[.[//\\0\112]]]55436_88`79`;;<:aa==b>bbAA@cB?DDddECGGHeeFIIfffJPPPP_Pcc^^^^QQ`QQQMMgKNLPPhOhQTTURiSRRRRRaVVVbVVedfeddfefdefegdgdegggdfgfegdfegfhhedhhdhhehhhhegdhhgdhheghhghhedfhhdfhhefhhfhhgdfhhgfhhegf !!"""####$$$%&!!(($$$))))***$$$++++,,,''X'''YY----////00001111////33334444000033336666...222555888111144446666:::////;;;99==222>>:::3333???;;;4444???99AA...BB:::0000CCC;;;1111CCCDDDDZD[[EEEEDDDDZDFF\FFFFF\FFF[[EEEEAA==555  H H BB>>888  H H CCC6666???FF\FFFJJJJJ]JJJJJ][[EEEEFF\FFFKKK^KKKKK^KK[[EEEEMMMMNNNNOOOOPPPP_PQQ`QQQPPPP_PRRRRRaQQ`QQQRRRRRaMMMMTTTTUUUUPPPP_PVVVbVVQQ`QQQVVVbVVNNNNTTTTXXX#X$RRRRRaVVVbVVLLLSSSWWW "!ZZ+)Z*OOOOUUUUXXX#X$//\\0\MMMM112]]]PPPP_Pcc^^^^QQ`QQQcc^^^^--,[.[88`79`SSS;;<:aa//\\0\TTTT==b>bb112]]]UUUU==b>bbVVVbVVcc^^^^--,[.[DDddECLLLGGHeeF//\\0\NNNNIIfffJ112]]]OOOOIIfffJRRRRRacc^^^^DDddEC88`79`WWW "!PPhOhQIIfffJXXX#X$==b>bbGGHeeF;;<:aaZZ+)Z*PPhOhQeddfefdedfefedgdegdgegdfgdgffgedfegdgdfegfefeggfhhdedhhedhhehhhhedhhgdegdhheghhdgdhhghheeghhgghhhhedhhdfedfhhefhhddfhhfhheefhhffhhhhdfhhgdgdfhhgfhhfgfhhghhefhhegegfhhgf+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70&&,#,#,#,#,#)'#)*# ))# )+# #370&&,#,#,#,#,#)'#)*# ))# )+# #370&&,#,#,#,#,#)'#)*# ))# )+# #370&&,#,#,#,#,#)'#)*# ))# )+# #370&&,#,#,#,#,#)'#)*# ))# )+# #370&&, #, #, #, #)1#)4# )3# )5# #370&&, #, #, #, #)1#)4# )3# )5# #370&&, #, #, #, #)1#)4# )3# )5# #370&&, #, #, #, #)1#)4# )3# )5# #370&&, #, #, #, #)1#)4# )3# )5# #370&&,#,#,#,#),#)/# ).# )0# #370&&,#,#,#,#),#)/# ).# )0# #370&&,#,#,#,#),#)/# ).# )0# #370&&,#,#,#,#),#)/# ).# )0# #370&&,#,#,#,#),#)/# ).# )0# #370&&,#,#,#,#)"#)%# )$# )&# #370&&,#,#,#,#)"#)%# )$# )&# #370&&,#,#,#,#)"#)%# )$# )&# #370&&,#,#,#,#)"#)%# )$# )&# #370&&,#,#,#,#)"#)%# )$# )&# #370&&,#,#,#,#)#) # )# )!# #370&&,#,#,#,#)#) # )# )!# #370&&,#,#,#,#)#) # )# )!# #370&&,#,#,#,#)#) # )# )!# #370&&,#,#,#,#)#) # )# )!# #370&&,#,#,#,#,#)'#)*# ))# )+# 370&&,#,#,#,#,#)'#)*# ))# )+# 370&&,#,#,#,#,#)'#)*# ))# )+# 370&&,#,#,#,#,#)'#)*# ))# )+# 370&&,#,#,#,#,#)'#)*# ))# )+# 370&&, #, #, #, #)1#)4# )3# )5# 370&&, #, #, #, #)1#)4# )3# )5# 370&&, #, #, #, #)1#)4# )3# )5# 370&&, #, #, #, #)1#)4# )3# )5# 370&&, #, #, #, #)1#)4# )3# )5# 370&&,#,#,#,#),#)/# ).# )0# 370&&,#,#,#,#),#)/# ).# )0# 370&&,#,#,#,#),#)/# ).# )0# 370&&,#,#,#,#),#)/# ).# )0# 370&&,#,#,#,#),#)/# ).# )0# 370&&,#,#,#,#)"#)%# )$# )&# 370&&,#,#,#,#)"#)%# )$# )&# 370&&,#,#,#,#)"#)%# )$# )&# 370&&,#,#,#,#)"#)%# )$# )&# 370&&,#,#,#,#)"#)%# )$# )&# 370&&,#,#,#,#)#) # )# )!# 370&&,#,#,#,#)#) # )# )!# 370&&,#,#,#,#)#) # )# )!# 370&&,#,#,#,#)#) # )# )!# 370&&,#,#,#,#)#) # )# )!# 370 8r\@#$=>@AABBC]^abJKKL]^^__``acdde5667    "#%&(+-.012469<?CFIMQUZ[]^`cegjlosvwyz|  !"$'),/369=AEJMPTX\aeinsx~  !%).036:<?ABEIMRV[`fimqvy} &-5=DLU]fkqv|&/7@HOW`hqz '1:DNYajs} $)*17>AEHKMOV\ckrz~  '-3:@GOV[aflsy~ #,4=GPZ`gnv|    ' 0 : B K S Z [ \ ^ _ a b d e g j l o p r t u w z |          $+#+#$*#)#)+))+*+$(#'#'+$(*#')#')+''+(')')+(*))+*+$&#%#%+$&*#%)#%)+$&(#%'#%'+$&(*#%')#%')+%%+&%)%)+&*%'%'+&(%')%')+&(*''+(')')+(*))+*+$#+#$*#)#)+$(#'#'+$(*#')#')+$&#%#%+$&*#%)#%)+$&(#%'#%'+$&(*#%')#%')+%%+&%)%)+&*%'%'+&(%')%')+&(*''+(')')+(*))+*+%%+&%)%)+&*%'%'+&(%')%')+&(*''+(')')+(*))+*+"$!#+!#"$*!#)!#)+"$(!#'!#'+"$(*!#')!#')+"$&!#%!#%+"$&*!#%)!#%)+"$&(!#%'!#%'+"$&(*!#%')!#%')+!%!%+"&!%)!%)+"&*!%'!%'+"&(!%')!%')+"&(*!'!'+"(!')!')+"(*!)!)+"*!+!"$$*$($(*$&$&*$&($&(*&&*&(&(*((**"$"$*"$("$(*"$&"$&*"$&("$&(*"&"&*"&("&(*"("(*"*" $ $* $( $(* $& $&* $&( $&(* & &* &( &(* ( (* * "$ "$* "$( "$(* "$& "$&* "$&( "$&(* "& "&* "&( "&(* "( "(* "* "024677/135702467/1357/13575757675702467/1357/135702467/1357/1357357357467357357467357357467357 02467/1357/1357 02467/1357/1357 02467/1357/1357 02467/1357/135713571357 246713571357 246713571357 246713571357 24671357135724671357135724671357135724671357.02467-/1357.02467-/1357-/1357.02467-/1357-/1357.02467-/1357-/1357 .02467-/1357-/1357 .02467-/1357-/1357 .02467-/1357-/1357 .02467-/1357-/1357 02467 02467 02467 02467024670246702467 .02467 .02467 .02467 .02467.02467.02467.02467,.02467,.02467,.02467,.02467 ,.02467 ,.02467 ,.02467 ,.02467 ,.02467 ,.02467 ,.02467 ,.02467,.02467,.02467,.02467                     024677/1357/1357702467/1357/135757576757576702467/1357/135702467/1357/1357357357467357357467357357467357357467 02467/1357/1357 02467/1357/1357 02467/1357/1357 02467/1357/135713571357 246713571357 246713571357 246713571357 2467135713572467135713572467135713572467135713572467.02467-/1357-/1357.02467-/1357-/1357.02467-/1357-/1357.02467-/1357-/1357 .02467-/1357-/1357 .02467-/1357-/1357 .02467-/1357-/1357 .02467-/1357-/1357 02467 02467 02467 0246702467024670246702467 .02467 .02467 .02467 .02467.02467.02467.02467.02467,.02467,.02467,.02467,.02467 ,.02467 ,.02467 ,.02467 ,.02467 ,.02467 ,.02467 ,.02467 ,.02467,.02467,.02467,.02467,.02467                             8v   *T~&Pz"LvHrDn@Wn&=Tk #:Qh       "!#$&%'(*)+,.-/0213487658787:98787<;8787=>8787@?8787BA8787DC8787EF8787HG8787IJ8787LK8787NM8787PO8787QR8787TS8787VU8787XW8787YZ8787\[8787^]8787`_8787ab8787dc8787fe8787hg8787ij8787lk8787nm8787po8787qr87vutsvuvuxwvuvuzyvuvu{|vuvu~}vuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvu     ! "$#%'&(*)+-,.0/1234675:98=<;@?>CBAFEDIHGLKJNMOQPRTSUWVXZY[]\^`_abcdfegihjlkmonprqsutvxwyz{|~}67:9=<@?CBFEIHLKfeihlkonrqutxwz{~67:9=<@?CBFEIHLKfeihlkonrqutxwz{~STUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@AB   111222RRR555ZZZjjjkkllmmnnooppqqrrsst tu!uv"vw#wx$xy%yz&z{'{|(|})}~*~+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRCDEFGHIJKLMNOPQRSTUVWX Y  Z  [  \ ]^_ ` a b c defghij kl m  n !o!"p"#q#$r$%s%&t&'u'(v()w)*x*+y+,z,-{-.|./}/0~0334455566778899::;;<<==>>??@@A AB BC CD DE EFFGGHHIIJJ^_ KKa b LLdeMMghNNNOOOQRPPGHQQE@@A ASSC CD DTTFFGGUUIIJJVV::;;WW==>>XX7788YY44334455566778899::;;<<==>>??@@A AB BC CD DE EFFGGHHIIJJ@@A ASSC CD DTTFFGGUUIIJJVV::;;WW==>>XX7788YY44555ZZZ[[\\]]^^__` `a abbccddeeffgghhii[[\\]]^^__` `a abbccddeeffgghhiijjj[[\\]]^^__` `a abbccddeeffgghhiijjj[[\\]]^^__` `a abbccddeeffgghhiijjjkkllllmmnnoonnooppllqqrrsst tu!uv"vrrssw#wu!uv"vx$xnnoopplly%yz&z{'{|(|})}~*~+,-./0z&z{'{1})}~*~2,-3/04rrssw#wu!uv"vx$xnnooppllkkllmmnnooqqrrsst tu!uv"vy%yz&z{'{|(|})}~*~+,-./01234w#wx$xpp1234w#wx$xppkkmmqqt ty%y|(|+.1234w#wx$xpp56789:;<=>?@ABCDEFGHIJKLMNOPQR** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1 D= #$=>CD]^abJKKL5667     $'),.135789;<=>AEHJMOQTVWYZ^adgiknprtuvy{}    "%),/246:=@CEGJLNPQRV[_bfilpsuxz} "%(*-/127;?BFILNRUXZ]_abfilnqsuvy{}~ #*16;BGLQ\gpy "*2:@FNTZ`n| &0:BJR]fov&/<GR[fox   $(,02468>DHLPTVX^dhlptvx   $(,02468>DHLPTVX^dhlptvx    ! , 7 @ F J N R T V a l u ~    # * 1 6 B L V ` h p z  " . 8 D P Z d n v    ! & 2 < F N X ` h n }     & . 6 > D J L N T Z ` f j n r v x z | ~  &.6>DJLNTZ`fjnrvxz|~ &.6>DJLNTZ`fjnrvxz|~ &.46<BFJLNTZ^bfhnrvz|~'7GWgw/?O_o/O_o?'GWg?O_O_/?o/o7w?7GWw/?Oo/OoGW?OO/?_o/_o'7gw?__'g/?o/o7w?'7Ggw/?_o/_o'Gg?__7Gw/?o/oG?/?O_o/O_o'7Wgw?O_O_'Wg/?Oo/Oo7Ww?OOW/?_o/_o'7gw?__'g/?o/o7w?'7GWgw/?O_o/O_o'GWg?O_O_7GWw/?Oo/OoGW?OO'7Ggw/?_o/_o'Gg?__7Gw/?o/oG?'7Wgw'Wg7WwW'7gw'g7w'7Wgw'Wg7WwW'7gw'g7w'7GWgw'GWg7GWwGW'7Ggw'Gg7GwG'7Wgw'Wg7WwW'7gw'g7w#3CScs&6FVfv +;K[k{.>N^n~ +K[k.N^n;{>~#CSc&FVf ;K[{>N^~ K[N^+;k{.>n~+k.n3s6v3CSs6FVv +;Kk{.>Nn~ +Kk.NnCSFV ;K{>N~ KN+;[k{.>^n~+[k.^n#3cs&6fv;[{>^~[^#c&f#3Ccs&6Ffv#Cc&Ff3Cs6FvCF#3Scs&6Vfv#Sc&Vf3Ss6VvSV"2BRbr%5EUeu *:JZjz -=M]m} *JZj -M]m:z=}"BRb%EUe :JZz =M]} JZ M]*:jz-=m}*j-m2r5u2BRr5EUu *:Jjz -=Mm} *Jj -MmBREU :Jz =M} J M*:Zjz-=]m}*Zj-]m"2br%5eu:Zz=]}Z]"b%e"2Bbr%5Eeu"Bb%Ee2Br5EuBE"2Rbr%5Ueu"Rb%Ue2Rr5UuRU!1AQaq )9IYiy )IYi9y!AQa 9IYy IY)9iy)i1q1AQq )9Iiy )IiAQ 9Iy I)9Yiy)Yi!1aq9YyY!a!1Aaq!Aa1AqA!1Qaq!Qa1QqQ'7GWgw/?O_o/O_o?'GWg?O_O_/?o/o7w?7GWw/?Oo/OoGW?OO/?_o/_o'7gw?__'g/?o/o7w?'7Ggw/?_o/_o'Gg?__7Gw/?o/oG?/?O_o/O_o'7Wgw?O_O_'Wg/?Oo/Oo7Ww?OOW/?_o/_o'7gw?__'g/?o/o7w?'7GWgw/?O_o/O_o'GWg?O_O_7GWw/?Oo/OoGW?OO'7Ggw/?_o/_o'Gg?__7Gw/?o/oG?'7Wgw'Wg7WwW'7gw'g7w'7Wgw'Wg7WwW'7gw'g7w'7GWgw'GWg7GWwGW'7Ggw'Gg7GwG'7Wgw'Wg7WwW'7gw'g7w#3CScs&6FVfv +;K[k{.>N^n~ +K[k.N^n;{>~#CSc&FVf ;K[{>N^~ K[N^+;k{.>n~+k.n3s6v3CSs6FVv +;Kk{.>Nn~ +Kk.NnCSFV ;K{>N~ KN+;[k{.>^n~+[k.^n#3cs&6fv;[{>^~[^#c&f#3Ccs&6Ffv#Cc&Ff3Cs6FvCF#3Scs&6Vfv#Sc&Vf3Ss6VvSV"2BRbr%5EUeu *:JZjz -=M]m} *JZj -M]m:z=}"BRb%EUe :JZz =M]} JZ M]*:jz-=m}*j-m2r5u2BRr5EUu *:Jjz -=Mm} *Jj -MmBREU :Jz =M} J M*:Zjz-=]m}*Zj-]m"2br%5eu:Zz=]}Z]"b%e"2Bbr%5Eeu"Bb%Ee2Br5EuBE"2Rbr%5Ueu"Rb%Ue2Rr5UuRU!1AQaq$4DTdt )9IYiy ,<L\l| )IYi ,L\l9y<|!AQa$DTd 9IYy <L\| IY L\)9iy,<l|)i,l1q4t1AQq4DTt )9Iiy ,<Ll| )Ii ,LlAQDT 9Iy <L| I L)9Yiy,<\l|)Yi,\l!1aq$4dt9Yy<\|Y\!a$d!1Aaq$4Ddt!Aa$Dd1Aq4DtAD!1Qaq$4Tdt!Qa$Td1Qq4TtQT 0@P`p(8HXhx(HXh8x @P`8HXxHX(8hx(h0p0@Pp(8Hhx(Hh@P8HxH(8Xhx(Xh 0`p8XxX ` 0@`p @`0@p@ 0P`p P`0PpP 8v                *T~&Pz"LvHrDn@j<f8b 4^  0 Z  , V ( R | $ N x J t FpBl>h:d 6`2\.X.E\s+BYp(       "!$#&%('*),+.-0/214387657887:97887<;7887>=7887@?7887BA7887DC7887FE7887HG7887JI7887LK7887NM7887PO7887RQ7887TS7887VU7887XW7887ZY7887\[7887^]7887`_7887ba7887dc7887fe7887hg7887ji7887lk7887nm7887po7887rq78vutsuvvuxwuvvuzyuvvu|{uvvu~}uvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuv     ! "$#%'&(*)+-,.0/1324765:98=<;@?>CBAFEDIHGLKJNMOQPRTSUWVXZY[]\^`_acbdfegihjlkmonprqsutvxwy{z|~}76:9=<@?CBFEIHLKfeihlkonrqutxw{z~76:9=<@?CBFEIHLKfeihlkonrqutxw{z~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~$$$,,,<<<=5+=>6,>?7-?@8.@A9/AB:0BC;1CD<2DE=3EF>4FG?5GH@6HIA7IJB8JKC9KLD:LME;MNF<NOG=OPH>PQI?QRJ@RSKASTLBTUMCUVNDVWOEWXPFXYQGYZRHZ[SI[\TJ\]UK]^VL^_WM_`XN`aYOabZPbc[Qcd\Rde]Sef^Tfg_Ugh`VhiaWijbXjkcYkldZlme[mnf\nog]oph^pqi_qrj`rskastlbtumcuvndvwoewxpfxyqgyzrhz{si{|tj|}uk}~vl~wmxnyozp{q|r}s~tuvwxyz{|}~                     "!$#    &%(' "!*)  ,+.-0/2143 "!$#               &%(' "!* )    , + .-0/21 4 3!! "!"$"#  # # %.%-    &0&/    '2'1(4(3)) "!*$*#    + +  ,,,           &%(' "!* )    , + %.%-    &0&/    '2'1(4(3)) "!*$*#    + +  --..// 001& 1%2(!2'3*"3)4,#4+5.$5-60%6/72&7184'839(9:$):#; *; <<<--..// 001& 1%2(!2'3*"3)4,#4+5.$5-60%6/72&7184'839(9:$):#; *; <<<--..// 001& 1%2(!2'3*"3)4,#4+5.$5-60%6/72&7184'839(9:$):#; *; <<<--..// 001& 1%2(!2'3*"3)4,#4+5.$5-60%6/72&7184'839(9:$):#; *; >6,>@8.@B:0BD<2DF>4FH@6HJB8JLD:LNF<NPH>PD<2DRJ@RTLBTVNDVXPFXZRHZ\TJ\^VL^`XN`bZPbd\Rdf^Tfh`VhLD:LNF<NPH>PD<2DjbXj^VL^`XN`ldZld\Rdf^Tfnf\nLD:LNF<Nph^pD<2D@8.@B:0Brj`rH@6HJB8JtlbtTLBTVNDVvndvZRHZ\TJ\xpfx^VL^`XN`bZPbd\Rdf^Tfh`VhLD:LNF<NPH>PD<2D>6,>@8.@B:0BF>4FH@6HJB8JRJ@RTLBTVNDVXPFXZRHZ\TJ\jbXj^VL^`XN`ldZld\Rdf^Tfnf\nLD:LNF<Nph^pD<2Drj`rtlbtvndvxpfxbZPbh`VhPH>Prj`rtlbtvndvxpfxbZPbh`VhPH>P>6,>F>4FRJ@RXPFXjbXjldZlnf\nph^prj`rtlbtvndvxpfxbZPbh`VhPH>Pyqgyzrhz{si{|tj|}uk}~vl~wmxnyozp{q|r}s~tuvwxyz{|}~               ** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1   # Ā̀Ԁ    vie$swift-im-2.0+dev6/BuildTools/DocBook/Fonts/Gentium Basic/GenBasB.ttf0000644000175000017500000102244012227051773024750 0ustar kismithkismith`Feat$x$$GDEFٷl8GPOS]3BGSUB F|$Glat(`LLGloc3FOS/24g!L`Silfas..Sill$$cmap>tMTcvt G]TfpgmY7]psgasp^glyfy`^4head0O4(6hhea Y4`$hmtxmU4 ploca-v@:maxp?\G0 nameGPA1post~S>gprepǸr #$=D]bJL]__ace57 **"(%$&' #       !  )    &Dlatn IPA markmkmk ">BR  p/.0. +       2 b h n t z     " ( ( . 4 : @ @ @ F L h n t z R h n t z X ^ d j p v    |    ( ( . 4   ^ d j p$*0666<BHN . 4T ^ d j pZ`flrx   ~ h n t z T ^ d j p         &, h n t z2 8>>>DJ P`flrV\\\bh ^ d j pn t z`flrHN . 4    r~ t z, n t z   d j p    "( . 4::: @666<F h n t zL R X^d^jpv|v ^ d j p ^ d j p       ^ d j p  "" 8>>>D""$*0006< ^ d j pBHN . 44 HNNNTZ`flrxHN . 4~    ( ($*     h n t z   &,28>D ( ( J J PVVV, h n t z\ ^ d j pbhhhnt h n t zz   HN . 4 HN . 4\\\bT ^ d j p h n t zHN . 4 ^ d j p    n t zPVVVh ^ d j p Z`flr",28$* "HN . 4 h n t z ^ d j pHN . 4"   ( ^ d j p.444:8>>>D:@ F\\\b( ^ d j p   LRRRX^,28d jpv| t z v       ^ d j p  HN . 4$*L ~ h n t z(::: 8>>>D    8>>>DHN . 4 ,28 HN . 4, h n t z   "    $ T ^ d j p*0,286  < ^ d j pB   H NTZ`flr`flrx~x       ( ( HN . 4pv|8>>>D    pv| ( ^ d j p ^ d j ppv|8>>>Dpv| ( ( ^ d j pJ J  ^ d j p ( (, h n t z R h n t z( ^ d j pT ^ d j p   & ( (,28 n>DJHN . 4P`flrV\HN . 4     bhnhtz h n t z    8>>>D "D    ^ d j p " (N . 4,28    h n t z8>>>D , h n t z      B (N . 4 HN . 48>>>DT h n t z   < d j p R h n t z    ( (PVVV    ^ d j p ( ^ d j p" ( d j p(,28.4 h n t z:@F L$*T ^ d j pR X ^HN . 4dj jp < ^ d j pv|HN . 4 ^ d j p`flr M AO# &48TL>>AA99rXZXZ5  vvyy CZC.\PTP'iBQq[.u|bZ PZ  y~~^ y[$/Z/!Gh_ HqH$CGRQO'Z C~wy*CLy~\@Z@A66NCAyZyCZ{Cf]wC.C33*yZbib /yPCqbZbZ}Z}}]NZNNHZ ycZcZ0r  *#llGuZ&Z&asZsZ;;;ug//Z3; pZpy.Z2A!y(y;,r;S}P;yZCZZAsA.EEyZwNGrr/C33}$$[y3.ZH~G*s(ss)q CEZ*Z*pHZ3[_yEE bAyA  qZgs\ZQHZoQy8*Z z``!KKcr  LC/N.AlZL\r'_TQQTT~Z +         4 v |       $ * * * 0 6 < < < B H N N N T Z ` f ` l r x ~ x ~ |    &,28>DJPV\bhnnntz   x x x |   "(.444:@FFFLR | X x ~ ^djdpv||| "( | " ~      N N N T |      0   444:$***06<BH TNTTTZ` x ~ f | lrx~xnjnp     v |  |  N N N T ,8Dx~x444***0,28>D  |  x ~  &&&, x ~ 2dddt8>>>DJJJPV \bbhntz& N N N T>>>  V\b0    "    N N N T&&," x ~   N N N T"(( | . x x x4:@F@LRXBH Tf | ^djd p<B<v|"444: |  v ***0  djdp"(f |   * * * 0$*0006< BHNTZ fZ l`ffflr x ~ x~~~ T\bbN N N N T * * * @F@L8& | Z fZ lbbbN x x x"(>>> j & ^djd v |  &&&,2 8 0>  r x ~ D444JP444:V\x~xbhhntzzz~ x ~  XBXv |  "  x ~     28D***0  * * *  &FLdddt   "NXBXv(. 4:@@@F N N N TL28D Z ` f ` lRX^Xdj pv| ***0x~x x ~  * * * 0  ,28Dr x x x282hNh ~ |  TX^Xd@F@LX x ~ 8  >>>   | 444: bhh<  x x xD:f | NXBXv * * * 0&   @F@Lx~x v | $*  06<BHNTdddtZ`fx~xlhNhr x f. ~"(P444:      j &&,PPPb &:>>>dddt828D28D28D  28D N N N T  RX^Xd 8dddtNP444: R X^Xd M AO# ~~^Z# PEEE'll&C.\vZv /s(ss)AA4Z4Z66N QOq~///Z3==;;;xgoQlZl_ Z Jj~Z+ _/Z/!''ssZ gAllMMgAPTPiAA=$ Z8 XZXCZ.&Z&as/1ZA~8yjZl^~ ZK;ujZc\AX~Aq33yl]tuZ//qu=#tZZ/Z_;,/ *A~PXw(@Z@A2PPKKb~ddM-Z-SFZFEZE)'s,gg~Z$E/3_ssZ4AZ /_d.g~*sZT_7Y7q@@))CEPEsZ/s__~9AZh;ZsrZ/LL~Z~$tZt;{6= C$S_T/g;.rt&AsCF^Z^DlKc8-@3=ZU; ;DZ;q)l_uu&/Z**"(%$&' #       !  ) 1Z+$$/%%&&]''X((o))**?++,,--T..l//|001122033445566J7778899::!;;_<<n==DD EEuFFbGGHHII.JJKK+LLMM9NNOOPPiQQRRSSITTUU(VVWWXXYYZZY[[\\O]](bbccxddVeeff'gghhhiijjkkkll6mm$nnfooDppaqqrrssttuuvvswwxx#yyzz{{r||}}~~j< m-ydc z2-#p{"MqN*"1S  5    &  0  G@C,&>RQ  !!""##*$$L%%&&''((/))!**`++,,--..$//0011 22K3344=55.6677889:;;;<<== >>U??B@@%AABBHCCDD4EEFF[GGWHHIIJJALLMMNNOOPPPQQ RRSS)TTUUVVWWXX\YYZZ[[v\\]]e__'aabbccee ff~gghh iijj,kk3ll}mmnnooppqqrrssttuu)vv wwxxyywzz{{||:}}~~  F8Etg%^ 12)1-w&"!HNkG|h,B[X+ V{`$(ALv5i )0Wl(gJ%?cZY 1m  9        /\+ty>  !!""##$$%%&&''&((2))P*+D,,--..//K0011U2233^445577o8899R::;;<< == >>}??@@AABBCCDD*EEsFF#GGHH2IIJJ!KKLLMMINN@OOPPQQRRSSTTUUVVWWXXYYZZ[["\\d]]^^__`` aabbccddzeeffggQhh=iiSjjkk ll'mmnnooppqqprrss-ttuuvvuwwxxyyzz{{||q}}~~*f3M:b;x a.3C% ,Tj4 'rFn0]~8$. _ 67Oe</ & $*0# &,MO# qZbeq#qq ZZZ&%# !"        $ #"         ! ((   JzcyrllatnIPA VIT aaltccmpccmp$0Tpbtb &6H &6HLM   LM"(FdnxJJdCCo7CCJJppopp0u&&(( 2288EEFFHHRR XXddoo??FFffoo }}  N ,  F ')uy^+bw%d!{`    $$&&((**]] __aaccttvvxx zz  f0     EM   DD"JJ(in DDLL!#$%&')*,./<XXXXd@ JSIL D  4 t~!%-3:DHU]adq~37=BEKQTWY[cikru #(1?_';Io     " & 0 : D !"!&"""""""+"H"`"e%%,b1Bj $(09AGJX`dht&7=ADJQSVY[chkru#'1?^.>Lx     & 0 9 D !"!&"""""""+"H"`"d%%,`0BjT1hw8:"+ xjyޖދtq_/0TOn  2:>BDFFrxz|VFJNBBD8<:<>@B@bcdeYfgh<jikmlnoqprsutvwTxzy{}|~9.#F?G@IBHAUZQi}fl~g&!GAHBVMXOYPik}gq|f.0?u-"~hpmonq/1625/0o) $* + @>:DSRt{;7=9<8JCVNXPWOkjp#%(':7;8ICKELFJD[R\SWNZQ]Tc_ead`jl^43 "$&*(,nmht]v_zcxa|e- ,  12435srvwyxz>@ t~!%-3:DHU]adq~37=BEKQTWY[cikru #(1?_';Io     " & 0 : D !"!&"""""""+"H"`"e%%,b1Bj $(09AGJX`dht&7=ADJQSVY[chkru#'1?^.>Lx     & 0 9 D !"!&"""""""+"H"`"d%%,`0BjT1hw8:"+ xjyޖދtq_/0TOn  2:>BDFFrxz|VFJNBBD8<:<>@B@bcdeYfgh<jikmlnoqprsutvwTxzy{}|~9.#F?G@IBHAUZQi}fl~g&!GAHBVMXOYPik}gq|f.0?u-"~hpmonq/1625/0o) $* + @>:DSRt{;7=9<8JCVNXPWOkjp#%(':7;8ICKELFJD[R\SWNZQ]Tc_ead`jl^43 "$&*(,nmht]v_zcxa|e- ,  12435srvwyxz>@*+Pn 9,K PXYD _^-, EiD`-,*!-, F%FRX#Y Id F had%F hadRX#eY/ SXi TX!@Yi TX!@eYY:-, F%FRX#Y F jad%F jadRX#Y/-,K &PXQXD@DY!! EPXD!YY-, EiD` E}iD`-,*-,K &SX@Y &SX#!#Y &SX#!#Y &SX#!#Y &SX#!@#Y &SX%EPX#!#!%E#!#!Y!YD- ,KSXED!!Y-dH/ /ܸܸи/EX/>Y+017!!'!!L2P22] +A&6FVfv ]A]/+01%#"&&&5466632''666672C%4(3B%4)c<MAE?2(@,!3$'@,!3 !  -7 ////01#6666667#66667[ &$ S%.4/#3J &$ S CLB 3BG GH DH//!/EX:/: >YEXC/C >Y ++и#и *и,и3и<и EиG013#3#'#'#'6666737#'6666736666736666737#_  A  S "# YS " Y  @  N " UN " @ "#  !#  9  9 #!  !#   gLRh W1+M+R +A&6FVfv ]A]A ]A  ) 9 I Y i y ]MиMии+и6иM=иRY/%)?+-=$!1',>l>7%5A@93XD. 3JkO+cYA  #YEX%/%>YK3+A+ A!  ' 7 G W g w ]A  ' 7 G W g qAv q014&#"3267#"&&&5466632'666674&&&#"3267#"&&&5466632F528?<461WxHHsQ+.VyKJtO* ',*  ',( !-28><471WxHHsQ+.UyKJtP*"prF|]66]|FF|]66\}  IlF#prE|]66]|EF|]66\}AoƻE+U++c+A&6FVfv ]A]KE9K/A]A )9IYiy ]U9K9Acc]A cc)c9cIcYcicycc ](c+9`E+9+qEX:/:>YEX@/@>YP +nh+@A!'7GWgw]A'7GWgqAvqh(и(/01666654&#"267&&&&'&&'7#"&'#"&&&5467&&54666326654&&&#'66667!/?%A.( L9i//a^X&5(,GYu  %V" =7(3o3+_T> fBOgit=!&>kPLlF!5Wo;"QY]/+7 j4o;BDE FP4F"-it|@@}EFc>z%# <&BH"+ I!>76?1`^jjLHQe9)DY04lf]%9sni/$I#*C/#- //01#6666667[ &$ S%.4/#3BG j@K+A&6FVfv ]A] //01&&&54667Y}|=Er6Y+и01%'!'66667!76667!!       !         !XK +A ]A  ) 9 I Y i y ]//01%'6654&&&'6666663.Oj600F6|{n'2AW,!M W5I Y +A&6FVfv ]A]+01%#"&&&54666321C&5(3C$4((@,!3$'@,!3#@ //01'66667 ,2/ , +.+- 7  =%G&/'/ܸA]A )9IYiy ]&и/ A  & 6 F V f v ]A ]EX/>Y!+ A!  ' 7 G W g w ]A  ' 7 G W g qAv q014&&&#"32666%#"&&&54666328L-.D.kd.F-E|hht=Azljr;5ˆB2n}2oee恁ed|"+/EX/>Y0135666654&'&&'6666667Nh> <[BS^d[M,8aKS  (, Q#" *  SS3# +#и/A ]A  ) 9 I Y i y ]#3и3/#5EX/>Y+*01!!'66666654&&&#"'466632!2666767uU2%;*);$ 7>=#J^M~Y07X~k PJե|aN$*I6$:I% D4n[; @^?-Yewv$,8LH/I/%и%/ܸ%Ap]A0@P]A]A]A]A]?%?DиD/JEX/>Y:*+A!'7GWgw]A'7GWgqAvq01#"&&&'73266654&&&##"'666654&&&#"'466632Axg'`fe-)-RLI&/S=$+BL! Vg8!4&. 5;<"=n\[P%8N0=dF'}TpA(>+a"/ =]@E^9Z?EF"B4,=" =*dU:0Oc3&NG97Vm7!^+ии#/EX/>Y +9 и01!#!5666655!'666673:?:7 6)AR.&):<8, =  II  )*(N>+A]A )9IYiy ])/+/EX/>Y&1+:+A!'7GWgw]A'7GWgqAvq7:901#"&&&'73266654&&&#"'6666667!26767!66325ot.ffc+25\OC2T<"#@Y7Pt5/ ,% $(% P -n@dd3WzH(<(Z%. )Gb9DiG%0(!$dpwpb#$11) KKDAl_0I1/2/1и/A&6FVfv ]A]2ܹ A ]A  ) 9 I Y i y ])и)/#/EX/>Y,+A!'7GWgw]A'7GWgqAvq),901"3266654&&&#"&&&546$76632!+e0"Y+01'6667!"'66667!.ZTLA47>>1Wsh7A $(D  lȰ5 )A:Y`WO$L/+C+A]A )9IYiy ]C9/A]A )9IYiy ]A&6FVfv ]A]%4/%99HC9%NEX*/*>Y> +* A!  ' 7 G W g w ]A  ' 7 G W g qAv q01654&&&#"4&&&'32666%#"&&&546667&&&&5466632f";N,W-; !4#V&AU. 1  6F%)@+G}afi6(Ie<-P>$?mT_].!:O.4`J,&9.&[_4F*.=]:U@/=@G)4S9#;NWNi=4Xq>4bXN0BX^w< 4GX[/8PdwB\q?:;-_"wTnBˆY"b{T3:Y#+A]A(8HXhx ]01#"&&&5466632#"&&&54666321C&5(3C$4(1C&5(3C$4(9(@,!3$'@,!3'(@,!3$'@,!3X"5m//901%'7776676677m5R    M5' " #'# 5Tm  ++01!'66667!!'66667!m                 !   5m// 901'66667%%'667m   9{ ! ! $&# ,5Ab1EI(+ +<(9ED#K\RV-1C&5(3C$4(K{k``e;`m9nhdb_0|v.A'%&FzZ4.St(@,!3$'@,!3UcSoOd+'+<+E+A&6FVfv ]A]и/AEE]A EE)E9EIEYEiEyEE ]AOO&O6OFOVOfOvOO ]AOO]qEX./. >YEX6/6 >YEX/>YEX"/">YT_+kJ+.AyqA!(8HXhx]A(8HXhq" A  ' 7 G W g w ]A ]" 9@01&&#"326667#"&&&'#"&&&5466666326673266654&&&#"326667#"$&54666663271X\-7!!'(F_nw<4+ )=8<(3iT63KavE((*7Q,  +90TՁQi`g/$sz|?s}k-4UvI /$$UmM*%>/0?%8sx?~udK*$)#& R*`=>.YTTka#4<W)[L3xFyǦwBa% @EX/ >YEX/>YEX/>Y+01!566766667!566'륖(` ERKDI ]7;:+ N7 f&/x II *"h II!!i '8G,+ C+ACC]A CC)C9CICYCiCyCC ]4C 94/A44]A 44)494I4Y4i4y444444 ]ܺ#C 9,<EX/ >YEX/>YEX / >YEX / >Y>(+#(>9(+и+/1A!11'171G1W1g1w111111111]A11'171G1W1g1qAv11q9<иYEX/>YAyqA!(8HXhx]A(8HXhq+A++'+7+G+W+g+w++++++ ]A++]01%#"&&&54662'&&#"326667/@|zv9eZb6j`O A3N"IF?/CkBIWb5 A\:T呠d$*1-$XO2Qvhw9 !7, /0/1/ܸ0 и /и/+A++]A ++)+9+I+Y+i+y++++++ ]EX/ >YEX/>YAyqA!(8HXhx]A(8HXhqи/$A!$$'$7$G$W$g$w$$$$$$$$$]A$$'$7$G$W$g$qAv$$q01#!5665'666632"326666654&&&3XvDDE 1AV5&"7*&USK8":q}ŗiCI!\  Sk   8Uv^̋G+5TEX/ >YEX/ >YEX/>Y)+/01!56654&'5!#&&#!!&&&&##3326667 ODEAHq( K(%! #-=*,PBc.A2)0+_UBI! $I NRI]ak#%$C 1P;+*JEX/ >YEX / >YEX/>Y#+ 01356654&'5!#&&#!!&&&&##+DEAH{& M)&o$  "-=*p&A1I! $I NRI]ak#&" I7 >?/@/)ܸи/?5и5/A&6FVfv ]A])EX:/: >YEX./.>Y: Ay qA!  ( 8 H X h x ]A  ( 8 H X h q.A!'7GWgw]A'7GWgqAvq01'&&&&#"32674&&&'5!#"&&&&&546632G ?!CHN+CGE6">gG7W# A89(Jpd-InP-j![`[(0,"0B'0Puh|;1  II-=M,!Cfg`&+:+,/-/,и/'и-ܹ$EX / >YEX/ >YEX/>YEX/>Y%+01356654&'5!!4&'5!!5665!+DEAH DFAH DFBHDEAII! $II"k $II"! #II!. #I?L/+EX / >YEX/>Y01356654&'5!?DEAH DFBHI! $II"! #I^o(& +EX'/' >Y+01#"&&&5466673266654&&&'5!oDF,ET'CHJ"-VC**888_&3) )I<?"RnrP '! 14+0'Mv I+3m/+/EX / >YEX/ >YEX/>YEX*/*>Y* 9.* 901356654&'5!66&&'5!67#"&'+DEAH DFt ,' -"S"',HHB-#AII! $II".  II ?I T #I+ 5 +EX/ >YEX/>Y01!56654&'5!3326667 YDEAH DF*H7q.?0'0+_UBI! $II"] 1P;5|.vEX / >YEX-/- >YEX/>YEX/>YEX/>Y 9 9' 901"!5665#!5665&!266663!aC$ IHDOX^`BH\ED #IZ  pw  S #II!-M #II!I!C!+&'(/)/(и/! и /)ܹEX/ >YEX/ >YEX/>YEX/>Y9 90135665&'5!24&&&'5!&&'+I@AHd 5(HB`t!4'I &= III &};%NI7 )*/+/ܹA]A )9IYiy ]* и / A  & 6 F V f v ]A ]EX%/% >YEX/>Y%AyqA!(8HXhx]A(8HXhqA!'7GWgw]A'7GWgqAvq014&&&#"326667#"&&&54666321Z~MHoK&5[yEBoQ.,OnQ~ʌKY݄ʊFugXGzpđS@Ŗ[}Z2j~nm? 5*6/7/6и/7ܺ9&A&&]A &&)&9&I&Y&i&y&& ]/EX/ >YEX/>Y!+9+Ay++qA!++(+8+H+X+h+x+++++++++]A++(+8+H+X+h+q.и./0135665'666632#"&''3266654&&&#"+DE&E 6BtJ&AU_a,.X")?#+TA(/UvH) %B4I! \  .ZVEt\F.i BfGGlI% I7 AܸB/C/2ܹA]A )9IYiy ]B(и(/ A  & 6 F V f v ]A ]7(29EX-/- >YEX / >YEX#/#>Y<+-AyqA!(8HXhx]A(8HXhq A!'7GWgw]A'7GWgqAvq7 9014&&&#"32666#"&&&&&'#"&&&54666323266671Z~MHoK&5[yEBoQ.76//]_^__/|ȎMY݄ʊF6bP8hb]- *ugXGzpđS@,J63AC@i耈nmze(60" 1@9A/B/Aи/Bܺ9-5и<A<<]A <<)<9<I<Y<i<y<< ]EX / >YEX/>YEX"/">Y" 9 2Ay22qA!22(282H2X2h2x222222222]A22(282H2X2h2q5и5/0135665'66327#"&'##"&'"32654&&&+DE$E jy7)Ig>;#/"!RQE7 4AIS (~CrI! \%.Ro@Es]EL I  !2 #I$y.S?$f KϸL/M/ܸL'и'/ и /'и/A]A )9IYiy ]9и9/'CACC&C6CFCVCfCvCC ]ACC]EX,/, >YEX/>YA!'7GWgw]A'7GWgqAvq,>Ay>>qA!>>(>8>H>X>h>x>>>>>>>>>]A>>(>8>H>X>h>q01#"&&&'&&&66673266654&&&&&&&5466632'&&&&#"6RoU$]a]# GBR^3-T@'6YqwqY65oy6hZE B9?@2G.7YrxrY7y-^[Q<$$#5CDB;bF'1I0=YE87=QjI/sdD"&,( /?&,7/I?8YEX/ >YEX/ >YEX/>Y и!01!566665#"'66667!#&&&&##,.<$"I .& K $=.I 2P:RVO HPO.K3 I+!)׸*/+/ܸ*и/#EX/ >YEX(/( >YEX / >YA!'7GWgw]A'7GWgqAvq01#"&&&54&'5!3266654&'5!!DFIlgOAH DF"JwVGb=AH"ΎK9uz $II"`d3@jJs $I 0@EX/ >YEX/ >YEX / >Y 901&&'5!6&&&'5!0DM -9=9 YEX / >YEX)/) >YEX / >YEX/>Y 9 9" 901&&'5!36&&&'5!,:"#-.$.2>G16$dO*:"   { II $k  I7EX/ >YEX(/( >YEX/>YEX/>Y9 иик!9'и*и601!5666&'!5667&&&&'5!66&&'5!,66.>AZ5#-!U4Ͽ5-$9,! c",I W II'( II!+/ II T I1^+++9EX/ >YEX$/$ >YEX/>Y901!566665&&&&'&&&&#'6666326&'5!Y-=$$Z\X# "4*KJA8%QSO#1MDB$=/I P/  I 2H II  I4JEX/ >YEX/ >YEX/>Y01!'!"'3!!26667P$y('# W%-+!d" a ae^55">V4i2>[>}@!+++01!!!! )    ! "!(@ //01&&&&'7n ,.) R-(*'8  5@)+++01'66667!!'66667!D) + " " m$   qd5// /901''66667/"%  B #$${X f 5~ +01!'66667!     & !    / /01766667  >HA >  y8 ASyT/U/KܹT%и%/A&6FVfv ]A]и/-EXE/E >YEX/>YEX / >YNANN'N7NGNWNgNwNNNNNN ]ANN]N9E3Ay33qA!33(383H3X3h3x333333333]A33(383H3X3h3q01%266675#"&'#"&&&54676666754&&&#"''666632326667*3=&YEX/>Y4A]A(8HXhx ] A  ' 7 G W g w ]A ]/49014&&&#"326667#"&&&'4&&&'566667666632$#4+%,Ke:}wkQ/"..2F  =+<&@vA}.0$ +A$$&$6$F$V$f$v$$ ]A$$]EX/ >YEX/>YAyqA!(8HXhx]A(8HXhq)A))')7)G)W)g)w)))))) ]A))]01%#"&&&5466632'&&#"326667}Anc\/QsEPw!SPC C!`P,Q>&)G`64>M5JW. AzolQ (4:4* bk(WaP}V..'Au/@A/B/%ܹ0и/Aи/0и9A99&969F9V9f9v99 ]A99]#/EX/ >YEX/>YEX / >Y>A>>'>7>G>W>g>w>>>>>> ]A>>] >94Ay44qA!44(484H4X4h4x444444444]A44(484H4X4h4q4901%#"'#"&&&54666324&&&'5667667%&&#"326u.^Q>H.MJN.Ch@Gk/]685n4& #$kC2T=#(@R)7cr4'/B*AzodV%W7A# E %%B#0 R:@&Q|WP}W.7A 9l:/;/:-и-/и/;ܹ A ]A  ) 9 I Y i y ]#и#/EX5/5 >YEX(/(>Y+5AyqA!(8HXhx]A(8HXhq(A'7GWgw ]A]01"!2654&&&!326667#"&&&5467666632/D/M)Dl "()'#@[;;EV; EnedYEX*/* >YEX!/!>Y6 +'и(01&&#"3&&#!5665#'73546667666632{&$ 0Z /$%  UP/M;E?wNE+?(%OMI-WC* #$<4 TsV$%# _  II # $YRmY&$3" !*sXL+++A]A )9IYiy ]A&6FVfv ]A]]L9]/ &+9&/A&&]A &&)&9&I&Y&i&y&& ]>Q] 9XL>9q+9uEXb/b >YEXh/h >YEXE/E>Y0+bAyqA!(8HXhx]A(8HXhqE!A!!!'!7!G!W!g!w!!!!!!!!!]A!!'!7!G!W!g!qAv!!qQEb9X09qEb9014&&&#"32666&&'3266654&&&###"&&&&&546667&546667&&&&54666326674M34*3N65(p0T%.51VuD;V96bDrS & -[Nu[$+Lfw@4qmbJ,8aMr+F28\A$HxWEx0W)  "F*X3YB'.F/2YB'.E 2)!!=.2@#&"M[3#+  +YEX/>YEX/>Y( A ]A  ( 8 H X h x ]%( 901!56654&&&#"!56654&&&'5666676632H< +:{QDABB608YLF%,sN+P=% 4'I=O.HC IIn*/ F))^T=bF  I?8[(+ к9/A]A )9IYiy ]ܸ*EX/ >YEX/>Y$+01356654&&&'56673#"&&&5466632?D@65VD@YEX / >Y;1+ A'7GWgw ]A]01%#"&&&5466673266654&&&'5666673#"&&&5466632)AQ(BEC'SD,+99.0//$645ODA(C)-=$2&.=#2%ktoK ' 22* Oq]3>#F -"8( )"8' )0q4p.+. и./EX/ >YEX/>YEX)/)>Y)9-)901356654&&&'5667%66&!7#"&'0BB 4+aD,!(-W+i"+JE7 *:,%Il.2F$)1#II g I   I5B6+ и //EX/>Y0135666654&&&'5666675+7 4*2VNL'+!7)Il-2 F)I0X '0++T+'>и>/I9TZEXYEXC/C >YEXN/N >YEX/>YEX/>YEX+/+>YN A ]A  ( 8 H X h x ]#к>N 9IN 901!5666654&&&#"!5666654&&&#"!56654&&&'566667666632666632$- (,vA 2'$. %-tJOI  =J& >?  II  =J& YEX*/* >YEX/>YEX/>Y* A ]A  ( 8 H X h x ]'%901!56654&&&#"!56654&&&'5666676632H< (6AO0DABB630SLH$* uN+P=% 4'I7I, 6) II+'. F*dV=bF  IA)*/+/ܹA]A )9IYiy ]* и / A  & 6 F V f v ]A ]EX%/% >YEX/>Y%AyqA!(8HXhx]A(8HXhqA!'7GWgw]A'7GWgqAvq014&&&#"326667#"&&&5466632!R0%?S.YEX>/> >YEX&/&>YEX/>Y>A]A(8HXhx ]A'7GWgw ]A]!99>9014&&&#"326667#"&&&'!56654&&&'566667666632W(BS+#-=,5M;0'@.#;NVZ);AJ,K^BB509TGA&* 7`QDD{\6`X*,$>$&IlBveJ* +T II %0 F*{1?&7rA UCD/E/<ܹD"и"/A&6FVfv ]A]к"<9'и'/<6и6/EX,/, >YEX6/6 >YEX/>YEX/>YA'7GWgw ]A], Ay qA!  ( 8 H X h x ]A  ( 8 H X h q901%267&&&&#"5665#"&&&54666766663266667 7c;18<0Q<"&>OT^L-NMQ0;zc@,DP#ADD7:?%62( '  3&86/"%N{UUW,I-@(AzoXjJ  +$  % % R-I0q5EX,/, >YEX3/3 >YEX/>Y3 A ]A  ( 8 H X h x ].,901#&&&&#"!5654&&&'&&&&'566667666632k I'/49S^ۄ $7QFC** DMT+&S+>LH>(H5+PBK II!- F*'D3LCϸD/E/ܸD$и$/ и /$и/A]A )9IYiy ]$:A::&:6:F:V:f:v:: ]A::]и/EX)/) >YEX/>YA!'7GWgw]A'7GWgqAvq)7Ay77qA!77(787H7X7h7x777777777]A77(787H7X7h7q01#"&&&'&&66732654&&&'&&&&5466632'&&#"+EVYQ KNM! Q .@O+HF)DV.*SB*=dD+WPD E^BcE% 9GDX]7* 2,-2AS)+ иEX/ >YEX / >YEX/ >YEX/>Y  ии$A$$'$7$G$W$g$w$$$$$$ ]A$$]01%#"&&&5#'7357!&&&&##3266675+M9"yNH&1$  %.3C '2?*`Y!DgF1$Yѕ!$%# ,3G+"x@˸A/B/8ܹ, иAи/"и/EX / >YEX6/6 >YEX/>YEX/>Y'A''''7'G'W'g'w'''''' ]A''] '901%#"&&&'#"&&&54&&&'5666673266674&&&'566767x%YSA !>cQD /ZH,1.2SLK*#+1;E)5.dH%  +3p2* %A36A$ Ex^05 F  .>N+ .$-6 F .)   @EX/ >YEX/ >YEX / >Y 901&&'5!66&&'5!#  %06 +1'-&"CY =$3 II   I *vEX/ >YEX / >YEX)/) >YEX / >YEX/>Y 9 9" 901&&'5!36&&&'5!# %-+ $** 52+0M'"EY   H 9! II  (_  I ;EX/ >YEX,/, >YEX/>YEX/>Y9 ик%9+и.и :01!5666&''!566667&&&&'5!766&&'5!2),~%4& #3%& py %})8( #0!III*6II  III1@EX!/! >YEX0/0 >YEX / >Y) !901#"&&&54666776677&&'5!66&&'5!& +dpz@&J9#"/22e%%U# 22'*&"GY nf0 3:4  WVE, II   I=JEX/ >YEX/ >YEX/>Y 01!'!"'3!!26667r,MG$'hWK;BC<. -dnF+;T6V@;Y9-Ay--qA!--(-8-H-X-h-x---------]A--(-8-H-X-h-q01'6654&'766767&'&&&54662'&&#"326667$6`N%WK., NL_Zb6j`O A3N"IF?/CkBIWb5 @|z;NU%F:*G ; "*,1!*呠d$*1-$XO2Qvhw9 !7,A\: BE+"( 7@+&"1@7"2@+!"8@A"D A"DA"DAm"DA"DA"DAD}E]F/G/ܸFи/2A22&262F2V2f2v22 ]A22]и/ A ]A  ) 9 I Y i y ]/EX/ >YEX/>YEXA/A>Y9-Ay--qA!--(-8-H-X-h-x---------]A--(-8-H-X-h-q01'6654&'766767&'&&&5466632'&&#"326667#6`N%WK., A&)G`64>M56Anc.NU%F:*G ; "*,1!zolQ (4:4* bk(WaP}V..'@JW.BEA"H5A"HA"HAm"H?}"X8"">m">0"QyA"RLA"RA"R2Am"R2A"RI"x"X^"x"X"x"XD"xm"XD7Aw>2+A>>&>6>F>V>f>v>> ]A>>]> и />и/2и/2$/:/01&&&&''66665&&'667'66667&&'66667667 && !?@D'$ $$ 083 % $% $]X! && =?D&+(CHA2%+^YXAHCECo`:u="JKHcnCD*%3@HCPG ## [\)%sk'+(/)/ܹA]A )9IYiy ](и/ A  & 6 F V f v ]A ]EX#/# >Y+#AyqA!(8HXhx]A(8HXhq014&&&#"326667#"&&&5466632 !( !'5Ui3.M84Ti5+M9!-# --#.TDuU0"9M+EuU0":MY A+6+A&6FVfv ]A]6 иии6!/ / 9 901'5&&&&5466675666672632'&&&&'6666752E*&D45. FwK:k]" !VSE A+;&8>I/>3WOJ&hCkP6 /NrS @tjY|X   B;/ |=ioiiB+8+A88]A 88)898I8Y8i8y88 ]89/A]A )9IYiy ]A&6FVfv ]A]JB9J/`kEX#/#>YO]+#5A!55'575G5W5g5w555555555]A55'575G5W5g5qAv55q016654&&&'&&'%#"&&&'&&&666732654&&&'&&&&546667&&5466632'&&#"I/!DhH  Be@/+EVXQ KNM! P .@N,HE0L].0aN1&4#-=dD+WPC E^B=A$B]81eQ3/  /2LB@' 9,B<=HU,A'GdE( 1=@= 5W>"1<(?5/AUlE"D?7"M/>bC# 9GEY_2-!3.0?VpFu //01#"&&&5466632#?W50G/%@W3-G0=dH(!/EX2/2 >YEX+/+ >Y!++ и4и4/01&'665&&#"35665#"&&&54666323c&* " 0]I- DjJ`EF*kLGtGXa0p@DFAIw    >X86mW7I"2a^Ug9I"( #I0UN5:+O#+H*+HA##]A ##)#9#I#Y#i#y## ]A**]A **)*9*I*Y*i*y** ]HWEX5/5>YEX/>YC/+A!'7GWgw]A'7GWgqAvq01#"&&&'&&&666732654&&&&&&&546666654&&&#"!56654666766321bc)^R<P -=H&BM'ARVRA'0HUH0$C`;+K7 D@/GV':T_h73LZL3CfufC@rU2 -P7).=0>gK)!Y|I # .xwL'5Ag@RhB*&0(.@79Mm'Q^ +M++8\++A]A )9IYiy ]A&6FVfv ]A]: 9MUA\\]A \\)\9\I\Y\i\y\\ ]EX3/3 >Y#++01#"&&&54666324&&&#"32666565'66327#"&''#"&'#"332654&4]KJ~\44\~JK]4J)Hd<;cH))Hc;'b^  Y   3)-YZ8+)++NBA!BB'B7BGBWBgBwBBBBBBBBB]ABB'B7BGBWBgBqAvBBq01#"&&&5466632'&&&&#"326667274&&&#"326667#"&&&&&5466632,5[SM'Ff=F|d!E>4 6 &4$)K9!+EW,+1?* QwvǑQQvwȑQw-Tv``wS-c`vT-9F& 8i^^{G  "?1$MyTEoM)%vќZZvwҜZZw`Z22Z`m1[~-' K+3<+%.+F%9%M/)/0/7/EX/ >YEX/ >YEXA/A >YEXJ/J >Yик/929F9015665#"'66667!#&&##"#5665##5665&Ȕ6633&f  c& )  '$ S!* !# ! !( $ $$ J $$ \$ #  //01&&&&'!  AJA   63CmǸ /!/ܸA]A )9IYiy ] и/A&6FVfv ]A] +и 01#"&5466632#"&5466632+9 :<+88?Y*9 :=+98>%@0><%@0<=%@0><%@0<5cm1?/0/ ++и  и"и)013#!!'7#'6666737!'66667!766667   cR   _x *0. }   c   w)++$ !    +      (IK1+и1?EX,/, >YEX%/% >YEX / >YEX/>Y+%02и>и E014&3!5665#!56676&&&'5!#&&#!!&&&&##3326667R PDC /J`>F  $B1e% K&'"  (&-QB_.B3)^ Q+_UBI! II  I NRI]ak#&& H 1P;7! ?:@/A/ܹA]A )9IYiy ]@0и0/09 A  & 6 F V f v ]A ]09%и%/0,и,/8и8/?и?/+/EX>/> >YEX5/5 >YEX?/? >YEX"/">YEX,/,>Y+>9"A!'7GWgw]A'7GWgqAvq+>95AyqA!(8HXhx]A(8HXhq014&'32666%&&#"#"&''7&&5466632766667#,m>BoQ.,qCHoK&FH,OnQU!- #32ZNC7\QK'2XA%y`< 3@!6F)$B_;?y`;5o*--E+и/-%++и01'!'66667!76667!!!'66667!       !  0  Y   c       !s !   5m "/ +01!'66667!7%&&''667m      5R        4A! #&# 5m / +01!'66667!667'66667%%m     9        ~6(A $'" +?p9+к(999,и,/93 /-/EX/>Y+( 94и70135666655!'66667!5&&&&'&&&&#'6666326&'5!!#-<$ #NPN# "4*MLD8%EDC#1MDB %$=/I My.  I 2htHn II a I. P+H6+6 и /H9,<H9HREX&/& >YEXA/A >YEX/>YEX/>YEX / >Y1A11'171G1W1g1w111111 ]A11] 19 19&9<&901%#"&&&'#"&''4&&&'56673266674&&&'666673267MNI)K}>,l20<HQF .2/ZK.6(.9# CEC$  +!a.%3F'eYJ?\y[!$'. F,N9! 6--,&   . $/3/;# A@ǸA/B/ܹ+A++]A ++)+9+I+Y+i+y++ ]иA!и!/ A  & 6 F V f v ]A ]EX&/& >YEX/>Y:0+&AyqA!(8HXhx]A(8HXhqA!'7GWgw]A'7GWgqAvq+&901&&&&#"3266657#"&&&5466632&&&&#"'76632:FJ 0E,'>O)7P4/Mdji+gt>Dw]??;@X_#'BAE)5:g37wqeL-J8Y?"3]POf;A{qoBH~fcW".g( 0#(и>/EX7/7 >YEX3/3 >YEX/>YEX/>Y7 A ]A  ( 8 H X h x ]и/'и'/(и(/=и>иDADD'D7DGDWDgDwDDDDDD ]ADD]01%#"&5467&&'&&'&'6666667#"'66663!267##3267<6YH8OK@{F  *7>;2 *" ) 2//*CEF$*H+=>>31/D8n(6!?lSGm7 &CM_x ;+N<#3)J7 K- OE 9+A  & 6 F V f v ]A ]29 92/A22]A 22)292I2Y2i2y22 ]EX/>YA+-A--'-7-G-W-g-w------ ]A--]01&&#"#"&&&5466673266654&&&&&5466676632O%480X)! .FR#AB?*SC)'7;-1/.  6I)>~31[E) 371 E:@q[>CUoS%" .3- ! K~a7˿;OgP/-##*J&6:G5H/I//ܹи/Hи/%и%/*и*//3и3/7и7/;и@A@@&@6@F@V@f@v@@ ]A@@]EX*/* >Y87+*AyqA!(8HXhx]A(8HXhq01#"&'#"&&&546667754&&&#"''666632675!5326&.X 2\#+#.I3h'2/ 9KU)$6$ߺ239 3 +"21# + ;3(  /  ":()E5 XX 8+ #J)M*/+/ܹA]A )9IYiy ]* и / A  & 6 F V f v ]A ] и/и/EX%/% >Y++%AyqA!(8HXhx]A(8HXhq014&&&#"3265!#"&&&5466632 .) "-<1 +I`48Y>"'Ea;6Y?#*N=$2G**N:#fXX3dN0'E]74eO1(G_A 6AXn|e,+]+x+"]9]4Aee&e6eFeVefevee ]Aee]tиt/Axx]A xx)x9xIxYxixyxx ]EXL/L >YEXT/T >YEX/>YEX'/'>Yu+A'7GWgw ]A]"L9u4и4/]и]/jиToAyooqA!oo(o8oHoXohoxooooooooo]Aoo(o8oHoXohoq01!326667#"&&&'#"&&&54666766754&#"''66666632666632&&5532666"!2654&&& %'D;X:>CM/ A9-  3?:-GXdb["Kn"!FGDXZ-#= ?S0#6;@85+ 3*;&  /iZ; .#BW36N28P37X@;l\I OZU$3  35UB/17( Er3b+4:, -g8iW5U; A ? @/A/ܹA]A )9IYiy ]@1и1/19 A  & 6 F V f v ]A ]19и/,/?/EX/ >YEX6/6 >YEX#/#>YEX-/->Y,?9#A!'7GWgw]A'7GWgqAvq,?96AyqA!(8HXhx]A(8HXhq014&'32666%&&#"#"&''7&&5466632766667 N)R1h6?$AZm}BC|2 (/.g6AIlBz1 (+,,^&R'4.Xt)W"'27a?bCraF'"?bcW" Ac1E *+1+ +A  & 6 F V f v ]A ] 2ܸ <иY%+A7A77]A77(787H7X7h7x777777 ]016673266654&'66667#"&&&54666667#"&&&54666324 !5A7&PE.$>FD"K~[SW-3N[P93B%4(2B&5(m9mheb`/|v.A'%  &GzZ3.StFK{k``e;'@-!3$(?,!3  +A]A )9IYiy ]/EX/ >YA]A(8HXhx ]01'667#"&&&5466632AE?3O<a2C$4(1C&5'? !!+'@-!3$(?,!3Ky+/+01%'!'66667!  I!  $!#  EX / >Y+01##'66667%!  :D? ~! NQ #&# M) 6" " =cI5+ и5:EX/ >YEX9/9 >YEX#/#>YE+#0A00'070G0W0g0w000000 ]A00]6и701&&#"3&&##"&&&546667326665#'73546667666632c(662[ 2"#  VN(AQ(@DC)SB*'6;*`&.!vND-?%"ONJ-VE*44+<6(]s@$&% EI#*SQR(/K,D3G-ZTK /+'L>EI#*SQR(/K,D3G-ZTK /+')A.$50MR`$%)A.$50NR`$%,%-5EX / >YEX/>Y 9(01!!'6766667667%&'&&&&'3!266&VP"HE=3p:IPS'\b;965332533014;[ºA"5Kc?9?EE=  A#GEX/ >YEX/ >YEX/>YEX#/#>Y015467354673A9 %&$: 9 %&$:#-;KNK:#\-#-;KNK:#\-} GEX/ >YEX / >YEX/>YEX/>Y01'7'7s::gs9:V--UIV--U0 ';(2++ +A ]A  ) 9 I Y i y ]A&6FVfv ]A]A((&(6(F(V(f(v(((((( ]A((]+и#и-и701%#"&&&5466632#"&&&5466632#"&&&546663201C&5(3C$4(1C&5(3C$4(1C&5(3C$4((@,!3$'@,!3#(@,!3$'@,!3#(@,!3$'@,!3"$ *@"$@7"2@7 SC#+H+A&6FVfv ]A]H5ܸH9EX4/4 >YEX(/( >YEX-/- >YEX/>YEX/>Y:G+( Ay qA!  ( 8 H X h x ]A  ( 8 H X h qM01%26667&&#"%!#"&&&5466632!#&&#!!&&&&##3326667%7.'/|AGoK'4ZzS  5D9<,}ɍLZ܂(:33!( K(%" #-=* (MBc.A2)i 0.GzpđS+_UB j~n  NRI]ak#&& G 1P;A1 Z;E+& + +&и/A]A )9IYiy ]A&6FVfv ]A] \EXL/L >YEXT/T >YEX:/:>YEX@/@>Y%+TAyqA!(8HXhx]A(8HXhqи@A!'7GWgw]A'7GWgqAvq:+A++'+7+G+W+g+w++++++ ]A++]01"!2654&&&4&&&#"32666%!326667#"&'#"&&&546666632676632Uh N %C">V36M2'AU.6L2G %'-"?\=9DR5 Boeb4U;Cf^sA!=Wl~Fe;%0-KGnR8#I}MG2~Pf;3]POi=5_ ExX2/&IY0XPM[H~fBsaF(QH(!1%?S[]5I +01!'66667!     '* !   58I +01!'66667!8  K   * !   MW-.///. и /A&6FVfv ]A]/*ܹ!A!!]A !!)!9!I!Y!i!y!! ]/&/01'&&546667'&&546667(7?:/ 31'BX0A%/JE(7?:/ 31'BW0B%/IE &bB7qhY20{B7FK &bB7qhY20{B7FIU-.///. и /A&6FVfv ]A]/ܹ A ]A  ) 9 I Y i y ]//01'6654&''666666'6654&''666666'BW0B$0JE(7?:/ 31'BW0A$0JE(6?;/ 307qhY20|A7FK &bB7qhY20|A7FK &bIWG +A&6FVfv ]A]/01'&&546667(7?:/ 31'BX0A$0JE &bB7qhY20{B7FIUG +A ]A  ) 9 I Y i y ]/01'6654&''666666'BW0B$0JE(7?:/ 317qhY20|A7FK &b5*+Ļ+A]A )9IYiy ]иEX/ >Y ++#+A]A(8HXhx ]01#"&5466632#"&5466632!'66667!!,.)!+W!,.)!+W  Y   D3'503&3'403& !   9EX / >YEX/>Y 9 9013'66667 ,1, 4 ,1, =GjA  m"\#"<v@  /EX/>Y01%'666675 !%$ z !%#   dBC/D/C(и(/A&6FVfv ]A]D=ܹA]A )9IYiy ]=и/( и /(0и0/=7и7/0/7// /EX,/, >YEX3/3 >YEX9/9 >Y +3AyqA!(8HXhx]A(8HXhq0132676654&'&&#"'#"&''&&'7&&5467'766763277n ! "P**O ! N**Pʼn+c45d-* *[j3b,,'   R**N "! P**Q  +,c34e-,  A V-f43c--A %EX/ >YEX/>Y01%54673q9 %&$#-;KNK:#\}E%EX/ >YEX/>Y01'7Ds9:V--U&(c1DO++ к9/A]A )9IYiy ]ܸ)и)/D8иOTиeEX/ >YEXU/U >YEX9/9 >YEXS/S >YEX/>YEXJ/J>Y_3+$+ CиPиQ01!56654&&&'56673#"&&&5466632%&&#"3&&#!5665#'73546667666632D@65VD@YEX2/2 >YEX/>YEX)/)>Y>+"/и001!5666654&'&&#"3&&#!5665#'73546667666632667+7 $! 0Z /$%  UP/M;E?wNE+?(%OMI=7.>l9+!7)Il93  <4 TsV$%# _  II # $YRmY&$3"  )I7\Y++Y и /Yи/Yи/A++]A ++)+9+I+Y+i+y++ ]+9и9/+?и?/+KиK/'/U/01&&&&'67&&&&''667'66667&&'667'66667&&&&'66667667 && !>?C'# "  && >@D' CH@3&.^Y! && !=?C&" #]X! && AzMCHA2"-]YXAHCYEX3/3>YEXG/G>YmU+=c+ A!  ' 7 G W g w ]A  ' 7 G W g qAv q=и +014&&&#"3267#"&&&54666324&#"3267#"&&&5466632'666674&&&#"3267#"&&&5466632!-29?<460WxHHtQ+.VyKJtO*rF528?<461WxHHsQ+.VyKJtO* ',*  ',( !-28><471WxHHsQ+.UyKJtP*"IlF#prF|]66]|FF|]66\}ZprF|]66]|FF|]66\}  IlF#prE|]77]|EF|]66\}"$ {@+"( @"$ @+"(@+"( @?", d@", G@",H@L", @7"2 @7"2 @K %4AN]kxci+++++A]A )9IYiy ] ииA]A )9IYiy ]$и$/A&6FVfv ]A],и,/2и:и:/A]A )9IYiy ]@и@/Acc&c6cFcVcfcvcc ]Acc]cGcMиM/Uи[и[/и/cиiиGиcи//EX/>Y+!++y+)и)//и!7и7/!и/=и=/и/DиJиJ/7Xܸ`иfиf/ylиl/rиr/Xи!и014632#"&4632#"&6632#"&546632#"&546632#"&54632#"&546632#"&546632#"&546%2##"&546%2##"&546#"&54632#"&54632#"&54632#"&54632#"&54632#"&54632  6< d<757 E 7"2 /@+!"8 @+!"8 @+!"8 e@?8 // /01%&&'3 %S((@%////EX/ >Y Ay qA!  ( 8 H X h x ]A  ( 8 H X h q01#"&&&#"'6666323267%4BQ.+NJI&$!!K3BP..SKD -B"a)aT9!'!+)bU9!'!8;O: +01!'66667!  %(# $'#  '//+01#"&&&'66667326667P\d37g[NCKM!#NLD XQsJ##JsQ .C-,D.?CRmY+A]A )9IYiy ] +01#"&5466632R+9 :<+88?%@0><%@0<0 !"/#/ܹA]A )9IYiy ]"и/A&6FVfv ]A] ++014&#"3267#"&&&5466632J) ((,(CW.'B.(BV/)A/03#.26Q5^E(.A(6^G(/B8Di +A ]A  ) 9 I Y i y ] 9//9901'6654&'766676`N%WK.-{'NU%F:*G ; "*WOeE2 / ///01&&'&&'2 15/ 42 15/  8 85#R4B&. B*4)!8*ZN+G=4%#&( ///01#667% $(}((A?"D#A"D# A"DA"D#"DAX"D#A+"D A"D#A"DA"DA?"D#A?"D# WA"DA?"D#WA"DA"D#WA"DA "D#WA"DA"DA:"DA"D#pAm"DA"D#hA"D# A"DA?"DAEAE2EF/G/(ܹ3и/Fи/(!и!/($и$/<A<<&<6<F<V<f<v<< ]A<<]EX/ >YEX!/! >YEX/>YEX / >YAAAA'A7AGAWAgAwAAAAAA ]AAA] A97Ay77qA!77(787H7X7h7x777777777]A77(787H7X7h7q01%#"&'#"&&&546666632667674%&&#"32666E,\R@(-+KFG'?z`;8RiJ444([*(   03 }_6)O>&$:H$',6s5(KZ/?&=xr;|sfL, (4 %"'!R*"$z@"$# {@~"${@?"$#| {@"$|@>"$#|@ "$|@>"$#|@ *"$|@,"$|@,"$|@I"$#|@~"$|@?"$#||@"${@R"$@"$#|@"$|@"$#|@"$#|@ "$~@?%"$|I" @IR"@ H+-+A]A )9IYiy ]9/A]A )9IYiy ]!>к?-!9!J9/EXD/D >YEX(/(>Y +DA]A(8HXhx ](A'7GWgw ]A] 3и3/?D901#"&54666324&&&#"326667#"&&&'4&&&'566667666632=*9 :=+98>#<%@04+%,Ke:}wkQ/"..2F  =+<&@v"E?"E}FMG/H/ܹA]A )9IYiy ]Gи/ 2и2/ <к=9EXB/B >YEX/>Y(7+BA]A(8HXhx ] A  ' 7 G W g w ]A ]=B9014&&&#"326667#"&&&'46667666632&&&&#"666632$=Q-#fIP}#3I/*Ids}?Vfp60I3MXa/8kT3)6744+%0Qj:}wkQ/!/ye/1'#/0 36,7.&h+<&;ti"%F@i "%F?i "%F UI+E+, +A ]A  ) 9 I Y i y ] ,9/A]A )9IYiy ]/ ,94AII&I6IFIVIfIvII ]AII]WEX$/$ >YEX9/9>YEX>/>>YEX@/@>Y+$и/9A!'7GWgw]A'7GWgqAvq/901&&&&'3266654&"32654&&&466$32#"&&&'&'#5665&&&&'A}"FA}"FA}"FA}m"FAD}ERaS/T/ܸSи/2A22&262F2V2f2v22 ]A22]и/ A ]A  ) 9 I Y i y ]L//EX/ >YEX/>YEXA/A>YL9-Ay--qA!--(-8-H-X-h-x---------]A--(-8-H-X-h-q01'6654&'766767&'&&&5466632'&&#"326667#&&&&'6`N%WK., A&)G`64>M56Anc.NU  AJB %F:*G ; "*,1!zolQ (4:4* bk(WaP}V..'@JW.BE  6>89/:/ܸ9 и /A&6FVfv ]A] A ]A  ) 9 I Y i y ]EX4/4 >YEX/>YA!'7GWgw]A'7GWgqAvq4%A%%]A%%(%8%H%X%h%x%%%%%% ]01#"&&&5467666673266654&&&#"&&&&'666632HeSe8EKF((8!-J40L`18?J. 4def7QvGkR,Nj?,KEN+!:,,Z^U}R(,%@T2@y(d.4$ +A ]A  ) 9 I Y i y ]$0EX/ >YEX)/)>YA'7GWgw ]A]AyqA!(8HXhx]A(8HXhq0173266654&&&#"&&&&&&7666632#"&&&'^5M=56`G)&>Q,P`!C CPS!wƏPEsQ/\cnA'.-V|PaW(kb *4:4( QlozA .WJ7/"& @7/"& @7/"&@7/"&@7D/LWMX/Y/ܸXи/9 A ]A  ) 9 I Y i y ]4A44&464F4V4f4v44 ]A44]QиQ/Q//EX/ >YQ9-Ay--qA!--(-8-H-X-h-x---------]A--(-8-H-X-h-q01'6654&'766767&'&&&54662'&&#"326667&&'$6`N%WK., NL_Zb6j`O A3N"IF?/CkBIWb5 @|z;NU%m83( %F:*G ; "*,1!*呠d$*1-$XO2Qvhw9 !7,A\: BE(T !4NEX/>Y0@+M++#+A!'7GWgw]A'7GWgqAvqиMи+Eи#H01!326667#"&&&'#'666673547#'666673666632'&&#"!!!9IT)8BP3 ]i2\RD A.hKA:. j!!8!]|K 8.%DV0;ut!,,!pn6$'.*!JNCzbZO(/ <=/>/ܸ= и /A&6FVfv ]A]$A$$]A $$)$9$I$Y$i$y$$ ] 3и3/EX8/8 >YEX/>YA!'7GWgw]A'7GWgqAvq8)Ay))qA!))()8)H)X)h)x)))))))))]A))()8)H)X)h)q01#"&&&546667666673266654&&&#"&&&&'666632/Zބof0CE>##"=U3:mR2DnHCSe< Cu3hYg:\q8/0. O%+0&G8"1x˚v7 2(@T1R<4 44++A]A )9IYiy ]+6EX&/& >YEX0/0>Y A  ' 7 G W g w ]A ]&AyqA!(8HXhx]A(8HXhq017666673266654&&&&&#"&&&&&&7666636#"&&&< 4cWIBkC/?FI"N2A O`j6bZe9vz|,7! 9whvQ2OX$-1*$dT:\Au"G^Au"G1A?u"G1A=NO/P/2ܹи/Oи/"и2-и>иGAGG&G6GFGVGfGvGG ]AGG],/EX/ >YEX/>YEX / >Y"+ LALL'L7LGLWLgLwLLLLLL ]ALL] L9BAyBBqA!BB(B8BHBXBhBxBBBBBBBBB]ABB(B8BHBXBhBqB9".и101%#"'#"&&&54666325!'66667!54&&&'56673#667%&&#"326u.^Q>H.MJN.Ch@Gk/]6 72n4&# #$kC2T=#(@R)7cr4'/B*AzodV%1; E %%_#0 R:@&Q|WP}W.7A{FWX/Y/ܹ8%и%/X/и//8Gи/PAPP&P6PFPVPfPvPP ]APP]EX4/4 >YEX / >YEX*/*>YB +*UAUU'U7UGUWUgUwUUUUUU ]AUU]%*U94KAyKKqA!KK(K8KHKXKhKxKKKKKKKKK]AKK(K8KHKXKhKq74K901&&&&#"667#"&&&'#"&&&5466632546667666632&&#"326{&9D   )  %.^Q>.LJN/Ch@Ii.^6&9&>BE!2ZE(n$fH2T=#(@R)8b ,435?! 'R[ #- I5'(D3/B*AzogT&Dp]O$+ !-/|9@'Q}UP}W.7AIJ/K/ܹ+иJ6и6/ A  & 6 F V f v ]A ]+>H/EX;/; >YEX&/&>YEX1/1>YA'7GWgw ]A];AyqA!(8HXhx]A(8HXhq&A!'7GWgw]A'7GWgqAvq,19>;901%267&&#"32666'&66666#"&&&5#"&&&54666324&&&'56677b<%jC2T=#(@R3=, ,870>qaNjA,KIM-Ch@Gk/]685o4&76:@&Q|WP}W.st#398(h]@/Oe6]-?)AzodV%W7A# E %%AFǸG/H/ܹGи/A&6FVfv ]A])949BиB/DиD/9/EX&/& >YEX/>Y&AyqA!(8HXhx]A(8HXhq A!  ' 7 G W g w ]A  ' 7 G W g qAv q)&9499D9901&&&&#"3266657#"&&&546666632&&'&&&&''%&&'%79FJ!ZZ$=O+7P3,J_gf+ju>8Obr@=j)D= 8W  973/8ld6Q6Ol?7s{)seAH~fBsaF(/+NDW,n), G^(*)M *c"'P@"'Q@ "'Q? "'Q !=Ÿ>/?/ܸ> и /и +%и+-и-/9A99]A 99)999I9Y9i9y999999 ]EX/ >YEX/>Y +"Ay""qA!""("8"H"X"h"x"""""""""]A""("8"H"X"h"q%и%/&и )и2A!22'272G2W2g2w222222222]A22'272G2W2g2qAv22q01#!5665#'666673'666632"3#326666654&&&3XvDDE w 1AV5&$"7*&USK8":q}ŗiCI!\  SkAa@  8Uv^̋G !=Ÿ>/?/ܸ> и /и +%и+-и-/9A99]A 99)999I9Y9i9y999999 ]EX/ >YEX/>Y +"Ay""qA!""("8"H"X"h"x"""""""""]A""("8"H"X"h"q%и%/&и )и2A!22'272G2W2g2w222222222]A22'272G2W2g2qAv22q01#!5665#'666673'666632"3#326666654&&&3XvDDE w 1AV5&$"7*&USK8":q}ŗiCI!\  SkAa@  8Uv^̋G !=Ÿ>/?/ܸ> и /и +%и+-и-/9A99]A 99)999I9Y9i9y999999 ]EX/ >YEX/>Y +"Ay""qA!""("8"H"X"h"x"""""""""]A""("8"H"X"h"q%и%/&и )и2A!22'272G2W2g2w222222222]A22'272G2W2g2qAv22q01#!5665#'666673'666632"3#326666654&&&3XvDDE w 1AV5&$"7*&USK8":q}ŗiCI!\  SkAa@  8Uv^̋G ?L3+/+#+A]A )9IYiy ]A33&363F3V3f3v33 ]A33]#AEX/ >YEX*/*>Y* A!  ' 7 G W g w ]A  ' 7 G W g qAv q01&&&&'3266654&&&466$32#!5665&&&&ME^xN#a7pK4UUr[&Ie>4Xu?DFde ,8FF# (-<ȍDuh^/LvQ* !.sP}ŗiCI!L8$E! +49A"H#5A"HA"H#"HAX"H#2A+"HA"H#A"HA?"H#A"HA"HA"H2A:"H%AP"H#%5hAP"H#%hAm"HA"HA?"HADBP++ +A ]A  ) 9 I Y i y ]L 9L/ALL]A LL)L9LILYLiLyLL ]%%9%9+HиH/%R/EX / >YEX/>YEX>/>>Y 9 CAyCCqA!CC(C8CHCXChCxCCCCCCCCC]ACC(C8CHCXChCqIܹ*001'6654&'766767&'&&&5467666632!326667#"!2654&&&6`N%WK., LDKqBggAGL&b]- "()'#@[;;EV; Ene2NU/D/M)D%F:*G ; "*,1"|jK#DqQ HyX1/)FX1 DE$D`<ME0ADB`n++ +A ]A  ) 9 I Y i y ]j 9j/Ajj]A jj)j9jIjYjijyjj ]%%9%9+fиf/%pR/\//EX / >YEX/>YEX>/>>YWH+*0+R9 aAyaaqA!aa(a8aHaXahaxaaaaaaaaa]Aaa(a8aHaXahaqg01'6654&'766767&'&&&5467666632!326667##"&&&'66667326667"!2654&&&6`N%WK., LDKqBggAGL&b]- "()'#@[;;EV; Ene2NUP\d37g[NDKM!#NKD /D/M)D%F:*G ; "*,1"|jK#DqQ HyX1/)FX1 DEQsJ##JsQ .C-,D.$D`<ME0?r? :h;/YEX/>Y"+A!'7GWgw]A'7GWgqAvq6'A'']A''('8'H'X'h'x'''''' ]01%267!"#"&&&546766667!&&&&#"&&&&'666632b[#/AKEHSX-\k:'**.EW.>IV3 =kfh9TtEY/(/K5vJ2#2YzG(;LpJ$-%@U3YEX/>Y9*Ay**qA!**(*8*H*X*h*x*********]A**(*8*H*X*h*q;A;;';7;G;W;g;w;;;;;; ]A;;]01%#"&&&546667&&5466676632'&&&&#"326667cAsh`.Vb5+AM#Ve/="?B#URFH -G5%8&x >fI(dW?IY6DV0"@^;,L=- bR(E:/"  ?H@ >3 &3DY P,:%?P/(+w"(# @ 7+z"(@+w"(# @ "(@+'"(# @2+>"(@+"(# @+"(@+?"(# @+"(@+"(@+"(2@+R"(%@+'"(#%@ 7+'"(#%@ +"(@+"(@+?"(+DLs/EX#/# >YEX/ >YEX/>YEXH/H>Y)6+9'<01'6654&'766767!56654&'5!#&&#!!&&&&##3326667!6`N%WK.,HDEAHq( K(%! #-=*,PBc.A2)I "NU%F:*G ; "*,'EI! $I NRI]ak#%$C 1P;+_UBYE+DLj\/f//EX#/# >YEX/ >YEX/>YEXH/H>YaR+)6+\9'<01'6654&'766767!56654&'5!#&&#!!&&&&##3326667!#"&&&'666673266676`N%WK.,HDEAHq( K(%! #-=*,PBc.A2)I "NUP\d37g[NDKM!#NKD %F:*G ; "*,'EI! $I NRI]ak#%$C 1P;+_UBYEDQsJ##JsQ .C-,D.:>CEX4/4 >YEX/>Y'+4(013'666666733326665#"'6666733#"'66667!b( K@O,>=7! #-=*.>.%I WDEAH!6@C=0 .VB',-&  &G:+VI9I!! $IA Bd8 +A88&868F8V8f8v88 ]A88] 8988-и-/EX/ >YEX/>Y23+329(Ay((qA!((((8(H(X(h(x(((((((((]A((((8(H(X(h(q=A=='=7=G=W=g=w====== ]A==]01%#"&&&546667&&54676632'&&&&#"326667YEXP/P >YEX / >YEX:/:>YA'7GWgw ]A]F Ay qA!  ( 8 H X h x ]A  ( 8 H X h q /A!//'/7/G/W/g/w/////////]A//'/7/G/W/g/qAv//q5:901%26667&&#"#"&&&5466666732666''#"&&&54666663266667 (2>' ^::Y<&>O  5Ytxr*Jc;#,+% Cy@)K9!,MKM,A}d=9TlM349$42,/' /4"% SLR~m6H+;oh9{pU2)"  A"eA"KA"KA"DA:"UAm"K7"* @7"* @7"*@7"*@7R"*@7"*@0"K a:0"Ka:0"Kb:0"Kb:0"Kb0?"Kb+:"+ @+:"+@+:"+@+:"+@+?:"+* )l*/+/*и/+ܹ%EX / >YEX/>YEX/>Y 01356654&'5!!56654&&&##*DFBHEFBIDE*>)'9%AII! $II"! #II!    Q #I">"7"U :"HX"#>Xp?8"@??8["L>[[6ź2+ 29 /A ]A  ) 9 I Y i y ]ܸ2!и'и2,EX+/+ >YEX/>Y+!+!-и001#"&&&54666325665#'66667354&&&'566733#->$1%-=#2&fD@ }65VD@*~YEX/>Y01356654&&&'56673?D@65VD@YEX/>Y + и0135665#'66667354&&&'566733#?D@ }65VD@*~["LMg9+EX/ >YEX/>YA'7GWgw ]A]901%#"&54&&&'56666733267DgP;\L60(TPJAWA9I*|8?" F DP, ".",H@",G@",_@z",R@/",#H@ d?L",H@?L",J@??L",H!l!Y + иEX/ >YEX/>Y + и 01#!5665#'6666734&'5!3HBHDE AH DFX. #II! $II"k?^ ",-+y+EX*/* >YEX / >YA'7GWgw ]A]01%#"&&&5466673266654&&&'5666673)AQ(BEC'SD,+99.0//$645ODA(CktoK ' 22* Oq]3>#F 0q"N o:0q"NR:0q"NS0?q"NS0qHf + 4EXYEX/>YEX/>Y /+ <95<901%#"&'!566566667666632&&&&#"%66&!7qGB5 *B,%BB#@[:INN"4lW8*788?C%G8# $-X*i"+!   IIx^,%#./ 48.9-#`W"II"g +". @+".w@+".+?".+DE/F/Eи/@иFܺ9(A((]A (()(9(I(Y(i(y(( ]и/EX / >YEX/ >YEX/>YEX;/;>Y;9+A++]A++(+8+H+X+h+x++++++ ]?;901356654&'5!6632'66665&&#"67#"&'+DEAH DFT@\0R=#2DG!  $.#&,HHB-#AII! $II"0TS5?B"O>?"O#>H0(\ +и и/%$/EX/>Y'+и'01#!566665#'6666734&&&'5666673m!6)+8   4*2VOK',IIK-2 F)d06 +и!и 'и'/.и3-/EX/>Y5+!+и5и!/и201#!566665#'6666735#'6666734&&&'5666673#3m!6)+8    4*2VOK',+DBII  *-2 F)g9ú+&и,и,/32/EX#/# >YEX&/& >YEX9/9 >YEX / >Y#A]A(8HXhx ]01##!566665&&#"'66663234&&&'56666732674BP. !6)+8 %! L4BP. 4*2VOK',,C")aT9IIQ+)bU9P-2 F)_ 8;$v,`&+&&9ии/&/EX/>Y99013566665'6666774&&&'5666677F+8   4*2VOK', !6)Ii**' [-2 F*k))& \uI+"/ @+"/+?"/+?R"/#@ .c# + и#EX/ >YEX/>Y +и !и(01!5665#'6666734&'5!!!3326667 YDE~ sAH DF($*H7q.?0'0+_UBI! $II"+a 1P; <1 + и и1&и1+EX!/! >YEX/>Y ++'и*и,и /и601!5665#'6666735#'6666734&'5!!!!!3326667 YDE~ s~ sAH DF($($*H7q.?0'0+_UBI!/vJ $II"ava 1P;=]2 + и2$EX/ >YEX/>Y'0++701!5665&&#"'6666334&'5!3267##3326667 YDE  $!!K1@M. AH DF-B"M1@N.*H7q.?0'0+_UBI!+)VG.U $II"J 9;)UF- 1P;2Y' + и'EX/ >YEX/>Y 99,01!5665'6666774&'5!%3326667 YDE oAH DF *H7q.?0'0+_UBI!P@%% 8 $II"}#" m 1P;0"P0m"Po0?"Po5|"0 w@5|"0[@5?|"0[0"Q|0"Q0"Q[0m"Qb0"Qb0?"QbK)L/M/EܹL.и./.и/<иYEX?/? >YEX/>YEX/>Y? A ]A  ( 8 H X h x ])A))')7)G)W)g)w)))))) ]A))]<:901!56654&&&#"#"&&&5466673266654&&&'5666676632I< )6AN0*BR(DHC#PE.)8:*01/&60"VXP* sN6S8 3'I7I, 6)oqM& /2+  Lw(/ F *dV&D^8  I0 I*0++*309и*AиA/EX?/? >YEXD/D >YEX./.>YEX / >YA'7GWgw ]A]D$A$$]A$$($8$H$X$h$x$$$$$$ ]A ?901%#"&&&5466673266654&&&#"!56654&&&'5666676632 ;Q0FPY/3dO20AA89595& (6AO0DABB5/"VWP* uN1Q:!ohoS&+##' 5:1/$K|)7I+ 6) II+*. G *dV!>Z8+&"1 @+&"1 Y@+&"1@+&"1@+&"1+?&"1^&9]:/;/ܸ:!и!/'и'/-EX&/& >YEX4/4 >Y+01&&'#"&&&546666673265&&'5!24&&&'5!`t ,$@LT*-YH,'%! -b.GK F#d 5(HB;%NCscV&4*""%!3(&III &+^&DE/F/Eи/> и /Fܹ9и9/EX / >YEX/ >YEX/>YEX9/9>Y6'+ 9= 90135665&&'5!24&&&'5!#"&&&546666673267&&'+I@ F#d 5(HB*"ALS*-YH,'%! -b-CC&*!4'I &&III &7aWO%4*""%!3(} &=I^: IԸJ/K/EܹJ,и,/9к:,E9?и?/EX8/8 >YEX?/? >YEX/>Y'+? A ]A  ( 8 H X h x ]:8901!56654&&&#"#"&&&5466673266654&&&'566667666632,DF!5%K^sE+CR'CHJ"-VC**888^'2'61#Y]X#*R{o6+\M1BHI!FY1:eOqnrP '! 14+0'Mv%/I*Nf<Ex^ #I* K@:++@'и:/и//@3EX?/? >YEXF/F >YEX./.>YEX / >YA'7GWgw ]A]F"A""]A""("8"H"X"h"x"""""" ]A ?901#"&&&5466673266654&&&#"!56654&&&'566667666632 2$:BF"2V>#2B@ 9!!5%K^sE*!DF63#Y]X#*R{o6+\L1KWq^*3)1< )+$AJX~RFY1:eO@II!b'1I*Nf<Ex^*^ MƺB<+ +B+и<1и1/B5EXA/A >YEXH/H >YEX0/0>Y +H&A&&]A&&(&8&H&X&h&x&&&&&& ]C0A901#"&&&546666673266654&&&#"!56654&&&'566667666632$Ec>HUa4,fX;%.+% 9<@#+J6!5%K^sEHIDF63#Y]X#*R{o6+\M1hs5,"$(%)$0$9w|FY1:eO@ #II!b'1I*Nf<Ex^1 K?A9++A3и/A)и9/и//EX?/? >YEXF/F >YEX / >YA!'7GWgw]A'7GWgqAvqF$A$$]A$$($8$H$X$h$x$$$$$$ ]A ?901#"&&&5466673266654&&&#"!56654&&&'5666676666325J-!IUc:do<4KS 8CL)0S>$$7%?Ma?HIDG63$Y\X#+Lzjb6+_O47Kuc(0#+=B482 /P;!RxkFY18[B #II!K'1I*B^<Ex^A"R#A"R#2LA"R1A"R#2/"R1AX"R#2IA+"R5A"R#24A"R1A?"R#22A"R2A"R+An"R#IUA"R#IEA"R#I;A:"R<AP"R#<LhAP"R#<hA"R#2<pAm"R2A"R#2<hA"R4A?"R2A +a,/-/,"и"/и/-ܹ  и /EX'/' >YEX/>Y+A!'7GWgw]A'7GWgqAvq'AyqA!(8HXhx]A(8HXhq01326667'&&&&#"#"&&&5466632A+YEX2/2>YEX6/6>Y;(92A!'7GWgw]A'7GWgqAvq;(9AyqA!(8HXhx]A(8HXhq014&'32666%&&#"46666632766667#"&''7&&Q3-1p>YuC2-.1p>YuD+MmRXEX/0-#LW^{WEO '28$LWu[B #'Qi[B#(Qh[yV/60w  (Y焉h40k(ZA7$ &++A]A )9IYiy ]A  & 6 F V f v ]A ]191/A11]A 11)191I1Y1i1y111111 ]ܺ4995/EX+/+ >YEX1/1 >YEX!/!>Y+AyqA!(8HXhx]A(8HXhq!A!'7GWgw]A'7GWgqAvq4!59014&&&#"32666#"&&&54666326654&'%!R0%?S."2@7"2# @7"2@7?"2# @7"2@7)"2z@7M"2#@ 7"2#@7"2#@7R"2@7'"2#@ 7'"2#@ /7"2#@7"2@7"2#@7"2@7? "27 +],/-/ܹи/,"и"/  и /EX'/' >YEX/>Y  +'AyqA!(8HXhx]A(8HXhqA!'7GWgw]A'7GWgqAvq01&&&&#"3266677#"&&&5466632 8WsDBhK+8ZuB@lQ/,OnQ~ʌKY݄ʊFYxG;qhxiL={}/[}Z2j~nm7" @77ݸ8/9/ܹA]A )9IYiy ]8&и&/ A  & 6 F V f v ]A ].и./4и4/55/EX+/+ >YEX!/!>Y+AyqA!(8HXhx]A(8HXhq!A!'7GWgw]A'7GWgqAvq4!59014&&&#"32666#"&&&54666326654&'%1Z~MHoK&5[yEBoQ.?pwQS,OnQ~ʌKY݄VYEX/>YEX///>Y ܸ#и$и=Ay==qA!==(=8=H=X=h=x=========]A==(=8=H=X=h=q013'66667333&&&&546666632326667!566666654&&&#"h' KaX*"CeeɍK4d[K 4P;' .TvH7YF4!$:T9#NWT *@+NEA{jO-JzKR)B0Y_VLvaROS1[o>'AUZY%4WQTavL0 8"SP0 8m"S60 8Bf"(++A]A )9IYiy ]" и"+(1и"8к9(9D7/EX>/> >YEX&/&>YEX/>Y>A]A(8HXhx ]A'7GWgw ]A]!99>9016&&&#"326667#"&&&'!56654&&&'566667666632W&?Q+&0?,3N?5'>*":NW],9AJ,K^BB 4*9YLE&+8`QDDxZ4\Y,,$>%,MlBxfL+ )Z IIO+1 F*81@%;r?"3 M@?"31@*?:W;/YEX/>Y 1A11]A11(181H1X1h1x111111 ]и/'A''''7'G'W'g'w'''''' ]A''] $ 913и3/01356654&'5!6632#"&''3266654&&&#"*DFBH,3B%#F#tJ&AU_a,.X")?#+TA(/UvH(+ %B4I! $II f.ZVEt\F.h BfGGkH% IA tWX/Y/%ܹ0иX;и;/A&6FVfv ]A]@и@/0HEXE/E >YEX+/+>YEX6/6>YS+6A'7GWgw ]A]E Ay qA!  ( 8 H X h x ]A  ( 8 H X h q169HE 901%26667&&&&#"&&&&#"!5665#"&&&546667666632546667666632113.6:*RA)(@R+=B $' 3&^L+HIR4:|gB,DP#AEE/W5'9%@DD.YG+)/"Gz^UZ. 162 ";,Hx[kII,?(C}oXeH  'Ju`P$+  -/ATU/V/8ܹTиUи/A&6FVfv ]A]"и"/81и1/EX'/' >YEX1/1 >YEXO/O>YEX/>YA'7GWgw ]A]' Ay qA!  ( 8 H X h x ]A  ( 8 H X h q9O;A!;;';7;G;W;g;w;;;;;;;;;]A;;';7;G;W;g;qAv;;q01%267&&&&#"#"&&&546667666632666673266654'666666#"&&&5 7c;18<0Q<"&>O-NMQ0;zc@,DP#ADD7:?%62( '  3=& #/71( >qaNjA86/"%N{UUW,-@(AzoXjJ  +$  % L-st%+ 8)h]?/Oe6FF[ +;+A&6FVfv ]A]@и/A  & 6 F V f v ]A ]@A;9HEX6/6>Y+D+6$A!$$'$7$G$W$g$w$$$$$$$$$]A$$'$7$G$W$g$qAv$$qAD901267&&&&#"'46663232666'&66666#"&&&5455#"&3 3"!00]VPpF / - /:7- 1cN1:`|A}xX"#2961kY:>tk+r)"w3B V+8V+LB+VA&6FVfv ]A]BDиD/LXEX'/' >YEX1/1 >YEXQ/Q>YEX/>YA'7GWgw ]A]' Ay qA!  ( 8 H X h x ]A  ( 8 H X h qQ'9Q=A!=='=7=G=W=g=w=========]A=='=7=G=W=g=qAv==q01%26667&&&&#"%#"&&&546666676632666673266654'4666663#"&&&5U(LIH$9DK';yd?7Wm`_vP|M#8HLIEC GMP*0+# 1  ( % ,76.DuX>fI)4D&y"8(>~~m{B)vvTN{gS;*- 0'!! -$+&e=MjA$*60kZ:$JnK0q"U0q"U0qm"U}q"U*0?q"U*0?:"U#*"5 N@"51@"52@ "52? "52?R"5#2<@L"VL"V#mL"VL"VL"V#Lm"VL?"VL?m"V#3MN/'и'/  ܸ' AP ]A0 ]A ]A  ]Ap ]A ] и/ G'GOEX / >Y1B+ A'7GWgw ]A]01%#"&&&546666673266654&&&&&546667666632&&&&#"-FU(@EE5Y@# '&! /321&"9I(CC>0[H,%$ ,-,,!YauW $%$!% #>hOOȸ:YiP "  %)#+!<_CMǸf"6 6@f"6# 6@f"6 @f"6@f"6#@f"6@f? "6f?"6#@{"Wv{"Wv"Wv?"Wv"7B@"7C@"7C?"7C"x"X5"x"XD"x"X="x"X["xn"X#[g"x:"XN"x"X#NDh"xX"X#D^p"xX"X#Dp"xY"X#D=p"x"X#DNp"x"XD"x"XF"?x"XD!xER=S/T/ܹKиSи/Nи/(иN5иK7иBEX4/4 >YEXA/A >YEX/>YEX/>YFAFF'F7FGFWFgFwFFFFFF ]AFF]MܸкF9M и!иD'и.ܸD6и'7и.=и'CиL01#67#"&&&'#"&&&55#'66677354&&&'566667!54&&&'56673266675!Hj  +3%YSA !>cQD /ZH,c  O1.2SLK*#c5.dH%~1;E)+y) K2* %A36A$ Ex^~ 05 F  .z-6 F .z .$?>N+"jR1#+;+L+;иFкG#9ALL]A LL)L9LILYLiLyLLLLLL ]O#9P/EX/>YEX/>Y6A66'676G6W6g6w666666 ]A66]69GP9OP90167#"&&&'#"&&&54&&&'5666673266674&&&'5667666654&'%j.ag  +3%YSA !>cQD /ZH,1.2SLK*#+1;E)5.dH%(4 %'&X[\)h) K2* %A36A$ Ex^05 F  .>N+ .$-6 F .u-+'4vM"j"u^"j"u"j"u["j"uF"?j"uD@9S:/;/ ܸи/:и/ и(A((&(6(F(V(f(v(( ]A((] 2A22]A 22)292I2Y2i2y22 ]EX!/! >YEX8/8 >YEX/>Y-A!--'-7-G-W-g-w---------]A--'-7-G-W-g-qAv--q01#"&&&54676&&&'&'5!3266654&&&'7!A/$ X[.HeSrm5`\ #/Ay0=#!YEX:/: >YEX/>Y5+A!'7GWgw]A'7GWgqAvq5ии!и5(01%266655!3##"&&&55#'6666734&'5!!4&'5!Gc=!KvDEz(kIlgO} nBH DE:BHs@jJf[`d30"kaLΎK9uz $II"k $I+45/6/ܸ5и/%1и1/2/EX/ >YEX*/* >YEX / >Y A!  ' 7 G W g w ]A  ' 7 G W g qAv q1 2901#"&&&54&'5!3266654&'5!6654&'%&PyTIlgOAH DF"JwVGb=AHi&'p#ORS'ڃΎK9uz $II"`d3@jJs $I 4vM+" @+" @+"@+"@+?" "YB ?"Y "@EX / >YEX/>YEX!/!>Y 90176666766667!56664'!#  %06a";'-&"I $ II   I A_ &++A]A )9IYiy ]&9A  & 6 F V f v ]A ]9&9CEX1/1 >YEX@/@ >YEX!/!>Y!19 A!  ' 7 G W g w ]A  ' 7 G W g qAv q9!19014&&&'326#"&&&5466677&&'5!6&&&'5!S!& 8'&5% C) )NrI8\C% (D *1'+-%e,8K65N9(324 YEX/>YEX/>Y 901766766667!566'! DM w,9> =?P3 6)QI $ IIF I "Z "Z "Z m"Z m"Z "Z ?"Z": @": @": n@":o@":o@?":o m"[ m"[";@";@"\="\"\#"\::"\-m"\#"\#"\%"\27;EX:/: >YEX"/">Y +":9"/A//'/7/G/W/g/w////// ]A//]01666632&&#"#"&&&5466673266677&&'5!'*0txu1F;'-3'GIl+,pwu1F<(-2'G;83 42Y  l^# 1:7 hrwZ$  196 -LYEX8/8 >YEX/>Y+AyqA!(8HXhx]A(8HXhq3+901!!566665&&&&'&&&&#"&&&&54666766&'5!y-=$$KJE /)(10+"9_|D9YB/HF1MDC $=/I WN~3(!*$F .:!DoO,&_ II  (HI/J/Iи/BB9J*ܹ:A::]A ::):9:I:Y:i:y:: ]EX/ >YEX/>Y9=и%01!566665&&&&'&&&&#'666632666632'666654&#"\-=$$[]Y# !4*KJA!4%STP#;J]<8X< :GD -'&8$=/I zQ0  I 0H/P;"$YEX / >YEX/>Yи01!'5!267&&&&#!!26667N&w8a<V$" d(*(ppc32U2 JW- 80TB;+A]A )9IYiy ]EX-/- >YEX1/1 >YEX/>Y7+A!'7GWgw]A'7GWgqAvq1'47901#"&&&5466673266654&&&"&&&&'!"'3!663*Jcqz;Uh:3EG-nN+N;#BlO'[3 -LH'*j Mk?"GzfP7-?C 02(en@cDC}_7dnF+/`"ZD+A]A )9IYiy ]EX6/6 >YEX:/: >YEX/>Y@+A!'7GWgw]A'7GWgqAvq:.=@901#"&&&54666732654&&&#"'&&&&''57!"'3!6632,Lgv?Vk=-6AJQ*'D]6)),$  |(%! W&+0  FyY2G{gQ8&59 2:4 /L5Y'+013566546666654&&&#"'&&5466632WL1IVI1$$7I*-C727B+7>8@"+, +A&6FVfv ]A]и/A ]A  ) 9 I Y i y ]4,:EX'/' >YEX/>Y'AyqA!(8HXhx]A(8HXhq0135665546666654&&&#"'&&5466632WL(G>)PRI}9O;04?..D,#3%   EqR-$?W36L;17D1 I 8B"+4+, +A ]A  ) 9 I Y i y ]A&6FVfv ]A],:EX'/' >YEX/>Y'AyqA!(8HXhx]A(8HXhq013566546666654&&&'&&5466632VH/HRH/9Q2*A. CMIHyic00IUI0KRIHgN?BN6=]=5F'- &Fh>/UvFJcH8>N; IWlW+//015666654&'&&'66667m7E( &=.dk_ #A5l<  ;  <8lQ+g+A]A )9IYiy ]-%+ +01!'666654&#"'466632326767{U)32( -324ZwB7X?" 7SqK ;l,|qK 39B- )B7#'9%4:EWnG$! 3ZQBy9!+9A!!]A !!)!9!I!Y!i!y!! ]>!999D+4$+01#"&&&'732654&&&##"'666654&#"'466632/TvGEHH@i5?O'0 ;C $+'+,+NkA@[9$5!+D.Q3YD' &J)%HB(5 @ &)*)9# %>6%/</+" !3BG+UV/W/Vи/ и /WIܹ3A33]A 33)393I3Y3i3y33 ]%и%/I+и+/I,кN I9/*/EX#/#>YEX/>YEX$/$>YEX,/,>YF6+,N015666654&'&&'66667'66667!'666654&#"'466632326767X-:"!4%OTL 6+ !%$ z !%# 7dI'/! !%$ *H`6Zh Jz[/}* ^) *  I$c[<)2"  5,== AVuU G +ILŸM/N/ܹ$и$/)иM2и2/6и6/2CJкK69 /A/EX/>YEX/>YEX/>YEX/>Y*ܹкJ 9*KиL01%'66667#!5666655!'67766735666654&'&&'6666735 !%$ z !%#  /%.  #! H-:"!4%OTL 6+ݴ   ^ %% Y}  * ^) *5 +nqeO++)Aee&e6eFeVefevee ]Aee]e?,jOe9oкpO9s /`/EX/>YEX/>YEX/>YEX/>Y:1+*ܹи`Rj 9o 9*pиq01%'66667#!5666655!'6776673#"&&&'73266654&&&##"'666654&#"'46663235 !%$ z !%#  /%.  #! Hc&D`98;:4V*.#%+  0: "+K #?W54I/ ,#8(ϴ   ^ %% Y}  O)G6 .  ,!- - !!!3  /)&0&")5, / /01#6666667\ &$ ^(393&53 , //01#6666667g &$ _(4:3'5 y //01#6666667a &$ Y&161%5  //01#6666667p &$ ^(393&5 UAW'q +A&6FVfv ]A]и #++01#"&&&5466632#"&&&54666321C&5(3C$4(1C&5(3C$4((@,!3$'@,!3(@,!3$'@,!3U'Ⱥ +A&6FVfv ]A]и EX/ >Y#+A]A(8HXhx ]01#"&&&5466632#"&&&54666321C&5(3C$4(1C&5(3C$4(9(@,!3$'@,!3'(@,!3$'@,!3AT +A ]A  ) 9 I Y i y ]EX/ >Y01'666656&''666666$=T0A"TJ&4<8- 91 6cWK2+-5!9IJ $h7/01'6676&&&'666666 AfIG<:0D**7?=2%4 2pus4?A?(H6R  =JQ3 qGY +A&6FVfv ]A]+01#"&&&5466632q0A$3&1@$2''=*1#&=+ 2jU %+ и //01'667'66667U $(-N, $(' -#%$, d<Z   5I5I5*{ +01!'66667!*  Y   [ !   5I +01!'66667!     * !   P% //01&&&&'  AJB   6 / ///01&&'&&'j2 15/ 53 15/  8 8 / /01766667  >HA > y8 P // /01%&&'3 $S((  //016"&'%&&'3 ),* " " { $/((P  ///01766663%&&'3$ * &)&  $b0((q+(%//!/#/ ܹ01#"&&&#"'6666323267%&&'3|4BQ.+NJI&$!!K3BP..SKD -B" $ )aT9!(!+)bV9!(!9;(((*5O+A]A )9IYiy ]&/./0/01&&666654&#"'5466632%&&'3H$& ( 3! ),# !!! 5E%!1!h $3"$ +"  1&' (('`//+01#"&&&'66667326667P\d37g[NDKM!#NKD XQsJ##JsQ .C-,D.'` */!+01&&&&'#"&&&'66667326667  9A9 @P\d37g[NDKM!#NKD - q 6QsJ##JsQ .C-,D.'` * /!+01766667#"&&&'66667326667y  6?9 P\d37g[NDKM!#NKD [ =8 QsJ##JsQ .C-,D.s';%//2#+ ܹ01#"&&&#"'6666323267#"&&&'66667326667z3CP.+NKI%%! L4BP..RKD!,C"&P\d37g[NDKM!#NKD )aT9!'!+)bU9!'!8;qQsJ##JsQ .C-,D.'`,JY+A]A )9IYiy ]9(/A2+01&&666654&#"'&&5466632#"&&&'66667326667#00 #B+ 28,P/ '@U.+='P\d37g[NDKM!#NKD 3+%!$' 0)%&(    9-"-QsJ##JsQ .C-,D.(Z ///01#667%D $(}((]@p////EX/ >Y Ay qA!  ( 8 H X h x ]A  ( 8 H X h q01#"&&&#"'66663232674BQ.+NJI&$!!K3BP..SKD -B"a)aT9!'!+)bU9!'!8;S z/ ///017'5666677'&&&&'S #! "$ "$ !$" ;"  ""  O3 +01!'66667! I %(# $'# ST +01!'66667!Y  %'# $'# O: +01!'66667!  %(# $'# S: +01!'66667!Y  %(# $'# 9: +01!'66667!   %(# $'# 2# +01!'66667!E %(# $'# 2^ +01!'66667!E %(# $'# LA + +01!'66667!!'66667!L   ! #% "$! #% "$! -  ++01!'76667!7!'76667! ! *  ! *k  !      CZmǸ /!/ܸA]A )9IYiy ] и/A&6FVfv ]A] +и 01#"&5466632#"&5466632+9 :<+88?Y*9 :=+99>%@0><%@0<=%@0><%@0<s?iY+A&6FVfv ]A] +01#"&5466632*9 :=+98>%@0=<%A0=sCmY+A&6FVfv ]A] +01#"&5466632*9 :=+98>%@0><%@0<c +A ]A  ) 9 I Y i y ]//901'666654&'%2ip;K*&''\_^*\33/4vM, !"/#/ܹA]A )9IYiy ]"и/A&6FVfv ]A] ++014&#"3267#"&&&5466632F( )(+(CV.'B/(BW/(B.03#.26Q5^E(.A(6^G(/B5'*_+A]A )9IYiy ]9&/ / &901&&666654&#"'&5466632#00 #B, 18, P/ '@U.+='5+%!#' 0)%%(!   9-#-CDi +A ]A  ) 9 I Y i y ] 9//9901'6654&'766676`N%WK.,{'NU%F:*G ; "*WOeED+Q +A&6FVfv ]A] /+01#"&&&54673267DOU*>5#R4B&. B*4)!8*ZN+G=4%#&-T//EX/>Y01!#!5T'''//EX/>Y01!!#''''A //01&&']%m83( (T !4 //01%766667! -50 L!? "P // /01%&&'3 zL!!D: ///01&&'%&&'3 )-)  z:/! !!DPA  ///01%766667%&&'3D */+  zS / !!Dq(%!/#/// ܹ01#"&&&#"'6666323267%&&'3|4BQ.+NJI&$!!K3BP..SKD -B" z)aT9!(!+)bV9!(!9;N!!DU*5O+A]A )9IYiy ]&/./0/01&&666654&#"'5466632%&&'3H$& ( 3! ),# !!! 5E%!1!h z"$ +"  1&'O!!D'`s */!+01&&&&'#"&&&'66667326667| &*& P\d37g[NDKM!#NKD s/ QsJ##JsQ .C-,D.'`s ( /+01%766667#"&&&'66667326667n (,) P\d37g[NDKM!#NKD i /QsJ##JsQ .C-,D.s';%//2#+ ܹ01#"&&&#"'6666323267#"&&&'66667326667z3CP.+NKI%%! L4BP..RKD!,C"&P\d37g[NDKM!#NKD )aT9!'!+)bU9!'!8;lQsJ##JsQ .C-,D.'`,JY+A]A )9IYiy ]9(/A2+01&&666654&#"'&&5466632#"&&&'66667326667#00 #B+ 28,P/ '@U.+='P\d37g[NDKM!#NKD 3+%!$' 0)%&(    9-"-QsJ##JsQ .C-,D.(P ///01#667%C (I! !]pW#//// ܸ01#"&&&#"'66663232674BQ.+NJI&$!!K3BP..SKD -B"9)aT9!'!+)bU9!'!8;So EX / >Y01!'66667!Y  %(# $'# ZEǸ /!/ܸA]A )9IYiy ] и/A&6FVfv ]A] +и 01#"&5466632#"&5466632+9 :<+88?Y*9 :=+99>%@0><%@0<=%@0><%@0<sEY+A&6FVfv ]A] +01#"&5466632*9 :=+98>%@0><%@0<  3/EX/>YEX/>Y+013'!#3dY::LR͎H_<ee#_ 2_xdy H L6A-j8_5(X5(# = | S L 7 N _ a O U((X555AUw74++7w+?++5O+77fY+Q  \4}(d5ALA|AA&#0g?Ni0g5003Ay0cA0ZL"  , =V1w74+O+7Y+AAAAAAAAAAAg?ggg03A3A3A3A3A""""7s Y *i9F#0U-!35w7jAH555 .Aa-F*Tv4*?#nAA3AAy K4,A}77rA5m5MIII_5 dA}&&73II64+4+4+?77*K7Y+Y+Y+g?6O ?082<AAAAAAAAAAAAAAAAAAAAAAAAAALALALALALALALALALALALALALALALALALALALALALALALALALALALAAAwwLLLL}AAAAA>(w7w7w7w7w7 p(w<|A|A|A|A|AsAA3AAAAAAAAAAAAAAAAAAA??A4+4+4+44+4+4+4+4+4+4+4+4+4+4+4+4+4+4+4+A:5A&+######$A$A$A$A$A$A$A777777000000w+w+w+w+w+F*gggg gg?g?gg?g?9???!K?NNi0i0i0i0i0++++$+g5gg5g00$++++  000555000000~0O+O+O+O+O+O+OO+c#*4* 13A3A3A3A3/3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3AFAAAAAAA7777777777777777777777777777777nAy0y0y0*cAbAT3000}00ZLZLZLZLZLZLZLZLCffffffff""""""""""""""!j"j"j"j"j"j" @Y+Y+Y+Y+Y+Y+Y+Y+Y+Y+Y+Y+Y+Y+h)++++++   Q Q Q        , ,   ====4=\4\4\4\4\4\4a-SZSZ-q7W83GG5DZ:XUUA7355_55Pqs]_SOSOS22-ss,5CT-Tqs]Ss) 8888v:bj   T*^8"<"DR,<V< !Z""\"#$$b$$$%(%L%n&'(|)*+@--.`/4/011245857 7889l:::;;>>>(>4>@>L>X?p?|??????????@@ @@$@0@<@H@ABpCDnDETFzGHIIJ\JKMMNdNNOPQQQRSTUUWXYZfZZ[\\~\]*^^^^(^4_T`aaBabxbccddddDeteefghhhik`klkxkkkkkkkkknnnnnnnoZo~opppq"qvqqqqqqqqr rr$r4rDrPr`rlr|rrrrrrrrrssstNtZtftrtttttttttuuu*u6uBuNu^ujuzuuuuuuuuuuvvv*v6vFvRvbvnv~vvvvvvvvvwww"w.w:wFwRxxxyyyy{R{^{j{v{|}~~~~~ڀ "&2ʇ6<~Ѝ܍ ,8HXdp|4JVbnz¦Ƨ̧اF &6BR^nz­ҭޭ*6B:F$0N^jv‚ÜĤİļ".>N^n~ƊƖƢnjT`lxȄȐȜȨ ,8ʎʚʦʲʾ*6BNZfr~ˊ˖ˢˮ˺ht̘̤̰̼̀̌ΈΔΠάθ x^j^jҜӎԄ:׶8\ـښ&vۺ>t~ܠ(݀:ފL6XzB"lJn >tJX\f Y2+ +8*<f x, "    f( 8  8% 8* 8. 8A 8F 8J 8b 8z  8  8  8  8  8 8  9  9 9  93  9< 9F 9d 9i 9n 9 9 9 9 9 9 V  * T2 $  " X "> D` . 8 "  47 69  9 9 &9  : :) 0:1 0:a *:  2:  &:  :;  ;M  ;k  ; ; ; 0; < < <<)  ?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      uni00A0uni25CCuni1EADuni1EA5 uni1EA5.VNuni1EA7 uni1EA7.VNuni1EAB uni1EAB.VNuni1EA9 uni1EA9.VNabreveuni1EB7uni1EAF uni1EAF.VNuni1EB1 uni1EB1.VNuni1EB5 uni1EB5.VNuni1EB3 uni1EB3.VNuni01CEamacronuni01DFuni0227uni01E1 aringacuteuni1EA3uni1EA1uni0251 a.SngStoryaacute.SngStoryagrave.SngStoryacircumflex.SngStoryuni1EAD.SngStoryuni1EA5.SngStoryuni1EA7.SngStoryuni1EAB.SngStoryuni1EA9.SngStoryabreve.SngStoryuni1EB7.SngStoryuni1EAF.SngStoryuni1EB1.SngStoryuni1EB5.SngStoryuni1EB3.SngStoryuni01CE.SngStoryatilde.SngStoryamacron.SngStoryadieresis.SngStoryuni01DF.SngStoryuni0227.SngStoryuni01E1.SngStoryaring.SngStoryaringacute.SngStoryuni1EA3.SngStoryuni1EA1.SngStoryaeacuteuni01E3uni1EA4 uni1EA4.VNuni1EA6 uni1EA6.VNuni1EAA uni1EAA.VNuni1EA8 uni1EA8.VNuni1EACAbreveuni1EAE uni1EAE.VNuni1EB0 uni1EB0.VNuni1EB4 uni1EB4.VNuni1EB2 uni1EB2.VNuni1EB6uni01CDAmacronuni01DEuni0226uni01E0 Aringacuteuni1EA2uni1EA0AEacuteuni01E2uni1E03uni1E07uni1E05uni0253uni1E02uni1E06uni1E04uni0181 ccircumflex cdotaccentuni1E09uni0254uni0254.TopSerif Ccircumflex Cdotaccentuni1E08Eurouni0186uni0186.TopSerifuni1E0Buni1E0Funi1E0Duni0257uni0256Dcaronuni1E0Auni1E0Euni1E0CDcroatuni0189uni018Auni1EBF uni1EBF.VNuni1EC1 uni1EC1.VNuni1EC5 uni1EC5.VNuni1EC3 uni1EC3.VNuni1EC7ebreveecaronuni1EBDemacronuni1E17uni1E15 edotaccentuni1EBBuni1EB9uni0229uni1E1Duni01DDuni0259uni025Buni1EBE uni1EBE.VNuni1EC0 uni1EC0.VNuni1EC4 uni1EC4.VNuni1EC2 uni1EC2.VNuni1EC6EbreveEcaronuni1EBCEmacronuni1E16uni1E14 Edotaccentuni1EBAuni1EB8uni0228uni1E1Cuni018Euni0190uni1E1Funi1E1Euni01F5 gcircumflexgcaronuni1E21 gdotaccent g.SngBowluni01F5.SngBowlgcircumflex.SngBowlgbreve.SngBowlgcaron.SngBowluni1E21.SngBowlgdotaccent.SngBowluni01F4 GcircumflexGcaronuni1E20 Gdotaccent hcircumflexuni021Funi1E27uni1E23uni1E96uni1E25 Hcircumflexuni021Euni1E26uni1E22uni1E24Piibreveuni01D0itildeimacronuni1E2Funi1EC9uni1ECBuni0268 i.Dotlessuni0268.Dotlessijuni0269Ibreveuni01CFItildeImacronuni1E2Euni1EC8uni1ECAuni0197IJ j.Dotlessuni0237uni1E31uni01E9uni1E35uni1E33uni0199uni1E30uni01E8uni1E34uni1E32uni0198lacuteuni1E3Buni1E37uni1E39uni019Auni2C61uni026BLacuteuni1E3Auni1E36uni1E38uni023Duni2C60uni2C62uni1E3Funi1E41uni1E43uni1E3Euni1E40uni1E42nacuteuni01F9ncaronuni1E45uni1E49uni1E47uni0272engNacuteuni01F8Ncaronuni1E44uni1E48uni1E46uni019D Eng.UCStyleuni019D.LCStyleEng.BaselineHookEngEng.Kom ohungarumlautuni1ED1 uni1ED1.VNuni1ED3 uni1ED3.VNuni1ED7 uni1ED7.VNuni1ED5 uni1ED5.VNuni1ED9obreveuni01D2uni1E4Duni022Duni1E4Fomacronuni1E53uni1E51uni022Buni022Funi0231uni1ECFuni1ECDuni0275 oslashacuteemptysetohornuni1EDBuni1EDDuni1EE1uni1EDFuni1EE3 Ohungarumlautuni1ED0 uni1ED0.VNuni1ED2 uni1ED2.VNuni1ED6 uni1ED6.VNuni1ED4 uni1ED4.VNuni1ED8Obreveuni01D1uni1E4Cuni022Cuni1E4EOmacronuni1E52uni1E50uni022Auni022Euni0230uni1ECEuni1ECCuni019F OslashacuteOhornuni1EDAuni1EDCuni1EE0uni1EDEuni1EE2uni03A9uni1E55uni1E57uni1E54uni1E56uni02A0uni024Buni01AAuni024Aracutercaronuni1E59uni1E5Funi1E5Buni1E5DRacuteRcaronuni1E58uni1E5Euni1E5Auni1E5Csacuteuni1E65 scircumflexuni1E67uni1E61uni1E63uni1E69uni0283Sacuteuni1E64 Scircumflexuni1E66uni1E60uni1E62uni1E68uni1E97uni1E6Buni1E6Funi1E6DTcaronuni1E6Auni1E6Euni1E6C uhungarumlautubreveuni01D4utildeuni1E79umacronuni1E7Buni01D8uni01DCuni01DAuni01D6uringuni1EE7uni1EE5uni0289uhornuni1EE9uni1EEBuni1EEFuni1EEDuni1EF1uni028A UhungarumlautUbreveuni01D3Utildeuni1E78Umacronuni1E7Auni01D7uni01DBuni01D9uni01D5Uringuni1EE6uni1EE4uni0244Uhornuni1EE8uni1EEAuni1EEEuni1EECuni1EF0uni1E7Duni1E7Funi028Cuni0263uni1E7Cuni1E7Euni0245wacutewgrave wcircumflex wdieresisuni1E87uni1E98uni1E89WacuteWgrave Wcircumflex Wdieresisuni1E86uni1E88uni1E8Duni1E8Buni1E8Cuni1E8Aygrave ycircumflexuni1EF9uni0233uni1E8Funi1E99uni1EF7uni1EF5uni01B4Ygrave Ycircumflexuni1EF8uni0232uni1E8Euni1EF6uni1EF4uni01B3uni01B3.RtHookzacuteuni1E91 zdotaccentuni1E95uni1E93Zacuteuni1E90 Zdotaccentuni1E94uni1E92uni01A9uni0292uni01EFuni01B7uni01EEuni0294uni02C0uni0242uni0241uniA78B uniA78B.LrguniA78C uniA78C.LrguniA789 uniA789.Wideuni02BC uni02BC.Lrguni2219uni2011uni00ADuni02D7uni02CA acutecombuni030Buni02CB gravecombuni0302uni0302_acutecomb.VNuni0302_gravecomb.VNuni0302_tildecomb.VNuni0302_hookabovecomb.VNuni0306uni0306_acutecomb.VNuni0306_gravecomb.VNuni0306_tildecomb.VNuni0306_hookabovecomb.VNuni030C tildecombuni02CDuni0331uni02C9uni0304 uni0304.Shortuni035Funi035Euni033FuniA78Auni0308 dotbelowcombuni0307uni031Buni030A hookabovecombuni0327uni0328uniF130uniF131 acutecomb.LP gravecomb.LP uni0302.LPuni0302_acutecomb.VNLPuni0302_gravecomb.VNLPuni0302_tildecomb.VNLPuni0302_hookabovecomb.VNLPuni0306_acutecomb.VNLPuni0306_gravecomb.VNLPuni0306_tildecomb.VNLPuni0306_hookabovecomb.VNLP uni030C.LP tildecomb.LP uni0304.LP uni0308.LP uni0307.LP .notAccess+++L</"+G</"+fP9"+xfP9"++ E}iD  0Zl  Z  Z  Z  Z  Z  Z  Z  Z  Z  Z y yZ C CZ] } }Z N NZ  &Z  Z{  Z  ZE E EZ b bZ  uZ  2Z X XZQ  Z^ ~ ~Z . .Z  Z! / /Z  ZA @ @Z   PZ  oZ l lZ  Z s sZ . CZG     L8T  u . |[  $ [ T *  #  _ _T; ; ug; 6 6' P iPT; ; S; l l _ _/ / Z3/* 3 y3 K cK' '   s s )s( A A  (  ;   /  '   y y y y ] }Z  &Q  ^ ~ ~   P      G                   \    ; ; ug; ; ug; ; ug;; ; ug; _ h/ / Z3/ / Z3s/ / Z3// / Z3// / Z38 A  A  A A A A v vG ~ Z s sZ & &Z Q Q  B/ $ Z3$    0Z y  y y^ ~ ~ c cZ 9 r9 /   s s y y  & y b  &  &.E E .E E EE E EE E [^ ~ g^ ~ ~  G^ ~    P   P   P; ; ug;mZ   ! o      r      Gr       y      3      q   q         G                                  Q   Z   G C RLCG C RLCG C RLG C RLG C RLCG C RLCG C RLrG C RLrG C RLLyG C RLC3G C RLCqG C RLCqG C RLG C RLG C RLLG C RLCG C RLCG C RLLG C RLCG C RLCG C RLCG C RLCG C RLCG C RLCG C RLQG C RLCZG C RLC  B  B y bA y y y A y y y yP y y y y* y y y y y y y b y y y  y y y y* y y y y y y y y y y y y y y y y y y y bi y y y yZ ~  ~   Ly  L8T  L8T > > C C C CZ C CZ  Z              ] } f] } }] } }] } } ] f * *Z  Zu . |[u . |[u . |[ ` ` . [ . |[    N N N N N NZ N NZ p pZ p pZ p pZ  Z  r    \r    !y    3      q    !      \    Z      q        &A  &  &.A  &  &P  &  &*  &  &  &  &  &  &  &  &.  &  &  &Z  &Z  &  Z  Z$ [    *  # *  #  *  # q *  #  *  #  *  #  H $H H $ H $H H $Hq H $H H $H H $H{  z{  {  {  {  {   _ _ _ _ _ _ _ _ _ _T _ _T          Z; ; ug;q; ; ug;; ; ugD; ; ug;; ; ug ; ; ug;Z; ; ug;; ; xg;; ; ug;; ; xg;;  ug= 6 6E E EE E EE E EE E EE E .E E EE E EE E EZ F FZE  Z 6 6 6 6' P i9' P iP' P iPT' P iPT P P  ^  u  uZ  uZ  Z; ; S$; ; S;; ; S;; ; S; d d d d   L L  2  2Z  2Z  2  Z  Z  Z   Z l ) l l l l X A X X X XZ _  _  _ _ _ _ _ _ _ _ M M g gQ  Q  Q  Q  Q  ZQ  Z  Z  Z  Z  Z  Z  Z/ / Z3/Z/ / Z3r/ / Z3/ / Z3sr/ / Z3/ / Z38y/ / Z3// / Z3/3/ / Z3/ / Z3// / Z3/q/ / Z3// / Z3/ / Z38/ / Z38/ / Z3// / Z3/ / Z3s/ / Z3// / Z3// / Z3// / Z3/Z/ / Z3/ $ 3$/ $ Z3 = = =  =  = F = =Z = =^ ~ ~^ ~ gA^ ~ ~^ ~ A^ ~ ~^ ~ ~P^ ~ ~^ ~ ~*^ ~ ~^ ~ ~^ ~ ~^ ~ ~^ ~ g^ ~ ~^ ~ ~^ ~ ~^ ~ g^ ~ ^ ~ ~^ ~ ~^ ~ ~^ ~ ~^ ~ ~Z s sZ s \ t tZ t ] t  t t t t t tZ* 3 y* 3 y3 3 3 .  . . 4 4Z K K K K ) )  Z' ' ' ' ' ' ' ' ' ' ' ' ! / ! / /! / /! / /Z! / /Z! / /  j  j                            Z  s s )ss s )ss s )s(s s )s(A @ @A @ @A @ @ZA @ @Z A AZ A Aq A A A J A  A A A A A   A   A A A A A A A AZ A A M M A A A  A  A J A AZ A A  +    P   P   P   P   P   P   P   P   P   P   P   P   P   PZ  Z  Z          Z  1  (D      o  oZ  Z    =           l U l  l l l l l l l lZ;  ;       /  / d /   / ) /   /   /   /  Z /   / # s \ s  s s s s s s s s s s s sZ ^ ^Z v vZ'  '  '  '  '  '   . , . C . C . C . CZ . CZ & &Z    q - -Z - -   7 7Y    Z  & l &l u &ul llW  Z pM      q         14   D##    14 % Z   '   ,Aq eqO      Z Z   q  Z b Z 69 ; 48<BHNRVZ^bhn| "8Ndjnrvz~,D`v,D\t $(4Xx0Tj"@Xp48<@D`|2Vz  ( L p $ < `  8 \     $ * . B F \ ` f l r  4 P t "&*.28Tp4X|*NRVZfjnz~:^ .Rv*Nr&Jn"FjBf>b:^z">Zv:Vr6Rh~  6Ll2Jbz8Nd2Hl  4 P t !!(!L!p!!!!""4"X"t""""###8#T#p#####$$4$P$j$$$$$%%&%J%n%%%%& &&&B&^&z&&&&&' '"':'R'j''''''(((<(`(((())8)\)))))**0*L*h******++<+\+|+++++,,(,H,h,,,,,-- -@-`-v-----...,.B.Z.r......//./F/^/v//////0 0.0R0n00001161Z1~1112222V2z2223 3.3L3p3t33333344@4d444455<5`55556686\66667747J7`7v7777778828H8^8t88888899*9B9Z9r99999:::2:J:b:z:::::;; ;8;P;h;;;;;<<0>(>L>p>>>>>???CTCjCCCCCCDD&DDDbDDDDDEE,EBEXEnEEEEEEFFF6FNFfF~FFFFFGGG0GFG\GrGGGGGGHH*H.H2H6H)y@9&&((2288DDFFHHJJRRXXin  !!$$%%&&''(())**++DDEE LLMM ]]^^__``aabbccddttuuvvwwxxyyzz{{       !"#$%&'()*+,-./012  !"#$%&'(     '.5<CJQX_gnu|#3BP`o}#/5;?CGKQW]ciosw{ !$%"#&'()*+,- . / 0 1 23456789:;<=+"+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0"! 0"! 0"! 0"! 0"! 0$! 1$! 1$! 1$! 1$! 1!* 1# ! 1# ! 1# ! 1# ! 1# ! 1# ! 1# ! 1# ! 1# ! 1# ! 10 1)11111-1.1&1'1/ 1(11111,1%111     LM    )4?JU     +1+1+1+1+1!1!1!1!1!12LLOiVC$$%%&&''(())*+,,--..//00112234578899:=AACCDDEGHHIIJJKKLLMMNOPQRRSSTTUVWWXXYYZZ[[\\]]bcddeeffgnoopwxxy45679:>?BCCDEFIJJLMNPQRSST\]]^^__``acddenop qrsst  -.5789@ATUUV]^abeftuz{                 "$&(*,.02468:<>@BDFHJLNPRTVXZ\^`bdfhjlnprtvxz|~   "$&(*,.02468:<>@BDFHJLNPRTVXZ\^`bdfhjlnprtvxz|~,' ", "1' "1 "1,' "1,'1 "'1 ",1 "1 " &!+!+ &+ &!+ &!!0+!0+0+0! &!0 &0 &0!0!0!+ &0+ &0!+0 & &!0 &+!0+ %   % % * ** * %* %* % * %  /// * /*/*/ /* % / %/ %/ / %* %/*/ %* %/ $ $ $)))) $) $) $) $...).).)..) $. $. $.. $) $.). $) $. "1!0/ %* $). #(-.=L[jy-<KZix(8HXhxIc,m4v;~A KWY  P F  @ | 9 v     ljmkpponq ttsurwwvx||{y}zV~WW''X'''&!!$$$YY----...2225557788899<==>>@AABB:::////;;;G I FF\FFF  H H 1111CCC4444???[[EEEELLLMMMMNNNNOOOOSSSTTTTUUUUWWW "!XXX#X$YY'%(&ZZ+)Z*--,[.[//\\0\112]]]55436_88`79`;;<:aa==b>bbAA@cB?DDddECGGHeeFIIfffJPPPP_Pcc^^^^QQ`QQQMMgKNLPPhOhQTTURiSRRRRRaVVVbVVedfeddfefdefegdgdegggdfgfegdfegfhhedhhdhhehhhhegdhhgdhheghhghhedfhhdfhhefhhfhhgdfhhgfhhegf !!"""####$$$%&!!(($$$))))***$$$++++,,,''X'''YY----////00001111////33334444000033336666...222555888111144446666:::////;;;99==222>>:::3333???;;;4444???99AA...BB:::0000CCC;;;1111CCCDDDDZD[[EEEEDDDDZDFF\FFFFF\FFF[[EEEEAA==555  H H BB>>888  H H CCC6666???FF\FFFJJJJJ]JJJJJ][[EEEEFF\FFFKKK^KKKKK^KK[[EEEEMMMMNNNNOOOOPPPP_PQQ`QQQPPPP_PRRRRRaQQ`QQQRRRRRaMMMMTTTTUUUUPPPP_PVVVbVVQQ`QQQVVVbVVNNNNTTTTXXX#X$RRRRRaVVVbVVLLLSSSWWW "!ZZ+)Z*OOOOUUUUXXX#X$//\\0\MMMM112]]]PPPP_Pcc^^^^QQ`QQQcc^^^^--,[.[88`79`SSS;;<:aa//\\0\TTTT==b>bb112]]]UUUU==b>bbVVVbVVcc^^^^--,[.[DDddECLLLGGHeeF//\\0\NNNNIIfffJ112]]]OOOOIIfffJRRRRRacc^^^^DDddEC88`79`WWW "!PPhOhQIIfffJXXX#X$==b>bbGGHeeF;;<:aaZZ+)Z*PPhOhQeddfefdedfefedgdegdgegdfgdgffgedfegdgdfegfefeggfhhdedhhedhhehhhhedhhgdegdhheghhdgdhhghheeghhgghhhhedhhdfedfhhefhhddfhhfhheefhhffhhhhdfhhgdgdfhhgfhhfgfhhghhefhhegegfhhgf+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70+".70&&,#,#,#,#,#)'#)*# ))# )+# #370&&,#,#,#,#,#)'#)*# ))# )+# #370&&,#,#,#,#,#)'#)*# ))# )+# #370&&,#,#,#,#,#)'#)*# ))# )+# #370&&,#,#,#,#,#)'#)*# ))# )+# #370&&, #, #, #, #)1#)4# )3# )5# #370&&, #, #, #, #)1#)4# )3# )5# #370&&, #, #, #, #)1#)4# )3# )5# #370&&, #, #, #, #)1#)4# )3# )5# #370&&, #, #, #, #)1#)4# )3# )5# #370&&,#,#,#,#),#)/# ).# )0# #370&&,#,#,#,#),#)/# ).# )0# #370&&,#,#,#,#),#)/# ).# )0# #370&&,#,#,#,#),#)/# ).# )0# #370&&,#,#,#,#),#)/# ).# )0# #370&&,#,#,#,#)"#)%# )$# )&# #370&&,#,#,#,#)"#)%# )$# )&# #370&&,#,#,#,#)"#)%# )$# )&# #370&&,#,#,#,#)"#)%# )$# )&# #370&&,#,#,#,#)"#)%# )$# )&# #370&&,#,#,#,#)#) # )# )!# #370&&,#,#,#,#)#) # )# )!# #370&&,#,#,#,#)#) # )# )!# #370&&,#,#,#,#)#) # )# )!# #370&&,#,#,#,#)#) # )# )!# #370&&,#,#,#,#,#)'#)*# ))# )+# 370&&,#,#,#,#,#)'#)*# ))# )+# 370&&,#,#,#,#,#)'#)*# ))# )+# 370&&,#,#,#,#,#)'#)*# ))# )+# 370&&,#,#,#,#,#)'#)*# ))# )+# 370&&, #, #, #, #)1#)4# )3# )5# 370&&, #, #, #, #)1#)4# )3# )5# 370&&, #, #, #, #)1#)4# )3# )5# 370&&, #, #, #, #)1#)4# )3# )5# 370&&, #, #, #, #)1#)4# )3# )5# 370&&,#,#,#,#),#)/# ).# )0# 370&&,#,#,#,#),#)/# ).# )0# 370&&,#,#,#,#),#)/# ).# )0# 370&&,#,#,#,#),#)/# ).# )0# 370&&,#,#,#,#),#)/# ).# )0# 370&&,#,#,#,#)"#)%# )$# )&# 370&&,#,#,#,#)"#)%# )$# )&# 370&&,#,#,#,#)"#)%# )$# )&# 370&&,#,#,#,#)"#)%# )$# )&# 370&&,#,#,#,#)"#)%# )$# )&# 370&&,#,#,#,#)#) # )# )!# 370&&,#,#,#,#)#) # )# )!# 370&&,#,#,#,#)#) # )# )!# 370&&,#,#,#,#)#) # )# )!# 370&&,#,#,#,#)#) # )# )!# 370 8r\@#$=>@AABBC]^abJKKL]^^__``acdde5667    "#%&(+-.012469<?CFIMQUZ[]^`cegjlosvwyz|  !"$'),/369=AEJMPTX\aeinsx~  !%).036:<?ABEIMRV[`fimqvy} &-5=DLU]fkqv|&/7@HOW`hqz '1:DNYajs} $)*17>AEHKMOV\ckrz~  '-3:@GOV[aflsy~ #,4=GPZ`gnv|    ' 0 : B K S Z [ \ ^ _ a b d e g j l o p r t u w z |          $+#+#$*#)#)+))+*+$(#'#'+$(*#')#')+''+(')')+(*))+*+$&#%#%+$&*#%)#%)+$&(#%'#%'+$&(*#%')#%')+%%+&%)%)+&*%'%'+&(%')%')+&(*''+(')')+(*))+*+$#+#$*#)#)+$(#'#'+$(*#')#')+$&#%#%+$&*#%)#%)+$&(#%'#%'+$&(*#%')#%')+%%+&%)%)+&*%'%'+&(%')%')+&(*''+(')')+(*))+*+%%+&%)%)+&*%'%'+&(%')%')+&(*''+(')')+(*))+*+"$!#+!#"$*!#)!#)+"$(!#'!#'+"$(*!#')!#')+"$&!#%!#%+"$&*!#%)!#%)+"$&(!#%'!#%'+"$&(*!#%')!#%')+!%!%+"&!%)!%)+"&*!%'!%'+"&(!%')!%')+"&(*!'!'+"(!')!')+"(*!)!)+"*!+!"$$*$($(*$&$&*$&($&(*&&*&(&(*((**"$"$*"$("$(*"$&"$&*"$&("$&(*"&"&*"&("&(*"("(*"*" $ $* $( $(* $& $&* $&( $&(* & &* &( &(* ( (* * "$ "$* "$( "$(* "$& "$&* "$&( "$&(* "& "&* "&( "&(* "( "(* "* "024677/135702467/1357/13575757675702467/1357/135702467/1357/1357357357467357357467357357467357 02467/1357/1357 02467/1357/1357 02467/1357/1357 02467/1357/135713571357 246713571357 246713571357 246713571357 24671357135724671357135724671357135724671357.02467-/1357.02467-/1357-/1357.02467-/1357-/1357.02467-/1357-/1357 .02467-/1357-/1357 .02467-/1357-/1357 .02467-/1357-/1357 .02467-/1357-/1357 02467 02467 02467 02467024670246702467 .02467 .02467 .02467 .02467.02467.02467.02467,.02467,.02467,.02467,.02467 ,.02467 ,.02467 ,.02467 ,.02467 ,.02467 ,.02467 ,.02467 ,.02467,.02467,.02467,.02467                     024677/1357/1357702467/1357/135757576757576702467/1357/135702467/1357/1357357357467357357467357357467357357467 02467/1357/1357 02467/1357/1357 02467/1357/1357 02467/1357/135713571357 246713571357 246713571357 246713571357 2467135713572467135713572467135713572467135713572467.02467-/1357-/1357.02467-/1357-/1357.02467-/1357-/1357.02467-/1357-/1357 .02467-/1357-/1357 .02467-/1357-/1357 .02467-/1357-/1357 .02467-/1357-/1357 02467 02467 02467 0246702467024670246702467 .02467 .02467 .02467 .02467.02467.02467.02467.02467,.02467,.02467,.02467,.02467 ,.02467 ,.02467 ,.02467 ,.02467 ,.02467 ,.02467 ,.02467 ,.02467,.02467,.02467,.02467,.02467                             8v   *T~&Pz"LvHrDn@Wn&=Tk #:Qh       "!#$&%'(*)+,.-/0213487658787:98787<;8787=>8787@?8787BA8787DC8787EF8787HG8787IJ8787LK8787NM8787PO8787QR8787TS8787VU8787XW8787YZ8787\[8787^]8787`_8787ab8787dc8787fe8787hg8787ij8787lk8787nm8787po8787qr87vutsvuvuxwvuvuzyvuvu{|vuvu~}vuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvuvu     ! "$#%'&(*)+-,.0/1234675:98=<;@?>CBAFEDIHGLKJNMOQPRTSUWVXZY[]\^`_abcdfegihjlkmonprqsutvxwyz{|~}67:9=<@?CBFEIHLKfeihlkonrqutxwz{~67:9=<@?CBFEIHLKfeihlkonrqutxwz{~STUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@AB   111222RRR555ZZZjjjkkllmmnnooppqqrrsst tu!uv"vw#wx$xy%yz&z{'{|(|})}~*~+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRCDEFGHIJKLMNOPQRSTUVWX Y  Z  [  \ ]^_ ` a b c defghij kl m  n !o!"p"#q#$r$%s%&t&'u'(v()w)*x*+y+,z,-{-.|./}/0~0334455566778899::;;<<==>>??@@A AB BC CD DE EFFGGHHIIJJ^_ KKa b LLdeMMghNNNOOOQRPPGHQQE@@A ASSC CD DTTFFGGUUIIJJVV::;;WW==>>XX7788YY44334455566778899::;;<<==>>??@@A AB BC CD DE EFFGGHHIIJJ@@A ASSC CD DTTFFGGUUIIJJVV::;;WW==>>XX7788YY44555ZZZ[[\\]]^^__` `a abbccddeeffgghhii[[\\]]^^__` `a abbccddeeffgghhiijjj[[\\]]^^__` `a abbccddeeffgghhiijjj[[\\]]^^__` `a abbccddeeffgghhiijjjkkllllmmnnoonnooppllqqrrsst tu!uv"vrrssw#wu!uv"vx$xnnoopplly%yz&z{'{|(|})}~*~+,-./0z&z{'{1})}~*~2,-3/04rrssw#wu!uv"vx$xnnooppllkkllmmnnooqqrrsst tu!uv"vy%yz&z{'{|(|})}~*~+,-./01234w#wx$xpp1234w#wx$xppkkmmqqt ty%y|(|+.1234w#wx$xpp56789:;<=>?@ABCDEFGHIJKLMNOPQR** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1 D= #$=>CD]^abJKKL5667     $'),.135789;<=>AEHJMOQTVWYZ^adgiknprtuvy{}    "%),/246:=@CEGJLNPQRV[_bfilpsuxz} "%(*-/127;?BFILNRUXZ]_abfilnqsuvy{}~ #*16;BGLQ\gpy "*2:@FNTZ`n| &0:BJR]fov&/<GR[fox   $(,02468>DHLPTVX^dhlptvx   $(,02468>DHLPTVX^dhlptvx    ! , 7 @ F J N R T V a l u ~    # * 1 6 B L V ` h p z  " . 8 D P Z d n v    ! & 2 < F N X ` h n }     & . 6 > D J L N T Z ` f j n r v x z | ~  &.6>DJLNTZ`fjnrvxz|~ &.6>DJLNTZ`fjnrvxz|~ &.46<BFJLNTZ^bfhnrvz|~'7GWgw/?O_o/O_o?'GWg?O_O_/?o/o7w?7GWw/?Oo/OoGW?OO/?_o/_o'7gw?__'g/?o/o7w?'7Ggw/?_o/_o'Gg?__7Gw/?o/oG?/?O_o/O_o'7Wgw?O_O_'Wg/?Oo/Oo7Ww?OOW/?_o/_o'7gw?__'g/?o/o7w?'7GWgw/?O_o/O_o'GWg?O_O_7GWw/?Oo/OoGW?OO'7Ggw/?_o/_o'Gg?__7Gw/?o/oG?'7Wgw'Wg7WwW'7gw'g7w'7Wgw'Wg7WwW'7gw'g7w'7GWgw'GWg7GWwGW'7Ggw'Gg7GwG'7Wgw'Wg7WwW'7gw'g7w#3CScs&6FVfv +;K[k{.>N^n~ +K[k.N^n;{>~#CSc&FVf ;K[{>N^~ K[N^+;k{.>n~+k.n3s6v3CSs6FVv +;Kk{.>Nn~ +Kk.NnCSFV ;K{>N~ KN+;[k{.>^n~+[k.^n#3cs&6fv;[{>^~[^#c&f#3Ccs&6Ffv#Cc&Ff3Cs6FvCF#3Scs&6Vfv#Sc&Vf3Ss6VvSV"2BRbr%5EUeu *:JZjz -=M]m} *JZj -M]m:z=}"BRb%EUe :JZz =M]} JZ M]*:jz-=m}*j-m2r5u2BRr5EUu *:Jjz -=Mm} *Jj -MmBREU :Jz =M} J M*:Zjz-=]m}*Zj-]m"2br%5eu:Zz=]}Z]"b%e"2Bbr%5Eeu"Bb%Ee2Br5EuBE"2Rbr%5Ueu"Rb%Ue2Rr5UuRU!1AQaq )9IYiy )IYi9y!AQa 9IYy IY)9iy)i1q1AQq )9Iiy )IiAQ 9Iy I)9Yiy)Yi!1aq9YyY!a!1Aaq!Aa1AqA!1Qaq!Qa1QqQ'7GWgw/?O_o/O_o?'GWg?O_O_/?o/o7w?7GWw/?Oo/OoGW?OO/?_o/_o'7gw?__'g/?o/o7w?'7Ggw/?_o/_o'Gg?__7Gw/?o/oG?/?O_o/O_o'7Wgw?O_O_'Wg/?Oo/Oo7Ww?OOW/?_o/_o'7gw?__'g/?o/o7w?'7GWgw/?O_o/O_o'GWg?O_O_7GWw/?Oo/OoGW?OO'7Ggw/?_o/_o'Gg?__7Gw/?o/oG?'7Wgw'Wg7WwW'7gw'g7w'7Wgw'Wg7WwW'7gw'g7w'7GWgw'GWg7GWwGW'7Ggw'Gg7GwG'7Wgw'Wg7WwW'7gw'g7w#3CScs&6FVfv +;K[k{.>N^n~ +K[k.N^n;{>~#CSc&FVf ;K[{>N^~ K[N^+;k{.>n~+k.n3s6v3CSs6FVv +;Kk{.>Nn~ +Kk.NnCSFV ;K{>N~ KN+;[k{.>^n~+[k.^n#3cs&6fv;[{>^~[^#c&f#3Ccs&6Ffv#Cc&Ff3Cs6FvCF#3Scs&6Vfv#Sc&Vf3Ss6VvSV"2BRbr%5EUeu *:JZjz -=M]m} *JZj -M]m:z=}"BRb%EUe :JZz =M]} JZ M]*:jz-=m}*j-m2r5u2BRr5EUu *:Jjz -=Mm} *Jj -MmBREU :Jz =M} J M*:Zjz-=]m}*Zj-]m"2br%5eu:Zz=]}Z]"b%e"2Bbr%5Eeu"Bb%Ee2Br5EuBE"2Rbr%5Ueu"Rb%Ue2Rr5UuRU!1AQaq$4DTdt )9IYiy ,<L\l| )IYi ,L\l9y<|!AQa$DTd 9IYy <L\| IY L\)9iy,<l|)i,l1q4t1AQq4DTt )9Iiy ,<Ll| )Ii ,LlAQDT 9Iy <L| I L)9Yiy,<\l|)Yi,\l!1aq$4dt9Yy<\|Y\!a$d!1Aaq$4Ddt!Aa$Dd1Aq4DtAD!1Qaq$4Tdt!Qa$Td1Qq4TtQT 0@P`p(8HXhx(HXh8x @P`8HXxHX(8hx(h0p0@Pp(8Hhx(Hh@P8HxH(8Xhx(Xh 0`p8XxX ` 0@`p @`0@p@ 0P`p P`0PpP 8v                *T~&Pz"LvHrDn@j<f8b 4^  0 Z  , V ( R | $ N x J t FpBl>h:d 6`2\.X.E\s+BYp(       "!$#&%('*),+.-0/214387657887:97887<;7887>=7887@?7887BA7887DC7887FE7887HG7887JI7887LK7887NM7887PO7887RQ7887TS7887VU7887XW7887ZY7887\[7887^]7887`_7887ba7887dc7887fe7887hg7887ji7887lk7887nm7887po7887rq78vutsuvvuxwuvvuzyuvvu|{uvvu~}uvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuvvuuv     ! "$#%'&(*)+-,.0/1324765:98=<;@?>CBAFEDIHGLKJNMOQPRTSUWVXZY[]\^`_acbdfegihjlkmonprqsutvxwy{z|~}76:9=<@?CBFEIHLKfeihlkonrqutxw{z~76:9=<@?CBFEIHLKfeihlkonrqutxw{z~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~$$$,,,<<<=5+=>6,>?7-?@8.@A9/AB:0BC;1CD<2DE=3EF>4FG?5GH@6HIA7IJB8JKC9KLD:LME;MNF<NOG=OPH>PQI?QRJ@RSKASTLBTUMCUVNDVWOEWXPFXYQGYZRHZ[SI[\TJ\]UK]^VL^_WM_`XN`aYOabZPbc[Qcd\Rde]Sef^Tfg_Ugh`VhiaWijbXjkcYkldZlme[mnf\nog]oph^pqi_qrj`rskastlbtumcuvndvwoewxpfxyqgyzrhz{si{|tj|}uk}~vl~wmxnyozp{q|r}s~tuvwxyz{|}~                     "!$#    &%(' "!*)  ,+.-0/2143 "!$#               &%(' "!* )    , + .-0/21 4 3!! "!"$"#  # # %.%-    &0&/    '2'1(4(3)) "!*$*#    + +  ,,,           &%(' "!* )    , + %.%-    &0&/    '2'1(4(3)) "!*$*#    + +  --..// 001& 1%2(!2'3*"3)4,#4+5.$5-60%6/72&7184'839(9:$):#; *; <<<--..// 001& 1%2(!2'3*"3)4,#4+5.$5-60%6/72&7184'839(9:$):#; *; <<<--..// 001& 1%2(!2'3*"3)4,#4+5.$5-60%6/72&7184'839(9:$):#; *; <<<--..// 001& 1%2(!2'3*"3)4,#4+5.$5-60%6/72&7184'839(9:$):#; *; >6,>@8.@B:0BD<2DF>4FH@6HJB8JLD:LNF<NPH>PD<2DRJ@RTLBTVNDVXPFXZRHZ\TJ\^VL^`XN`bZPbd\Rdf^Tfh`VhLD:LNF<NPH>PD<2DjbXj^VL^`XN`ldZld\Rdf^Tfnf\nLD:LNF<Nph^pD<2D@8.@B:0Brj`rH@6HJB8JtlbtTLBTVNDVvndvZRHZ\TJ\xpfx^VL^`XN`bZPbd\Rdf^Tfh`VhLD:LNF<NPH>PD<2D>6,>@8.@B:0BF>4FH@6HJB8JRJ@RTLBTVNDVXPFXZRHZ\TJ\jbXj^VL^`XN`ldZld\Rdf^Tfnf\nLD:LNF<Nph^pD<2Drj`rtlbtvndvxpfxbZPbh`VhPH>Prj`rtlbtvndvxpfxbZPbh`VhPH>P>6,>F>4FRJ@RXPFXjbXjldZlnf\nph^prj`rtlbtvndvxpfxbZPbh`VhPH>Pyqgyzrhz{si{|tj|}uk}~vl~wmxnyozp{q|r}s~tuvwxyz{|}~               ** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1** *#** ##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1**##1   # Ā̀Ԁ    vie$swift-im-2.0+dev6/BuildTools/DocBook/Fonts/Delicious/0000755000175000017500000000000012227051773022273 5ustar kismithkismithswift-im-2.0+dev6/BuildTools/DocBook/Fonts/Delicious/Delicious-Roman.ttf0000644000175000017500000005674012227051773026020 0ustar kismithkismithpFFTMFO]GDEF)UpGPOSUGSUBltU OS/22'x`cmapWƳg\gaspUhglyfB*a GheadCm6hhea4$hmtxt(locamaxp)NX name s-Ql postS|a__< .zz.K@2@ H@ PAz Av M,UZ@,=>.v*@P/S#-.6Hdc>HC/;)+H,!,,!fH+ P7#PPP 7$PPP~PP*P$7P$7P- .O   $O BpGR-*R121(R<RLRR1T2?R/2P    "M(QM(:1  ;ox/!.ovO`!-R;>H1@f*       7PPPP *P$7$7$7$7$7$7.O.O.O&O<*******11111 Q1111161PPPP R~71  <<0-||I;(p$<f` .0//!!'\F.,!,!S"C 9(k J*(, 4 |`T@~1Sx    " & 0 : D!"""""""+"H"`"e% 1Rx    & 0 9 D!"""""""+"H"`"d%b>%,߱޽޺ޮޒ{x  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`a~pdehtnjriۀqgskwycm׷lxbz}Ƭ޲u|{ov"<n^n J\:R$6JZJx ,FVv,V  8JXj  @ v , L j  0 h  < R z   R r r @h"h 4f$\>p@v*f8F~`n `$x4j&pD|B $<Xn < J X f !!L!f!!!"*"<"L"\"""""###&#F#b#x###U 6#"&54632#547'"'<F0% $Uc^nXG@ #'73#'7~! ! ????= 7#33#3##7##7#737#73733Er<UXKM<s<Q PG E<sBB̹BB> 4>54'5#5&'75. 546753&'!$r%)QA-PGK5' ! TH-H2(7M- 7$62%*B[ =:)<% HW78 '6 10. (04&"2%##"&4632326?"&4624&"2+>++>B4 NnNN7' (8 NnNNn+>++>>++>+H  7NNnN )nNNnN>++>+*W >54&#"3#"&5#"32>32&#"#".5467&546323#3272-(>@)8./)acU - @!)?!XOEi?Ejca' /.<-'% #?1O_d^;%!! 6E3PQ%U=:7@#'7~! ??/n3 .54673}r]kT>a㚅G9=sn# '654&'(r}l]Ga>s=#Z|7#'#7'5' "jFMLAb$q4>*{{*@6q-/ %##5#53533::ܭ<qW 72'6765'6 )X-0 W WaE06#5<<Hf 6#"&54632'"'F0% $lS #S?w "2>4&".4>2\;;\;\&\\&&\\ESZSEESZS?rQQrrQQc+!##52?3+J~+4$E0 >23!'>54&#"'>FS]@/L3%AJ-DEE\iH B>)H$'9:#HX'22'#527654#"5767654&#"'65E0$?W]T!&8,A=,&Y e>50)L>W{5: (^:#1"(%5Av !##5#'6733#BGDR@v>AWLCZ%'#567654&#"3#6323V]PH^@20CA[`B):%1d0.P<I/ !%4&#"32632#".546Q?8 :6)hZb>+I\$V>2J&7;&$(PFU18`Q]I=fE4TX/;P '65!5VBX~@mtE@)v 5%4.'264#">#"&5467.54632Q 3 ^?`>_)2+) & `lVHiA623bHJSC0 /$1W1KJp\>*(1"UcbH9\B3FTLJ/L!.+W !%654.#"322'>7#"'&546F6)h<0::2K(cs@+!cZ&$(OF0A]7X\.692hW*naH 6#"&54632#"&54632'"'"'"'F0% $0% $q 72'6765'6#"&54632 )X-0 M'"'"W WaE0 % % !> %'7 //d&&,`%!5%!5~<<<<!> 77'!//d&&HL %6#"&546327#54>?654&#"5632'"'=R"+)/+;D1PF0% $<3*(=n/ " 9 >;0@j<3+J&#"32>7"&547#"&54>327332654&#"3267#"&54632%?&5  H#.:05'J1G' AE=[}|J,/.Tڑ7e0QY%."" 2 O2.YU59) A{~@9DTٜᯏ7xW v 3#'##3KRCCRV#vPv $%4.+3264+326'2#"'~-1#F-BQ"rJJ3?q<']8'=Ea=`$0 Ba`:%k*Q W?_gl7%#".54>32&#"327!`-Bb68b>_C:K*C%#E/Q7);bo>:pc=+4 9WY&,TS2)Pv #32>54'2+c]0B A]26`@6 .ON-@7^jzk\6vPv #3#3!vCEEvPxv #3##xثPvBEv7%#".54>32&#"32753?eBc68b>/F.7M6N!#E05#J :bp>:pc=5"^x:,TS3Pv !###333PPPP'v Pv#Nvv]v'653QNd#6<Pv !##33PPPUv Puv3!vBvPv#7##3PFPjvW4"RvPv !##3&53RPjP/'Tv0(7"2>4.2".4>8L::L:tY11YtY11C:YRFRY::YRFRY|@gmlmg@@gmlmgPv #32654&'2#"'#80!;FWCgo]1%P6W2"2>4.O?A,*,4P+1YtY1NgL::L:CL -Ech36mg@@gm6f+:YRFRY::YRFRYPv 4&+326#'&'#32rWC8.?InNPgRJA@S v`bMr-#2&#"#"&'732654.546+S-&A*78OO8lN%S"?6/=8PO8_("&00)(?,2S8M^!94=/#;.3P4OV v###5!N00FOv %"&53253``PPnp~v#3XWvv v# #3347UppUUasPsevvB! # v !#'#373\WWRC3 v#53˸OUvq:$v !!57#50xvEFDEd#3#]]::x lM#3M>>Bd#53#5ݛ]]::G@'# #3'FF8@3y!5>K<<-1&'7 uC>4^->B?H* +%5326#"&5#"&54?54&#"'>323272 $ 5D(0 O.7#"&'3>32x- >8#2 N /K0&Y!LE TZ7;# -HB,-NM7!#1%#"&54>32&#"327N#`e&]AL4"*3*:=-*D*!e?tV2-#FT),XI,2 #%5&#"326&'&5#"&54>3253d+/ -8Ag& N.T[.U;+5L:<&v1,*.Y:aU071 4&#"!327#".54632]6-,< %9*1Z#`(3M)s]G^F.IH/9)XL>0,3T[/`pI ""3##'53547#aaLBB %A:P 1Nv(819G#;2#"&54>7.54>7.5463234&"24+326GE5  |"1CTCc!' 0:^J,y2R22RY`;7)2Z(lT #7/Q^7>% # T3J\TEETE: '&?R!#4&#"#3>32L#&WLL^&H2t%"!x DA<#'4632#"&J"!"!@'653'4632#"&QJ`"!"!:_'59R !##373PLLZgL#&5OcbDR!#4&#"#4&#"#3632>32L#I7L#@@LLG@Y[!t$#- t$#3x$34R!#4&#"#3>32L#&XLL^&H2t%""x$ DA1 "2654.2".546N9?p?\'(\~\('GY)DD)YUr>=sSSs=>rTP%4.#"32>2#"'#3>- B 3#!.`TZ 'B*\"LLJ8<$"!>@'.K?Y&IP;&M%2P %5&#"3276#5#".54>32e)4CDjJLL$Z0I(-O2IqnG-M.KT-/\Q2R:&#"#3>32:$LM  D  ' /u"%#"'732654/&54632&#"ueHNAE2(8>hWcC,S(1@#6\ZFH:65%&.4,S@B#!(1"! -,A%#"&5'53533#326?,:'1BFFca'  5;O 1WW:7P%"&'#"&5332673'X(D9L#![L#2ADt%"#a& #367?qKRa'lݷ #'./#3367*]QD CTPp_Oh=&KY=rr  !#'#7'373UwtPSjgP쪪 F'6767367zw&O"(PrK;%,:"<8," )567#5!!uAoA@B(^"(#".=4Ȏ=46;#"3" 9!336,; ;% !l67*2G/F2N46#7(B -7"Q#<(^","+'326=4>?.=4&+7323"3!9 5! ; ;3G2*76"7-.7#67*2Fr>32327#"&#" 0&$7 1%#44$D:HD::. 4632#"&653:'"'" <%!0% a`X\SL1;7&'67#5&'&5476751-.]C*",(:0K-A-<1/J2p<=yD)-t&/- << 5EytBAE&%#"&/#6='7354632&#"3#33267< ,$;'_BG! 1[G:0%Z2?=.Su<Xd &)&#"!#3#3267#"&'#73&547#73>32&7\ ^F*S>b_ nIgP mdlu?  ?p v#3##5#535'#53'33#3Ӕ)P)puUTty=Y<<X<:rs ;4&#"3262&#"#"'732654.5467&'&546-3!.73-H$` '.-#(&4 )&(!&A*5E 2KP17,)).!$9%=D " ,DH;J7 #"&54632#"&54632&#&#"%"%x6) &6324&"32'#"&54632&#"327⟠sp>JTPI<*-3-16%/z N⠠qsfLKj'"K79M"/u",%#"'732654/&54632&#"67&'ueHNAE2(8>hWcC,S(1@#6\Z:?Q* c7CWFH:65%&.4,S@B#!(1"! -,EM-GKW<!> %'7'7 //I//d&&&&.%5!5!; <v5#, &546 4&#"327#'&'#32&4&+32 pqtq5x 3V=U8(nC5(#%zN⠠qtdyc;9)IjL# =0x!50x;;!p7"264&2"&4N77N7rRRrRY7N77NdRrRRr-%!5%##5#53533s::;<<}}<;1'>7>Dt -M?C=- I>P 654&5332673"&'#"'d L#"ZL'W(X%!C-zt%"#a'2 9%'65&#"327#"&54632'34D@@EF}*#%+&2]PUgS;G83 }r G./1cvSTl+H. #"&54632'"'0% $RK'654&#"53KB%&Q $h%?!>,  /1!%#"&54>32&#"327'>7N#`e&]AL4"*3*:=-*DS>Dt -M*!e?tV2-#FT),XI,?C=- I@> 77'7'////d&&&&*6. %4632#"&3327#"&54?>5'"'"=R"+)6$;D1P$ 0% _0*(=n-!" 9 ><.Bi;3 r 3#'##37&'7KRCCRVD uC>4#vj->B?H r 3#'##37'>7KRCCRV>Dt -M#v?C=- I r 3#'##37&''67KRCCRVk 9@A8 PI>#vj.FG-7\P > 3#'##3'23267#"&#"'6KRCCRVnU$2$U  J#v, &!, +: , "3#'##3'#"&54632#"&54632KRCCRVJ&#&##v"%"% z 3#'##364&#"326"&5462KRCCRV"%$R?V=?V#v0$$0$hX>>,+? v3#3#3!5##35JZCS]0#iCEEv7R+'654&#"5.54>32&#"327xB%&Q $2I'8b>aA>G(G##E/P7#0J?!>,  (D]a3:pc=+4!=(Gd,TT3,3' Pr #3#3!%&'7 rF>7vCEEvj-7>Dt -MvCEEv?C=- IPr #3#3!%&''67< 9@E4 PI@vCEEvj.FI-7\SP, ##3#3!7#"&54632#"&54632%"%"vCEEv"%"% r &'7# sE>6N-7#>Dt -M Nr?C=- Ivr &''67# 9@A8 PI>N.FG-7\Pv, #"&54632#"&54632#[&#&#WN"%"%vP> #!##3&53%23267#"&#"'6RPjPU$2$U  J/'Tv0(, &!, +:7r%&'7"2>4.2".4>| uC>4"L::L:tY11YtY11->B?H:YRFRY::YRFRY|@gmlmg@@gmlmg7r''>7"2>4.2".4>A>Dt -ML::L:tY11YtY11r?C=- I:YRFRY::YRFRY|@gmlmg@@gmlmg7u )&''67"2>4.2".4> 7B?: NK>L::L:tY11YtY11.HG-5^P:YRFRY::YRFRY|@gmlmg@@gmlmg7>%523267#"&#"'6"2>4.2".4>U$2$U  JL::L:tY11YtY11>, &!, +::YRFRY::YRFRY|@gmlmg@@gmlmg7,+7"2>4.2".4>7#"&54632#"&546328L::L:tY11YtY11u&#&#C:YRFRY::YRFRY|@gmlmg@@gmlmg"%"%7t -32>54&#"#7.54>3273#"'k,; +< $2.:60Y:&"2)840Y;!) 6ZN* 8]O*ա-W6le? u-U6mg@ Or&'7"&53253 uC>4``PP->B?Hnp~Or'>7"&53253I>Dt -M``PPr?C=- Inp~Or &''67"&53253 9@A8 PI>``PP.FG-7\Pnp~O, %%"&53253'#"&54632#"&54632``PP&#&#np~"%"%5%#"'732654.5463654&#"#'5354632&#"eH\RHN)74KK4XE=.+@LBBlDLf'24JJ4|EF27-,'.!$9&CH.25* 1LALZKL )&(!'B* +1%5326#"&5#"&54?54&#"'>32327&'72 $ 5D(0 O.4)J!-k0(*0J=#6d?#$+/&1CB3->B?H* +3%5326#"&5#"&54?54&#"'>32327'>72 $ 5D(0 O.Dt -M)J!-k0(*0J=#6d?#$+/&1CB?C=- I* +5%5326#"&5#"&54?54&#"'>32327&''672 $ 5D(0 O.)J!-k0(*0J=#6d?#$+/&1CB3.FG-7\P*A23267#"&#"'65326#"&5#"&54?54&#"'>32327U$2$U  J $ 5D(0 O.32327#"&54632#"&546322 $ 5D(0 O.32327"264.2"&42 $ 5D(0 O.32632/ !#$E X< P 3"L0A_2VS),=?*FJ^.`#;UG`8:,:+ I0.gBB*1(<.G#K?6"P?#$*/&1CCpI1R)'654&#"5.547>32&#"327KB%&Q $HE1N*!H")4)9 :+/D#4?!>,  )TtB$/-"C,%.*XK,/ 1 &4&#"!327#".54632&'7]6-,< %9*1Z#`(3M)s]G^E uC>4F.IH/9)XL>0,3T[/`pI "I->B?H1 (4&#"!327#".54632'>7]6-,< %9*1Z#`(3M)s]G^y>Dt -MF.IH/9)XL>0,3T[/`pI "?C=- I1 *4&#"!327#".54632&''67]6-,< %9*1Z#`(3M)s]G^ 9@A8 PI>F.IH/9)XL>0,3T[/`pI "I.FG-7\P1 ,84&#"!327#".54632#"&54632#"&54632]6-,< %9*1Z#`(3M)s]G^&#&#F.IH/9)XL>0,3T[/`pI "m"%"%  #7&'7J uC>4t->B?H #'>7J\>Dt -M?C=- I  #7&''67J 9@A8 PI>t.FG-7\P #"&54632#"&54632#3&"&"BJJ"%"%nQ)!#4&#"#3>3223267#"&#"'6L#&XLL^&H2U$2$U  Jt%""x$ DAH, &!, +:1 "2654.2".546%&'7N9?p?\'(\~\(' uC>4GY)DD)YUr>=sSSs=>r->B?H1 !"2654.2".546'>7N9?p?\'(\~\('>Dt -MGY)DD)YUr>=sSSs=>rL?C=- I1 #"2654.2".546%&''67N9?p?\'(\~\('8 9@A8 PI>GY)DD)YUr>=sSSs=>r.FG-7\P1 /"2654.2".54623267#"&#"'6N9?p?\'(\~\('YU$2$U  JGY)DD)YUr>=sSSs=>r, &!, +:1 %1"2654.2".5467#"&54632#"&54632N9?p?\'(\~\('&#&#GY)DD)YUr>=sSSs=>r"%"%6. #"&54632#"&546327!54'"'"'"'0% $0% $u<<1F",#7.54>3273#"'32>54&#"2#33.M0245(\?yh#2 h #2 o|#E-YN1Sh#F=sS(A>!rj(B? pP%"&'#"&5332673&'7'X(D9L#![LD uC>4#2ADt%"#a&9->B?HP%"&'#"&5332673'>7'X(D9L#![L>Dt -M#2ADt%"#a&?C=- IP!%"&'#"&5332673&''67'X(D9L#![L 9@A8 PI>#2ADt%"#a&9.FG-7\PP#/%"&'#"&5332673#"&54632#"&54632'X(D9L#![L&#&##2ADt%"#a&]"%"% F('6767367'#"&54632#"&54632zw&O"(PrK;&#&#%,:"<8,"%"%R#J7Vv "3#3#3!".54>3)9 9(D8U-.T766XK('IS37CEE?dh42ga=1<"34&&#"327&54!327#"&'#".54>32632+= 6N#2 2#N | 3"K1A_*J7X?\(.M0`1A(f1?&BB*1(+#NSs=-YN1TTpI" , #53'#"&54632#"&54632˸OU&#&#vq:"%"% q)&#"3#'>7'73>7632q("  p n &8$)   < 8j/$`  :Or+2+:.J*SU29y<0n &''67n 9@A8 PI>^.FG-7\P<p 67&'\:?Q* c7CWEM-GKW<0#d "&'73266.UTU00$:73YZ3572V-> #"&54632&$v!#)i "264.2"&4%#0$!DV=?V=#$$0$.?V??V|S.547>3#"%C# 3;?! ! 0Ru23267#"&#"'6yU$2$U  J, &!, +:;1'>7'>7>Dt -M>t -:?C=- I?=- 7 (s^ "&462%s[ܮ$kE4&#"#327#".54632)"!., !II3DWG6Hn#77#, C:/$!EX2IsU8< <Dꮬf!5f<<`!5`<<. "&/67n )X-0  WaE00 2'6765'6 )X-0  WaE0tZ 72'6765'6 )X-0 Z WaE0/ "&/67#"&/67 )X-0  )X-0  WaE0 WaE0/ 2'6765'632'6765'6 )X-0  )X-0  WaE0 WaE0t|Z 72'6765'632'6765'6 )X-0  )X-0 Z WaE0 WaE0!}v#&=#5353} %&KBמBJB!}u#&5#535#53533#} %&K ABJABB'2"&4ffeeeeFf  6#"&54632#"&54632#"&54632'"''"''"'F0% $ 0% $ 0% $.'/7?4&"2%#'"&4632327"&4624&"2$"&4624&"2+>++>B'A NnNN7  ,>1NnNNn+>++>NnNNn+>++>>++>+H 7NNnN  )nNNnN>++>+nNNnN>++>+!> %'7 //d&&!> 77'!//d&&} #R?lC)###53!#7##36a1a2r,r3Cuta,s6E!O # J9o!o8(u73#''5#7'7'35$=K&@?&L<&%=#I=<'L=%%=H$A@&K?%$54#"'>#5#52763.?.7lw .f0+. ,=M!f..Q^83p+&%1s-*3##52?3J~*5$E0 (s^2"&4^,13#3#'7#537#53L%*c9<,j8A;Z;_G;Z; qW 72'6765'6y )X-0 W WaE04f 7#"&54632&"&".$ $ r'7="!#37#".#"3##'5354632JJ# +aaLBBpV4=)L7(:P 17RO !#&5&#"3##'53546253O#?+ aaLBBb#JC63#!:P 17JT&`!D z'O B f   4   /copyright 1994-1996, Jos Buivengacopyright 1994-1996, Jos BuivengaDeliciousDeliciousRomanRomanDelicious-Roman:1140922925Delicious-Roman:1140922925Delicious-RomanDelicious-Roman001.000001.000Delicious-RomanDelicious-Romant  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghjikmlnoqprsutvwxzy{}|~nbspace  latn ,latnkernz.fl`8~XfrxfBBBBt- &24 &*24 79:<YZ\$DFHR &*2479:<\ 7oo$DFHR79<FHR6$&*247DFHLRUVXZ\z{|}~'$DFHLRUX\z{|}~%$DFHRUXz{|}~9$&*24DFHLPQRSTVWXY]z{|}~IFHR79:<7/$&*24DFHPQRSWXz{|}~ &*24&*24VW &*24$. $&)*./234579:<>IUYZ\^z{|}~ &]&]swift-im-2.0+dev6/BuildTools/DocBook/Fonts/Delicious/Delicious-Bold.ttf0000644000175000017500000005665012227051773025624 0ustar kismithkismithpFFTMFO]GDEF)U8GPOSUxGSUBltUX OS/23'x`cmapWƳg\gaspU0glyf/X GheadC6hhea4$hmtxx"loca|6maxp)RX name%,߱޽޺ޮޒ{x  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`a~pdehtnjriۀqgskwycm׷lxbz}Ƭ޲u|{ov :lVf:Lr(@*<tHj B`"Xj&4DXfx  P  4 P x  N  . D l  6 ` ` .RRl|L.PvBfPh J&z0hPX H ^tH~Hv"^"p,Bb~  & 4 L d | !!6!H!x!!!" ":"H"V"~"""##.#F#\#j##F7"&462#&547/<'/<'Z <-$<,$cjaM|25! #'73#'73!<Ir<JLLL0#3##7##7#737#7373373#3SB F[K[SR D CZKZeKK`n`ɰ`n``n+ ,654'#5.'75'.546753&'4-..UD-%Q24,C+1YF-&M<.:#O*,X2F[78*;" )I,&.$"&4624&"2##"&46323274&"2StSSt!,!!,b &CRvRR;(KR . .tSStS,!!,!*::SStS  ,!!,!(Y@54&#"3#"&5#"32>32&#".5467.546323#327!*RT&31(O^*7NRL#kBKk^\ #-1A9' E5)0}F Zn6R 1?JOH3V1#'73=IL%g> .5467> z Yg8agKZ9lg- '654&'7- fY zKfal9ZKR#'#7'5'37sil>?kit%ff%]8ll8&) %##5#53533XXͤZ ea 7'654'6325Y?C-'hZ+a?$*,%#53Z;} 7#"&54632.'.'<,%,$s`#`]{ %#"&54624&"32)^Bahffe6\67-dCrNgrpK]\LEab>!##52?3>ht9A UL6)'>54&#"'>323E&"J^+JXdGN'>T"JIK.@Y)%+5>54#"5767654&#"'>327^sA6dMu,551B"G$.E =HN,8MbEj=P P9JP5OB3.VQv !##5#'6733#5;dGf9[*Qrl<[%'#52>54&#"!#632^Q7fL0#2V 0E[cS|:O!Q8#%fZP) %#".54%6324&#"32a^3M($(P[ 4(CZg3-.&Rb3UZ0G2SDiB-."36$:P '67#5!~VYQuLr-s!{^#v -4#"64.'3267#"&5467&54632'I#"$ J&L2'&1iqXLl54XcLOY0-9=L2%'+" .B(??+WhfL9N 5TKXQL0AH!Y %'67#"&546324.#"327682Ja`]8N(h%R1%+&݂6NRxbJb3Y[49$&5 ; 7#"&54632#"&54632.'.'.'.'<,%,$,%,% e 7'654'632#"&546325Y>C- .'.''hZ+d< $,%,%2%'7GGk99*k!5!!5!ttZZ2%'7'7GG99?V $7#"&54632#546?654#"562.'.'H Z+5'00;pD<,%,$0c-476H48F+1TI-3>#"&'#"&54632733267654&#"327#"&54632&#"327_ 4J.)c`%/TE.Fso}h5zؐ0 1Pa(%\4e*b3qyp}j0ؚߧiIT#0- v !#'##3'p<32&#"327 a-Bb7:b=f=*-L!7  ;)D4+:co>9qc=-K3LP"&IK.&Iv +324+328a@A_4nMH:=l\5v6^j@FHv )!#3#3T籱vabGv #3##!͢m:bv1&#"32753#".54>32+9L/C ;)&gFfBd8:c>-cPM&Zi/&JJ.:aq>:qc<Jv !###3353mmmmvAv3#3llv\v'653Iki-L /Jv !#'#373dmmuvGuv)33uivOv!#7##3673mZm [I\IvU8Jv!##33imm)*v-2!".54>324."2>3ZtZ33Z9:Z3T 0@0 0@0vvod<3234."2> -07B[$3Z9:Z3MM%A 0@0 0@0k#7 mJ:qb=–Z!&'70~MT<eDCKVR&)%#"&'#"&54?54#"'>32327'5326"%!0/MBC:G$_.GT -9&!HIC):]11#?%2GE:!YM#"'3>324.#"32>.U9ZVi:W_k$%5)%)5aW4Af-12*B:*%#".54>32&#"327X'?^*)_B(Q5'3 0 *4?4&Pq>@uT >">I 89&2,&%'&'#"&54>3253'5&#"6 ( <*Qd*P7.i t4$\".673 #[[i@@ (( v $3Xn K84  ":6@N#"26;2#"&5467.5467.5463234&"3264+3269B=    5IWGg7#$))+aP0"}!H F$'L: K/G.>T  ;4Sc>A"6+* S.P`'93&b5& A6K!#4&#"#3>32iEiiMM7j!MB23#37#"&54632gg+(+(~)%)%> '>537#"&54632.!*g+(+(*/ K ,~)%)%L !#'#373eii=3#&53lgJYL#!#4&#"#4#"#3>32>32i*>i+HiiOF-  n-`>&##: K!#4&#"#3>32i8BiiMM7j4$MB, %".54>24&"26*^^**^^*i4V44V4AsPPsA@sQQs@EeeEFddMQ#"'#36324#"32>'N6Kjj/=W_lT.4 /'$ 2[X52$3f2z&B;(@3+Q$#5#".54>325.3276j:AX#(_C'aj4&$8Ki>Az[ &6418# )I:&#"#3632:   ij, c $3,$%#"&'732654/.54632.#"jK#R'@0 +2fH+W77+/z!%HO#F."G8 EK$ 6>BB%#"&5'53533#326?4 31?CcYW E6( KXXY#J%.'#"&533273 0 R!H?i3Ci +:&$EHl2jq #367FnMqkD##'./#3367 qd2  3hoZOdV:CW0M  !#'#7'373xXUsvJKs퓓 A'6767367-DW*4, oX='Hѥ O9C"" )567#5!3lgDPS^U^0#+"&=4Ȏ=46;#"30""0?. .=0FL -* NT9R,:E9,R:T++F ?+*B#3ZZ^,#%"+'326=467.=4&+7323, .?0#!G ), LF0=. :,R9T*+? F++T:R,9#"&#"5>32326? /. 1)- A e @ . 462"&653./<'/<'Z -$<-%gc8M|2*;##5&'&5476753&'>?!/,$8G-I3=25R-": 5%.  UD>a 3 ><=GtqCK CD ?   *%/#6='7354632&#"3#3>?&4'C&gBI-*+F1,)/ T_ H{EA&I>mZ_KU $$.#"!#3#327#"&'#73=#73>32!;,! ((LPD]dh&6X(<q`H"UU^vAoTVjEv3#3##5#53'#53'3g`l\fvklv[8[[8[ vz<H#"'732654&'.5467&54632&#"4&#"326z=-%$bQRe&NG#''6-&=1jbLMR)I/ $1, `%%.)1/Q.KY=N1!"0I/GER.N$"   %"!$%<K #"&54632#"&54632K-* -*!*- *- v9& &6 4&"2&#"327#"&54632 oʏ(+#&%) &16MPSG@| Xʏʏv5B%-E9+iPIe"^^'#"&'#"54?54#"'>32327'5326^$$:e-00H#6@ kk",o7k *G&%.0&55r +C * %'7'7GGGGc9999(%5!5!YZv9 &%#'#32'4+326$ &6 4&"2AC^NW=L&!V^:   oʏHddb9;%@ Y.a ] Xʏʏ.9!5!9$.Yg@"&4624&"2@WzWWz *<**<8zWWzW<**<*& %##5#53533!5!XX wnnTS*!%'>?%Zq0/TS;D"PK#%#"&'#"'654&=332>737&&8 E1- i)-i 2 ,')G@P}~iq $'67.#7#"'&767632531E@\DE 1(>3"1.V`1+R%.1=VR &}Noa (WN\`$'&;; 7#"&54632.'.',%,%=_'>54&73_G-4 'x)K&?1 E.]_"&4624&#"32_QPP(! (( !oxxyjMMjL0* %'7'7'7'7GGGG999963"462"&4>?6=3327#"&/<'/<'IZ+5&1/B18C-$<,$&0c+676I38F(1TH  !#'##3'&'7p<?p<323267p<54&5.54>32&#"326?G-4 '.E'3tP.Q*,M> ;)<2,GK&?1 @CZa4Oi K/'?\&IK. J%H )!#3#3&'7T籱40|OT>vab}DAMVUH )!#3#3'>?T籱4L00SvabLBD"PH )!#3#3&''67T籱 06C6B1]MXvab}D?:#D@c VH3 #)!#3#3#"&54632#"&54632T籱-* -* vab*, *,  &'7#31|NT;$llDBLVRv '>?#3Yr0/T ll*R323267imm ,2&Q-&-9P")*v-)* C#* 2'&'7".54>324."2>0|OT;3ZtZ33Z9:Z3T 0@0 0@0DAMVRHvod<?".54>324."2>Zq0/T3ZtZ33Z9:Z3T 0@0 0@0*S;D"Pvod<324."2>05D6C0_JXe3ZtZ33Z9:Z3T 0@0 0@0D@:#DBa Vtvod<323267".54>324."2>,1'P-'-9Q"N3ZtZ33Z9:Z3T 0@0 0@0)* C#* 7vod<324."2>#"&54632#"&546323ZtZ33Z9:Z3T 0@0 0@0+-* -* vvod<3273#"'`wsvs2.:72Z; !!2(;92[;#!b |,W6le> s,V7nf@ I&'7"&532531{PT=ddmmDAMVSqqI'>?"&53253L~10Sddmm*LBD"PwqqI &''67"&5325306C6C0_JXUddmmD?:#DBa VqqI3 %%"&53253'#"&54632#"&54632ddmm:-+!-+ qq*, *,  ,<%#"'732654/&54632654#"#'5354632&#",eOTJ*6B!*-?H9i!3i??oHXt !!7z!MV0O%#F!D9NR)! M@FQ]V#* 0   B8&)/%#"&'#"&54?54#"'>32327'5326&'7"%!0/MBC:G$_.GT -9N0~MT<&!HIC):]11#?%2GE:!YDCKVR&)1%#"&'#"&54?54#"'>32327'5326'>?"%!0/MBC:G$_.GT -9SL00S&!HIC):]11#?%2GE:!YLBD"P&)5%#"&'#"&54?54#"'>32327'5326&''67"%!0/MBC:G$_.GT -9v09@8@1[OX&!HIC):]11#?%2GE:!YD=:"D>e V$)>%#"&'#"&54?54#"'>32327'5326#"&#"'>323267"%!0/MBC:G$_.GT -9,1'P-&-9P"&!HIC):]11#?%2GE:!Y)* C#* &)5A%#"&'#"&54?54#"'>32327'5326#"&54632#"&54632"%!0/MBC:G$_.GT -9b-+ -+ &!HIC):]11#?%2GE:!Y*- *- &)1;%#"&'#"&54?54#"'>32327'5326"&4624&#"32"%!0/MBC:G$_.GT -9@E`CF^ &!HIC):]11#?%2GE:!Y#`EE`E  2)/=%!327#"&'#"&54?5.#"'>32632'."&'32> *C:$LZ2T?,/?HJ+!c1Y&8MJem'>- l   78$#L11+6&P@ <]00G)955tK!11!#-9$   *=)%'>54&5.54>32&#"3267+6$G-4 '-?'_D%2#5,.%=9,C3$*&?1 CU\/AtT@"]JHi!+%%!327#".54632'.#"&'7.! E:6D-WuDctH ,L&3YDAMVU+'%!327#".54632'.#"'>?.!{DBLVU 3#37'>?hhKL00SLBD"P"3#37&''67hhw09@8A0[OX{D=:"D>e V  #"&54632#"&54632#3 -+ -+ Hhh*- *- hE'!#4&#"#3>32#"&#"'>323267i8BiiMM7 ,2'Q-&,:Q"j4$MB)* C#* , %".54>24&"26&'7*^^**^^*i4V44V4#1~MT=AsPPsA@sQQs@EeeEFddDCKVS, %".54>24&"26'>?*^^**^^*i4V44V4"Zq0/TAsPPsA@sQQs@EeeEFddS;D"P, #%".54>24&"26&''67*^^**^^*i4V44V4N09@8A0[OXAsPPsA@sQQs@EeeEFddD=:"D>e V, ,%".54>24&"26#"&#"'>323267*^^**^^*i4V44V4g,2'P-&,:Q$AsPPsA@sQQs@EeeEFdd)* C#* , #/%".54>24&"26#"&54632#"&54632*^^**^^*i4V44V45-+!-+!AsPPsA@sQQs@EeeEFdd*- *- /' #"&54632#"&546327!5!8.'.'.'.'v,%,%,$,%?Z,G *32>54&#"#7.54>3273#"'3V (V (2"66)^A266i_|#93Z.#94Yx!F?sSTh!GdJ%.'#"&533273&'7 0 R!H?i3Ci J0LT;+:&$EHl2jq5DBLVRJ %.'#"&533273'>? 0 R!H?i3Ci NL0/T+:&$EHl2jqLBD"PJ$%.'#"&533273&''67 0 R!H?i3Ci $1:>=<0ZOX+:&$EHl2jq5D<=D>e VJ$0%.'#"&533273#"&54632#"&54632 0 R!H?i3Ci 9-* -*!+:&$EHl2jqR*- *-  A!-'676736?#"&54632#"&54632-DW*4, oX='0-*!-+!Hѥ O9C"Ș*- *- C3#3hh2Vv)".54>3!#3#3V9W/0V8D籱\>ci54g`32632'."&#"327&541#D9$O^L97Q?_+*^AW4+ n=)(; #J@#L1AAQr=?uTDDtK!10"S$:5637"Q/+1 3 #537#"&54632#"&54632ʨmuio'-* -* vu*, *, x &#"3#'>7'73>7>32x!6  nhH38"4:5J;1.B !Xm>94se I 0 ;F-  &''6709@8A0^KXeD=:"D@c V- &'7670TK^0<=D5"Qc@D=?$p"./727p %&4.4(! I+f+4'! ,-Q0 #"&54632.!,#v)"+$t#"&4624&#"32tE0/DF^ `EE`E  k6.546;#"'*B,*!"3=*$-@ D|#"&#"'>323267|,2&Q-&-9Q")* C#* /!'>?'>?)~0.@ ZYr0/T)BD!@6RC,hZ+_A $ '654'6325Y>C!,Kf]+a@ $ ea 7'654'6325Y?C-'hZ+a?$  67#"'67#"&9V>C'5Y>C,lV+_A :hZ+_A $ '654'632'654'6325Y>C!,5Y?D.Kf]+a@ $f]+c> $ hd 7'654'632'654'6325Y?C.5Y>C,*hZ+_A$hZ+_A $v#&=#5353y44~~h`]e`v./535#53533#y~~~~hy`FW14VD`o``o"&462kjjjjj1} 7"&4632"&4632"&4632.<'.'.<'.'.<'.'<-$<,$-$<,$-$<,$,6>$"&4624&"2&"&4624&"2"&4632327##4&"2RvRRv . .StSSt!,!!,RvRR;(KRKb &CV . .vRRvR. . rvRRvR. . :SStS  :&,!!,!2%'7GGk992%'7'7GG99 #Xa`G"###53#57##3673;YCXD[9\EQ^ ^QpN?t. .#  # J9o!o8(u73#''5#7'7'35$=K&@?&L<&%=#I=<'L=%%=H$A@&K?%$4.2".4>8L::L:tY11YtY11C:YRFRY::YRFRY|@gmlmg@@gmlmg< ##5#52726;#'67654#"'>323#[I"!Cv>)'$:$/'2>MW3cmLIJ 410S52EQ &!##52?3ht9A UL(s^2"&4^%&%#'7#537#5373#3=8"X!8C#Q!^'7Y4YX&2Y4ea 7'654'6325Y>C-'hZ+a? $(} 7#"&54632.'.'<,%,$r'7=$!#37#".'&#"3##'5354>32gg+ ! 1\\j>>%>D&7>(~&# ;:Xn M-/E$%!#&5&#"3##'535463253l31&\\i??bK/ gJz$84Xn M$UX )Y!D zD B f  2   &copyright 1994-1996, Jos Buivengacopyright 1994-1996, Jos BuivengaDeliciousDeliciousBoldBoldDelicious-Bold:1140922920Delicious-Bold:1140922920Delicious-BoldDelicious-Bold001.000001.000Delicious-BoldDelicious-Boldt  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghjikmlnoqprsutvwxzy{}|~nbspace  latn ,latnkernz.fl`8~XfrxfBBBBt- &24 &*24 79:<YZ\$DFHR &*2479:<\ 7oo$DFHR79<FHR6$&*247DFHLRUVXZ\z{|}~'$DFHLRUX\z{|}~%$DFHRUXz{|}~9$&*24DFHLPQRSTVWXY]z{|}~IFHR79:<7/$&*24DFHPQRSWXz{|}~ &*24&*24VW &*24$. $&)*./234579:<>IUYZ\^z{|}~ &X&Xswift-im-2.0+dev6/BuildTools/DocBook/Fonts/Delicious/Delicious-Heavy.ttf0000644000175000017500000005757012227051773026022 0ustar kismithkismithpFFTMFO_\GDEF)WGPOSWHGSUBltW( OS/23'x`cmap7 XzgaspWglyf_=A ITheadC6hheau4$hmtx]W~locaљmaxp(VX nameR%post !U/w_< S@2@ H QA Av M,8Z!,!vPS  .'dM$,(&' ,,f'5 #543 $6-6~36*6$4$6 .5Op)R978)877?526 M.M"  ovz#ovO`&R+ 'f 44-4*6$$$$$$.5.5.5.5< 76666 /v W|I(p<f`  \,,S"H 9(kJ$( tXR@~1Sx    " & 0 : D!""""""+"H"`"e% 1Rx    & 0 9 D!""""""+"H"`"d%b>%,߱޹ޭޑzw  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`a~pdehtnjriڀqgskwycmlxbz}Ƭݲu|{ov$>pZj8Jn 8r(^2Xn:X$\p 2@Rdr  P . J p  T 4 J l * P P v *LT$2Rn $HdL$Dd$Vn&nF x<,hD\tBp<h Lf  8 P n !!,!8!D!\!t!!!""$"F"X"""# ##F#R#`####$$"$8$H$|$8 "&546327#.54?"47$#48~ * !-*@.]0:|V!6 573#'73_K`N0RR0!#37#3##7##7#737#7373373@GG :+ 0~G~98/.Fiii +654''7&'77'.5467&'%+#tS-M@B-' 49?rU-(PS+ 97; ! 04Re;: 7h" "&R%#"&=#"32632"#".5467.546323#32754&#"3`C5;=_( < ".I)PI xIQy^\ #;@ K<5 j,]2OW+My 3FOTN(w w&$\#'73M"iܡRRnD .5467?G_N$T^t4[qTPu:enA 4&'>AT^`rFJPu:eٍtFz K#'#7'7'37gk0-_fu((w(XX(EppE, %##5#53533v `v 7'654'632KLG8,#71D0J/ C!-6%#53o' "&54632}"47$"47-"#1-"#1sc#3؇{"&462&"3265FfffF!%ޕGCM?3#5273c6Qkhk$3'>54&#"'>3239F#P!!j1PfaB^-6 k!&MN>v.|,Y)#5>54.#"5767654#"'>32F#KI/!::?\ #s#-r(@Y:)/?54&#"!#632hT(4dQ(GO,# G`gV~9dI6s{uR".54>7632"3254Ha&3^kD56,IY`j$#;WxFH|^EYVUlJi:,]vJ&L '67#5!rNw tDjo~Hv &32654'654#"#"&547&54632;'$I661 ^RzkZuSPg\7> ,*0- (#(5>#\kjQWE?CQ[WOA>GZ%'67#"&546324&#"3276.6)IY`dHa&# #$^M]lJiWx4,\+$&' "&54632"&54632}"47$"47$"47$"47-"#1-"#1 -"$1-"$1 ` 7'654'632'"&54632KLG8,#6R"47$"471D0G2 C!,-"$1-"$1'"7'7eab'Ryu!5!!5!LL kk'!%'!eabeRP'_ #"&4632#546?654.#"5632}"47$"487>-z 4*,/\+CM*@.*@.1:7332654&#"3267#"&54632#"&'#"&54632"32>?&l !-gdxKg@7Nb_eS(,H.51SQ.#= !**t+eeeu+4EHEӜE_X"";M9Yig&[v !#'##3' ~ //v5v#%#"'32'4+324.+326h6Yo0"2B55!I !/8engRV$L V9~. %#".54>32&#"327#h0Di=:}T,b7.G(9 3$?70"8_rARedO['">A'$5v +324.+32>8{Uхz0$94"/ >S^v%6A$$<:4v )!#3#3mئvn3v #3##!Snv&#"32753#".54>329CI(: 3$# OqDj? :~U2oGb#O['"?@'$7^tASc 6v !###3353v-v3#3v\v '>5385%$($8% g#s6v !#'#373sv3vv)33vv 6v!#7##3673wsw  L`Lv ! '6v!##33mu()v+ N$".4>24."2>6cc66cc6{ $4$  $4$ spne??enpne??e .;&&;.@.<%&<.4v #"'#324&+32݂[ t72 W[v`n/" b3#"&'.54>2324."32> Lg17T/7b~b7JL%#f "."  "& IHC_h6;pc<¥x &'78lbdPoU;Sc^)%#"&'#"54?54&#"'>32327'3265*#&, F'$">;3%e0Mb r )+,#$+(BX.R#-JJ/>+9"'3632"3254^e1,Yc5\($"PG3\9eS03y%#"&54632&#"327![)dwwg'TA"4%'555'gj"U4;?8+*%5&#"32>"#"&'#"&54>32538% A$ / B!^k0V<)  ,16V#"(a9^O- %!327#".54632'.#"%'O;*%h,Ie*fOr$( *F+Z*RwGgrO6B!473##'5354>? KK11*<<[ (uzh./F :1AH":;2#".5467.547.546323#32654.*"325P=    =N[!?;#7"C$0mX0&{5,7(9 `2.#->U  5:Yf9&8 &(&R%Tfi+* BHC7!4#"#3632AH*.? b 3.033"&5462/I#48F47-"#2."$0< '>53"&5462(<1B#48F47f':&-"#2."$08 !'#373Ra|?) 3&53K"M9j8$!4#"#4&#"#3632632=,  B1*L1[' X %3 37!4#"#3632AH*.? b $3.0 ".4>2"26432"32>454) 7=Y%*WA N $Pk80WY5 k-Q5#".54>32&32>59))Ib%2Z9&k"#. A% &Kg=.^V7 ) !0+375?&#"#3632,# + \$3+"&/73254'.7>32&#"$T5O(7'O%3e[4YL620 H5T.#a3$"'2 :+/RP +N8 hXXsc6&'#"53326?3;$O=- 3:d  [| !#3673hB/E⋓!#&'#336531 3-BC) XF?v !'#7'37303-/ii C7673'>7673&)?V-:!#(CKΡ j  )567#5!3KcZjK`|f^=+"=4Ȏ=4;#";$""*$$*E: >> ;'(i''o@77>.3.^=;2=4635"&=4+32+("")$$)E:==;'(i''oA66?%"&#"5>32326?2$ 4-0 1A!f&A m&" 2#"&546365#48$"47*!.* !-#xa/:R;$#5&'&547>753&'>?+(7F-V>B5 V+-$9 G$ C>8P40?;DIrlF(,BE M  ('#6='754>32&#"3#372+//BP+Q=(:&"N8!!- 6K_a5F1g T{H/TNg<(.#"!#3#32>7#"&'#7357#73>32,9)j! 23g;),=8g_0!L3't=V-,gkke.0'xijkd~')v3'3733#3##5#53'#EWKJWM{sq9qq9 v8D%#"&'732676.'.5467.5462.'"326'4&7$/nY1P86CC$+3:6+!$KX5` #  %.,Q#" h #"&54632#"&54632 * .1! . * .1!/U.!#1.!.!#1.!v5#:R.*#"326?#"&546324767632"'&'&%4'&'&'&#"32>    %' /DMd^P @7**MPPSMO(*,/HIIH.-2 "+-&@98@ "76B>p@6&)'1L!d]MjWJI,++.GJWZIN'**'NNU@8%!!p@B:9Al^a)#"&'#"54?54&#"'>32327'3265a"# 5t.-'M%:K xW !n!!q2C#>"89x# 0 #' 7'7'7eHIavue>?'RPR%5!5!Z”rv5.6E74767632"'&'&%4'&'&'&#"32>'3254##32#&v**MPPSMO(*,/HIIH.-2 "+-&@98@ "76B>p@ vkDY'JVWJI,++.GJWZIN'**'NNU@8%!!p@B:9AlE' Ts?A"G?YYR**& %#5#535335!xxwhbbrffrqq+167'+8lbdPsU;Sc^ Q##6765&53326?3&'#'  - .;$O=B674,<%=d  [| L3:&'67.;.54632'37(HEHP  #'!=D:& `e]z) 2~4 UM 9 )74X|** 'R 7"&54632}"47$"48."#1-"#2:s '676153R^D6@ #J"-F]n "&5462'"3264&WWX]zLKzzKL81299d7' %7'7'JeHIavue>?'RPR4K!2"&44?6=3327#"&F48F46?,z 4*,/Z-CL*@.*@=9C/=66;G!6, oP !#'##3'&'7 ~ //8lbdPvU;Sc^ !#'##3'67' ~ //P8lbdPvU;Sc^ !#'##3''7>?./ ~ //VC.FP;.C;9vZ ;P%[.9E &!#'##3'"&#"'>32326? ~ //eQ# 3&/'!T  9 v!I#! C M &!#'##3'#"&54632#"&54632 ~ // * -1!. * .1! .v.!#1-".!#1-" !#'##3'#"&546324&"2 ~ //I34HH43Iav4HH43II>v)'##35!#3#3%'''0@Ɛu..vn :.'676153.54>32&#"326? Q^D/E' >jC1V7/F5 3%;A.K*@ #JEY\08pd> d&!4V!=B( c'(F4 )!#3#3&'7mئ=8iedNvnhU9Uc^4 )!#3#367'mئ8lbdPvnlU;Sc^- )!#3#3'7>?./mئC.EP;.C;9vnZ ;P%[.94N ')!#3#3#"&54632#"&54632mئ * .1!/ * -1!.vn_.!#1.! .!#1.! 33&'7-68lbdPvU;Sc^ 3367'-8lbdPvU;Sc^/33'7>?./-C.EP;.C;9vZ ;P%[.9#M33#"&54632#"&54632-W * .1! . + .1! .v.!#1.!.!#1.!6E+!##33'"&#"'>32326?mQ# 3&/(!S  9 u()v+ NI!I#! C *&'7".4>24."2>8lbdP6cc66cc6{ $4$  $4$ U;Sc^Kpne??enpne??e .;&&;.@.<%&<.*67'".4>24."2>8lbdP6cc66cc6{ $4$  $4$ U;Sc^Gpne??enpne??e .;&&;.@.<%&<.4'7>?./".4>24."2>C.FP;.C;986cc66cc6{ $4$  $4$ Z ;P%[.9pne??enpne??e .;&&;.@.<%&<.E+@"&#"'>32326?".4>24."2>KQ# 3&/(!T  9 6cc66cc6{ $4$  $4$ !I#! C pne??enpne??e .;&&;.@.<%&<.N$2@".4>24."2>#"&54632#"&546326cc66cc6{ $4$  $4$ l * .1! . * -1!/spne??enpne??e .;&&;.@.<%&<..!#1.! .!#1.!t /7&#"32>543#"'#7.54>32e )f! 2&DB6cA#!'2-CA6c@$b&F:)j"$4%oB)Y7nf@ *Y7le> 5&'7".5326538lbdP1\|\1%P%U;Sc^3YO//OY3{HBBH{567'".5326538lbdP 1\|\1%P%U;Sc^3YO//OY3{HBBH{5#'7>?./".532653C.EP;.C;9&1\|\1%P%Z ;P%[.9A3YO//OY3{HBBH{5N .%".532653'#"&4632#"&546321\|\1%P% * -1!. * .1! .3YO//OY3{HBBH{j.D2.!/!"2.! )2'5354>32#"&/776/&7>54&#"#8++AY1e{ /bO(E118 .+6,3%v j54L"gU/ #*8 5%>V q 93?0'3"($# )/%#"&'#"54?54&#"'>32327'3265&'7*#&, F'$">;3%e0Mb r )+Y8lbdP,#$+(BX.R#-JJ/>+U;Sc^)/%#"&'#"54?54&#"'>32327'326567'*#&, F'$">;3%e0Mb r )+8madN,#$+(BX.R#-JJ/>+U;Sc^)9%#"&'#"54?54&#"'>32327'3265'7>?./*#&, F'$">;3%e0Mb r )+C.EP;.C;9,#$+(BX.R#-JJ/>+NZ ;Q$[.9)E%#"&'#"54?54&#"'>32327'3265"&#"'>32326?*#&, F'$">;3%e0Mb r )+ Q# 3&/(!S  9 ,#$+(BX.R#-JJ/>+v!I#!C )7E%#"&'#"54?54&#"'>32327'3265#"&54632#"&54632*#&, F'$">;3%e0Mb r )+8 * -1!/ * .1! .,#$+(BX.R#-JJ/>+.!#1.! .!#1.!)3;%#"&'#"54?54&#"'>32327'3265#"&46324&"2*#&, F'$">;3%e0Mb r )+PI34HH43,#$+(BX.R#-JJ/>+fIIfI.5<%326?#"&'#"'.'4?4&#"'67632>3232"3.((#D* "b+7^Q+)3.$ >;3)5?+IM,ZhQq !Y$2N Z)='):P2(BX .R"}DP1#454:)'676153&'&547>32&#"326?R^D.B5!\,0LG441 54)92@ #J"IrlF*,!MFC>T ]$ ,F $%!327#".54632'.#"&'7%'O;*%h,Ie*fOr$(8lbdN *F+Z*RwGgrO6B!47+U;Sc^$%!327#".54632'.#"67'%'O;*%h,Ie*fOr$(.8lbdN *F+Z*RwGgrO6B!47/U;Sc^.%!327#".54632'.#"/7>?./%'O;*%h,Ie*fOr$(=C.FP;.C;9 *F+Z*RwGgrO6B!47Z ;Q$[.9,:%!327#".54632'.#"#"&54632#"&54632%'O;*%h,Ie*fOr$(: * .1! . + .1! . *F+Z*RwGgrO6B!47.!#1.!.!#1.!  33&'7/:8lbdPoU;Sc^ 3367'/8lbdNsU;Sc^%33'7>?.//C.EP;.C;9Z ;Q$[.9"33#"&54632#"&54632/T * -1!. * .1! .U.!#1.!.!#1.!71!4#"#3632"&#"'>32326?AH*.? P# 3&/'"S  9 b $3.0D!I#!C  ".4>2"264&'72"26467'2"264/7>?./2"264"&#"'>32326?2"264#"&54632#"&5463232E #(E #(2>A3W72"=A3W7KJAD _I.YM/hxI.YN06 &'#"53326?3&'7;$O=- i8lbdP3:d  [| 2U;Sc^6&'#"53326?367';$O=- 8iedN3:d  [| 6U9Uc^6'&'#"53326?3'7>?./;$O=- C.FO<.C;93:d  [| Z ;Q$[.96%3&'#"53326?3#"&54632#"&54632;$O=-  * .1! . * .1!/3:d  [| .!#1.!.!#1.! C$27673'>7673#"&54632#"&54632&)?V-:!#(C * .1! . + .1! .KΡ j K.!#1.!.!#1.!/33/jv!".54>3!#3#3?e; ;e>Dӝ .*$>bl32i_=n 87w (/%326?#"'#".4>3262$"2647"3.((#D* "b+O9:O7W33W7S:7'737>32.`X6L"E( E5Y:0& !zi/E)7XT g?FFl '7>?./SC.FP;.C;9Z ;Q$[.9 .'767/TCFH7>AF5b4H'T -/S!Q "./732>7672;.' l" h,,,,e\-.& k3' 0 #"&5463232326?P# 3&/'"S  9 D!I#!C '>?>?'YF5`my8AES!O^# i0AN+(s^ "&462/sNfefe][#327#"&54632'.#"[;- N"RRdM54'632MJG9 +$6MJG#)&6TB0H1H!,B0F @!,cy 7'654'632'654'632KLG8,#6&D-G8,#(4D0J/ C!,Fc(0J/ C!v#.=#53533~j )(oo~jkOyFHxNkv'&'535#53533#n 87 sooo~j1g^cb99x6462"ssssssH #"&546323"&546323"&54632j"47$"47#48$"47#48$"47-"#1-"#1-"#1-"#1-"#1-"#1%-5= #+"&46326?"&462$4&"24&"2$"&4624&"2mPz!$ZZZA4'8q++[[[  e  [[[  l @[[[ H[[[    h[[[  '"7'7eab'R'!?'teab'R#3 }`H"###53#57#'#36?3:K]J^KHK^gW Xg\:St00* 13 J9o!o8(u73#''5#7'7'35$=K&@?&L<&%=#I=<'L=%%=H$A@&K?%$54#"'>323J3.Z_6bɅ,f)<9K9 cUe  'dT$ "`>8 Mx$ 3#527673bC1 lhj(s^2"&4^?#537#5373#3#7E@VGGK.k&kZ3'k&kh`v 7'>54'632K.>,#$15s)0G C! "&54632n!.1#"-1-"$0,#$0r'7e=e  !3#"&'&3##'53547>H 0#2NGG,,@=;9G+3(t{ e.L.,0 !&5#"3##'5354>n&+SGG,,(>KG>68t{ e>0D9j!D?g B f  4  / Gcopyright 1994-1996, Jos Buivengacopyright 1994-1996, Jos BuivengaDelicious HeavyDelicious HeavyRegularRegularDelicious-Heavy:1140922923Delicious-Heavy:1140922923Delicious-HeavyDelicious-Heavy001.000001.000Delicious-HeavyDelicious-Heavyt  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghjikmlnoqprsutvwxzy{}|~nbspace  latn ,latnkernz.fl`8~XfrxfBBBBt- &24 &*24 79:<YZ\$DFHR &*2479:<\ 7oo$DFHR79<FHR6$&*247DFHLRUVXZ\z{|}~'$DFHLRUX\z{|}~%$DFHRUXz{|}~9$&*24DFHLPQRSTVWXY]z{|}~IFHR79:<7/$&*24DFHPQRSWXz{|}~ &*24&*24VW &*24$. $&)*./234579:<>IUYZ\^z{|}~ &[&[swift-im-2.0+dev6/BuildTools/DocBook/Fonts/Delicious/Delicious-Italic.ttf0000644000175000017500000006031012227051773026135 0ustar kismithkismithpFFTMFO`GDEF)ZGPOSZ0[{ZDhGSUBltZ$ OS/23%7x`cmapwmW`gaspYglyf LXhead~Cg6hhea4$hmtx2!loca8%maxp*QX nameF-V postȩX  C_< rzzrdN@2P H QAz Av M,,\,=.o9\^DT*-.7(qt/#/1 '(,!,,!;++:'+++:$+++u++*+#:+ .+2 BFP-  O #pG2-2--- ,,,C,,-!-\,QF6"( a$Q4. 18;oxlK!.ov59-3>)2GOSC:++++++++*+#:#:#:#:#:#: B B B B$ ------------,,,,,-----6-6666+r:-- h)xDk71jCu 3(tG<f`a*NcQ2'L&. !FArnF9(Z J@(( hV@~1Sx    " & 0 : D!"""""""+"H"`"e% 1Rx    & 0 9 D!"""""""+"H"`"d%b>%,߱޽޺ޮޒ{x  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`a~pdehtnjriۀqgskwycm׷lxbz}Ƭ޲u|{ߋov"<n \l LbVp<Nbr0h0>Tz2~6x . B  H x * P ~ & `  D b <HJ*P>Lr.HpD|Rx:ZJ8~dT\N \x&`$pT , : j !!4!N!h!!!!" ","F"v""""""##*#L#l###$4$F$V$f$$$$$%%.%@%d%x%%%%&,, 6#"&54632#67'"&y"7<( #F0% $Uf[XUb\6 '?3'?36/!1/!1???= 7#33#3##7##7#737#73733Er<UXKM<s<Q PG E<sBB̹BB$,2&'#7&'77.546?34'>$#6$A$cL - NB A9%( iO - E|*4P6 %1S6 .(8Mo =:);$ Ok788>+#0' @. (04&"2%##"&4632326?"&4624&"2+>++>B4 NnNN7' (8 NnNNn+>++>>++>+H  7NNnN )nNNnN>++>+9,:#"&54677>54&'732>54'#'632,~dɹ ` :$8!75U).+IT_X"4:-XgX8a$&|/5(/. ) D0  "''"QU-'#M\'?3/!1?D&54y)',;GcV-0'($<'>7654&'7 4w#"&54>324#"32>2qMPR ;`9PNI_3M!.45L:JmsS5oa=lZ[p1:W`rtG##72?GMJB~+6%0 /3!'67>54&#"'>32Zn7 _\&?+!1N1I5|9_B>0_'a& 01#Z(2#'>54#"'7>7654&#"'>329,"6LhW -^T6w$-- m81`)j,0:7->5Y>82Q0K9D',31&3Y 3#'67#7673C@ B"8bWAu20^Ad#Y#632'#'67>54&#" 0oMSK/H:+/GR<}PC;%_1($P/+327654#">32#".5467k) 6*.ip )   >Qs[4CQ>$ 8<'j*2P X H=`=R3G1K '67!7 j4e @p,m@@ v%1#"&54767.5463274#">4'&'32&B vKBV<;((tP>KMR.?D;@2&3B2+4P&!0!FPPAGE"7)Oe7:?\EL/6%Aj Q3,<'g%'6767654#"327#"&57>32 6m) P*>q+#$>Qm`KG=**/>#  %'7 //d&&,`!5!!5!~~$<<!> 77'!//d&&; +6#"&546327#>?>54+"'7;2'"'= Y1TB1 Xo:Y F0% $? K7+d:()3 VADe% 6+J&#"32>7"&547#"&54>327332654&#"3267#"&54632%?&5  H#.:05'J1G' AE=[}|J,/.Tڑ7e0QY%."" 2 O2.YU59) A{~@9DTٜᯏ7xWv !#'##3 RcS+W"wv+u$#"'32'4+23264.+326[39k;^bC eJ 0 8M ,(F%+HbQB3j~k5aKI& U:&#"327#"&5467632%2N)OE?EP>$e.f`63Vr`Z1;(jJk,3jV>g+v 3#324+32>׬da[#DwycP]:U,vyeGzL Enn+v #3#3! $ dvCEEv+v #3## /PdvBEv:&#"32?3#"&5467632(0N)OE?E4&!J)Ddga63VrOZ3<(jJkkV>g+v ###33dP//PdP++v'v +v#dNdvv]v'67hU gvne"6=+}# #37654'3T*PdP-#}E5v$9 +_v%!3_ dL[>>v+v#67##367dPDFmDPdj\" v::RvRE!S+v##3>7dRDPdi?v*.Tv # :#4#"32>".54>32j3O(j*D) 0G%-?^70F&"AoRzs/6ReXy-KQ.1lpY8,JQ->U+v 7"'#3274&+326/)'PdYu5M:8)1AYvKTm72d.;(4327#"./67.54>324#">   BG!1(+1<aSW"@oE0F&Nj3N(KD< Q  #"5|Y?~R,JR"Ksm/:+v#'&'#324&+326lTN'PdYuQM:8)*@aV vK^72i$.#"#"'732654.54632/7-A 191 zZU<):5.P.BA.uV%K>'@-0 '&>%Ys:70J.5*-D+Vk)2t###7 XNX tF.FBv#"&5473327>#9W6TP=P=_w =v{4XK*iV##~"$|Fv #36=*\iWO vgdLv;]iPv'#.'#36736?>=3Q U0U P  \ TF#)6ԧL "2vB,&B%)! dv #'#5373ΐ\lGޅWfC`-3-#7.'77>7Tp"O$A04#? o8T3wA7\*W=8C v !!5#73 CvFFDF d1#3#1 ]z] ::x lM#3M>>d$#73#7$ ]z] ::G@'# #3'FF8_@3y!5!>‡<21./7'/RH*^-@?I-)327#"&5467#"54>32&#"3267=  "(m2X!>g?9 *I*%4o \&( "/L9xNFGfg&L82"&'36324#"32>%SgL&7KFG5{bT?9/#:'@$"4iISlTu7)=[[-&#"3267#"&54>32'.+2K *Q$b/KR9`;9."Zl02//(&4sN7pa>-("&54>32654'7&#"32>7UY7xQ<;$:eM 8.F&/7" pXOb*V-73KZs-4TW*:L&2-3267#"&54>324#"6.-"LVbLM7xO7CR89S >1U+)[qOMi8/+v@- E&#"3'6767'737632 7QT ^,= FAn6f 9 7#"&54632&#"32676%.Vq8.3f9 %M0:x6'  H!==j^>;5;#")C CX|0#?,:X'%3k!/ME5'+  &L>^w?`8!+K+-,'6?654&#"#3>32R+. %\ 2LnL/c036$&%;G E%+@(13D,#7#"&4632NJNw'"'"$ 0% ? #"&4632'67'"'".W-WV Vy$ 0% ,.6:,# #367>54&'632`+JnJAM>2 /32>32L;0$[2L;6#T1LNLh%3>U)rt/Q(t/P,^#J42(>a,!#654#"#3>32L;,0d1LNLh/36t+P0\(C(1- "&54>324&#"32>OO6rIOO7r^*35K*3)?#xSFiyTEhC9Pcs38P8VU!Q$"&'#?36324#"32>)= &LRGI LW5FH6\`N*]-3&32&#"32>7gL%/[JI :a9'\:31K($-!9$vNiN6sgB53SX-4O)?@,u&#"#37632u:54LNM44#D@ZJ"&#"#"'73264/&54632*-@$?UFgLHP%A'A3`DnN*K%-(" -%GL\<20L4$CKO#F=A#327#"&7'7373= a5 )*9D6,6AFF:$1/;4P 1WW6#&47#"&5473327B! a156;L;.RW4a2"4,A)2t)zA"#37654'"MdRG&8E8l,6+('&7#33764'~R kTGP1O+p/4@/SCV@rr'^+#''57'37654'!vwUYBlSR^ 5!t] F'>737654''YC2GgQI -7:+BN3B2j46  !!767#7~>AG=$^a2#";#"&54?654˖?>;a; 5#59,("%9 58; (I; 6)-"=/O)>DQ#3<<^&,"+'32>?>7.54?64&+732&%: 6995 4$;;+'"7/P)>C6 (G<,6), !=r#"&#"7>3232    $|# " %{#6hH D >D 0 4632#"673b'"'x6<' !0% 0%]dXZ]1: &'67#7.54676?32 $?$5;G - FE+'AJ - %.+@H'(t#$9:; jKA,F DC Yc.x&%#"&/#6='7354632&#"3#33267< ,$;'_BG! 1[G:0%Z2?=.Su<XdS*&#"!#3#3267#"&7#736767#73>32S&-[Ju((NF*Z$ Gc_oa(:X(Cm`lu? ?o"73&'77>73#3##7#73&'5 c%P4#? o803V  O  <<14\*W=8C3{C<6'<<>rs M4&#"3262&#"#"'732654.'.5467. 546-3!.73-D  DH;J7 #"&54632#"&54632&"&""%"%x6) &6324&"32'#"&54632&#"327⟠sp>JTPI<*-3-16%/z N⠠qsfLKj'"K79M"Kk{(327#"&5467#"54>32&#"32676{. R&C)_>$ *B'T$9g %'7'7 //J//d&&&&.%5!5!; <v5#, &546 4&#"327#'&'#32&4&+32 pqtq5x 3V=U8(nC5(#%zN⠠qtdyc;9)IjL# =5x!75 x;;p9 "&5463274&#"3263Ed?3Da1+#(C+"+ApC3?aC3A_#-C+#,D-%!5%##5#53533s::;<<}}<31'>?N~2YC=- I>P 654&5332673"&'#"'d L#"ZL'W(X%!C-zt%"#a'2 92),'654&547'&#"&5463273) 65  Z& =!4&%0CGzb 73 p.(6>gF$IlT ;K!(>DVDc,G. 7#"&54632&#'"$ $ RO '654#"73OP)!^ &h6$D/  /Sj "&5463274#"32>< 77'7'////d&&&&d *4632#"3;2&+"54?>'"&= Y2SB1 Xo;X  0% $? G6+d:()3 VBCe%* @r !#'##3 ./7RcS+W"w(/RH)v-@?Ir !#'##3 '>?RcS+W"wO}2Xv&C=- Ii !#'##3 &''>7RcS+W"w"-=`9.}3v,="> .TFB !#'##3 '.#"'>32RcS+W"w2 ?/K$ 7)X"v5 51'% ), "!#'##3 #"&54632#"&54632RcS+W"ww&#&#v"%"%z "!#'##3 4&#"326"&54632RcS+W"w-,N&2L0&2Lv *+Y3&0K3&0Kv#3#3!7##37  $ cS+]"OvvCEEv:R&'654#"7.5467632&#"327OP)!^ &MC63Vr`;&5J)PE?EP>4G6$D+$ &WV>g+4 ='jJk,3%+r #3#3!%./7 $ d'/RH*vCEEvj-@?I+r #3#3!%'>? $ d-O~2XvCEEvC=- I+i #3#3!%&''>7 $ dA-=`9-}3vCEEvm,="> .SF+, ##3#3!7#"&54632#"&54632 $ d&#&#vCEEv"%"%+r ./7#'/RH)dNd-@?Iv+Er '>?#EO~2Y4dNd3C=- Iv+fi &''>7#f-=`9.}3:dNd,="> .TFv+C, #"&54632#"&54632#%"%"fdNd"%"%v+"B###3>77'.#"'>32dRDPdi?'2 ?/K$ 7)X"v*.Tv #  51'% ):r+./74#"32>".54>32'0RH)Kj3O(j*D) 0G%-?^70F&"Ao-@?IrRzs/6ReXy-KQ.1lpY8,JQ->U:r+'>?4#"32>".54>32O}2X j3O(j*D) 0G%-?^70F&"Ao3C=- I)Rzs/6ReXy-KQ.1lpY8,JQ->U:i .&''>74#"32>".54>32-=`9.}3j3O(j*D) 0G%-?^70F&"Ao,="> .TFxRzs/6ReXy-KQ.1lpY8,JQ->U:B"7'.#"'>324#"32>".54>322 ?/K$ 7)X"%j3O(j*D) 0G%-?^70F&"AoB 51'% )Rzs/6ReXy-KQ.1lpY8,JQ->U:,#/;4#"32>".54>32#"&54632#"&54632j3O(j*D) 0G%-?^70F&"Ao"%#%#Rzs/6ReXy-KQ.1lpY8,JQ->U"%"%:l -7&#"32>54#"'#7&54>3273O(*D) RD@"AoD ?2IM-?^7&!8VRzs/U,6ReX$EA>U =1lpY8Br./7#"&5473327'/RH*>#9W6TP=P=_w =-@?I{4XK*iV##~"$|Br'>?#"&5473327O}2Xo>#9W6TP=P=_w =3C=- I{4XK*iV##~"$|Bi &''>7#"&5473327-=`9.}3Z>#9W6TP=P=_w =,="> .TF{4XK*iV##~"$|B,!-#"&5473327'#"&54632#"&54632>#9W6TP=P=_w ={&#&#v{4XK*iV##~"$|"%"% E<%#"&'732654.5467654&#"'67'737>32&#"pN'Q%>%H,@?,jI8+,J6 X,> FD|FH`!* %?UKKQ"3!/$*!7%FR#*&5*Szhi 1LAMPF8?(# -(-)1327#"&5467#"54>32&#"3267./7=  "(m2X!>g?9 *I*%4o c'/RH*\&( "/L9xNFGfg&L8m-@?I-)1327#"&5467#"54>32&#"3267'>?=  "(m2X!>g?9 *I*%4o N~2Y\&( "/L9xNFGfg&L8C=- I-)4327#"&5467#"54>32&#"3267&''>7=  "(m2X!>g?9 *I*%4o -=`9.}3\&( "/L9xNFGfg&L8p,="> .TF-0='.#"'>32327#"&5467#"54>32&#"32672 ?/K$ 7)X#=  "(m2X!>g?9 *I*%4o  51'% )\&( "/L9xNFGfg&L8-)5A327#"&5467#"54>32&#"3267#"&54632#"&54632=  "(m2X!>g?9 *I*%4o %"%"\&( "/L9xNFGfg&L8"%"%-)4>327#"&5467#"54>32&#"3267#"&7>26&#"32=  "(m2X!>g?9 *I*%4o tH,+3IV39*\&( "/L9xNFGfg&L8+?>,+?>D0$$0$- '7%#"54>326323267#"4#"6'&#"327&546%X*X!>g?*V,17C.-#KTd`89S !*I*%7;1J$59xN85>1U+)[+v@-Gfg&LKG-R0'7>54#"7.54>32&#"3267P)!   $379`;9?'.+2K *Q5F6$D   *g?7pa>1."Zl02//(8-%3267#"&54>324#"6./7.-"LVbLM7xO7CR89S ?'/RH*>1U+)[qOMi8/+v@-*-@?I-%3267#"&54>324#"6'>?.-"LVbLM7xO7CR89S ZN~2Y>1U+)[qOMi8/+v@-}C=- I-(3267#"&54>324#"6&''>7.-"LVbLM7xO7CR89S g-=`9.}3>1U+)[qOMi8/+v@--,="> .TF-)53267#"&54>324#"6#"&54632#"&54632.-"LVbLM7xO7CR89S B%#%#>1U+)[qOMi8/+v@-N"%"%, ./7#'/RH*NJN^-@?I,$ #7'>?NJNO~2YC=- I,g &''>7#g-=`9.}35NJNa,="> .TF,1 #"&54632#"&54632#1&"&"-NJN"%"%,*!#654#"#3>32'.#"'>32L;,0d1LNLh/362 ?/K$ 7)X#t+P0\(C(1L 51'% )- $"&54>324&#"32>./7OO6rIOO7r^*35K*3)?#$'/RH*xSFiyTEhC9Pcs38P8VUO-@?I- $"&54>324&#"32>'>?OO6rIOO7r^*35K*3)?#@O}2XxSFiyTEhC9Pcs38P8VUC=- I- '"&54>324&#"32>&''>7OO6rIOO7r^*35K*3)?#W-=`9.}3xSFiyTEhC9Pcs38P8VUR,="> .TF- 0"&54>324&#"32>'.#"'>32OO6rIOO7r^*35K*3)?#@2 @/L$ 7)X#xSFiyTEhC9Pcs38P8VU 51'% )- (4"&54>324&#"32>#"&54632#"&54632OO6rIOO7r^*35K*3)?#T%#%#xSFiyTEhC9Pcs38P8VUs"%"%6. "&54632"&546327!5!2'0#'"'0#'|0% $0% $9<-C("'#7&54>3273&#"74'32>029F6rI$2.G7r 5K)?#j6yFiQf6{Ehcs3A%C$8VU6%#&47#"&54733277./7B! a156;L;.RW4'/RH*a2"4,A)2t)zAt-@?I6%#&47#"&54733277'>?B! a156;L;.RW4)N~2Ya2"4,A)2t)zAC=- I6(#&47#"&54733277&''>7B! a156;L;.RW4L-=`9.}3a2"4,A)2t)zAw,="> .TF6)5#&47#"&5473327'#"&54632#"&54632B! a156;L;.RW4f&#&#a2"4,A)2t)zA"%"%F ,'>737654/#"&54632#"&54632'YC2GgQI a&"&"-7:+BN3B2j46"%"%+#NJN:v#3#3!".547>3"3  $ .C#(=^6 0K'evCEE*HN+-'3h`<@Ntn,-&63267#"'#"&54>326324#"6%&#"327&54/,#KVbX(DXOO7rHZ*Hb7CR89S Q)?#*3N->1U+)[NNxSEiSS8/+v@-j8VV%8Pf W-, ,#7.'77>7'#"&54632#"&54632Tp"O$A04#? o8%#%#T3wA7\*W=8C}"%"% q!&#"3#'>7'737>32q,6 p n &9#)   < 7 B7/$`KA:Oq+2,80I,QQ2P8A)5d &''>7d-=`9.}3a,="> .TFDx &'767x2Y6M'36`+ IX;-GN7#k#"&/7326?k)(6$B8  @0%G##5."C!!1L "'&676 () "(()i #"&7>26&#"32iH,+3IV39++?>,+?>D0$$0$S.7>3*): '3  /K%! 7 Ul'.#"'>32:2 ?/K$ 7)X# 51'% )31'>?'>?"~2CN~2Y=- 7 C=- I(s^ "&462%s[ܮGke327#"&54>324#"6e#"63BJ::*[<*3?*+? f. %A7 DU=:oP+# Y1!<<Zf%!5!ff<`%!5!``<a#67#"a5e(` QS%gD N'6'6326c)`!ISQ%gD qW7'67>5'6327g(9 )WaD1! c67#"'67#"5e(` 5e(` QS%gD 7QS%gD Q'6'632'6'6326c)`!6c)`!ISQ%gD 7SQ%gD y]T7'6'632'6'6326c(`!6c(`!SQ%gD 7SQ%gD 2v#&54?#7373"0 " KBמB#B7DBu#3#&7#737#7373/  KBB@JBB'"&462feeeee&f  6#"&54632#"&54632#"&54632'"&'"&'"&F0% $ 0% $ 0% $. (08@4&"2%##"&4632326?"&4624&"2$"&4624&"2+>++>B4 NnNN7' (8 NnNNn+>++>NnNNn+>++>>++>+H  7NNnN )nNNnN>++>+nNNnN>++>+!> %'7 //d&&A>+77'A//d&&r} #?8ln(###7%#7##367ha818a?3+ ,E*3?C: -_-s6E4 4 F#Fn nJ9o%!!o68(u#''5#7'7'35773?&L<&%=#I=$$=K&<%=H$A@&K?%$54#"'>32.?1=(M#kw 2+)5!/#X(-BUk83x+%1@##72?MJB~)8%0 (s^2"&4^1#3#'7#737#737 G K7j F D!4`;Z;_G;Z;VAvQ7'6'6328b(` TP%gD (f 6#"&54632'"'F0% $r'7= E0#7#"&54654&#"3'676767'737>32MJMp) 39PQS  U,; FApP* '  76(9M?Xuch< 1RI@  E(.7.3'>77'737>L H FLF2QS)0,(FAXzv$BS !n6 6(=9d%g ! )z9*? 1R?D4 ayA 2 M  k 6     -4.0, I1995 Jos Buivenga-4.0, I1995 Jos BuivengaDeliciousDeliciousItalicItalicDelicious-Italic:1140922924Delicious-Italic:1140922924Delicious-ItalicDelicious-Italic001.000001.000Delicious-ItalicDelicious-Italict  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghjikmlnoqprsutvwxzy{}|~nbspace  latn ,latnkern*^dV f@Vh^RRR^\\\\\\vTTT- &247 &*24 79<YZ\$DFHR &*2479:<\7oo$DFHR779<FHR6$&*247 DFHLRUVXZ\z{|}~\ &$<DFHRTVz{|}~IDFHR79<$DFHRz{|}~$&*24VWz{|}~ &*24$&*24z{|}~* $)./234579:<>IUYZ\^z{|}~ &\&\swift-im-2.0+dev6/BuildTools/DocBook/Fonts/Delicious/Delicious-BoldItalic.ttf0000644000175000017500000006216412227051773026747 0ustar kismithkismithpFFTMFOdXGDEF)\GPOSdz\tGSUBlt\ OS/237x`cmap(J\gasp\glyf NheaduC6hhea4$hmtxBhloca?Smaxp)LX name5X|3post Z_< b||bdI@2P H! QA| Av M,$Q,4,o9P^cT$-.4$qq2/) $$,,,--).&(((.#)&t*()(#:( .(- B8M O#p:+02--- ) )<*)-".[+P>9!% `!B4-|-  8%oxkM(ov48!&-)5DS.(((()(#:#:#:#:#:#9 B B B B$0000000----- )-----/-8888(t1h"w<j4FjBu -(tG<f`U*TWR/ 'L", EbkE9(ZJ<($ |`T@~1Sx    " & 0 : D!""""""+"H"`"e% 1Rx    & 0 9 D!""""""+"H"`"d%b>%,߱޹ޭޑzw  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`a~pdehtnjriڀqgskwycmlxbz}Ƭݲu|{ދov(BtDTt FZF`6H\n2n&@Nj$N(Bz  ( 6 H 2 f F x  @ z  < r .dp@|Rx <LNNr8n(RP `&Z6~:`.N|4p@  P !&!t!!"""8"P"x""""#.#<#J#X#~##$$F$$$$%%l%~%%%%%&&Z&n&&&&&&'.'l$ #7>?#"&46321 Z)1'1'|krN>n32,$<+$QB #?3#?3,9E,9E|CHC4#3##7##7#737#7373373#3 P ? C\D\QP D?\C\gD Deieɯeieei +7654'7#7./77'&7>?3&'2?3< !@;&-@G `J<8 C&%PbL4!ȅ7> P!04[H`46#8:UPt, ($"&4624&"2"&46327##4&"2StSSt!,!!,RvRR;3&Fb5V . .vRRvR. . :SStS &,!!,!9,-.5467676'.'732654'#'632,}c\bbzv5.3' QBs C *L4XfXG\Y`{j0&+ 82EDar%8NP#?3,:E|Cc&'&>a+40g&$ Id=k)'4DUf\x7'>7>&'7?!?* K-ANp`(5`]O2)*d<_S$W#''7'7'37zGc(PWpo"d2j(^a%V1ff1-/ %##5#53533XX̝\pW7'67>?>3" @-4!!6<.?(   4)7#53T$| 7#"&46321'1';,$<+$l #w\w#"&54>324#"32>2pKRS9_9RPiE':#,<5JjwS3la>rXjI_.0EMaqL##727LFa8l A>R23!'67654&#"'>32 BB%XVY.Q()$B'r+_VAbW-Y`4/H[)#'67654.#"'>?654&#"'763200&g^MR]B8 0*-m+=:8b'6<!A$eT"#L2sWO  J-8T3#'67#?6?3>;=#Opw"ztnXv6" jS***Y#632#'27>54%:PePO[F@7E&P^B;W#$OS6G^/!32>54&72#".54767QO.#2<=U3Xzb*<Je?1<\"`&+=>K$*RKEi%>B%Џ>/)E '67#7 fG^RqAkb v$1#"&5467&546324#"67>4.'326w+4 uKDXG6?pPC()c;#.- *! *X$':cIG. GTXD:e.COm""H3>$# 9 0O!.K$c'6767>54#"7"&54632Cl QO.>!1<=U3Xyc8F/ȗ;2G1<\"a%h>L#*RKEiCT$ 7#"&46327#"&546321'1'(1'1';,$<+$,%+$p7'67>?>3#"&54632" @-4!!41'1'6<.?(   ),%+$8%'7BCq99,`!5!!5!~~ TT8%'7'7BC99-"-#76?>56+"&/7;2#"&4632:.Z "Z J3 <N(51'1'*7B6.'77T)q !@",1,$<+$-7B#&''.7>32733267654&#"7'.54632&#"7>?P!3 H7$PO* TE1Dtnwo5|ؐ0<$A 0R{*!GQU  g.o{pvq0՛߫en v !#'##3'qQv&_3Ov)v%#"'32'#3654&4.+326l\a,t^N`>y4,-> $0!325*I!A!07>*"d-i_ 1Ih91PWL7!+7>^ L q"18qa= (v+324+32>gbq^~^KD@1H$7v8N@e`(v #3#3!avadcv(v #3## ,m`v`cv.&#"3?3#"&54>326+HD!2&B i+[Qia*JtC0PWL8 +7&A4qDyN )v ###337bp-,obn')vvv#]m\vv\v '767_%D8\vx9 Q -}&~#'#376'i!pao"S= ~+)5.vS=L*cv%!3c dlU``v(v#7#'#367dr1$ ]D 1rdW v6nh9kivj $ + (v##347bjz8ob`0v*'vLP8:"2#".54>4#"32>L@T-?]61H&->]|P+A!P+B!Qj?0koX8.LS.0knX8Nsg'}Lre(v #"&/#326&+326\%natez ,E#"0C^xvvO07 E.E'7./7.54>324#"6A;9K d iHI >nE2J%mN+A!tv?"2y980}Q=T0OR!Dhb*e(v#/#324+326]Ma#naYvoj#!7KQ.vNdP T#&#"#"&/732654/&7>32C5$3'RP{['J;/4&:'kG qT%H<8-$%;:U[zP)5%)Q4[Nc%-t###7PmPtg gBv#".73327< 7hF7L :o<"%^;v|HoJ/K`1z(G1z8v #376=(nfuBmvtbLv.7OuMv #73#36736?65&533i A*o0v j8*qBjw]+"Qrv]H[R/ qV=:0v #'#5373~]]˃yYZI.1#7&'.'7>73)m$ @8K?'e?A/@5U2d(j >P K(P3MK!v !!'#7   vZKgXfd4#3#4RfR^D^xlS#3S\\d'#73#7'RfR ^^:@5# #35lkJ]@y!5!>‡<+,&'75_EX,i=7JLK0)%#"&?#"54>3237&#"3267-% ' "6b;f?3_2 Y #;#  X 0$6|wNK?]Z"  t,2 654#"32>"&/3>32Z505*)$;$Nbj%5JG!>d #Q=LQ"cT:uc?-(&#"326?#"'&5467>32?4#H '"71 $&7X/#,$#X" C\L"6D< D2SA*(+  -'"&54632654&/7&#"32>7X[_, S,5f')8FJ vZx F%- 'ANZx"Pp!4-!326?#"&54>324#"67ZQ*!;-]0NN5uNj *=*E,$I < +tQLgrS1#G #"3#'67'734>32  CJ4.=6 ; 8 1"R X?kD'd& L#@B(?-9#"546?32654&#'>7#"&54632&#"32676'"$3NT&M'&+ 252^!" 7 A@haMP )*-"/d8 @#0M-e,^< <<'$%TBbz OK69>' )'>?6&#"#3>32 !,@ T,jjk- /:7e;>-5-1P& !)B #7#"'&547632KiJ  & $'~   & %: '7>77#"'&547632N E6/ !Q  & $'FDQ ' ~   & %)!#'#3>7>54&#'632#ш#hji;-D%#/.mC + E6(EX< &7GeF9R^}*!#6#"#6&"#3632632k:%=1j; 2S/kMm9[g328k:6N-jMjK)78hnwO'7*A-"&54>324#"32>QQ6pGQQ :]OC!3 C!3 {UEg|V2k]<Ef4NJe4MJ"Q&7#?3632#"3276#""kHCCkF%/C 3S4A ME&&^*k$3-GU(2[W3p'.&* .Q#7#"&54>32&#"32>7ck"<RG8sF%]Y"+>:&9xlVFmN-JK(aGZ8+y.#37>32yF +,hKi  *W EJ  '&#"#"&/73276/.7>32<$> (XP cP F77+G*d  jF1"8%"  /+m8LK+68C-BF>;A#327#"547'7373;Q,!*=Ab+8 = ` X#H6a #PWW9733273.'.?#".98j93Q-j@ &- P(*5hw$h; &9(4!#376.' ^bo8e 76GK4%##337>&'c ghIp#h!G76IM;[#3G(#'#57'37>&'dtwEyVltA. )&/c*/ !E'>737654'!&aK4Bbp<R7A;9CLM6.5#7<  3!767#7 v y{ TaZ^!^a!#";#"?6H>7>;aF  =/ H?n" +  B-F,K11K%Z-1S+K?PR;B#3ZZ^)!"+'32>?67&?6'&+732) 4 C-DA  =.  NAl#IS7-S9Z"G33K4Z-0|!#"&#"7>32326!%r*/ &q- 4$?} 4'/ 4632#"67673O1'1r1 Z <+$,{krC%&2-;+7#7&'&54676?3&'>?02 R< A%#,$9M <  > 1(1K$TBR<"X= 72SA*BDE B <. *%/#6='7354632&#"3#3>?&4'C&gBI-*+F1,)/ T_ H{EA&I>mZ_KU Q(.#"!#3#327#"&7#7347467#73>327-94 5F=L^P\cwh46X6;!q_>"UULH^vAoTVjE 73&'7>73#3##7#73'^#NK?' h@F_7g{ m,]-K(P1XK!J<|]*]`]@ vz<H#"'732654&'.5467&54632&#"4&#"326z=-%$bQRe&NG#''6-&=1jbLMR)I/ $1, `%%.)1/Q.KY=N1!"0I/GER.N$"   %"!$%<K #"&54632#"&54632K-* -*!*- *- x5*D%#"'&'&54676324'&'&#"276'&#"326?#"&54632,+LKSUIK+-VKIWVKK*+O +T-&@98 ! oAdEI{(*0''# 16DYTF8[HJ+**+IJZU+**,JHX@8K !!8597?EG543.D9+^[Kc Mk+#"&?#"54>3237'&#"3267")K-N/"M&C - D|$ n(_Z<0GE X!0 '7'7ܢxOjwOy7;7(%5!5!YZv4+:C%"'&'&5467624'&'&'&#"32>#/#32'4+326,-JIIL*-VKIIKVP "+-&@98@ "76B>p@gCYNWED' V^: ZIL)**+JLWU+**+U@8%!!p@B:9Alf^db@4%A Y.a=8x!78x;;!p7 #"&7>326&#"3267 ]8:G ]8:GS--8PT:8PT4$($(& %##5#53533!5!XX wnnTS-,'>?L%0W@=@GK#%#"&'#"'654&=332>737&&8 E1- i)-i 2 ,')G@P}~iq 5/$'6'&7'&&7>4673/4B!dC=>eO>=(V/2!.3' j]AT_A4iNMEL!M'<80 %'7'7'7'7@wOAxO;7;7s /4632#"&>7>?3;2&+".61'1':.Z "Z J3 <M 0,$,$.7B6.'77R+q" !@" &${ !#'##3'&'7qQv&_3O5_EX,v=7JLK{ !#'##3''>?qQv&_3OZs%0WvG6@Gq !#'##3'&''67qQv&_3OB9L>/G8v;5-@E9GK #!#'##3''.#"'67676'qQv&_3O4##  8<4&-5  v%&%*=1&: "!#'##3'#"&54632#"&54632qQv&_3O-*!-+!v*- *- | !#'##3'"&7>24&#"326qQv&_3OEX5FV  v&V=A,+<`  v#3#3!7##37'^y.c JIvegfv.O.'6'.#7&547>32&#"326?7K/2Y   1Ih91P5*I!A!07>*6P0#J1G)"18qa= L7!+7>^ L+ ({ #3#3!%&'7a+5j:X+vadcvu==DLK({ #3#3!%'>?a0Zs%0WvadcvG6@G(q #3#3!%&''67aYA9L>/G8vadcvu;5-@E9G(: ##3#3!%#"&54632#"&54632aU-*!-+!vadcv*- *- { &'7#5j:X+']m\==DLKvY '>?#>i'GW`V>8<Cvhq &''67#hB9J?/H73\n];5, @E9GvU: #"&54632#"&54632#U-* -*!<]m\*- *- v(K&##3477'.#"'67676'bjz8ob`0k4"#  8<4&-5  v*'vLP8(% *=1&:{&&'72#".54>4#"32>5_EX,$@T"@mC1H'"@mP*B!P+B!=7JLKQj?=T.LS.=SNsg'}Lre:{('>?2#".54>4#"32>L%0WX@T"@mC1H'"@mP*B!P+B!+@=@GQj?=T.LS.=SNsg'}Lre:q *&''672#".54>4#"32>A/":O/H8l@T"@mC1H'"@mP*B!P+B!;*%$(@E9GQj?=T.LS.=SNsg'}Lre:K*8'.#"'67676'2#".54>4#"32>4"#  8<4%/3  k@T"@mC1H'"@mP*B!P+B!:(% *=1&Qj?=T.LS.=SNsg'}Lre:: ,82#".54>4#"32>#"&54632#"&54632L@T"@mC1H'"@mP*B!P+B!j-* -* Qj?=T.LS.=SNsg'}Lre*- *- 9n!+273#"'#7&54>&#"4'32>L(82CF"@nC#@2KJ"@m^*B! +B!~C=T B=Sm Msh'4 /nLqfB{&'7#".733275h;X+< 7hF7L :o<"%^;=?#".73327Zs%0W< 7hF7L :o<"%^;+G6@G|HoJ/K`1z(G1zBq &''67#".73327B2J?/H7\< 7hF7L :o<"%^;;.!, @E9G|HoJ/K`1z(G1zB: ,#".733277#"&54632#"&54632< 7hF7L :o<"%^;M-+!-+ v|HoJ/K`1z(G1z*- *- ? =%#"&/732676/.7>36&#"'>767'737>&#" uP >4"'!.*Y! `5$1#9 &3C&B = O9I **0Y6NX O 5N'AC29+uu%n$$+ $yATLBOQ K~U 6!0)/%#"&?#"54>3237&#"3267&'7-% ' "6b;f?3_2 Y #;#  X z5_EX,0$6|wNK?]Z"  t,t=7JLK0)1%#"&?#"54>3237&#"3267'>?-% ' "6b;f?3_2 Y #;#  X L%0W0$6|wNK?]Z"  t,@=@G0)3%#"&?#"54>3237&#"3267&''67-% ' "6b;f?3_2 Y #;#  X B9J?/H70$6|wNK?]Z"  t,t;5, @E9G02A'.#"'67676'#"&?#"54>3237&#"32674"# 8<4%/3  /-% ' "6b;f?3_2 Y #;#  X '% *=1&?0$6|wNK?]Z"  t,0)5A%#"&?#"54>3237&#"3267#"&54632#"&54632-% ' "6b;f?3_2 Y #;#  X -* -*!0$6|wNK?]Z"  t,*- *- 0)4@%#"&?#"54>3237&#"3267"&7>324&#"326-% ' "6b;f?3_2 Y #;#  X EX5G*+6L  0$6|wNK?]Z"  t,*=A,+326324#"6G%=! 47ZP+!B- d/^)BTb 32&#"326?K/2Y  (#,$#X" ?4#H '"71E"0#J1K$2SA*(+  C\L"6D<2-!'326?#"&54>324#"6&'77ZQ*!;-]0NN5uNj *=^5_EX,*E,$I < +tQLgrS1#(=7JLK-!)326?#"&54>324#"6'>?7ZQ*!;-]0NN5uNj *=uL%0W*E,$I < +tQLgrS1#h@=@G-!+326?#"&54>324#"6&''677ZQ*!;-]0NN5uNj *=zA9L>/G8*E,$I < +tQLgrS1#(;5-@E9G-!-9326?#"&54>324#"6#"&54632#"&546327ZQ*!;-]0NN5uNj *=-+ -+ *E,$I < +tQLgrS1#A*- *-   &'7#5_EX,B[Bi=7JLK ! #7'>?B[BM$/WA<@G j &''67#jA9AH/G8>B[Ci;5($@E9G G #"&54632#"&54632#G-+!-+!4C[C*- *- )+#6&#"#3>32'.#"'67676'8k:6N-jMjK)784"# 8<4%/3  hnwO'7*A*'% *=1&-""&54>324#"32>&'7QQ6pGQQ :]OC!3 C!3 F5_EX,{UEg|V2k]<Ef4NJe4MJQ=7JLK-$"&54>324#"32>'>?QQ6pGQQ :]OC!3 C!3 dZs%0W{UEg|V2k]<Ef4NJe4MJG6@G-&"&54>324#"32>&''67QQ6pGQQ :]OC!3 C!3 {A9L>/G8{UEg|V2k]<Ef4NJe4MJQ;5-@E9G-9"&54>324#"32>'.#"'676676'QQ6pGQQ :]OC!3 C!3 3"#  8 <4'-  {UEg|V2k]<Ef4NJe4MJ&%*=1&-(4"&54>324#"32>#"&54632#"&54632QQ6pGQQ :]OC!3 C!3 x-+ -* {UEg|V2k]<Ef4NJe4MJj*- *- /' #"&54632#"&546327!5!8.'.'.'.'v,%,%,$,%?Z-H *"'#7&54>3273&#"74'32>.27F6pG'21H :] !3 !3 f|7{EgWl7~2k]<4MJ'+3NI8%733273.'.?#".&'788j93Q-j@ &- P(*5|5_EX,hw$h; &9(4=7JLK8'733273.'.?#".'>?88j93Q-j@ &- P(*5M$0Vhw$h; &9(4CA<@G8)733273.'.?#".&''6788j93Q-j@ &- P(*5B9L>/G8hw$h; &9(4;5-@E9G8+7733273.'.?#".#"&54632#"&5463288j93Q-j@ &- P(*5-*!-+!hw$h; &9(4*- *- E ,'>737654'7#"&54632#"&54632!&aK4Bbp<SV,* -+!7A;9CLM6.5!5>*- *- (#C[Cv#3#3!"'&76763n+(#%X:D/<SvegfrlH/e ~T1&3327#"'#".7>326324#"6'&#"327&548]U.%:J1adP,BNBS ^Q/DY;Ks"&@ 90@ Y=$*E. (EOFf==Su?eAA=5M3"McHP Q:%1#7&'.'7>7'#"&54632#"&546323)m$ @8K?'e?-+ -+ A/@5U2d(j >P K(P3MK!y*- *- x&#"3#'>7'737>32x!6$ nhH38"4 :5Q43,B6:Xm=94y_ IJA@".l &''67lB9J?/H7i;5, @E9G<~ &'767~1\:Q97,K5GX;=394#o#"&/727o+)6$DT`>.#E"#JFK| &67>|3# K3  )i "&7>324&#"326iEX5G*+6L  *=A,+3#0)3 *5 %&L%; " Hm'.#"'67676'm4"# 8<4%/3  '% *=1&-,'>?'>?-'1H ZS'3]$;@9 4A<@G(s^ "&462!s#C8Gke 326?#"&546324#"6e*E= - "G$;[W@%<<Zf%!5!ff<`%!5!``<U$7>?#.'U" @-1! 6<.:-   T#'67>?>3#" @-4!!M6<.?(  lW 7'>'6320f;(" Pc.!$DW'7>?#./7>?#.'" @-1! " @-1! 6<.:-   6<.:-   R''67>?>3'67>?>3!" @-4!!" @-4!!M6<.?(   6<.?(  seZ'7'67>?>3'67>?>3" @-4! " @-4! !6<.<+   6<.<+  /u#.>7#7373s # xxhc;eM"'Pg3c u#3&7'737#7373tt@#zxyyidhdZfdhd'"&462feeeee" |  7#"&4632#"&4632#"&46321'1'1'1'1'1';,$<+$,$<+$,$<+$,08$"&4624&"2&"&4624&"2"&46327##4&"2RvRRv . .StSSt!,!!,RvRR;3&Fb5V . .vRRvR. . rvRRvR. . :SStS &,!!,!0"'7"pJy70/%'7'7/>pJ;7b} #h/lk(###7%#?#/#36?g V2F3W @G [:+ H?S7 BJBsFBDC-I#IX(XJ9o%!!o68(u#''5#7'7'35773?&L<&%=#I=$$=K&<%=H$A@&K?%$323,7f[&I 03C(B3.$B-Q1X LIJ 4. 0S=46<##727F`7l A>R(s^2"&4^)#3#'7#737#737$K4&Q#8A*hZ-Z^.0Z-ZU(-pW7'67>?>3" @-4!!6<.?(   $| 7#"&46321'1';,$<+$r'7=G#'#"'&7&3#'67'737>32#  & $C1CJ4.=6 ; 8v[@A!'DjEP  /+,X?kD'd& L-VY 1 G$&7&#"3#'67'737>32Ff:$*<CJ4.=6 ; 8ic(Y.9R^}8"-X?kD'd& L2aD 4 a >p 2 M k > ( . (F-4.0, I1995 Jos Buivenga-4.0, I1995 Jos BuivengaDeliciousDeliciousBold ItalicBold ItalicDelicious-BoldItalic:1140922922Delicious-BoldItalic:1140922922Delicious-BoldItalicDelicious-BoldItalic001.000001.000Delicious-BoldItalicDelicious-BoldItalict  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghjikmlnoqprsutvwxzy{}|~nbspace  latn ,latnkern*^dZ$jD^^      >```- &247 &*24 79:<YZ\$DFHR &*2479:<\7oo$DFHR779<FHR6$&*247 DFHLRUVXZ\z{|}~#$DFHR\ z{|}~ $z{|}~-$<DFHPQRSTVWXY]z{|}~ILODFHR79:<#$DFHPQRSWXz{|}~ &*24$&*24VWz{|}~ &*24$&*24z{|}~* $)./234579:<>IUYZ\^z{|}~ &Z&Zswift-im-2.0+dev6/BuildTools/DocBook/Fonts/Delicious/Delicious-SmallCaps.ttf0000644000175000017500000005757412227051773026631 0ustar kismithkismithpFFTMFO_`GDEF)VGPOS'WHGSUBltV OS/22B(x`cmapWƳg\gaspVglyfw IheadCW6hhea'4$hmtx.loca,@maxp)ZX nameUqR.v*@P/S#-.6HdO>HC/;)+H,!,,!fH+ P7#PPP 7$PPP~PP*P$7P$7P- .O   $O BpGR-ZAZZZAZZZoZZ^AZAZ7 Ys1M(QM(:1  ;oxz!.ovO`!-R;>H<@f*       7PPPPPPPP*P$7$7$7$7$7$7.O.O.O&O<AZZPZZZZZQKKKDK6A c c c cZ~7GA  <<0-||I;pC<f` .0//!!'\F.,!,!S"C 9(6k O., 4__|`T@~1Sx    " & 0 : D!"""""""+"H"`"e% 1Rx    & 0 9 D!"""""""+"H"`"d%b>%,߱޽޺ޮޒ{x  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`a~pdehtnjriۀqgskwycm׷lxbz}Ƭ޲u|{ov"<n^n >RPj*Vh|*D|(>L^x^"<Rj| & R n  & L j  0 f x D R  @ ~  `z2Fx (` BtDz.j<H>tDd0\\,nNZ(@Xt  <X   < N ~ !!!$L$f$U 6#"&54632#547'"'<F0% $Uc^nXG@ #'73#'7~! ! ????= 7#33#3##7##7#737#73733Er<UXKM<s<Q PG E<sBB̹BB> 4>54'5#5&'75. 546753&'!$r%)QA-PGK5' ! TH-H2(7M- 7$62%*B[ =:)<% HW78 '6 10. (04&"2%##"&4632326?"&4624&"2+>++>B4 NnNN7' (8 NnNNn+>++>>++>+H  7NNnN )nNNnN>++>+*W >54&#"3#"&5#"32>32&#"#".5467&546323#3272-(>@)8./)acU - @!)?!XOEi?Ejca' /.<-'% #?1O_d^;%!! 6E3PQ%U=:7@#'7~! ??/n3 .54673}r]kT>a㚅G9=sn# '654&'(r}l]Ga>s=#Z|7#'#7'5' "jFMLAb$q4>*{{*@6q-/ %##5#53533::ܭ<qW 72'6765'6 )X-0 W WaE06#5<<Hf 6#"&54632'"'F0% $lS #S?w"&62"2643rrrdMMdM ړO-v 3#526?3LF)6 >23!'6?6'&#"'>R/$4dX)HJV*!je%) E>0?xS9:#H/7+5:>54#"5767654'&#"'>32208 6,:'#2,4I+(Y!d/\P6)(A 9 $;%\<8 ) #'7)OE6"%Nv %#5#'67333dHAS@r?AXBCv7+57654&#"3#6;2=-RF\@0)J0'(N <",g1,Q>>&Vn/'%4'&#"32766;#"&5476QZ"8=4H +'1/B'01`XY[qE_&$NqV$1):;$C,fssF;v '6765!5E=WBZ;>v@nv@) ,:%#"&54767.'&546736&#"765.'32654 iLJg(5,0WMSK9688bW&3))%?* ]=10 %'7 //d&&,`%!5%!5~<<<<!> 77'!//d&&HL %6#"&546327#54>?654&#"5632'"'=R"+)/+;D1PF0% $<3*(=n/ " 9 >;0@j<3+J&#"32>7"&547#"&54>327332654&#"3267#"&54632%?&5  H#.:05'J1G' AE=[}|J,/.Tڑ7e0QY%."" 2 O2.YU59) A{~@9DTٜᯏ7xW v 3#'##3KRCCRV#vPv $%4.+3264+326'2#"'~-1#F-BQ"rJJ3?q<']8'=Ea=`$0 Ba`:%k*Q W?_gl7%#".54>32&#"327!`-Bb68b>_C:K*C%#E/Q7);bo>:pc=+4 9WY&,TS2)Pv #32>54'2+c]0B A]26`@6 .ON-@7^jzk\6vPv #3#3!vCEEvPxv #3##xثPvBEv7%#".54>32&#"32753?eBc68b>/F.7M6N!#E05#J :bp>:pc=5"^x:,TS3Pv !###333PPPP'v Pv#Nvv]v'653QNd#6<Pv !##33PPPUv Puv3!vBvPv#7##3PFPjvW4"RvPv !##3&53RPjP/'Tv0(7"2>4.2".4>8L::L:tY11YtY11C:YRFRY::YRFRY|@gmlmg@@gmlmgPv #32654&'2#"'#80!;FWCgo]1%P6W2"2>4.O?A,*,4P+1YtY1NgL::L:CL -Ech36mg@@gm6f+:YRFRY::YRFRYPv 4&+326#'&'#32rWC8.?InNPgRJA@S v`bMr-#2&#"#"&'732654.546+S-&A*78OO8lN%S"?6/=8PO8_("&00)(?,2S8M^!94=/#;.3P4OV v###5!N00FOv %"&53253``PPnp~v#3XWvv v# #3347UppUUasPsevvB! # v !#'#373\WWRC3 v#53˸OUvq:$v !!57#50xvEFDEd#3#]]::x lM#3M>>Bd#53#5ݛ]]::G@'# #3'FF8@3y!5>K<<-1&'7 uC>4^->B?H !'##33g65SQ;uZ&"'32&+654&+326541X5 Q ^l$76'3@4"5? Y, eLZ +(, 313A".547632&#"3278S/5:cR<98:&F;7#D1R\2qKU&4+"8RT/L&32Z3#32#3254_frVLG~|~zZ} 3!#3#3Z˜@BBZ^ 3##!O•B@A&#"32753#".547632.A9&F"!J@ZG!##373BNb|bqw  <[^!#3&53dObO  AA!".54>32"32>54.0L*%Y>?Y$$Y?. .*343S]1AwVXwwX'BF'%C?%FV54XEZ %#"'#32'#32654&b/I"Owgb(29>)[EU?1,5A-#".'.54>32327"32>54. &&)9O %Y>?Y$?@6* >. .*3 v )Zr?AwWXwCS-(BF'%B?%EV5&E@Z!/#32#32654&iOwgb@=( 29> \D<[?1,57e#"'732654/&54632&#"/&.!*8VS]47 ,%+!91SAH &*#3!51Y@Oh##5!MLACCY "&53253]TNN{[<Ė<\!#33RTQd_!#'#333MISP}SR[LZSP3Lqq !'#'373UwsVVpkR7#5373NTpqPE1 356?#5!31u^ *t_=B>C(^"(#".=4Ȏ=46;#"3" 9!336,; ;% !l67*2G/F2N46#7(B -7"Q#<(^","+'326=4>?.=4&+7323"3!9 5! ; ;3G2*76"7-.7#67*2Fr>32327#"&#" 0&$7 1%#44$D:HD::. 4632#"&653:'"'" <%!0% a`X\SL1;7&'67#5&'&5476751-.]C*",(:0K-A-<1/J2p<=yD)-t&/- << 5EytBAE&%#"&/#6='7354632&#"3#33267< ,$;'_BG! 1[G:0%Z2?=.Su<Xd &)&#"!#3#3267#"&'#73&547#73>32&7\ ^F*S>b_ nGeP mdlu?  ?p v#3##5#535'#53'33#3Ӕ)P)puUTty=Y<<X<:rs ;4&#"3262&#"#"'732654.5467&'&546-3!.73-H$` '.-#(&4 )&(!&A*5E 2KP17,)).!$9%=D " ,DH;J7 #"&54632#"&54632&#&#"%"%x6) &6324&"32'#"&54632&#"327⟠sp>JTPI<*-3-16%/z N⠠qsfLKj'"K79M"vS '##33)p(?>,Yv{{y?!> %'7'7 //I//d&&&&.%5!5!; <v5#, &546 4&#"327#'&'#32&4&+32 pqtq5x 3V=U8(nC5(#%zN⠠qtdyc;9)IjL# =0x!50x;;!p7"264&2"&4N77N7rRRrRY7N77NdRrRRr-%!5%##5#53533s::;<<}}<;1'>7>Dt -M?C=- I>P 654&5332673"&'#"'d L#"ZL'W(X%!C-zt%"#a'2 9%'65&#"327#"&54632'34D@@EF}*#%+&2]PUgS;G83 }r G./1cvSTl+H. #"&54632'"'0% $RK'654&#"53KB%&Q $h%?!>,  /<iZ "&54632&"32>4&IJEFIp>' &- ' iQO~QP6D(@[5APB@> 77'7'////d&&&&*6. %4632#"&3327#"&54?>5'"'"=R"+)6$;D1P$ 0% _0*(=n-!" 9 ><.Bi;3 r 3#'##37&'7KRCCRVD uC>4#vj->B?H r 3#'##37'>7KRCCRV>Dt -M#v?C=- I r 3#'##37&''67KRCCRVk 9@A8 PI>#vj.FG-7\P > 3#'##3'23267#"&#"'6KRCCRVnU$2$U  J#v, &!, +: , "3#'##3'#"&54632#"&54632KRCCRVJ&#&##v"%"% z 4&#"326"&5462 3#'##3;"%$R?V=?V-KRCCRV0$$0$hX>>,+?v v3#3#3!5##35JZCS]0#iCEEv7R+'654&#"5.54>32&#"327xB%&Q $2I'8b>aA>G(G##E/P7#0J?!>,  (D]a3:pc=+4!=(Gd,TT3,3' Pr #3#3!%&'7 uC>4vCEEvj->B?HPr #3#3!7'>7>Dt -MvCEEv?C=- IPr #3#3!%&''67< 9@A8 PI>vCEEvj.FG-7\PP, ##3#3!7#"&54632#"&54632&#&#vCEEv"%"%P(r #7&'7N uC>4vvj->B?HP(r #7'>7N>Dt -Mvv?C=- IPr #%&''67N2 9@A8 PI>vvj.FG-7\PPL,#7#"&54632#"&54632Nb&#&#vv"%"%P> #!##3&53%23267#"&#"'6RPjPU$2$U  J/'Tv0(, &!, +:7r%"2>4.2".4>7&'78L::L:tY11YtY11 uC>4C:YRFRY::YRFRY|@gmlmg@@gmlmg->B?H7r'"2>4.2".4>'>78L::L:tY11YtY11>Dt -MC:YRFRY::YRFRY|@gmlmg@@gmlmg-?C=- I7u)"2>4.2".4>%&''678L::L:tY11YtY11+ 9@A8 PI>C:YRFRY::YRFRY|@gmlmg@@gmlmg.FG-7\P7>5"2>4.2".4>723267#"&#"'68L::L:tY11YtY11OU$2$U  JC:YRFRY::YRFRY|@gmlmg@@gmlmg, &!, +:7,+7"2>4.2".4>7#"&54632#"&546328L::L:tY11YtY11u&#&#C:YRFRY::YRFRY|@gmlmg@@gmlmg"%"%7t -32>54&#"#7.54>3273#"'k,; +< $2.:60Y:&"2)840Y;!) 6ZN* 8]O*ա-W6le? u-U6mg@ Or %"&53253'&'7``PPZ uC>4np~j->B?HOr %"&53253''>7``PP>Dt -Mnp~?C=- IOr %"&53253'&''67``PP1 9@A8 PI>np~j.FG-7\PO, %%"&53253'#"&54632#"&54632``PP&#&#np~"%"%5%#"'732654.5463654&#"#'5354632&#"eH\RHN)74KK4XE=.+@LBBlDLf'24JJ4|EF27-,'.!$9&CH.25* 1LALZKL )&(!'B  !'##33&'7g65SQ;u- uC>4->B?H  !'##33'>7g65SQ;u>Dt -M'?C=- I  !'##33&''67g65SQ;u\ 9@A8 PI>.FG-7\P !'##3323267#"&#"'6g65SQ;u}U$2$U  J, &!, +: "!'##33#"&54632#"&54632g65SQ;uV&#&#"%"% !'##33"264.2#"&4g65SQ;u9%#0$!DV=>,+=#$$0$.?+,>?VM!5##35!#3#33.5RVș:lBCDAR*'654'&#"5.547632&#"327QB%&Q &EB5:cR<98:&F;7#/>+ ?, -XqKU&4+"8RT/L&3# Z} 3!#3#3&'7Z˜) uC>4@BBw->B?HZ} 3!#3#3'>7Z˜e>Dt -M@BB ?C=- IP &''67!#3#3 9@A8 PI>˜w.FG-7\PG@BBZ} #3!#3#3#"&54632#"&54632Z˜Ա&#&#@BB"%"%Z2  33&'7ZR uC>4w->B?HZ2  33'>7ZRH>Dt -M ?C=- IZ  33&''67ZR 9@A8 PI>w.FG-7\PZV33#"&54632#"&54632ZR&#&#"%"%Q%!#3&5323267#"&#"'6nObOU$2$U  J  A, &!, +:K !'".54>32"32>54.7&'70L*%Y>?Y$$Y?. .*34< uC>43S]1AwVXwwX'BF'%C?%FV54XE->B?HK !)".54>32"32>54.'>70L*%Y>?Y$$Y?. .*34>Dt -M3S]1AwVXwwX'BF'%C?%FV54XE7?C=- IK !+".54>32"32>54.7&''670L*%Y>?Y$$Y?. .*34g 9@A8 PI>3S]1AwVXwwX'BF'%C?%FV54XE.FG-7\PD!7".54>32"32>54.23267#"&#"'60L*%Y>?Y$$Y?. .*34sU$2$U  J3S]1AwVXwwX'BF'%C?%FV54XE, &!, +:K!-9".54>32"32>54.'#"&54632#"&546320L*%Y>?Y$$Y?. .*34K&#&#3S]1AwVXwwX'BF'%C?%FV54XE"%"%6. #"&54632#"&546327!54'"'"'"'0% $0% $u<<A] *7&#"32>547#"'#7.54>327l. j*3--$Y?2$01%Y>Uu'BF'rFV5im$GBwXf|$JAwV Tc "&53253&'7gTNNF uC>4{[<Ė<\->B?Hc "&53253'>7gTNN>Dt -M{[<Ė<\?C=- Ic &''67"&53253 9@A8 PI>%TNNw.FG-7\P7{[<Ė<\c %"&53253#"&54632#"&54632gTNN&#&#{[<Ė<\4"%"%  #"&54632#"&54632#5373&#&#TNTpqP"%"%*EZ33ZR7Vv "3#3#3!".54>3)9 9(D8U-.T766XK('IS37CEE?dh42ga=A3".54>3!#3#3=T"#U;ʛ$,,$Tq?>oQBDDAO1.L> , #53'#"&54632#"&54632˸OU&#&#vq:"%"% q)&#"3#'>7'73>7632q("  p n &8$)   < 8j/$`  :Or+2+:.J*SU29y<0n &''67n 9@A8 PI>^.FG-7\P<p 67&'\:?Q* c7CWEM-GKW<0#d "&'73266.UTU00$:73YZ3572V-> #"&54632&$v!#)i "264.2"&4%#0$!DV=?V=#$$0$.?V??V|S.547>3#"%C# 3;?! ! 0Ru23267#"&#"'6yU$2$U  J, &!, +:;1'>7'>7>Dt -M>t -:?C=- I?=- 7 MV263232632326367433263232632?262326323263#"&"#"&'&+#"'"&#"#"'#"'.'&'.'&'&5474&5467"'&#"#"'.#.#&'&54627>5654'.#""%254'&#"26323273276323276726?2263232632#""#"&#"32632;>363263226323263233272636=&'&/&#"#"&#""&#"#"&#"#"&#"'4'321 (  6 # (8# !#%&%1'$  )    * !542*(~     "-! (      - *        &   " & C66ac$ ! .   3:&u2 '(  ) !   #G .?Apj$ .D'!=5    "      E 1 -C    4Cv  3#3#3Cכwwv1q32< <Dꮬf!5f<<`!5`<<. "&/67n )X-0  WaE00 2'6765'6 )X-0  WaE0tZ 72'6765'6 )X-0 Z WaE0/!26?&54767'#26?&54767'  0-'A " 0-'A "  0E+\.  0E+\./!">?&3">?& 0-'A " 0-'A " 0E+\. 0E+\.t|Z!7">?&3">?& 0-'A " 0-'A "Z 0E+\. 0E+\.!}v#&=#5353} %&KBמBJB!}u#&5#535#53533#} %&K ABJABB'2"&4ffeeeeFf  6#"&54632#"&54632#"&54632'"''"''"'F0% $ 0% $ 0% $.'/7?4&"2%#'"&4632327"&4624&"2$"&4624&"2+>++>B'A NnNN7  ,>1NnNNn+>++>NnNNn+>++>>++>+H 7NNnN  )nNNnN>++>+nNNnN>++>+!> %'7 //d&&!> 77'!//d&&} #R?lC)###53!#7##36a1a2r,r3Cuta,s6E!O # J9o!o8(u73#''5#7'7'35$=K&@?&L<&%=#I=<'L=%%=H$A@&K?%$7632&#"#"&'&547>7.546323#7274'&#";"+VP    +9 # R8AOLK #@%,4/1v)b 9 C-(2,!1 #6;F1'?5 $3 ;! #23#'>54#"'>#5#52763.?.7lw .f0+. ,=M!f..Q^83p+&%1s-O-v 3#526?3LF)6 .KT"&#"#"&#""&#&'4#""&#"#"&#"/"&"#"&#"#"&#326232676;3272632327327>767>767654'4654&'27632327>3>367654&"'.5&547>322%"547632"&#"#"'#"'&#"#"'&'"&/""&#"#"&#"32232632#"&#"+.#&#"&#""&#"#"&#"##"'"&#&=676?6323263232623263232632747#" (  6 # (8# !#%&%1'$  )    * !542*(   (  "-! (      - *        &   " & C66ac$ ! .   3:&u2 '(  ) !   #G .?Apj$ .D'!=5    "      E 1 -C    4,13#3#'7#537#53L%*c9<,j8A;Z;_G;Z; qW 72'6765'6y )X-0 W WaE04f 7#"&54632&"&".$ $ r'7=_6 !33##!LwN•B@_!333##!KǍN>@•B@4u&Jz 2 &M  < & : &R-3.5, I1995 Jos Buivenga-3.5, I1995 Jos BuivengaDelicious SmallCapsDelicious SmallCapsRegularRegularDelicious-SmallCaps:1140922925Delicious-SmallCaps:1140922925Delicious-SmallCapsDelicious-SmallCaps001.000001.000Delicious-SmallCapsDelicious-SmallCapst  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghjikmlnoqprsutvwxzy{}|~nbspace  latn ,latnkern7x |FRx`x6.llll-M &24 FJRT 79:<YZ\$DFHJRT &*2479:<\ 7<oo$DFR9<FHR2$&*24DFHRUVXz{|}~"$DFRz{|}~ $z{|}~9$&*24DFHPQRSTVWXY]z{|}~W\FHR D D79:< 7<WD0$&*24DFHPQRSWXz{|}~ &*24&*24VW &*247 $)*./234579:<>DISUWYZ\^z{|}~ &]&]swift-im-2.0+dev6/BuildTools/CLang/0000755000175000017500000000000012227051774016727 5ustar kismithkismithswift-im-2.0+dev6/BuildTools/CLang/SConscript0000644000175000017500000000171512227051773020744 0ustar kismithkismithImport("env") #myenv = Environment() #myenv.Append(CPPPATH = ["."]) #myenv.Program("CLangDiagnosticsFlagsTool", ["CLangDiagnosticsFlagsTool.cpp"]) # #disabledDiagnostics = ["-wunreachable-code", "-wunused-macros", "-wmissing-noreturn", "-wlong-long", "-wcast-align", "-wglobal-constructors", "-wmissing-prototypes", "-wpadded", "-wshadow"] #clangDiagnosticsFlagsToolCommand = "BuildTools/CLang/CLangDiagnosticsFlagsTool -O" + env.Dir(".").abspath + " " + " ".join(disabledDiagnostics) + " " #clangDiagnosticsFlagsToolCommand += " ".join([flag for flag in env["CXXFLAGS"] if flag.startswith("-W")]) #clangDiagnosticsFlagsToolCommand += "\n" #clangDiagnosticsFlagsToolCommand += "dot -Tpng " + env.Dir(".").abspath + "/clang-diagnostics-overview.dot > " + env.Dir(".").abspath + "/clang-diagnostics-overview.png\n" #v = env.WriteVal("#/BuildTools/CLang/CLangDiagnosticsFlagsTool.sh", env.Value(clangDiagnosticsFlagsToolCommand)) #env.AddPostAction(v, Chmod(v[0], 0755)) # # swift-im-2.0+dev6/BuildTools/CLang/CLangDiagnosticsFlagsTool.cpp0000644000175000017500000001572612227051773024434 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include // ----------------------------------------------------------------------------- // Include diagnostics data from CLang // ----------------------------------------------------------------------------- #define DIAG(name, a, b, c, d, e, f, g) name, namespace diag { enum LexKinds { #include #include #include #include #include #include }; } #define GET_DIAG_ARRAYS #include #undef GET_DIAG_ARRAYS struct DiagTableEntry { const char* name; const short* array; const short* group; }; static const DiagTableEntry diagnostics[] = { #define GET_DIAG_TABLE #include #undef GET_DIAG_TABLE }; static const size_t diagnostics_count = sizeof(diagnostics) / sizeof(diagnostics[0]); // ----------------------------------------------------------------------------- using namespace boost; struct Properties { Properties() : have(false), implicitHave(false), dontWant(false), implicitDontWant(false), ignored(false), available(false), missing(false), redundant(false), alreadyCovered(false) { } std::string name; bool have; bool implicitHave; bool dontWant; bool implicitDontWant; bool ignored; bool available; bool missing; bool redundant; bool alreadyCovered; }; class GraphVizLabelWriter { public: GraphVizLabelWriter(const std::vector& properties) : properties(properties) { } template void operator()(std::ostream& out, const VertexOrEdge& v) const { std::string color; if (properties[v].missing) { color = "orange"; } else if (properties[v].redundant) { color = "lightblue"; } else if (properties[v].have) { color = "darkgreen"; } else if (properties[v].implicitHave) { color = "green"; } else if (properties[v].dontWant) { color = "red"; } else if (properties[v].implicitDontWant) { color = "pink"; } else if (properties[v].ignored) { color = "white"; } else if (properties[v].available) { color = "yellow"; } else { assert(false); } out << "[label=" << escape_dot_string(properties[v].name) << " fillcolor=\"" << color << "\" style=filled]"; } private: const std::vector properties; }; int main(int argc, char* argv[]) { // Parse command-line arguments std::set have; std::set dontWant; std::string outputDir; for (int i = 1; i < argc; ++i) { std::string arg(argv[i]); if (starts_with(arg, "-W")) { have.insert(arg.substr(2, arg.npos)); } else if (starts_with(arg, "-w")) { dontWant.insert(arg.substr(2, arg.npos)); } else if (starts_with(arg, "-O")) { outputDir = arg.substr(2, arg.npos) + "/"; } } // Build the graph and initialize properties typedef adjacency_list Graph; typedef graph_traits::vertex_descriptor Vertex; Graph g(diagnostics_count); std::vector properties(num_vertices(g)); for (size_t i = 0; i < diagnostics_count; ++i) { std::string name(diagnostics[i].name); properties[i].name = name; properties[i].implicitHave = properties[i].have = have.find(name) != have.end(); properties[i].implicitDontWant = properties[i].dontWant = dontWant.find(name) != dontWant.end(); properties[i].ignored = diagnostics[i].group == 0 && diagnostics[i].array == 0; properties[i].alreadyCovered = false; properties[i].available = true; for (const short* j = diagnostics[i].group; j && *j != -1; ++j) { add_edge(i, *j, g); } } // Sort the diagnostics std::list sortedDiagnostics; boost::topological_sort(g, std::front_inserter(sortedDiagnostics)); // Propagate dontWant and have properties down for(std::list::const_iterator i = sortedDiagnostics.begin(); i != sortedDiagnostics.end(); ++i) { graph_traits::adjacency_iterator adjacentIt, adjacentEnd; for (tie(adjacentIt, adjacentEnd) = adjacent_vertices(*i, g); adjacentIt != adjacentEnd; ++adjacentIt) { properties[*adjacentIt].implicitDontWant = properties[*i].implicitDontWant || properties[*adjacentIt].implicitDontWant; properties[*adjacentIt].implicitHave = properties[*i].implicitHave || properties[*adjacentIt].implicitHave; } } // Propagate 'available' property upwards for(std::list::const_reverse_iterator i = sortedDiagnostics.rbegin(); i != sortedDiagnostics.rend(); ++i) { properties[*i].available = properties[*i].available && !properties[*i].implicitDontWant; graph_traits::in_edge_iterator edgesIt, edgesEnd; graph_traits::edge_descriptor edge; for (tie(edgesIt, edgesEnd) = in_edges(*i, g); edgesIt != edgesEnd; ++edgesIt) { properties[source(*edgesIt, g)].available = properties[source(*edgesIt, g)].available && properties[*i].available; } } // Collect missing & redundant flags std::set missing; std::set redundant; for(std::list::const_iterator i = sortedDiagnostics.begin(); i != sortedDiagnostics.end(); ++i) { bool markChildrenCovered = true; if (properties[*i].alreadyCovered) { if (properties[*i].have) { properties[*i].redundant = true; redundant.insert(properties[*i].name); } } else { if (properties[*i].available) { if (!properties[*i].implicitHave && !properties[*i].ignored) { properties[*i].missing = true; missing.insert(properties[*i].name); } } else { markChildrenCovered = false; } } if (markChildrenCovered) { graph_traits::adjacency_iterator adjacentIt, adjacentEnd; for (tie(adjacentIt, adjacentEnd) = adjacent_vertices(*i, g); adjacentIt != adjacentEnd; ++adjacentIt) { properties[*adjacentIt].alreadyCovered = true; } } } // Write information if (!missing.empty()) { std::cout << "Missing diagnostic flags: "; for(std::set::const_iterator i = missing.begin(); i != missing.end(); ++i) { std::cout << "-W" << *i << " "; } std::cout<< std::endl; } if (!redundant.empty()) { std::cout << "Redundant diagnostic flags: "; for(std::set::const_iterator i = redundant.begin(); i != redundant.end(); ++i) { std::cout << "-W" << *i << " "; } std::cout<< std::endl; } // Write graphviz file if (!outputDir.empty()) { std::ofstream f((outputDir + "clang-diagnostics-overview.dot").c_str()); write_graphviz(f, g, GraphVizLabelWriter(properties)); f.close(); } return 0; } swift-im-2.0+dev6/BuildTools/Copyrighter.py0000755000175000017500000001051112227051773020614 0ustar kismithkismith#!/usr/bin/env python #coding=utf-8 import os, re, datetime, sys, subprocess DEFAULT_LICENSE = "gpl3" CONTRIBUTOR_LICENSE = "mit" LICENSE_DIR = "Documentation/Licenses" class License : def __init__(self, name, file) : self.name = name self.file = file licenses = { "gpl3" : License("GNU General Public License v3", "GPLv3.txt"), "mit" : License("MIT License", "MIT.txt"), } class Copyright : def __init__(self, author, year, license) : self.author = author self.year = year self.license = license def to_string(self, comment_chars) : return "\n".join([ comment_chars[0], comment_chars[1] + " Copyright (c) %(year)s %(name)s" % {"year" : self.year, "name" : self.author }, comment_chars[1] + " Licensed under the " + licenses[self.license].name + ".", comment_chars[1] + " See " + LICENSE_DIR + "/" + licenses[self.license].file + " for more information.", comment_chars[2], "\n"]) def get_comment_chars_for_filename(filename) : return ("/*", " *", " */") def get_comment_chars_re_for_filename(filename) : comment_chars = get_comment_chars_for_filename(filename) return "|".join(comment_chars).replace("*", "\\*") def parse_file(filename) : file = open(filename) copyright_text = [] prolog = "" epilog = "" inProlog = True inCopyright = False inEpilog = False for line in file.readlines() : if inProlog : if line.startswith("#!") or len(line.strip()) == 0 : prolog += line continue else : inProlog = False inCopyright = True if inCopyright : if re.match(get_comment_chars_re_for_filename(filename), line) != None : copyright_text.append(line.rstrip()) continue else : inCopyright = False inEpilog = True if len(line.strip()) == 0 : continue if inEpilog : epilog += line continue file.close() # Parse the copyright copyright = None if len(copyright_text) == 5 : comment_chars = get_comment_chars_for_filename(filename) if copyright_text[0] == comment_chars[0] and copyright_text[4] == comment_chars[2] : matchstring = "(" + get_comment_chars_re_for_filename(filename) + ") Copyright \(c\) (?P\d\d\d\d)(-(?P\d\d\d\d))? (?P.*)" m = re.match(matchstring, copyright_text[1]) if m != None : # FIXME: Do better copyright reconstruction here copyright = True if not copyright : epilog = "\n".join(copyright_text) + epilog return (prolog, copyright, epilog) def get_userinfo() : p = subprocess.Popen("git config user.name", shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=(os.name != "nt")) username = p.stdout.read().rstrip() p.stdin.close() if p.wait() != 0 : return None p = subprocess.Popen("git config user.email", shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=(os.name != "nt")) email = p.stdout.read().rstrip() p.stdin.close() if p.wait() != 0 : return None return (username, email) def get_copyright(username, email) : if email in ["git@el-tramo.be", "git@kismith.co.uk"] : license = DEFAULT_LICENSE else : license = CONTRIBUTOR_LICENSE return Copyright(username, datetime.date.today().strftime("%Y"), license) def check_copyright(filename) : (prolog, copyright, epilog) = parse_file(filename) if copyright == None : print "No copyright found in: " + filename #print "Please run '" + sys.argv[0] + " set-copyright " + filename + "'" return False else : return True def set_copyright(filename, copyright) : (prolog, c, epilog) = parse_file(filename) comment_chars = get_comment_chars_for_filename(filename) copyright_text = copyright.to_string(comment_chars) file = open(filename, "w") if prolog != "": file.write(prolog) file.write(copyright_text) if epilog != "" : file.write(epilog) file.close() if sys.argv[1] == "check-copyright" : file = sys.argv[2] if (file.endswith(".cpp") or file.endswith(".h")) and not "3rdParty" in file : if not check_copyright(file) : sys.exit(-1) elif sys.argv[1] == "set-copyright" : (username, email) = get_userinfo() copyright = get_copyright(username, email) set_copyright(sys.argv[2], copyright) else : print "Unknown command: " + sys.argv[1] sys.exit(-1) swift-im-2.0+dev6/BuildTools/Git/0000755000175000017500000000000012227051773016465 5ustar kismithkismithswift-im-2.0+dev6/BuildTools/Git/Hooks/0000755000175000017500000000000012227051773017550 5ustar kismithkismithswift-im-2.0+dev6/BuildTools/Git/Hooks/pre-commit0000755000175000017500000000063312227051773021554 0ustar kismithkismith#!/bin/sh IFS=' ' echo "Checking tabs & copyrights ..." for file in $(git diff --cached --name-only); do if [ ! -f $file ]; then continue fi if ! BuildTools/CheckTabs.py $file; then echo "ERROR: '$file' contains expanded tabs. Aborting commit." exit -1 fi if ! BuildTools/Copyrighter.py check-copyright $file; then echo "ERROR: '$file' has a copyright error. Aborting commit." exit -1 fi done swift-im-2.0+dev6/BuildTools/Git/Hooks/commit-msg0000755000175000017500000001023212227051773021550 0ustar kismithkismith#!/bin/sh # From Gerrit Code Review 2.4.2 # # Part of Gerrit Code Review (http://code.google.com/p/gerrit/) # # Copyright (C) 2009 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # CHANGE_ID_AFTER="Bug|Issue" MSG="$1" # Check for, and add if missing, a unique Change-Id # add_ChangeId() { clean_message=`sed -e ' /^diff --git a\/.*/{ s/// q } /^Signed-off-by:/d /^#/d ' "$MSG" | git stripspace` if test -z "$clean_message" then return fi # Does Change-Id: already exist? if so, exit (no change). if grep -i '^Change-Id:' "$MSG" >/dev/null then return fi id=`_gen_ChangeId` T="$MSG.tmp.$$" AWK=awk if [ -x /usr/xpg4/bin/awk ]; then # Solaris AWK is just too broken AWK=/usr/xpg4/bin/awk fi # How this works: # - parse the commit message as (textLine+ blankLine*)* # - assume textLine+ to be a footer until proven otherwise # - exception: the first block is not footer (as it is the title) # - read textLine+ into a variable # - then count blankLines # - once the next textLine appears, print textLine+ blankLine* as these # aren't footer # - in END, the last textLine+ block is available for footer parsing $AWK ' BEGIN { # while we start with the assumption that textLine+ # is a footer, the first block is not. isFooter = 0 footerComment = 0 blankLines = 0 } # Skip lines starting with "#" without any spaces before it. /^#/ { next } # Skip the line starting with the diff command and everything after it, # up to the end of the file, assuming it is only patch data. # If more than one line before the diff was empty, strip all but one. /^diff --git a/ { blankLines = 0 while (getline) { } next } # Count blank lines outside footer comments /^$/ && (footerComment == 0) { blankLines++ next } # Catch footer comment /^\[[a-zA-Z0-9-]+:/ && (isFooter == 1) { footerComment = 1 } /]$/ && (footerComment == 1) { footerComment = 2 } # We have a non-blank line after blank lines. Handle this. (blankLines > 0) { print lines for (i = 0; i < blankLines; i++) { print "" } lines = "" blankLines = 0 isFooter = 1 footerComment = 0 } # Detect that the current block is not the footer (footerComment == 0) && (!/^\[?[a-zA-Z0-9-]+:/ || /^[a-zA-Z0-9-]+:\/\//) { isFooter = 0 } { # We need this information about the current last comment line if (footerComment == 2) { footerComment = 0 } if (lines != "") { lines = lines "\n"; } lines = lines $0 } # Footer handling: # If the last block is considered a footer, splice in the Change-Id at the # right place. # Look for the right place to inject Change-Id by considering # CHANGE_ID_AFTER. Keys listed in it (case insensitive) come first, # then Change-Id, then everything else (eg. Signed-off-by:). # # Otherwise just print the last block, a new line and the Change-Id as a # block of its own. END { unprinted = 1 if (isFooter == 0) { print lines "\n" lines = "" } changeIdAfter = "^(" tolower("'"$CHANGE_ID_AFTER"'") "):" numlines = split(lines, footer, "\n") for (line = 1; line <= numlines; line++) { if (unprinted && match(tolower(footer[line]), changeIdAfter) != 1) { unprinted = 0 print "Change-Id: I'"$id"'" } print footer[line] } if (unprinted) { print "Change-Id: I'"$id"'" } }' "$MSG" > $T && mv $T "$MSG" || rm -f $T } _gen_ChangeIdInput() { echo "tree `git write-tree`" if parent=`git rev-parse "HEAD^0" 2>/dev/null` then echo "parent $parent" fi echo "author `git var GIT_AUTHOR_IDENT`" echo "committer `git var GIT_COMMITTER_IDENT`" echo printf '%s' "$clean_message" } _gen_ChangeId() { _gen_ChangeIdInput | git hash-object -t commit --stdin } add_ChangeId swift-im-2.0+dev6/BuildTools/UpdateDebianChangelog.py0000755000175000017500000000204312227051773022453 0ustar kismithkismith#!/usr/bin/env python import sys, re, email.utils assert(len(sys.argv) == 3) version = sys.argv[2] changelog = open(sys.argv[1]) last_version_line = changelog.readline() changelog.close() project = "" last_version = "" m = re.match("([\w-]+) \((.*)-\d+\)", last_version_line) if m : project = m.group(1) last_version = m.group(2) if project == "" : project="swift-im" if "dev" in version : distribution = "development" elif "beta" in version or "rc" in version : distribution = "beta development" else : distribution = "release beta development" if last_version != version : changelog = open(sys.argv[1]) changelog_data = changelog.read() changelog.close() changelog = open(sys.argv[1], "w") changelog.write(project + " (" + version + "-1)" + " " + distribution + "; urgency=low\n\n") changelog.write(" * Upstream development snapshot\n\n") changelog.write(" -- Swift Package Maintainer " + email.utils.formatdate() + "\n") changelog.write("\n") changelog.write(changelog_data) changelog.close() swift-im-2.0+dev6/BuildTools/Gource/0000755000175000017500000000000012227051774017167 5ustar kismithkismithswift-im-2.0+dev6/BuildTools/Gource/RunGource.sh0000755000175000017500000000020212227051773021430 0ustar kismithkismith#!/bin/sh gource --user-scale 2.0 --file-filter 3rdParty --user-image-dir BuildTools/Gource/UserImages -s .5 --hide filenames $@ swift-im-2.0+dev6/BuildTools/Gource/GetGravatars.py0000755000175000017500000000251312227051773022136 0ustar kismithkismith#!/usr/bin/env python import subprocess, os, sys, hashlib, urllib GRAVATAR_URL = "http://www.gravatar.com/avatar/%(id)s?d=404" if len(sys.argv) != 2 : print "Usage: " + sys.argv[0] + " " sys.exit(-1) output_dir = sys.argv[1] # Retrieve the list of authors authors = {} p = subprocess.Popen("git log --pretty=format:'%ae|%an'", shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=(os.name != "nt")) for line in p.stdout.readlines() : author_components = line.rstrip().split("|") authors[author_components[0]] = author_components[1] p.stdin.close() if p.wait() != 0 : print "Error" sys.exit(-1) # Get & save the avatars if not os.path.isdir(output_dir) : os.makedirs(output_dir) for email, name in authors.items() : print "Processing avatar for " + name + " <" + email + ">" filename = os.path.join(output_dir, name + ".png") if os.path.isfile(filename) : print "-> Already there. Skipping." continue m = hashlib.md5() m.update(email) url = GRAVATAR_URL % {"id" : m.hexdigest()} print "- Downloading " + url f = urllib.urlopen(url) input = None if f.getcode() == 200 : input = f.read() f.close() if input : print "- Saving file " + filename f = open(filename, "w") f.write(input) f.close() else : print "- No Gravatar found" swift-im-2.0+dev6/BuildTools/Gource/CreateVideo.sh0000755000175000017500000000061712227051773021723 0ustar kismithkismith#!/bin/sh # Can be 25, 30, or 60 (per Gource) FRAMERATE=25 BITRATE=3000K BuildTools/Gource/GetGravatars.py BuildTools/Gource/UserImages BuildTools/Gource/RunGource.sh --disable-progress --stop-at-end -640x360 $@ --output-framerate $FRAMERATE --output-ppm-stream - | ffmpeg -y -b $BITRATE -r $FRAMERATE -f image2pipe -vcodec ppm -i - -vcodec libx264 -vpre default BuildTools/Gource/SwiftGource.mp4 swift-im-2.0+dev6/BuildTools/CheckTabs.py0000755000175000017500000000144012227051773020145 0ustar kismithkismith#!/usr/bin/env python import os, sys foundExpandedTabs = False filename = sys.argv[1] if (filename.endswith(".cpp") or filename.endswith(".h")) and not "3rdParty" in filename : file = open(filename, "r") contents = [] contentsChanged = False for line in file.readlines() : newline = "" previousChar = None pastInitialSpace = False for char in line : if not pastInitialSpace : if char == ' ' and previousChar == ' ' : contentsChanged = True previousChar = '\t' continue pastInitialSpace = (char != ' ') if previousChar : newline += previousChar previousChar = char if previousChar : newline += previousChar contents.append(newline) file.close() if contentsChanged : sys.exit(-1) swift-im-2.0+dev6/BuildTools/SCons/0000755000175000017500000000000012227051773016767 5ustar kismithkismithswift-im-2.0+dev6/BuildTools/SCons/SConstruct0000644000175000017500000004630012227051773021024 0ustar kismithkismithimport sys, os, re, platform import SCons.SConf Import("env", "conf_env") root = Dir("../..").abspath # Override SConscript to handle tests oldSConscript = SConscript def SConscript(*arguments, **keywords) : if not keywords.get("test_only", False) or env["TEST"] : return apply(oldSConscript, arguments, keywords) env.SConscript = SConscript ################################################################################ # Extend the default build environment (not affecting the configure env) # # Keeping both environments separated mostly because of SCons Issue 2391, # although it doesn't hurt to separate them (e.g. not have pretty printed # strings in config.log) ################################################################################ #if env["PLATFORM"] == "win32" : # env["MSVC_BATCH"] = 1 # Pretty output def colorize(command, target, color) : colors = { "red": "31", "green": "32", "yellow": "33", "blue": "34" } prefix = "" suffix = "" if sys.stdout.isatty() and env["PLATFORM"] != "win32": prefix = "\033[0;" + colors[color] + ";140m" suffix = "\033[0m" return " " + prefix + command + suffix + " " + target if int(ARGUMENTS.get("V", 0)) == 0: env["CCCOMSTR"] = colorize("CC", "$TARGET", "green") env["SHCCCOMSTR"] = colorize("CC", "$TARGET", "green") env["CXXCOMSTR"] = colorize("CXX", "$TARGET", "green") env["SHCXXCOMSTR"] = colorize("CXX", "$TARGET", "green") env["LINKCOMSTR"] = colorize("LINK", "$TARGET", "red") env["SHLINKCOMSTR"] = colorize("LINK", "$TARGET", "red") env["ARCOMSTR"] = colorize("AR", "$TARGET", "red") env["RANLIBCOMSTR"] = colorize("RANLIB", "$TARGET", "red") env["QT4_RCCCOMSTR"] = colorize("RCC", "$TARGET", "blue") env["QT4_UICCOMSTR"] = colorize("UIC", "$TARGET", "blue") env["QT4_MOCFROMHCOMSTR"] = colorize("MOC", "$TARGET", "blue") env["QT4_MOCFROMCXXCOMSTR"] = colorize("MOC", "$TARGET", "blue") env["QT4_LRELEASECOMSTR"] = colorize("LRELEASE", "$TARGET", "blue") env["QT4_LUPDATECOMSTR"] = colorize("LUPDATE", "$TARGET", "blue") env["GENCOMSTR"] = colorize("GEN", "$TARGET", "blue") env["RCCOMSTR"] = colorize("RC", "$TARGET", "blue") env["BUNDLECOMSTR"] = colorize("BUNDLE", "$TARGET", "blue") env["NIBCOMSTR"] = colorize("NIB", "$TARGET", "blue") env["NSISCOMSTR"] = colorize("NSIS", "$TARGET", "blue") env["INSTALLSTR"] = colorize("INSTALL", "$TARGET", "blue") env["TESTCOMSTR"] = colorize("TEST", "$SOURCE", "yellow") env["FOCOMSTR"] = colorize("FO", "$TARGET", "blue") env["XSLTCOMSTR"] = colorize("XSLT", "$TARGET", "blue") env["XMLLINTCOMSTR"] = colorize("XMLLINT", "$SOURCE", "blue") env["DOXYCOMSTR"] = colorize("DOXY", "$SOURCE", "blue") #Progress(colorize("DEP", "$TARGET", "red") def checkObjCHeader(context, header) : context.Message("Checking for Objective-C header " + header + " ... ") ret = context.TryCompile("#include \n#include <" + header + ">", ".m") context.Result(ret) return ret ################################################################################ # Platform configuration ################################################################################ if ARGUMENTS.get("force-configure", 0) : SCons.SConf.SetCacheMode("force") def CheckPKG(context, name): context.Message( 'Checking for package %s... ' % name ) ret = context.TryAction('pkg-config --exists \'%s\'' % name)[0] context.Result( ret ) return ret def CheckVersion(context, library, version, define, header, value) : context.Message("Checking " + library + " version (>= " + version + ") ...") ret = context.TryRun(""" #include <%(header)s> #include int main(int argc, char* argv[]) { printf("%%d\\n", %(define)s); return 0; } """ % { "header" : header, "define": define }, ".c") ok = ret[0] and int(ret[1]) >= value context.Result(ok) return ok conf = Configure(conf_env) if not conf.CheckCXX() or not conf.CheckCC() : print "Error: You need a working compiler" Exit(1) env["HAVE_ZLIB"] = True if conf.CheckLib("z") : env["ZLIB_FLAGS"] = {"LIBS": ["z"]} else : env["ZLIB_BUNDLED"] = True if conf.CheckLib("resolv") : env["PLATFORM_FLAGS"]["LIBS"] = env["PLATFORM_FLAGS"].get("LIBS", []) + ["resolv"] if env["PLATFORM"] != "win32" : if conf.CheckLib("pthread") : env["PLATFORM_FLAGS"]["LIBS"] = env["PLATFORM_FLAGS"].get("LIBS", []) + ["pthread"] if conf.CheckLib("dl") : env["PLATFORM_FLAGS"]["LIBS"] = env["PLATFORM_FLAGS"].get("LIBS", []) + ["dl"] if conf.CheckLib("m") : env["PLATFORM_FLAGS"]["LIBS"] = env["PLATFORM_FLAGS"].get("LIBS", []) + ["m"] if conf.CheckLib("c") : env["PLATFORM_FLAGS"]["LIBS"] = env["PLATFORM_FLAGS"].get("LIBS", []) + ["c"] if conf.CheckLib("stdc++") : env["PLATFORM_FLAGS"]["LIBS"] = env["PLATFORM_FLAGS"].get("LIBS", []) + ["stdc++"] conf.Finish() # Boost boost_conf_env = conf_env.Clone() boost_flags = {} if env.get("boost_libdir", None) : boost_flags["LIBPATH"] = [env["boost_libdir"]] if env.get("boost_includedir", None) : if env["PLATFORM"] == "win32" : boost_flags["CPPPATH"] = [env["boost_includedir"]] else : # Using isystem to avoid getting warnings from a system boost # Unfortunately, this also disables dependency tracking boost_flags["CPPFLAGS"] = [("-isystem", env["boost_includedir"])] boost_conf_env.MergeFlags(boost_flags) conf = Configure(boost_conf_env) boostLibs = [("signals", None), ("thread", None), ("regex", None), ("program_options", None), ("filesystem", None), ("system", "system/system_error.hpp"), ("date_time", "date_time/date.hpp")] allLibsPresent = True libNames = [] for (lib, header) in boostLibs : if header : header = "boost/" + header else : header = "boost/" + lib + ".hpp" if not conf.CheckCXXHeader(header) : allLibsPresent = False break if env["PLATFORM"] != "win32" : libName = "boost_" + lib if not conf.CheckLib(libName) : libName += "-mt" if not conf.CheckLib(libName) : allLibsPresent = False break libNames.append(libName) if allLibsPresent : env["BOOST_FLAGS"] = boost_flags if env["PLATFORM"] != "win32" : env["BOOST_FLAGS"].update({"LIBS": libNames}) if not conf.CheckCXXHeader("boost/uuid/uuid.hpp") : # FIXME: Remove this workaround when UUID is available in most distros env["BOOST_BUNDLED_UUID_ONLY"] = True else : env["BOOST_BUNDLED"] = True conf.Finish() # Xss env["HAVE_XSS"] = 0 if env["PLATFORM"] != "win32" and env["PLATFORM"] != "darwin" : xss_flags = { "LIBPATH": ["/usr/X11R6/lib"], "LIBS": ["Xss"] } xss_env = conf_env.Clone() xss_env.MergeFlags(xss_flags) conf = Configure(xss_env) if conf.CheckFunc("XScreenSaverQueryExtension") : env["HAVE_XSS"] = 1 env["XSS_FLAGS"] = xss_flags conf.Finish() # GConf env["HAVE_GCONF"] = 0 if env["PLATFORM"] != "win32" and env["PLATFORM"] != "darwin" : gconf_env = conf_env.Clone() conf = Configure(gconf_env, custom_tests = {"CheckPKG": CheckPKG}) if conf.CheckPKG("gconf-2.0") : gconf_bare_env = Environment() gconf_bare_env.ParseConfig('pkg-config --cflags gconf-2.0 gobject-2.0 --libs gconf-2.0 gobject-2.0') gconf_flags = { "LIBS": gconf_bare_env["LIBS"], "CCFLAGS": gconf_bare_env["CCFLAGS"], "CPPPATH": gconf_bare_env["CPPPATH"], "CPPDEFINES": gconf_bare_env.get("CPPDEFINES", []), } gconf_env.MergeFlags(gconf_flags) if conf.CheckCHeader("gconf/gconf-client.h") and conf.CheckLib("gconf-2") : env["HAVE_GCONF"] = 1 env["GCONF_FLAGS"] = { "LIBS": gconf_env["LIBS"], "CCFLAGS": gconf_env["CCFLAGS"], "CPPPATH": gconf_env["CPPPATH"], "CPPDEFINES": gconf_env.get("CPPDEFINES", []), } conf.Finish() # Sparkle env["HAVE_SPARKLE"] = 0 if env["PLATFORM"] == "darwin" : sparkle_flags = { "FRAMEWORKPATH": ["/Library/Frameworks"], "FRAMEWORKS": ["Sparkle"] } sparkle_env = conf_env.Clone() sparkle_env.MergeFlags(sparkle_flags) conf = Configure(sparkle_env, custom_tests = { "CheckObjCHeader" : checkObjCHeader }) if conf.CheckObjCHeader("Sparkle/Sparkle.h") : env["HAVE_SPARKLE"] = 1 env["SPARKLE_FLAGS"] = sparkle_flags env["SPARKLE_FRAMEWORK"] = "/Library/Frameworks/Sparkle.framework" conf.Finish() # Growl env["HAVE_GROWL"] = 0 if env["PLATFORM"] == "darwin" : growl_flags = { "FRAMEWORKPATH": ["/Library/Frameworks"], "FRAMEWORKS": ["Growl"] } growl_env = conf_env.Clone() growl_env.MergeFlags(growl_flags) conf = Configure(growl_env, custom_tests = { "CheckObjCHeader" : checkObjCHeader }) if conf.CheckObjCHeader("Growl/Growl.h") : env["HAVE_GROWL"] = 1 env["GROWL_FLAGS"] = growl_flags env["GROWL_FRAMEWORK"] = "/Library/Frameworks/Growl.framework" conf.Finish() # Snarl if env["PLATFORM"] == "win32" : env["HAVE_SNARL"] = True # LibXML conf = Configure(conf_env, custom_tests = {"CheckVersion": CheckVersion}) if conf.CheckCHeader("libxml/parser.h") and conf.CheckLib("xml2") : #and conf.CheckVersion("LibXML", "2.6.23", "LIBXML_VERSION", "libxml/xmlversion.h", 20623) : env["HAVE_LIBXML"] = 1 env["LIBXML_FLAGS"] = { "LIBS": ["xml2"] } conf.Finish() if not env.get("HAVE_LIBXML", 0) : libxml_env = conf_env.Clone() libxml_env.Append(CPPPATH = ["/usr/include/libxml2"]) conf = Configure(libxml_env, custom_tests = {"CheckVersion": CheckVersion}) if conf.CheckCHeader("libxml/parser.h") and conf.CheckLib("xml2") : # and conf.CheckVersion("LibXML", "2.6.23", "LIBXML_VERSION", "libxml/xmlversion.h", 20623): env["HAVE_LIBXML"] = 1 env["LIBXML_FLAGS"] = { "CPPPATH": ["/usr/include/libxml2"], "LIBS": ["xml2"] } conf.Finish() # Expat if not env.get("HAVE_LIBXML",0) : expat_conf_env = conf_env.Clone() expat_flags = {} if env.get("expat_libdir", None) : expat_flags["LIBPATH"] = [env["expat_libdir"]] if env.get("expat_includedir", None) : expat_flags["CPPPATH"] = [env["expat_includedir"]] expat_conf_env.MergeFlags(expat_flags) conf = Configure(expat_conf_env) if conf.CheckCHeader("expat.h") and conf.CheckLib(env["expat_libname"]) : env["HAVE_EXPAT"] = 1 env["EXPAT_FLAGS"] = { "LIBS": [env["expat_libname"]] } env["EXPAT_FLAGS"].update(expat_flags) conf.Finish() # Bundled expat bundledExpat = False if not env.get("HAVE_EXPAT", 0) and not env.get("HAVE_LIBXML", 0) : print "Expat or LibXML not found. Using bundled Expat" SConscript("#/3rdParty/Expat/SConscript") env["HAVE_EXPAT"] = 1 env["EXPAT_BUNDLED"] = True ################################################################################ # IDN library ################################################################################ # ICU icu_env = conf_env.Clone() use_icu = bool(env["icu"]) icu_prefix = env["icu"] if isinstance(env["icu"], str) else "" icu_flags = {} if icu_prefix : icu_flags = { "CPPPATH": [os.path.join(icu_prefix, "include")] } icu_flags["LIBPATH"] = [os.path.join(icu_prefix, "lib")] icu_env.MergeFlags(icu_flags) icu_conf = Configure(icu_env) if use_icu and icu_conf.CheckCHeader("unicode/usprep.h") : env["HAVE_ICU"] = 1 env["ICU_FLAGS"] = icu_flags env["ICU_FLAGS"]["LIBS"] = ["icuuc"] icu_conf.Finish() # LibIDN libidn_conf_env = conf_env.Clone() libidn_flags = {} if env.get("libidn_libdir", None) : libidn_flags["LIBPATH"] = [env["libidn_libdir"]] if env.get("libidn_includedir", None) : libidn_flags["CPPPATH"] = [env["libidn_includedir"]] libidn_conf_env.MergeFlags(libidn_flags) conf = Configure(libidn_conf_env) if not env.get("HAVE_ICU") and conf.CheckCHeader("idna.h") and conf.CheckLib(env["libidn_libname"]) : env["HAVE_LIBIDN"] = 1 env["LIBIDN_FLAGS"] = { "LIBS": [env["libidn_libname"]] } env["LIBIDN_FLAGS"].update(libidn_flags) conf.Finish() # Fallback to bundled LibIDN if not env.get("HAVE_ICU", False) and not env.get("HAVE_LIBIDN", False) : env["HAVE_LIBIDN"] = 1 env["LIBIDN_BUNDLED"] = 1 # LibMiniUPnPc if env["experimental"] : #libminiupnpc_conf_env = conf_env.Clone() #conf = Configure(libminiupnpc_conf_env) #if conf.CheckCHeader("miniupnpc.h") and conf.CheckLib(env["libminiupnpc_libname"]) : # print "NOT IMPLEMENTED YET" #else : env["LIBMINIUPNPC_BUNDLED"] = 1 #conf.Finish() else : env["LIBMINIUPNPC_FLAGS"] = {} # LibNATPMP if env["experimental"] : #libnatpmp_conf_env = conf_env.Clone() #conf = Configure(libnatpmp_conf_env) #if conf.CheckCHeader("natpmp.h") and conf.CheckLib(env["libnatpmp_libname"]) : # print "NOT IMPLEMENTED YET" #else : env["LIBNATPMP_BUNDLED"] = 1 #conf.Finish() else : env["LIBNATPMP_FLAGS"] = {} # SQLite if env["experimental"] : sqlite_conf_env = conf_env.Clone() sqlite_flags = {} if env.get("sqlite_libdir", None) : sqlite_flags["LIBPATH"] = [env["sqlite_libdir"]] if env.get("sqlite_includedir", None) : sqlite_flags["CPPPATH"] = [env["sqlite_includedir"]] sqlite_conf_env.MergeFlags(sqlite_flags) conf = Configure(sqlite_conf_env) if conf.CheckCHeader("sqlite3.h") and conf.CheckLib(env["sqlite_libname"]) and not env.get("sqlite_force_bundled", False): env["HAVE_SQLITE"] = 1 env["SQLITE_FLAGS"] = { "LIBS": [env["sqlite_libname"]] } env["SQLITE_FLAGS"].update(sqlite_flags) else : env["SQLITE_BUNDLED"] = 1 env["SQLITE_ASYNC_BUNDLED"] = 1 conf.Finish() else : env["SQLITE_FLAGS"] = {} # Lua env["LUA_BUNDLED"] = 1 # Readline conf = Configure(conf_env) if conf.CheckCHeader(["stdio.h", "readline/readline.h"]) and conf.CheckLib("readline") : env["HAVE_READLINE"] = True env["READLINE_FLAGS"] = { "LIBS": ["readline"] } conf.Finish() # Avahi avahi_conf_env = conf_env.Clone() avahi_flags = {} if env.get("avahi_libdir", None) : avahi_flags["LIBPATH"] = [env["avahi_libdir"]] if env.get("avahi_includedir", None) : avahi_flags["CPPPATH"] = [env["avahi_includedir"]] avahi_conf_env.MergeFlags(avahi_flags) conf = Configure(avahi_conf_env) if conf.CheckCHeader("avahi-client/client.h") and conf.CheckLib("avahi-client") and conf.CheckLib("avahi-common") : env["HAVE_AVAHI"] = True env["AVAHI_FLAGS"] = { "LIBS": ["avahi-client", "avahi-common"] } env["AVAHI_FLAGS"].update(avahi_flags) conf.Finish() # Qt if env["qt"] : env["QTDIR"] = env["qt"] # OpenSSL openssl_env = conf_env.Clone() use_openssl = bool(env["openssl"]) openssl_prefix = env["openssl"] if isinstance(env["openssl"], str) else "" openssl_flags = {} if openssl_prefix : openssl_flags = { "CPPPATH": [os.path.join(openssl_prefix, "include")] } if env["PLATFORM"] == "win32" : openssl_flags["LIBPATH"] = [os.path.join(openssl_prefix, "lib", "VC")] env["OPENSSL_DIR"] = openssl_prefix else : openssl_flags["LIBPATH"] = [os.path.join(openssl_prefix, "lib")] openssl_env.MergeFlags(openssl_flags) openssl_conf = Configure(openssl_env) if use_openssl and openssl_conf.CheckCHeader("openssl/ssl.h") : env["HAVE_OPENSSL"] = 1 env["OPENSSL_FLAGS"] = openssl_flags if env["PLATFORM"] == "win32" : env["OPENSSL_FLAGS"]["LIBS"] = ["libeay32MD", "ssleay32MD"] else: env["OPENSSL_FLAGS"]["LIBS"] = ["ssl", "crypto"] if env["PLATFORM"] == "darwin" : if platform.mac_ver()[0].startswith("10.5") : env["OPENSSL_FLAGS"]["FRAMEWORKS"] = ["Security"] elif env["target"] in ("iphone-device", "iphone-simulator", "xcode") : env["OPENSSL_BUNDLED"] = True env["HAVE_OPENSSL"] = True else : env["OPENSSL_FLAGS"] = {} if env["PLATFORM"] == "win32" : env["HAVE_SCHANNEL"] = True # If we're compiling for Windows and OpenSSL isn't being used, use Schannel env.Append(LIBS = ["secur32"]) openssl_conf.Finish() # Bonjour if env["PLATFORM"] == "darwin" : env["HAVE_BONJOUR"] = 1 elif env.get("bonjour", False) : bonjour_env = conf_env.Clone() bonjour_conf = Configure(bonjour_env) bonjour_flags = {} if env.get("bonjour") != True : bonjour_prefix = env["bonjour"] bonjour_flags["CPPPATH"] = [os.path.join(bonjour_prefix, "include")] bonjour_flags["LIBPATH"] = [os.path.join(bonjour_prefix, "lib", "win32")] bonjour_env.MergeFlags(bonjour_flags) if bonjour_conf.CheckCHeader("dns_sd.h") and bonjour_conf.CheckLib("dnssd") : env["HAVE_BONJOUR"] = 1 env["BONJOUR_FLAGS"] = bonjour_flags env["BONJOUR_FLAGS"]["LIBS"] = ["dnssd"] bonjour_conf.Finish() # Cocoa & IOKit if env["PLATFORM"] == "darwin" : cocoa_conf = Configure(conf_env) if cocoa_conf.CheckCHeader("IOKit/IOKitLib.h") : env["HAVE_IOKIT"] = True cocoa_conf.Finish() # Qt try : myenv = env.Clone() myenv.Tool("qt4", toolpath = ["#/BuildTools/SCons/Tools"]) env["HAVE_QT"] = True except : env["HAVE_QT"] = False ################################################################################ # DocBook setup ################################################################################ if env.get("docbook_xml") : env["DOCBOOK_XML_DIR"] = env["docbook_xml"] if env.get("docbook_xsl") : env["DOCBOOK_XSL_DIR"] = env["docbook_xsl"] ################################################################################ # Set up git hooks ################################################################################ if env.Dir("#/.git").exists() : if not env.GetOption("clean") : env.Install("#/.git/hooks", Glob("#/BuildTools/Git/Hooks/*")) ################################################################################ # Replace #pragma once with proper guards on platforms that require it ################################################################################ if ARGUMENTS.get("replace_pragma_once", False) : env.Tool("ReplacePragmaOnce", toolpath = ["#/BuildTools/SCons/Tools"]) def relpath(path, start) : i = len(os.path.commonprefix([path, start])) return path[i+1:] for actual_root, dirs, files in os.walk(root) : if "3rdParty" in actual_root : continue for file in files : if not file.endswith(".h") : continue include = relpath(os.path.join(actual_root, file), root) env.ReplacePragmaOnce("#/include/" + include, "#/" + include) env.Append(CPPPATH = ["#/include"]) else : env.Append(CPPPATH = [root]) ################################################################################ # Project files ################################################################################ # Build tools env.SConscript(dirs = ["#/BuildTools/CLang"]) # Modules modules = [] for dir in os.listdir(Dir("#/3rdParty").abspath) : full_dir = os.path.join(Dir("#/3rdParty").abspath, dir) if not os.path.isdir(full_dir) : continue sconscript = os.path.join(full_dir, "SConscript") if os.path.isfile(sconscript) : modules.append("3rdParty/" + dir) for dir in os.listdir(Dir("#").abspath) : full_dir = os.path.join(Dir("#").abspath, dir) if not os.path.isdir(full_dir) : continue sconscript = os.path.join(full_dir, "SConscript") if os.path.isfile(sconscript) : modules.append(dir) # Flags env["PROJECTS"] = [m for m in modules if m not in ["Documentation", "QA", "SwifTools"] and not m.startswith("3rdParty")] for stage in ["flags", "build", "test"] : env["SCONS_STAGE"] = stage SConscript(dirs = map(lambda x : root + "/" + x, modules)) # SLOCCount if ARGUMENTS.get("sloccount", False) : for project in env["PROJECTS"] : env.SLOCCount("#/" + project) ################################################################################ # Print summary ################################################################################ print print " Build Configuration" print " -------------------" parsers = [] if env.get("HAVE_LIBXML", 0): parsers.append("LibXML") if env.get("HAVE_EXPAT", 0): parsers.append("Expat") if env.get("EXPAT_BUNDLED", False) : parsers.append("(Bundled)") print " Projects: " + ' '.join(env["PROJECTS"]) print "" print " XML Parsers: " + ' '.join(parsers) print " TLS Support: " + ("OpenSSL" if env.get("HAVE_OPENSSL",0) else ("Schannel" if env.get("HAVE_SCHANNEL", 0) else "Disabled")) print " DNSSD Support: " + ("Bonjour" if env.get("HAVE_BONJOUR") else ("Avahi" if env.get("HAVE_AVAHI") else "Disabled")) print swift-im-2.0+dev6/BuildTools/SCons/Version.py0000644000175000017500000000400312227051773020763 0ustar kismithkismithimport subprocess, os, datetime, re, os.path def getGitBuildVersion(project) : tag = git("describe --tags --exact --match \"" + project + "-*\"") if tag : return tag.rstrip()[len(project)+1:] tag = git("describe --tags --match \"" + project + "-*\"") if tag : m = re.match(project + "-(.*)-(.*)-(.*)", tag) if m : return m.group(1) + "-dev" + m.group(2) return None def git(cmd) : p = subprocess.Popen("git " + cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=(os.name != "nt")) gitVersion = p.stdout.read() p.stdin.close() return gitVersion if p.wait() == 0 else None def getBuildVersion(root, project) : versionFilename = os.path.join(root, "VERSION." + project) if os.path.isfile(versionFilename) : f = open(versionFilename) version = f.read().strip() f.close() return version gitVersion = getGitBuildVersion(project) if gitVersion : return gitVersion return datetime.date.today().strftime("%Y%m%d") def convertToWindowsVersion(version) : version_match = re.match("(\d+)\.(\d+)(.*)", version) major = int(version_match.group(1)) if version_match else 0 minor = int(version_match.group(2)) if version_match else 0 if version_match and len(version_match.group(3)) == 0 : patch = 60000 else : match = re.match("^beta(\d+)(.*)", version_match.group(3)) build_string = "" if match : patch = 1000*int(match.group(1)) build_string = match.group(2) else : rc_match = re.match("^rc(\d+)(.*)", version_match.group(3)) if rc_match : patch = 10000*int(rc_match.group(1)) build_string = rc_match.group(2) else : patch = 0 alpha_match = re.match("^alpha(.*)", version_match.group(3)) if alpha_match : build_string = alpha_match.group(1) if len(build_string) > 0 : build_match = re.match("^-dev(\d+)", build_string) if build_match : patch += int(build_match.group(1)) return (major, minor, patch) swift-im-2.0+dev6/BuildTools/SCons/SConscript.boot0000644000175000017500000003733512227051773021756 0ustar kismithkismithimport sys, os, re, platform, hashlib sys.path.append(Dir("#/BuildTools/SCons").abspath) ################################################################################ # Build variables ################################################################################ vars = Variables(os.path.join(Dir("#").abspath, "config.py")) vars.Add('cc', "C compiler") vars.Add('cxx', "C++ compiler") vars.Add('ccflags', "Extra C(++) compiler flags") vars.Add('link', "Linker") vars.Add('linkflags', "Extra linker flags") vars.Add(BoolVariable("ccache", "Use CCache", "no")) vars.Add(BoolVariable("distcc", "Use DistCC", "no")) vars.Add('distcc_hosts', "DistCC hosts (overrides DISTCC_HOSTS)") vars.Add(EnumVariable("test", "Compile and run tests", "none", ["none", "all", "unit", "system"])) vars.Add(BoolVariable("optimize", "Compile with optimizations turned on", "no")) vars.Add(BoolVariable("debug", "Compile with debug information", "yes")) vars.Add(BoolVariable("allow_warnings", "Allow compilation warnings during compilation", "yes")) vars.Add(BoolVariable("assertions", "Compile with assertions", "yes")) vars.Add(BoolVariable("max_jobs", "Build with maximum number of parallel jobs", "no")) vars.Add(EnumVariable("target", "Choose a target platform for compilation", "native", ["native", "iphone-simulator", "iphone-device", "xcode"])) vars.Add(BoolVariable("swift_mobile", "Build mobile Swift", "no")) vars.Add(BoolVariable("swiften_dll", "Build Swiften as dynamically linked library", "no")) if os.name != "nt" : vars.Add(BoolVariable("coverage", "Compile with coverage information", "no")) if os.name == "posix" : vars.Add(BoolVariable("valgrind", "Run tests with valgrind", "no")) if os.name == "mac" or (os.name == "posix" and os.uname()[0] == "Darwin"): vars.Add(BoolVariable("universal", "Create universal binaries", "no")) vars.Add(BoolVariable("mac105", "Link against the 10.5 frameworks", "no")) vars.Add(BoolVariable("mac106", "Link against the 10.6 frameworks", "no")) if os.name == "nt" : vars.Add(PathVariable("vcredist", "MSVC redistributable dir", None, PathVariable.PathAccept)) if os.name == "nt" : vars.Add(PathVariable("wix_bindir", "Path to WiX binaries", "", PathVariable.PathAccept)) if os.name == "nt" : vars.Add(PackageVariable("bonjour", "Bonjour SDK location", "yes")) vars.Add(PackageVariable("openssl", "OpenSSL location", "yes")) vars.Add(PathVariable("boost_includedir", "Boost headers location", None, PathVariable.PathAccept)) vars.Add(PathVariable("boost_libdir", "Boost library location", None, PathVariable.PathAccept)) vars.Add(PathVariable("expat_includedir", "Expat headers location", None, PathVariable.PathAccept)) vars.Add(PathVariable("expat_libdir", "Expat library location", None, PathVariable.PathAccept)) vars.Add("expat_libname", "Expat library name", "libexpat" if os.name == "nt" else "expat") vars.Add(PackageVariable("icu", "ICU library location", "no")) vars.Add(PathVariable("libidn_includedir", "LibIDN headers location", None, PathVariable.PathAccept)) vars.Add(PathVariable("libidn_libdir", "LibIDN library location", None, PathVariable.PathAccept)) vars.Add("libidn_libname", "LibIDN library name", "libidn" if os.name == "nt" else "idn") vars.Add(PathVariable("sqlite_includedir", "SQLite headers location", None, PathVariable.PathAccept)) vars.Add(PathVariable("sqlite_libdir", "SQLite library location", None, PathVariable.PathAccept)) vars.Add("sqlite_libname", "SQLite library name", "libsqlite3" if os.name == "nt" else "sqlite3") vars.Add("sqlite_force_bundled", "Force use of the bundled SQLite", None) vars.Add(PathVariable("avahi_includedir", "Avahi headers location", None, PathVariable.PathAccept)) vars.Add(PathVariable("avahi_libdir", "Avahi library location", None, PathVariable.PathAccept)) vars.Add(PathVariable("qt", "Qt location", "", PathVariable.PathAccept)) vars.Add(PathVariable("docbook_xml", "DocBook XML", None, PathVariable.PathAccept)) vars.Add(PathVariable("docbook_xsl", "DocBook XSL", None, PathVariable.PathAccept)) vars.Add(BoolVariable("build_examples", "Build example programs", "yes")) vars.Add(BoolVariable("enable_variants", "Build in a separate dir under build/, depending on compile flags", "no")) vars.Add(BoolVariable("experimental", "Build experimental features", "no")) vars.Add(BoolVariable("set_iterator_debug_level", "Set _ITERATOR_DEBUG_LEVEL=0", "yes")) ################################################################################ # Set up default build & configure environment ################################################################################ env_ENV = { 'PATH' : os.environ['PATH'], 'LD_LIBRARY_PATH' : os.environ.get("LD_LIBRARY_PATH", ""), } if "MSVC_VERSION" in ARGUMENTS : env = Environment(ENV = env_ENV, variables = vars, MSVC_VERSION = ARGUMENTS["MSVC_VERSION"]) else : env = Environment(ENV = env_ENV, variables = vars) Help(vars.GenerateHelpText(env)) # Default environment variables env["PLATFORM_FLAGS"] = {} # Default custom tools env.Tool("Test", toolpath = ["#/BuildTools/SCons/Tools"]) env.Tool("WriteVal", toolpath = ["#/BuildTools/SCons/Tools"]) env.Tool("BuildVersion", toolpath = ["#/BuildTools/SCons/Tools"]) env.Tool("Flags", toolpath = ["#/BuildTools/SCons/Tools"]) if env["PLATFORM"] == "darwin" : env.Tool("Nib", toolpath = ["#/BuildTools/SCons/Tools"]) env.Tool("AppBundle", toolpath = ["#/BuildTools/SCons/Tools"]) if env["PLATFORM"] == "win32" : env.Tool("WindowsBundle", toolpath = ["#/BuildTools/SCons/Tools"]) #So we don't need to escalate with UAC if "TMP" in os.environ.keys() : env['ENV']['TMP'] = os.environ['TMP'] env.Tool("SLOCCount", toolpath = ["#/BuildTools/SCons/Tools"]) # Max out the number of jobs if env["max_jobs"] : try : import multiprocessing SetOption("num_jobs", multiprocessing.cpu_count()) except NotImplementedError : pass except ImportError : pass # Default compiler flags if env.get("distcc", False) : env["ENV"]["HOME"] = os.environ["HOME"] env["ENV"]["DISTCC_HOSTS"] = os.environ.get("DISTCC_HOSTS", "") if "distcc_hosts" in env : env["ENV"]["DISTCC_HOSTS"] = env["distcc_hosts"] env["CC"] = "distcc gcc" env["CXX"] = "distcc g++" if "cc" in env : env["CC"] = env["cc"] if "cxx" in env : env["CXX"] = env["cxx"] ccflags = env.get("ccflags", []) if isinstance(ccflags, str) : # FIXME: Make the splitting more robust env["CCFLAGS"] = ccflags.split(" ") else : env["CCFLAGS"] = ccflags if "link" in env : env["SHLINK"] = env["link"] env["LINK"] = env["link"] linkflags = env.get("linkflags", []) if isinstance(linkflags, str) : # FIXME: Make the splitting more robust env["LINKFLAGS"] = linkflags.split(" ") else : env["LINKFLAGS"] = linkflags # This isn't a real flag (yet) AFAIK. Be sure to append it to the CXXFLAGS # where you need it env["OBJCCFLAGS"] = [] if env["optimize"] : if env["PLATFORM"] == "win32" : env.Append(CCFLAGS = ["/O2"]) else : env.Append(CCFLAGS = ["-O2"]) if env["target"] == "xcode" and os.environ["CONFIGURATION"] == "Release" : env.Append(CCFLAGS = ["-Os"]) if env["debug"] : if env["PLATFORM"] == "win32" : env.Append(CCFLAGS = ["/Z7"]) env.Append(LINKFLAGS = ["/DEBUG"]) if env["set_iterator_debug_level"] : env.Append(CPPDEFINES = ["_ITERATOR_DEBUG_LEVEL=0"]) if env["optimize"] : env.Append(LINKFLAGS = ["/OPT:NOREF"]) env.Append(CCFLAGS = ["/MD"]) else : env.Append(CCFLAGS = ["/MDd"]) else : env.Append(CCFLAGS = ["-g"]) elif env["PLATFORM"] == "win32" : env.Append(CCFLAGS = ["/MD"]) if env.get("universal", 0) : assert(env["PLATFORM"] == "darwin") env.Append(CCFLAGS = [ "-isysroot", "/Developer/SDKs/MacOSX10.4u.sdk", "-arch", "i386", "-arch", "ppc"]) env.Append(LINKFLAGS = [ "-mmacosx-version-min=10.4", "-isysroot", "/Developer/SDKs/MacOSX10.4u.sdk", "-arch", "i386", "-arch", "ppc"]) # Link against other versions of the OS X SDKs. # FIXME: This method does not work anymore, we need to set deployment targets. if env.get("mac105", 0) : assert(env["PLATFORM"] == "darwin") env.Append(CCFLAGS = [ "-isysroot", "/Developer/SDKs/MacOSX10.5.sdk", "-arch", "i386"]) env.Append(LINKFLAGS = [ "-mmacosx-version-min=10.5", "-isysroot", "/Developer/SDKs/MacOSX10.5.sdk", "-arch", "i386"]) env.Append(FRAMEWORKS = ["Security"]) if env.get("mac106", 0) : assert(env["PLATFORM"] == "darwin") env.Append(CCFLAGS = [ "-isysroot", "/Developer/SDKs/MacOSX10.6.sdk", "-arch", "i386"]) env.Append(LINKFLAGS = [ "-mmacosx-version-min=10.6", "-isysroot", "/Developer/SDKs/MacOSX10.6.sdk", "-arch", "i386"]) env.Append(FRAMEWORKS = ["Security"]) if not env["assertions"] : env.Append(CPPDEFINES = ["NDEBUG"]) if env["experimental"] : env.Append(CPPDEFINES = ["SWIFT_EXPERIMENTAL_FT", "SWIFT_EXPERIMENTAL_HISTORY", "SWIFT_EXPERIMENTAL_WB"]) # If we build shared libs on AMD64, we need -fPIC. # This should have no performance impact om AMD64 if env["PLATFORM"] == "posix" and platform.machine() == "x86_64" : env.Append(CCFLAGS = ["-fPIC"]) # Warnings if env["PLATFORM"] == "win32" : # TODO: Find the ideal set of warnings #env.Append(CCFLAGS = ["/Wall"]) pass else : env.Append(CXXFLAGS = ["-Wextra", "-Wall", "-Wnon-virtual-dtor", "-Wundef", "-Wold-style-cast", "-Wno-long-long", "-Woverloaded-virtual", "-Wfloat-equal", "-Wredundant-decls"]) if not env.get("allow_warnings", False) : env.Append(CXXFLAGS = ["-Werror"]) gccVersion = env.get("CCVERSION", "0.0.0").split(".") if gccVersion >= ["4", "5", "0"] and not "clang" in env["CC"] : env.Append(CXXFLAGS = ["-Wlogical-op"]) if "clang" in env["CC"] : env.Append(CXXFLAGS = ["-W#warnings", "-Wc++0x-compat", "-Waddress-of-temporary", "-Wambiguous-member-template", "-Warray-bounds", "-Watomic-properties", "-Wbind-to-temporary-copy", "-Wbuiltin-macro-redefined", "-Wc++-compat", "-Wc++0x-extensions", "-Wcomments", "-Wconditional-uninitialized", "-Wconstant-logical-operand", "-Wdeclaration-after-statement", "-Wdeprecated", "-Wdeprecated-implementations", "-Wdeprecated-writable-strings", "-Wduplicate-method-arg", "-Wempty-body", "-Wendif-labels", "-Wenum-compare", "-Wformat=2", "-Wfour-char-constants", "-Wgnu", "-Wincomplete-implementation", "-Winvalid-noreturn", "-Winvalid-offsetof", "-Winvalid-token-paste", "-Wlocal-type-template-args", "-Wmethod-signatures", "-Wmicrosoft", "-Wmissing-declarations", "-Wnon-pod-varargs", "-Wnull-dereference", "-Wout-of-line-declaration", "-Woverlength-strings", "-Wpacked", "-Wpointer-arith", "-Wpointer-sign", "-Wprotocol", "-Wreadonly-setter-attrs", "-Wselector", "-Wshift-overflow", "-Wshift-sign-overflow", "-Wstrict-selector-match", "-Wsuper-class-method-mismatch", "-Wtautological-compare", "-Wtypedef-redefinition", "-Wundeclared-selector", "-Wunknown-warning-option", "-Wunnamed-type-template-args", "-Wunused-exception-parameter", "-Wunused-member-function", "-Wused-but-marked-unused", "-Wvariadic-macros", "-Wno-c++11-extensions"]) # To enable: # "-Wnonfragile-abi2" -> deprecated, is now -Warc-abi i think # "-Wheader-hygiene" # "-Wnon-gcc", # "-Wweak-vtables", # "-Wlarge-by-value-copy", if env.get("coverage", 0) : assert(env["PLATFORM"] != "win32") env.Append(CCFLAGS = ["-fprofile-arcs", "-ftest-coverage"]) env.Append(LINKFLAGS = ["-fprofile-arcs", "-ftest-coverage"]) if env["PLATFORM"] == "win32" : env.Append(LIBS = ["user32", "crypt32", "dnsapi", "iphlpapi", "ws2_32", "wsock32", "Advapi32"]) env.Append(CCFLAGS = ["/EHsc", "/nologo"]) env.Append(LINKFLAGS = ["/INCREMENTAL:no", "/NOLOGO"]) if int(env["MSVS_VERSION"].split(".")[0]) < 10 : env["LINKCOM"] = [env["LINKCOM"], 'mt.exe -nologo -manifest ${TARGET}.manifest -outputresource:$TARGET;1'] env["SHLINKCOM"] = [env["SHLINKCOM"], 'mt.exe -nologo -manifest ${TARGET}.manifest -outputresource:$TARGET;2'] if env["PLATFORM"] == "darwin" and not env["target"] in ["iphone-device", "iphone-simulator", "xcode"] : env.Append(FRAMEWORKS = ["IOKit", "AppKit", "SystemConfiguration", "Security", "SecurityInterface"]) # Testing env["TEST_TYPE"] = env["test"] if "check" in ARGUMENTS : env["TEST_TYPE"] = "unit" env["checker_report"] = ARGUMENTS.get("checker_report", False) env["TEST"] = (env["TEST_TYPE"] != "none") or env.GetOption("clean") if env.get("valgrind", 0) : env["TEST_RUNNER"] = "valgrind --suppressions=QA/valgrind.supp -q --leak-check=full --track-origins=yes " env["TEST_IGNORE_RESULT"] = "ignore_test_result" in ARGUMENTS env["TEST_CREATE_LIBRARIES"] = "create_test_libraries" in ARGUMENTS # Packaging env["DIST"] = "dist" in ARGUMENTS or env.GetOption("clean") for path in ["SWIFT_INSTALLDIR", "SWIFTEN_INSTALLDIR"] : if ARGUMENTS.get(path, "") : if os.path.isabs(ARGUMENTS[path]) : env[path] = Dir(ARGUMENTS[path]).abspath else : env[path] = Dir("#/" + ARGUMENTS[path]).abspath ################################################################################ # XCode / iPhone / ... ################################################################################ target = env["target"] if target in ["iphone-device", "iphone-simulator", "xcode"] : # Extract/initialize all the information we need if target == "xcode" : # Get the information from the XCode environment env["XCODE_PLATFORM_DEVELOPER_BIN_DIR"] = os.environ["PLATFORM_DEVELOPER_BIN_DIR"] env["XCODE_SDKROOT"] = os.environ["SDKROOT"] env["XCODE_ARCH_FLAGS"] = sum([["-arch", arch] for arch in os.environ["ARCHS"].split(" ")], []) env["IPHONEOS_DEPLOYMENT_TARGET"] = os.environ["IPHONEOS_DEPLOYMENT_TARGET"] # Use absolute path sources so Xcode can highlight compilation errors in swiften env['CXXCOM'] = '$CXX -o $TARGET -c $CXXFLAGS $CCFLAGS $_CCCOMCOM ${SOURCES.abspath}' else : # Hard code values env["XCODE_PLATFORM_DEVELOPER_BIN_DIR"] = "/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin" if target == "iphone-device": env["XCODE_ARCH_FLAGS"] = ["-arch", "armv6", "-arch", "armv7"] sdkPart = "iPhoneOS" else : env["XCODE_ARCH_FLAGS"] = ["-arch", "i386"] sdkPart = "iPhoneSimulator" sdkVer = "4.3" env["XCODE_SDKROOT"] = "/Developer/Platforms/" + sdkPart + ".platform/Developer/SDKs/" + sdkPart + sdkVer + ".sdk" env["IPHONEOS_DEPLOYMENT_TARGET"] = "4.1" # Set the build flags env["CC"] = "$XCODE_PLATFORM_DEVELOPER_BIN_DIR/gcc" env["CXX"] = "$XCODE_PLATFORM_DEVELOPER_BIN_DIR/g++" env["OBJCCFLAGS"] = ["-fobjc-abi-version=2", "-fobjc-legacy-dispatch"] env["LD"] = env["CC"] env.Append(CCFLAGS = env["XCODE_ARCH_FLAGS"] + ["-fvisibility=hidden", "-miphoneos-version-min=" + env["IPHONEOS_DEPLOYMENT_TARGET"]]) if os.environ.get("GCC_THUMB_SUPPORT", False) : env.Append(CCFLAGS = ["-mthumb"]) env.Append(LINKFLAGS = env["XCODE_ARCH_FLAGS"]) env.Append(CPPFLAGS = ["-isysroot", "$XCODE_SDKROOT"]) env.Append(FRAMEWORKS = ["CoreFoundation", "Foundation", "UIKit", "CoreGraphics"]) env.Append(LINKFLAGS = env["XCODE_ARCH_FLAGS"] + ["-isysroot", "$XCODE_SDKROOT", "-L\"$XCODE_SDKROOT/usr/lib\"", "-F\"$XCODE_SDKROOT/System/Library/Frameworks\"", "-F\"$XCODE_SDKROOT/System/Library/PrivateFrameworks\""]) # Bit of a hack, because BOOST doesn't know the endianness for ARM env.Append(CPPDEFINES = ["_LITTLE_ENDIAN"]) # CCache if env.get("ccache", False) : env["ENV"]["HOME"] = os.environ["HOME"] for var in os.environ : if var.startswith("CCACHE_") : env["ENV"][var] = os.environ[var] if env.get("CC", "") != "" : env["CC"] = "ccache " + env["CC"] else : env["CC"] = "ccache gcc" if env.get("CXX", "") != "" : env["CXX"] = "ccache " + env["CXX"] else : env["CC"] = "ccache g++" conf_env = env.Clone() Export("env") Export("conf_env") variant = "" if env["enable_variants"] : fingerprint = ",".join([flag for flag in env["CXXFLAGS"] + env["CCFLAGS"] if not flag.startswith("-W") and not flag.startswith("-fvisibility")]) variant = "build/" + fingerprint if not os.path.exists(Dir("#/build").abspath) : os.mkdir(Dir("#/build").abspath) if os.path.exists(Dir("#/build/current").abspath) : os.unlink(Dir("#/build/current").abspath) os.symlink(os.path.basename(variant), Dir("#/build/current").abspath) Return("variant") swift-im-2.0+dev6/BuildTools/SCons/Tools/0000755000175000017500000000000012227051773020067 5ustar kismithkismithswift-im-2.0+dev6/BuildTools/SCons/Tools/nsis.py0000644000175000017500000000225412227051773021420 0ustar kismithkismithimport re, os import SCons.Util nsisFiles_re = re.compile(r'^\s*File "([^"]*)"', re.M) # FIXME nsisIncludes_re = re.compile(r'^\s*!include (translations-\S*)', re.M) """ TODO: - Extract the target from the nsis file - When a target is provided use the output function """ def generate(env) : """Add Builders and construction variables for qt to an Environment.""" Builder = SCons.Builder.Builder env['NSIS_MAKENSIS'] = 'makensis' env['NSIS_OPTIONS'] = ["/V2"] def winToLocalReformat(path) : return os.path.join(*path.split("\\")) def scanNsisContent(node, env, path, arg): contents = node.get_contents() includes = nsisFiles_re.findall(contents) + nsisIncludes_re.findall(contents) includes = [ winToLocalReformat(include) for include in includes ] return filter(lambda x: x.rfind('*')==-1, includes) nsisscanner = env.Scanner(name = 'nsisfile', function = scanNsisContent, argument = None, skeys = ['.nsi']) nsisbuilder = Builder( action = SCons.Action.Action('$NSIS_MAKENSIS $NSIS_OPTIONS $SOURCE', cmdstr = '$NSISCOMSTR'), source_scanner = nsisscanner, single_source = True ) env.Append( BUILDERS={'Nsis' : nsisbuilder} ) def exists(env) : return True swift-im-2.0+dev6/BuildTools/SCons/Tools/WindowsBundle.py0000644000175000017500000000167312227051773023234 0ustar kismithkismithimport SCons.Util, os def generate(env) : def createWindowsBundle(env, bundle, resources = {}, qtimageformats = [], qtlibs = []) : all_files = [] all_files += env.Install(bundle, bundle + ".exe") for lib in qtlibs : all_files += env.Install(bundle, os.path.join(env["QTDIR"], "bin", lib + ".dll")) all_files += env.Install(os.path.join(bundle, "imageformats"), [os.path.join(env["QTDIR"], "plugins", "imageformats", "q" + codec + "4.dll") for codec in qtimageformats]) for dir, resourceFiles in resources.items() : for resource in resourceFiles : e = env.Entry(resource) if e.isdir() : for subresource in env.Glob(str(e) + "/*") : all_files += env.Install(os.path.join(bundle, dir, e.name), subresource) else : all_files += env.Install(os.path.join(bundle, dir), resource) return all_files env.AddMethod(createWindowsBundle, "WindowsBundle") def exists(env) : return env["PLATFORM"] == "win32" swift-im-2.0+dev6/BuildTools/SCons/Tools/WriteVal.py0000644000175000017500000000055512227051773022203 0ustar kismithkismithimport SCons.Util def generate(env) : def replacePragmaOnce(env, target, source) : f = open(str(target[0]), 'wb') f.write(source[0].get_contents()) f.close() env["BUILDERS"]["WriteVal"] = SCons.Builder.Builder( action = SCons.Action.Action(replacePragmaOnce, cmdstr = "$GENCOMSTR"), single_source = True) def exists(env) : return True swift-im-2.0+dev6/BuildTools/SCons/Tools/SLOCCount.py0000644000175000017500000000120512227051773022210 0ustar kismithkismithimport SCons.Util, os.path, os def generate(env) : def createSLOCCount(env, source) : myenv = env.Clone() myenv["ENV"]["HOME"] = os.environ["HOME"] source = myenv.Dir(source) target = myenv.File("#/" + source.path + ".sloccount") # FIXME: There's probably a better way to force building the .sc if os.path.exists(target.abspath) : os.unlink(target.abspath) return myenv.Command(target, source, [SCons.Action.Action("sloccount --duplicates --wide --details " + source.path + " | grep -v qrc_ > $TARGET", cmdstr = "$GENCOMSTR")]) env.AddMethod(createSLOCCount, "SLOCCount") def exists(env) : return True swift-im-2.0+dev6/BuildTools/SCons/Tools/Flags.py0000644000175000017500000000032512227051773021475 0ustar kismithkismithimport SCons.Util def generate(env) : def useFlags(env, flags) : for flag in flags : env[flag] = env.get(flag, []) + flags[flag] env.AddMethod(useFlags, "UseFlags") def exists(env) : return True swift-im-2.0+dev6/BuildTools/SCons/Tools/ReplacePragmaOnce.py0000644000175000017500000000143212227051773023751 0ustar kismithkismithimport SCons.Util, os.path def generate(env) : root = env.Dir("#").abspath def relpath(path, start) : i = len(os.path.commonprefix([path, start])) return path[i+1:] def replacePragmaOnce(env, target, source) : guard = relpath(source[0].abspath, root).replace("/", "_").replace(".", "_").upper() data = source[0].get_contents() f = open(str(target[0]), 'wb') if "#pragma once" in data : f.write(data.replace("#pragma once", "#ifndef %(guard)s\n#define %(guard)s" % {"guard": guard})) f.write("\n#endif\n") else : f.write(data) f.close() env["BUILDERS"]["ReplacePragmaOnce"] = SCons.Builder.Builder( action = SCons.Action.Action(replacePragmaOnce, cmdstr = "$GENCOMSTR"), single_source = True) def exists(env) : return True swift-im-2.0+dev6/BuildTools/SCons/Tools/textfile.py0000644000175000017500000001364712227051773022300 0ustar kismithkismith# -*- python -*- # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be included # in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # __doc__ = """ Textfile/Substfile builder for SCons. Create file 'target' which typically is a textfile. The 'source' may be any combination of strings, Nodes, or lists of same. A 'linesep' will be put between any part written and defaults to os.linesep. The only difference between the Textfile builder and the Substfile builder is that strings are converted to Value() nodes for the former and File() nodes for the latter. To insert files in the former or strings in the latter, wrap them in a File() or Value(), respectively. The values of SUBST_DICT first have any construction variables expanded (its keys are not expanded). If a value of SUBST_DICT is a python callable function, it is called and the result is expanded as the value. Values are substituted in a "random" order; if any substitution could be further expanded by another subsitition, it is unpredictible whether the expansion will occur. """ __revision__ = "src/engine/SCons/Tool/textfile.py 5357 2011/09/09 21:31:03 bdeegan" import SCons import os import re from SCons.Node import Node from SCons.Node.Python import Value from SCons.Util import is_String, is_Sequence, is_Dict def _do_subst(node, subs): """ Fetch the node contents and replace all instances of the keys with their values. For example, if subs is {'%VERSION%': '1.2345', '%BASE%': 'MyProg', '%prefix%': '/bin'}, then all instances of %VERSION% in the file will be replaced with 1.2345 and so forth. """ contents = node.get_text_contents() if not subs: return contents for (k,v) in subs: contents = re.sub(k, v, contents) return contents def _action(target, source, env): # prepare the line separator linesep = env['LINESEPARATOR'] if linesep is None: linesep = os.linesep elif is_String(linesep): pass elif isinstance(linesep, Value): linesep = linesep.get_text_contents() else: raise SCons.Errors.UserError( 'unexpected type/class for LINESEPARATOR: %s' % repr(linesep), None) # create a dictionary to use for the substitutions if 'SUBST_DICT' not in env: subs = None # no substitutions else: d = env['SUBST_DICT'] if is_Dict(d): d = list(d.items()) elif is_Sequence(d): pass else: raise SCons.Errors.UserError('SUBST_DICT must be dict or sequence') subs = [] for (k,v) in d: if callable(v): v = v() if is_String(v): v = env.subst(v) else: v = str(v) subs.append((k,v)) # write the file try: fd = open(target[0].get_path(), "wb") except (OSError,IOError), e: raise SCons.Errors.UserError("Can't write target file %s" % target[0]) # separate lines by 'linesep' only if linesep is not empty lsep = None for s in source: if lsep: fd.write(lsep) fd.write(_do_subst(s, subs)) lsep = linesep fd.close() def _strfunc(target, source, env): return "Creating '%s'" % target[0] def _convert_list_R(newlist, sources): for elem in sources: if is_Sequence(elem): _convert_list_R(newlist, elem) elif isinstance(elem, Node): newlist.append(elem) else: newlist.append(Value(elem)) def _convert_list(target, source, env): if len(target) != 1: raise SCons.Errors.UserError("Only one target file allowed") newlist = [] _convert_list_R(newlist, source) return target, newlist _common_varlist = ['SUBST_DICT', 'LINESEPARATOR'] _text_varlist = _common_varlist + ['TEXTFILEPREFIX', 'TEXTFILESUFFIX'] _text_builder = SCons.Builder.Builder( action = SCons.Action.Action(_action, _strfunc, varlist = _text_varlist), source_factory = Value, emitter = _convert_list, prefix = '$TEXTFILEPREFIX', suffix = '$TEXTFILESUFFIX', ) _subst_varlist = _common_varlist + ['SUBSTFILEPREFIX', 'TEXTFILESUFFIX'] _subst_builder = SCons.Builder.Builder( action = SCons.Action.Action(_action, _strfunc, varlist = _subst_varlist), source_factory = SCons.Node.FS.File, emitter = _convert_list, prefix = '$SUBSTFILEPREFIX', suffix = '$SUBSTFILESUFFIX', src_suffix = ['.in'], ) def generate(env): env['LINESEPARATOR'] = os.linesep env['BUILDERS']['MyTextfile'] = _text_builder env['TEXTFILEPREFIX'] = '' env['TEXTFILESUFFIX'] = '.txt' env['BUILDERS']['MySubstfile'] = _subst_builder env['SUBSTFILEPREFIX'] = '' env['SUBSTFILESUFFIX'] = '' def exists(env): return 1 # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: swift-im-2.0+dev6/BuildTools/SCons/Tools/qt4.py0000644000175000017500000004240212227051773021153 0ustar kismithkismith """SCons.Tool.qt Tool-specific initialization for Qt. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be included # in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # __revision__ = "/home/scons/scons/branch.0/branch.96/baseline/src/engine/SCons/Tool/qt.py 0.96.92.D001 2006/04/10 23:13:27 knight" import os.path import re import SCons.Action import SCons.Builder import SCons.Defaults import SCons.Scanner import SCons.Tool import SCons.Util class ToolQtWarning(SCons.Warnings.Warning): pass class GeneratedMocFileNotIncluded(ToolQtWarning): pass class QtdirNotFound(ToolQtWarning): pass SCons.Warnings.enableWarningClass(ToolQtWarning) qrcinclude_re = re.compile(r'([^<]*)', re.M) def transformToWinePath(path) : return os.popen('winepath -w "%s"'%path).read().strip().replace('\\','/') header_extensions = [".h", ".hxx", ".hpp", ".hh"] if SCons.Util.case_sensitive_suffixes('.h', '.H'): header_extensions.append('.H') # TODO: The following two lines will work when integrated back to SCons # TODO: Meanwhile the third line will do the work #cplusplus = __import__('c++', globals(), locals(), []) #cxx_suffixes = cplusplus.CXXSuffixes cxx_suffixes = [".c", ".cxx", ".cpp", ".cc"] def checkMocIncluded(target, source, env): moc = target[0] cpp = source[0] # looks like cpp.includes is cleared before the build stage :-( # not really sure about the path transformations (moc.cwd? cpp.cwd?) :-/ path = SCons.Defaults.CScan.path_function(env, moc.cwd) includes = SCons.Defaults.CScan(cpp, env, path) if not moc in includes: SCons.Warnings.warn( GeneratedMocFileNotIncluded, "Generated moc file '%s' is not included by '%s'" % (str(moc), str(cpp))) def find_file(filename, paths, node_factory): for dir in paths: node = node_factory(filename, dir) if node.rexists(): return node return None class _Automoc: """ Callable class, which works as an emitter for Programs, SharedLibraries and StaticLibraries. """ def __init__(self, objBuilderName): self.objBuilderName = objBuilderName def __call__(self, target, source, env): """ Smart autoscan function. Gets the list of objects for the Program or Lib. Adds objects and builders for the special qt files. """ try: if int(env.subst('$QT4_AUTOSCAN')) == 0: return target, source except ValueError: pass try: debug = int(env.subst('$QT4_DEBUG')) except ValueError: debug = 0 # some shortcuts used in the scanner splitext = SCons.Util.splitext objBuilder = getattr(env, self.objBuilderName) # some regular expressions: # Q_OBJECT detection q_object_search = re.compile(r'[^A-Za-z0-9]Q_OBJECT[^A-Za-z0-9]') # cxx and c comment 'eater' #comment = re.compile(r'(//.*)|(/\*(([^*])|(\*[^/]))*\*/)') # CW: something must be wrong with the regexp. See also bug #998222 # CURRENTLY THERE IS NO TEST CASE FOR THAT # The following is kind of hacky to get builders working properly (FIXME) objBuilderEnv = objBuilder.env objBuilder.env = env mocBuilderEnv = env.Moc4.env env.Moc4.env = env # make a deep copy for the result; MocH objects will be appended out_sources = source[:] for obj in source: if isinstance(obj,basestring): # big kludge! print "scons: qt4: '%s' MAYBE USING AN OLD SCONS VERSION AND NOT CONVERTED TO 'File'. Discarded." % str(obj) continue if not obj.has_builder(): # binary obj file provided if debug: print "scons: qt: '%s' seems to be a binary. Discarded." % str(obj) continue cpp = obj.sources[0] if not splitext(str(cpp))[1] in cxx_suffixes: if debug: print "scons: qt: '%s' is no cxx file. Discarded." % str(cpp) # c or fortran source continue #cpp_contents = comment.sub('', cpp.get_contents()) try: cpp_contents = cpp.get_contents() except: continue # may be an still not generated source h=None for h_ext in header_extensions: # try to find the header file in the corresponding source # directory hname = splitext(cpp.name)[0] + h_ext h = find_file(hname, (cpp.get_dir(),), env.File) if h: if debug: print "scons: qt: Scanning '%s' (header of '%s')" % (str(h), str(cpp)) #h_contents = comment.sub('', h.get_contents()) h_contents = h.get_contents() break if not h and debug: print "scons: qt: no header for '%s'." % (str(cpp)) if h and q_object_search.search(h_contents): # h file with the Q_OBJECT macro found -> add moc_cpp moc_cpp = env.Moc4(h) moc_o = objBuilder(moc_cpp) out_sources.append(moc_o) #moc_cpp.target_scanner = SCons.Defaults.CScan if debug: print "scons: qt: found Q_OBJECT macro in '%s', moc'ing to '%s'" % (str(h), str(moc_cpp)) if cpp and q_object_search.search(cpp_contents): # cpp file with Q_OBJECT macro found -> add moc # (to be included in cpp) moc = env.Moc4(cpp) env.Ignore(moc, moc) if debug: print "scons: qt: found Q_OBJECT macro in '%s', moc'ing to '%s'" % (str(cpp), str(moc)) #moc.source_scanner = SCons.Defaults.CScan # restore the original env attributes (FIXME) objBuilder.env = objBuilderEnv env.Moc4.env = mocBuilderEnv return (target, out_sources) AutomocShared = _Automoc('SharedObject') AutomocStatic = _Automoc('StaticObject') def _detect(env): """Not really safe, but fast method to detect the QT library""" try: return env['QTDIR'] except KeyError: pass try: return os.environ['QTDIR'] except KeyError: pass moc = env.WhereIs('moc-qt4') or env.WhereIs('moc4') or env.WhereIs('moc') if moc: import sys if sys.platform == "darwin" : return "" QTDIR = os.path.dirname(os.path.dirname(moc)) SCons.Warnings.warn( QtdirNotFound, "QTDIR variable is not defined, using moc executable as a hint (QTDIR=%s)" % QTDIR) return QTDIR raise SCons.Errors.StopError( QtdirNotFound, "Could not detect Qt 4 installation") return None def generate(env): """Add Builders and construction variables for qt to an Environment.""" def locateQt4Command(env, command, qtdir) : if len(qtdir) == 0 : qtdir = "/usr" suffixes = [ '-qt4', '-qt4.exe', '4', '4.exe', '', '.exe', ] triedPaths = [] for suffix in suffixes : fullpath = os.path.join(qtdir,'bin',command + suffix) if os.access(fullpath, os.X_OK) : return fullpath triedPaths.append(fullpath) fullpath = env.Detect([command+'-qt4', command+'4', command]) if not (fullpath is None) : return fullpath raise Exception("Qt4 command '" + command + "' not found. Tried: " + ', '.join(triedPaths)) CLVar = SCons.Util.CLVar Action = SCons.Action.Action Builder = SCons.Builder.Builder splitext = SCons.Util.splitext env['QTDIR'] = _detect(env) # TODO: 'Replace' should be 'SetDefault' # env.SetDefault( env.Replace( QTDIR = _detect(env), # TODO: This is not reliable to QTDIR value changes but needed in order to support '-qt4' variants QT4_MOC = locateQt4Command(env,'moc', env['QTDIR']), QT4_UIC = locateQt4Command(env,'uic', env['QTDIR']), QT4_RCC = locateQt4Command(env,'rcc', env['QTDIR']), QT4_LUPDATE = locateQt4Command(env,'lupdate', env['QTDIR']), QT4_LRELEASE = locateQt4Command(env,'lrelease', env['QTDIR']), QT4_LIB = '', # KLUDGE to avoid linking qt3 library QT4_AUTOSCAN = 1, # Should the qt tool try to figure out, which sources are to be moc'ed? # Some QT specific flags. I don't expect someone wants to # manipulate those ... QT4_UICFLAGS = CLVar(''), QT4_MOCFROMHFLAGS = CLVar(''), QT4_MOCFROMCXXFLAGS = CLVar('-i'), QT4_QRCFLAGS = '', # suffixes/prefixes for the headers / sources to generate QT4_UISUFFIX = '.ui', QT4_UICDECLPREFIX = 'ui_', QT4_UICDECLSUFFIX = '.h', QT4_MOCHPREFIX = 'moc_', QT4_MOCHSUFFIX = '$CXXFILESUFFIX', QT4_MOCCXXPREFIX = '', QT4_MOCCXXSUFFIX = '.moc', QT4_QRCSUFFIX = '.qrc', QT4_QRCCXXSUFFIX = '$CXXFILESUFFIX', QT4_QRCCXXPREFIX = 'qrc_', QT4_MOCCPPPATH = [], QT4_MOCINCFLAGS = '$( ${_concat("-I", QT4_MOCCPPPATH, INCSUFFIX, __env__, RDirs)} $)', # Commands for the qt support ... QT4_UICCOM = '$QT4_UIC $QT4_UICFLAGS -o $TARGET $SOURCE', # FIXME: The -DBOOST_TT_HAS_OPERATOR_HPP_INCLUDED flag is a hack to work # around an issue in Qt # See https://bugreports.qt-project.org/browse/QTBUG-22829 QT4_MOCFROMHCOM = '$QT4_MOC -DBOOST_TT_HAS_OPERATOR_HPP_INCLUDED $QT4_MOCFROMHFLAGS $QT4_MOCINCFLAGS -o $TARGET $SOURCE', QT4_MOCFROMCXXCOM = [ '$QT4_MOC -DBOOST_TT_HAS_OPERATOR_HPP_INCLUDED $QT4_MOCFROMCXXFLAGS $QT4_MOCINCFLAGS -o $TARGET $SOURCE', Action(checkMocIncluded,None)], QT4_LUPDATECOM = '$QT4_LUPDATE $SOURCE -ts $TARGET', QT4_LRELEASECOM = '$QT4_LRELEASE -silent $SOURCE -qm $TARGET', QT4_RCCCOM = '$QT4_RCC $QT4_QRCFLAGS -name $SOURCE $SOURCE -o $TARGET', ) if len(env["QTDIR"]) > 0 : env.Replace(QT4_LIBPATH = os.path.join('$QTDIR', 'lib')) # Translation builder tsbuilder = Builder( action = SCons.Action.Action('$QT4_LUPDATECOM'), #,'$QT4_LUPDATECOMSTR'), multi=1 ) env.Append( BUILDERS = { 'Ts': tsbuilder } ) qmbuilder = Builder( action = SCons.Action.Action('$QT4_LRELEASECOM', cmdstr = '$QT4_LRELEASECOMSTR'), src_suffix = '.ts', suffix = '.qm', single_source = True ) env.Append( BUILDERS = { 'Qm': qmbuilder } ) # Resource builder def scanResources(node, env, path, arg): # I've being careful on providing names relative to the qrc file # If that was not needed that code could be simplified a lot def recursiveFiles(basepath, path) : result = [] for item in os.listdir(os.path.join(basepath, path)) : itemPath = os.path.join(path, item) if os.path.isdir(os.path.join(basepath, itemPath)) : result += recursiveFiles(basepath, itemPath) else: result.append(itemPath) return result contents = node.get_contents() includes = [included[1] for included in qrcinclude_re.findall(contents)] qrcpath = os.path.dirname(node.path) dirs = [included for included in includes if os.path.isdir(os.path.join(qrcpath,included))] # dirs need to include files recursively for dir in dirs : includes.remove(dir) includes+=recursiveFiles(qrcpath,dir) return includes qrcscanner = SCons.Scanner.Scanner(name = 'qrcfile', function = scanResources, argument = None, skeys = ['.qrc']) qrcbuilder = Builder( action = SCons.Action.Action('$QT4_RCCCOM', cmdstr = '$QT4_RCCCOMSTR'), source_scanner = qrcscanner, src_suffix = '$QT4_QRCSUFFIX', suffix = '$QT4_QRCCXXSUFFIX', prefix = '$QT4_QRCCXXPREFIX', single_source = True ) env.Append( BUILDERS = { 'Qrc': qrcbuilder } ) # Interface builder uic4builder = Builder( action = SCons.Action.Action('$QT4_UICCOM', cmdstr = '$QT4_UICCOMSTR'), src_suffix='$QT4_UISUFFIX', suffix='$QT4_UICDECLSUFFIX', prefix='$QT4_UICDECLPREFIX', single_source = True #TODO: Consider the uiscanner on new scons version ) env['BUILDERS']['Uic4'] = uic4builder # Metaobject builder mocBld = Builder(action={}, prefix={}, suffix={}) for h in header_extensions: act = SCons.Action.Action('$QT4_MOCFROMHCOM', cmdstr = '$QT4_MOCFROMHCOMSTR') mocBld.add_action(h, act) mocBld.prefix[h] = '$QT4_MOCHPREFIX' mocBld.suffix[h] = '$QT4_MOCHSUFFIX' for cxx in cxx_suffixes: act = SCons.Action.Action('$QT4_MOCFROMCXXCOM', cmdstr = '$QT4_MOCFROMCXXCOMSTR') mocBld.add_action(cxx, act) mocBld.prefix[cxx] = '$QT4_MOCCXXPREFIX' mocBld.suffix[cxx] = '$QT4_MOCCXXSUFFIX' env['BUILDERS']['Moc4'] = mocBld # er... no idea what that was for static_obj, shared_obj = SCons.Tool.createObjBuilders(env) static_obj.src_builder.append('Uic4') shared_obj.src_builder.append('Uic4') # We use the emitters of Program / StaticLibrary / SharedLibrary # to scan for moc'able files # We can't refer to the builders directly, we have to fetch them # as Environment attributes because that sets them up to be called # correctly later by our emitter. env.AppendUnique(PROGEMITTER =[AutomocStatic], SHLIBEMITTER=[AutomocShared], LIBEMITTER =[AutomocStatic], # Of course, we need to link against the qt libraries LIBPATH=["$QT4_LIBPATH"], LIBS=['$QT4_LIB']) # TODO: Does dbusxml2cpp need an adapter env.AddMethod(enable_modules, "EnableQt4Modules") def enable_modules(self, modules, debug=False, crosscompiling=False) : import sys validModules = [ 'QtCore', 'QtGui', 'QtOpenGL', 'Qt3Support', 'QtAssistant', 'QtScript', 'QtDBus', 'QtSql', # The next modules have not been tested yet so, please # maybe they require additional work on non Linux platforms 'QtNetwork', 'QtSvg', 'QtTest', 'QtXml', 'QtXmlPatterns', 'QtUiTools', 'QtDesigner', 'QtDesignerComponents', 'QtWebKit', 'QtHelp', 'QtScript', ] staticModules = [ 'QtUiTools', ] invalidModules=[] for module in modules: if module not in validModules : invalidModules.append(module) if invalidModules : raise Exception("Modules %s are not Qt4 modules. Valid Qt4 modules are: %s"% ( str(invalidModules),str(validModules))) moduleDefines = { 'QtScript' : ['QT_SCRIPT_LIB'], 'QtSvg' : ['QT_SVG_LIB'], 'Qt3Support' : ['QT_QT3SUPPORT_LIB','QT3_SUPPORT'], 'QtSql' : ['QT_SQL_LIB'], 'QtXml' : ['QT_XML_LIB'], 'QtOpenGL' : ['QT_OPENGL_LIB'], 'QtGui' : ['QT_GUI_LIB'], 'QtNetwork' : ['QT_NETWORK_LIB'], 'QtCore' : ['QT_CORE_LIB'], } for module in modules : try : self.AppendUnique(CPPDEFINES=moduleDefines[module]) except: pass debugSuffix = '' if sys.platform.startswith("linux") and not crosscompiling : if debug : debugSuffix = '_debug' self.AppendUnique(CPPPATH=[os.path.join("$QTDIR","include", "phonon")]) for module in modules : self.AppendUnique(LIBS=[module+debugSuffix]) self.AppendUnique(LIBPATH=[os.path.join("$QTDIR","lib")]) self.AppendUnique(CPPPATH=[os.path.join("$QTDIR","include")]) self.AppendUnique(CPPPATH=[os.path.join("$QTDIR","include",module)]) self["QT4_MOCCPPPATH"] = self["CPPPATH"] return if sys.platform == "win32" or crosscompiling : if crosscompiling: transformedQtdir = transformToWinePath(self['QTDIR']) self['QT4_MOC'] = "QTDIR=%s %s"%( transformedQtdir, self['QT4_MOC']) self.AppendUnique(CPPPATH=[os.path.join("$QTDIR","include")]) try: modules.remove("QtDBus") except: pass if debug : debugSuffix = 'd' if "QtAssistant" in modules: self.AppendUnique(CPPPATH=[os.path.join("$QTDIR","include","QtAssistant")]) modules.remove("QtAssistant") modules.append("QtAssistantClient") # FIXME: Phonon Hack self.AppendUnique(LIBS=['phonon'+debugSuffix+'4']) self.AppendUnique(LIBS=[lib+debugSuffix+'4' for lib in modules if lib not in staticModules]) self.PrependUnique(LIBS=[lib+debugSuffix for lib in modules if lib in staticModules]) if 'QtOpenGL' in modules: self.AppendUnique(LIBS=['opengl32']) self.AppendUnique(CPPPATH=[ '$QTDIR/include/']) self.AppendUnique(CPPPATH=[ '$QTDIR/include/'+module for module in modules]) if crosscompiling : self["QT4_MOCCPPPATH"] = [ path.replace('$QTDIR', transformedQtdir) for path in self['CPPPATH'] ] else : self["QT4_MOCCPPPATH"] = self["CPPPATH"] self.AppendUnique(LIBPATH=[os.path.join('$QTDIR','lib')]) return if sys.platform=="darwin" : if debug : debugSuffix = 'd' if len(self["QTDIR"]) > 0 : self.AppendUnique(LIBPATH=[os.path.join('$QTDIR','lib')]) self.AppendUnique(LINKFLAGS="-F$QTDIR/lib") self.AppendUnique(CPPFLAGS="-F$QTDIR/lib") self.AppendUnique(LINKFLAGS="-L$QTDIR/lib") #TODO clean! # FIXME: Phonon Hack self.Append(LINKFLAGS=['-framework', "phonon"]) for module in modules : if module in staticModules : self.AppendUnique(LIBS=[module+debugSuffix]) # TODO: Add the debug suffix self.AppendUnique(LIBPATH=[os.path.join("$QTDIR","lib")]) else : if len(self["QTDIR"]) > 0 : self.Append(CPPFLAGS = ["-I" + os.path.join("$QTDIR", "lib", module + ".framework", "Versions", "4", "Headers")]) else : self.Append(CPPFLAGS = ["-I" + os.path.join("/Library/Frameworks", module + ".framework", "Versions", "4", "Headers")]) self.Append(LINKFLAGS=['-framework', module]) if 'QtOpenGL' in modules: self.AppendUnique(LINKFLAGS="-F/System/Library/Frameworks") self.Append(LINKFLAGS=['-framework', 'AGL']) #TODO ughly kludge to avoid quotes self.Append(LINKFLAGS=['-framework', 'OpenGL']) self["QT4_MOCCPPPATH"] = self["CPPPATH"] def exists(env): return _detect(env) swift-im-2.0+dev6/BuildTools/SCons/Tools/wix.py0000644000175000017500000000321012227051773021244 0ustar kismithkismithimport re, os import SCons.Util from subprocess import call def generate(env) : wixPath = env.get("wix_bindir", "") if len(wixPath) > 0 and wixPath[-1] != "\\": wixPath += "\\" env['WIX_HEAT'] = wixPath + 'heat.exe' env['WIX_HEAT_OPTIONS'] = '-nologo -gg -sfrag -suid -template fragment -dr ProgramFilesFolder' env['WIX_CANDLE'] = wixPath + 'candle.exe' env['WIX_CANDLE_OPTIONS'] = '-nologo' env['WIX_LIGHT'] = wixPath + 'light.exe' env['WIX_LIGHT_OPTIONS'] = '-nologo -ext WixUIExtension' def WiX_IncludeScanner(source, env, path, arg): wixIncludeRegexp = re.compile(r'^\s*\<\?include (\S+.wxs)\s*\?\>\S*', re.M) contents = source.get_contents() includes = wixIncludeRegexp.findall(contents) return [ "" + include for include in includes ] heat_builder = SCons.Builder.Builder( action = '"$WIX_HEAT" dir Swift\\QtUI\\Swift -cg Files $WIX_HEAT_OPTIONS -o ${TARGET} -t Swift\\Packaging\\WiX\\include.xslt', suffix = '.wxi') candle_scanner = env.Scanner(name = 'wixincludefile', function = WiX_IncludeScanner, argument = None, skeys = ['.wxs']) candle_builder = SCons.Builder.Builder( action = '"$WIX_CANDLE" $WIX_CANDLE_OPTIONS ${SOURCES} -o ${TARGET}', src_suffix = '.wxs', suffix = '.wixobj', source_scanner = candle_scanner, src_builder = heat_builder) light_builder = SCons.Builder.Builder( action = '"$WIX_LIGHT" $WIX_LIGHT_OPTIONS -b Swift\\QtUI\\Swift ${SOURCES} -o ${TARGET}', src_suffix = '.wixobj', src_builder = candle_builder) env['BUILDERS']['WiX_Heat'] = heat_builder env['BUILDERS']['WiX_Candle'] = candle_builder env['BUILDERS']['WiX_Light'] = light_builder def exists(env) : return True swift-im-2.0+dev6/BuildTools/SCons/Tools/Test.py0000644000175000017500000000271012227051773021360 0ustar kismithkismithimport SCons.Util, os def generate(env) : def registerTest(env, target, type = "unit", is_checker = False) : if env["TEST_TYPE"] == "all" or env["TEST_TYPE"] == type : cmd = target[0].abspath if SCons.Util.is_List(target) else target.abspath params = "" # Special support for unittest checker if is_checker and env.get("checker_report", False) : params = " --xml > " + os.path.join(target[0].dir.path, "checker-report.xml") ignore_prefix = "" if env.get("TEST_IGNORE_RESULT", False) : ignore_prefix = "-" # Set environment variables for running the test test_env = env.Clone() for i in ["HOME", "USERPROFILE", "APPDATA"]: if os.environ.get(i, "") : test_env["ENV"][i] = os.environ[i] if test_env["PLATFORM"] == "darwin" : test_env["ENV"]["DYLD_FALLBACK_LIBRARY_PATH"] = ":".join(map(lambda x : str(x), test_env.get("LIBPATH", []))) elif test_env["PLATFORM"] == "win32" : test_env["ENV"]["PATH"] = ";".join(map(lambda x : str(x), test_env.get("LIBRUNPATH", []))) + ";" + test_env["ENV"]["PATH"] # Run the test test_env.Command("**dummy**", target, SCons.Action.Action(ignore_prefix + env.get("TEST_RUNNER", "") + cmd + " " + params, cmdstr = "$TESTCOMSTR")) def registerScriptTests(env, scripts, name, type) : if env["TEST_TYPE"] == "all" or env["TEST_TYPE"] == type : pass env.AddMethod(registerTest, "Test") env.AddMethod(registerScriptTests, "ScriptTests") def exists(env) : return True swift-im-2.0+dev6/BuildTools/SCons/Tools/AppBundle.py0000644000175000017500000000415712227051773022322 0ustar kismithkismithimport SCons.Util, os.path def generate(env) : def createAppBundle(env, bundle, version = "1.0", resources = [], frameworks = [], info = {}, handlesXMPPURIs = False) : bundleDir = bundle + ".app" bundleContentsDir = bundleDir + "/Contents" resourcesDir = bundleContentsDir + "/Resources" frameworksDir = bundleContentsDir + "/Frameworks" env.Install(bundleContentsDir + "/MacOS", bundle) env.WriteVal(bundleContentsDir + "/PkgInfo", env.Value("APPL\77\77\77\77")) infoDict = { "CFBundleDevelopmentRegion" : "English", "CFBundleExecutable" : bundle, "CFBundleIdentifier" : "im.swift." + bundle, "CFBundleInfoDictionaryVersion" : "6.0", "CFBundleName" : bundle, "CFBundlePackageType" : "APPL", "CFBundleSignature": "\77\77\77\77", "CFBundleVersion" : version, "CFBundleIconFile" : bundle, "NSPrincipalClass" : "NSApplication", "NSHumanReadableCopyright" : unichr(0xA9) + " 2010 Swift Development Team.\nAll Rights Reserved." } infoDict.update(info) plist = """ """ for key, value in infoDict.items() : plist += "" + key + "\n" plist += "" + value.encode("utf-8") + "\n" if handlesXMPPURIs : plist += """CFBundleURLTypes CFBundleURLName XMPP URL CFBundleURLSchemes xmpp \n""" plist += """ """ env.WriteVal(bundleContentsDir + "/Info.plist", env.Value(plist)) for (target, resource) in resources.items() : env.Install(os.path.join(resourcesDir, target), resource) for framework in frameworks : env.Install(frameworksDir, framework) return env.Dir(bundleDir) env.AddMethod(createAppBundle, "AppBundle") def exists(env) : return env["PLATFORM"] == "darwin" swift-im-2.0+dev6/BuildTools/SCons/Tools/BuildVersion.py0000644000175000017500000000072612227051773023053 0ustar kismithkismithimport SCons.Util import Version def generate(env) : def createBuildVersion(env, target, project) : buildVersion = """#pragma once static const char* buildVersion = \"%(buildVersion)s\";\n #define SWIFT_VERSION_STRING \"%(buildVersion)s\";\n """ % { "buildVersion" : Version.getBuildVersion(env.Dir("#").abspath, project) } env.WriteVal(target, env.Value(buildVersion)) env.AddMethod(createBuildVersion, "BuildVersion") def exists(env) : return true swift-im-2.0+dev6/BuildTools/SCons/Tools/Nib.py0000644000175000017500000000063412227051773021154 0ustar kismithkismithimport SCons.Util def generate(env) : env["IBTOOL"] = "ibtool" env["BUILDERS"]["Nib"] = SCons.Builder.Builder( action = SCons.Action.Action("$IBTOOL --errors --warnings --notices --output-format human-readable-text --compile $TARGET $SOURCE", cmdstr = "$NIBCOMSTR"), suffix = ".nib", src_suffix = ".xib", single_source = True) def exists(env) : return env["PLATFORM"] == "darwin" swift-im-2.0+dev6/BuildTools/SCons/Tools/DoxyGen.py0000644000175000017500000000144312227051773022020 0ustar kismithkismithimport SCons.Util, os def generate(env) : def modify_targets(target, source, env) : target = [env.File("html/index.html")] return target, source def generate_actions(source, target, env, for_signature) : if env.WhereIs("$DOXYGEN") and env.WhereIs("$DOT") : return [SCons.Action.Action("$DOXYGEN $SOURCE", cmdstr = "$DOXYCOMSTR")] else : return [] env["DOXYGEN"] = "doxygen" # FIXME: For some reason, things go incredibly slow (at least on OS X) # when not doing this. Some environment flag is having an effect on # this; find out which env["ENV"] = os.environ env["DOT"] = "dot" env["BUILDERS"]["DoxyGen"] = SCons.Builder.Builder( emitter = modify_targets, generator = generate_actions, single_source = True) def exists(env) : return True swift-im-2.0+dev6/BuildTools/CheckTests.py0000755000175000017500000000220412227051773020355 0ustar kismithkismith#!/usr/bin/env python import os, sys, re foundUnregisteredTests = False for (path, dirs, files) in os.walk(".") : if not "3rdParty" in path : for filename in [os.path.join(path, file) for file in files if file.endswith("Test.cpp") and file != "IdleQuerierTest.cpp" and file != "NotifierTest.cpp" and file != "ClientTest.cpp" and file != "ConnectivityTest.cpp" and file != "ReconnectTest.cpp"] : file = open(filename, "r") isRegistered = False registeredTests = set() declaredTests = set() for line in file.readlines() : m = re.match("\s*CPPUNIT_TEST_SUITE_REGISTRATION\((.*)\)", line) if m : isRegistered = True m = re.match("\s*CPPUNIT_TEST\((.*)\)", line) if m : registeredTests.add(m.group(1)) continue m = re.match("\s*void (test.*)\(\)", line) if m : declaredTests.add(m.group(1)) if not isRegistered : print filename + ": Registration missing" foundUnregisteredTests = True if registeredTests - declaredTests != set([]) : print filename + ": " + str(registeredTests - declaredTests) foundUnregisteredTests = True file.close() sys.exit(foundUnregisteredTests) swift-im-2.0+dev6/BuildTools/DebianizeVersion.py0000755000175000017500000000041512227051773021557 0ustar kismithkismith#!/usr/bin/env python import sys assert(len(sys.argv) == 2) version = sys.argv[1] version = version.replace("beta", "~beta") version = version.replace("rc", "~rc") version = version.replace("alpha", "~alpha") version = version.replace("-dev", "+dev") print version swift-im-2.0+dev6/BuildTools/GetBuildVersion.py0000755000175000017500000000075712227051773021375 0ustar kismithkismith#!/usr/bin/env python import sys, re sys.path.append("SCons") import Version, os.path assert(len(sys.argv) >= 2) only_major = False if "--major" in sys.argv : only_major = True if only_major : v = Version.getBuildVersion(os.path.dirname(sys.argv[0] + "/.."), sys.argv[1]) version_match = re.match("(\d+)\.(\d+).*", v) if version_match : print version_match.group(1) else : print "0" else : print Version.getBuildVersion(os.path.dirname(sys.argv[0] + "/.."), sys.argv[1]) swift-im-2.0+dev6/BuildTools/PackageSwiften.sh0000755000175000017500000000026512227051773021177 0ustar kismithkismith#!/bin/sh package=swiften-`date +%Y-%m-%d` git archive --prefix=$package/ --format=tar HEAD 3rdParty BuildTools Documentation QA Swiften scons scons.bat SConstruct > $package.tar swift-im-2.0+dev6/BuildTools/CheckTranslations.py0000755000175000017500000000351212227051773021737 0ustar kismithkismith#!/usr/bin/env python import os, sys, re, xml.dom.minidom def getText(nodelist): text = "" for node in nodelist: if node.nodeType == node.TEXT_NODE: text += node.data return text desktop_generic_names = set() desktop_comments = set() f = open("Swift/resources/swift.desktop", "r") for l in f.readlines() : m = re.match("GenericName\[(\w+)\].*", l) if m : desktop_generic_names.add(m.group(1)) m = re.match("Comment\[(\w+)\].*", l) if m : desktop_comments.add(m.group(1)) f.close() for filename in os.listdir("Swift/Translations") : m = re.match("swift_(.*)\.ts", filename) if m : language = m.group(1) finished = True f = open("Swift/Translations/" + filename, "r") document = xml.dom.minidom.parse(f) f.close() for message in document.getElementsByTagName("message") : source = message.getElementsByTagName("source")[0] sourceText = getText(source.childNodes) sourcePlaceholders = set(re.findall("%\d+%?", sourceText)) translation = message.getElementsByTagName("translation")[0] if "type" in translation.attributes.keys() and translation.attributes["type"]. value == "unfinished" : finished = False translationText = getText(translation.childNodes) translationPlaceholders = set(re.findall("%\d+%?", translationText)) if translationPlaceholders != sourcePlaceholders : print "[Error] " + filename + ": Placeholder mismatch in translation '" + sourceText + "'" if not finished : print "[Warning] " + filename + ": Unfinished" if language not in desktop_generic_names and language != "en" : print "[Warning] GenericName field missing in desktop entry for " + language if language not in desktop_comments and language != "en" : print "[Warning] Comment field missing in desktop entry for " + language swift-im-2.0+dev6/BuildTools/MSVS/0000755000175000017500000000000012227051774016533 5ustar kismithkismithswift-im-2.0+dev6/BuildTools/MSVS/GenerateProjects.py0000644000175000017500000000521712227051773022355 0ustar kismithkismithimport os, os.path projects = [("Swift", "Swift\QtUI\Swift.exe"), ("Slimber", "Slimber\Qt\Slimber.exe")] for (project, outputbin) in projects : if not os.path.exists(project) : os.mkdir(project) output = open(os.path.join(project, project + ".vcproj"), "w") headers = [] sources = [] for root, dirs, files in os.walk(os.path.join("..", "..", project)) : for file in files : if file.endswith(".h") : headers.append('') elif file.endswith(".cpp") : sources.append('') output.write(""" %(sources)s %(headers)s """ % { "project": project, "output" : outputbin, "headers" : '\n'.join(headers), "sources": '\n'.join(sources) }) output.close() swift-im-2.0+dev6/BuildTools/MSVS/Swift.sln0000644000175000017500000000244712227051773020353 0ustar kismithkismith Microsoft Visual Studio Solution File, Format Version 10.00 # Visual C++ Express 2008 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Swift", "Swift\Swift.vcproj", "{C67C3A5B-1382-4B4A-88F7-3BFC98DA43A2}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Slimber", "Slimber\Slimber.vcproj", "{597242B2-A667-47A1-B69E-D2C4281183D0}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 Release|Win32 = Release|Win32 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {C67C3A5B-1382-4B4A-88F7-3BFC98DA43A2}.Debug|Win32.ActiveCfg = Debug|Win32 {C67C3A5B-1382-4B4A-88F7-3BFC98DA43A2}.Debug|Win32.Build.0 = Debug|Win32 {C67C3A5B-1382-4B4A-88F7-3BFC98DA43A2}.Release|Win32.ActiveCfg = Release|Win32 {C67C3A5B-1382-4B4A-88F7-3BFC98DA43A2}.Release|Win32.Build.0 = Release|Win32 {597242B2-A667-47A1-B69E-D2C4281183D0}.Debug|Win32.ActiveCfg = Debug|Win32 {597242B2-A667-47A1-B69E-D2C4281183D0}.Debug|Win32.Build.0 = Debug|Win32 {597242B2-A667-47A1-B69E-D2C4281183D0}.Release|Win32.ActiveCfg = Release|Win32 {597242B2-A667-47A1-B69E-D2C4281183D0}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal swift-im-2.0+dev6/BuildTools/Cppcheck.sh0000755000175000017500000000122412227051773020020 0ustar kismithkismith#!/bin/sh cppcheck $@ \ --enable=all \ --inline-suppr \ --suppress=postfixOperator:3rdParty/hippomocks.h \ --suppress=stlSize:3rdParty/hippomocks.h \ --suppress=noConstructor \ --suppress=publicAllocationError:Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp \ -i 3rdParty -i .git -i .sconf_temp \ -i Swiftob/linit.cpp \ -i Swift/QtUI/EventViewer/main.cpp \ -i Swift/QtUI/ApplicationTest \ -i Swift/QtUI/ChatView/main.cpp \ -i Swift/QtUI/Roster/main.cpp \ -i Swift/QtUI/NotifierTest/NotifierTest.cpp \ -DSWIFTEN_BUILDING -DSWIFTEN_STATIC \ -U__BEOS__ -U__CYGWIN__ -U__QNNXTO__ -U__amigaos__ -Uhpux -U__sgi \ \ -I . \ -I Swift/QtUI \ . swift-im-2.0+dev6/BuildTools/Eclipse/0000755000175000017500000000000012227051773017326 5ustar kismithkismithswift-im-2.0+dev6/BuildTools/Eclipse/Swift (Mac OS X).launch0000644000175000017500000000412412227051773023113 0ustar kismithkismith swift-im-2.0+dev6/BuildTools/Eclipse/Swift (Windows).launch0000644000175000017500000000420012227051773023346 0ustar kismithkismith swift-im-2.0+dev6/BuildTools/Eclipse/Swift (Windows Debug).launch0000644000175000017500000000432212227051773024362 0ustar kismithkismith swift-im-2.0+dev6/BuildTools/Eclipse/CodeTemplates.xml0000644000175000017500000001121512227051773022601 0ustar kismithkismithswift-im-2.0+dev6/VERSION.swift0000644000175000017500000000001112227051774016056 0ustar kismithkismith2.0-dev6 swift-im-2.0+dev6/SwifTools/0000755000175000017500000000000012227051773015613 5ustar kismithkismithswift-im-2.0+dev6/SwifTools/SConscript0000644000175000017500000000377312227051773017637 0ustar kismithkismithImport("env") ################################################################################ # Flags ################################################################################ if env["SCONS_STAGE"] == "flags" : env["SWIFTOOLS_FLAGS"] = { "LIBPATH": [Dir(".")], "LIBS": ["SwifTools"] } ################################################################################ # Build ################################################################################ if env["SCONS_STAGE"] == "build" : swiftools_env = env.Clone() swiftools_env.UseFlags(swiftools_env["SWIFTEN_FLAGS"]) swiftools_env.UseFlags(swiftools_env["BOOST_FLAGS"]) sources = [ "Idle/IdleDetector.cpp", "Idle/ActualIdleDetector.cpp", "Idle/IdleQuerier.cpp", "Idle/PlatformIdleQuerier.cpp", "AutoUpdater/AutoUpdater.cpp", "AutoUpdater/PlatformAutoUpdaterFactory.cpp", "Linkify.cpp", "TabComplete.cpp", "LastLineTracker.cpp", ] if swiftools_env.get("HAVE_SPARKLE", 0) : swiftools_env.MergeFlags(swiftools_env["SPARKLE_FLAGS"]) swiftools_env.Append(CPPDEFINES = ["HAVE_SPARKLE"]) sources += ["AutoUpdater/SparkleAutoUpdater.mm"] if swiftools_env["PLATFORM"] == "win32" : sources += ["Idle/WindowsIdleQuerier.cpp"] elif swiftools_env["PLATFORM"] == "darwin" and swiftools_env.get("HAVE_IOKIT", False) : swiftools_env.Append(CPPDEFINES = ["HAVE_IOKIT"]) sources += ["Idle/MacOSXIdleQuerier.cpp"] elif swiftools_env["HAVE_XSS"] : swiftools_env.Append(CPPDEFINES = ["HAVE_XSS"]) sources += ["Idle/XSSIdleQuerier.cpp"] swiftools_env.UseFlags(swiftools_env["BREAKPAD_FLAGS"]) if env["HAVE_BREAKPAD"] : swiftools_env.Append(CPPDEFINES = ["HAVE_BREAKPAD"]) sources += ["CrashReporter.cpp"] swiftools_env["SWIFTOOLS_OBJECTS"] = [] Export("swiftools_env") SConscript(dirs = [ "Application", "Dock", "Notifier", "URIHandler", "Idle/IdleQuerierTest", "Idle/UnitTest", "Cocoa", "UnitTest" ]) swiftools_env.StaticLibrary("SwifTools", sources + swiftools_env["SWIFTOOLS_OBJECTS"]) swift-im-2.0+dev6/SwifTools/CrashReporter.h0000644000175000017500000000066112227051773020552 0ustar kismithkismith/* * Copyright (c) 2012 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include namespace Swift { class CrashReporter { public: CrashReporter(const boost::filesystem::path& path); private: struct Private; boost::shared_ptr p; }; } swift-im-2.0+dev6/SwifTools/Linkify.cpp0000644000175000017500000000233012227051773017722 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include namespace Swift { static boost::regex linkifyRegexp("^(https?://|xmpp:).*"); std::string Linkify::linkify(const std::string& input) { std::ostringstream result; std::vector currentURL; bool inURL = false; for (size_t i = 0; i < input.size(); ++i) { char c = input[i]; if (inURL) { if (c != ' ' && c != '\t' && c != '\n' && !(c == '*' && i == input.size() - 1 && input[0] == '*')) { currentURL.push_back(c); } else { std::string url(¤tURL[0], currentURL.size()); result << "" << url << ""; currentURL.clear(); inURL = false; result << c; } } else { if (boost::regex_match(input.substr(i, 8), linkifyRegexp)) { currentURL.push_back(c); inURL = true; } else { result << c; } } } if (!currentURL.empty()) { std::string url(¤tURL[0], currentURL.size()); result << "" << url << ""; } return std::string(result.str()); } } swift-im-2.0+dev6/SwifTools/Idle/0000755000175000017500000000000012227051773016470 5ustar kismithkismithswift-im-2.0+dev6/SwifTools/Idle/IdleQuerier.h0000644000175000017500000000045212227051773021054 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once namespace Swift { class IdleQuerier { public: virtual ~IdleQuerier(); virtual int getIdleTimeSeconds() = 0; }; } swift-im-2.0+dev6/SwifTools/Idle/IdleQuerierTest/0000755000175000017500000000000012227051774021543 5ustar kismithkismithswift-im-2.0+dev6/SwifTools/Idle/IdleQuerierTest/SConscript0000644000175000017500000000061012227051773023551 0ustar kismithkismithImport("env") if env["TEST"] : myenv = env.Clone() myenv.MergeFlags(myenv["SWIFTOOLS_FLAGS"]) myenv.MergeFlags(myenv["SWIFTEN_FLAGS"]) myenv.MergeFlags(myenv["BOOST_FLAGS"]) if myenv["HAVE_XSS"] : myenv.MergeFlags(myenv.get("XSS_FLAGS", {})) myenv.Append(LIBS = ["X11"]) myenv.MergeFlags(myenv["PLATFORM_FLAGS"]) tester = myenv.Program("IdleQuerierTest", ["IdleQuerierTest.cpp"]) swift-im-2.0+dev6/SwifTools/Idle/IdleQuerierTest/IdleQuerierTest.cpp0000644000175000017500000000071312227051773025321 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include using namespace Swift; int main() { PlatformIdleQuerier querier; while (true) { std::cout << "Idle time: " << querier.getIdleTimeSeconds() << std::endl; Swift::sleep(1000); } return 0; } swift-im-2.0+dev6/SwifTools/Idle/XSSIdleQuerier.cpp0000644000175000017500000000167412227051773022014 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #pragma GCC diagnostic ignored "-Wold-style-cast" namespace Swift { XSSIdleQuerier::XSSIdleQuerier() : display(NULL), info(NULL) { display = XOpenDisplay(NULL); assert(display); rootWindow = DefaultRootWindow(display); int event, error; available = XScreenSaverQueryExtension(display, &event, &error); if (available) { info = XScreenSaverAllocInfo(); } else { std::cerr << "Warning: XScreenSaver extension not found. Idle time detection will not work." << std::endl; } } XSSIdleQuerier::~XSSIdleQuerier() { XFree(info); } int XSSIdleQuerier::getIdleTimeSeconds() { if (available) { XScreenSaverQueryInfo(display, rootWindow, info); return info->idle / 1000; } else { return 0; } } } swift-im-2.0+dev6/SwifTools/Idle/IdleDetector.cpp0000644000175000017500000000040212227051773021537 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { IdleDetector::~IdleDetector() { } } swift-im-2.0+dev6/SwifTools/Idle/PlatformIdleQuerier.cpp0000644000175000017500000000217512227051773023120 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #if defined(SWIFTEN_PLATFORM_MACOSX) && defined(HAVE_IOKIT) && !defined(SWIFTEN_PLATFORM_IPHONE) #include #elif defined(SWIFTEN_PLATFORM_WINDOWS) #include #elif defined(HAVE_XSS) #include #endif #include #include #include namespace Swift { PlatformIdleQuerier::PlatformIdleQuerier() : querier(NULL) { #if defined(SWIFTEN_PLATFORM_MACOSX) #if defined(HAVE_IOKIT) && !defined(SWIFTEN_PLATFORM_IPHONE) querier = new MacOSXIdleQuerier(); #else querier = new DummyIdleQuerier(); #endif #elif defined(SWIFTEN_PLATFORM_WINDOWS) querier = new WindowsIdleQuerier(); #elif defined(HAVE_XSS) querier = new XSSIdleQuerier(); #else querier = new DummyIdleQuerier(); #endif } PlatformIdleQuerier::~PlatformIdleQuerier() { delete querier; } } swift-im-2.0+dev6/SwifTools/Idle/PlatformIdleQuerier.h0000644000175000017500000000073312227051773022563 0ustar kismithkismith/* * 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 namespace Swift { class PlatformIdleQuerier : public IdleQuerier { public: PlatformIdleQuerier(); ~PlatformIdleQuerier(); virtual int getIdleTimeSeconds() { return querier->getIdleTimeSeconds(); } private: IdleQuerier* querier; }; } swift-im-2.0+dev6/SwifTools/Idle/WindowsIdleQuerier.h0000644000175000017500000000055112227051773022427 0ustar kismithkismith/* * 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 namespace Swift { class WindowsIdleQuerier : public IdleQuerier { public: WindowsIdleQuerier(); virtual int getIdleTimeSeconds(); }; } swift-im-2.0+dev6/SwifTools/Idle/MacOSXIdleQuerier.h0000644000175000017500000000065312227051773022072 0ustar kismithkismith/* * 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 #include namespace Swift { class MacOSXIdleQuerier : public IdleQuerier { public: MacOSXIdleQuerier(); virtual int getIdleTimeSeconds(); private: io_service_t ioService; }; } swift-im-2.0+dev6/SwifTools/Idle/IdleDetector.h0000644000175000017500000000143212227051773021210 0ustar kismithkismith/* * 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 #include namespace Swift { class IdleDetector { public: IdleDetector() : idle(false), idleTimeSeconds(300) {} virtual ~IdleDetector(); void setIdleTimeSeconds(int time) { idleTimeSeconds = time; } int getIdleTimeSeconds() const { return idleTimeSeconds; } virtual bool isIdle() const { return idle; } boost::signal onIdleChanged; void setIdle(bool b) { if (b != idle) { idle = b; onIdleChanged(b); } } private: bool idle; int idleTimeSeconds; }; } swift-im-2.0+dev6/SwifTools/Idle/UnitTest/0000755000175000017500000000000012227051773020247 5ustar kismithkismithswift-im-2.0+dev6/SwifTools/Idle/UnitTest/SConscript0000644000175000017500000000013012227051773022253 0ustar kismithkismithImport("env") env.Append(UNITTEST_SOURCES = [ File("ActualIdleDetectorTest.cpp") ]) swift-im-2.0+dev6/SwifTools/Idle/UnitTest/ActualIdleDetectorTest.cpp0000644000175000017500000001042012227051773025311 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include using namespace Swift; class ActualIdleDetectorTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(ActualIdleDetectorTest); CPPUNIT_TEST(testDestructor); CPPUNIT_TEST(testHandleTick_Idle); CPPUNIT_TEST(testHandleTick_Idle_AlreadyIdle); CPPUNIT_TEST(testHandleTick_NotIdle); CPPUNIT_TEST(testHandleTick_NotIdle_AlreadyNotIdle); CPPUNIT_TEST_SUITE_END(); public: void setUp() { querier = new MockIdleQuerier(); timerFactory = new MockTimerFactory(); idleEvents.clear(); } void tearDown() { delete timerFactory; delete querier; } void testDestructor() { ActualIdleDetector* testling = createDetector(); testling->setIdleTimeSeconds(15); delete testling; querier->idleTime = 15; timerFactory->updateTime(15000); CPPUNIT_ASSERT_EQUAL(0, static_cast(idleEvents.size())); } void testHandleTick_Idle() { std::auto_ptr testling(createDetector()); testling->setIdleTimeSeconds(15); querier->idleTime = 15; timerFactory->updateTime(15000); CPPUNIT_ASSERT_EQUAL(1, static_cast(idleEvents.size())); CPPUNIT_ASSERT(idleEvents[0]); } void testHandleTick_Idle_AlreadyIdle() { std::auto_ptr testling(createDetector()); testling->setIdleTimeSeconds(15); querier->idleTime = 15; timerFactory->updateTime(15000); querier->idleTime = 30; timerFactory->updateTime(30000); CPPUNIT_ASSERT_EQUAL(1, static_cast(idleEvents.size())); CPPUNIT_ASSERT(idleEvents[0]); } void testHandleTick_NotIdle() { std::auto_ptr testling(createDetector()); testling->setIdleTimeSeconds(15); querier->idleTime = 15; timerFactory->updateTime(15000); querier->idleTime = 5; timerFactory->updateTime(30000); CPPUNIT_ASSERT_EQUAL(2, static_cast(idleEvents.size())); CPPUNIT_ASSERT(idleEvents[0]); CPPUNIT_ASSERT(!idleEvents[1]); } void testHandleTick_NotIdle_AlreadyNotIdle() { std::auto_ptr testling(createDetector()); testling->setIdleTimeSeconds(15); querier->idleTime = 5; timerFactory->updateTime(15000); CPPUNIT_ASSERT_EQUAL(0, static_cast(idleEvents.size())); } private: ActualIdleDetector* createDetector() { ActualIdleDetector* detector = new ActualIdleDetector(querier, timerFactory, 10); detector->onIdleChanged.connect(boost::bind(&ActualIdleDetectorTest::handleIdle, this, _1)); return detector; } void handleIdle(bool b) { idleEvents.push_back(b); } private: struct MockIdleQuerier : public IdleQuerier { MockIdleQuerier() : idleTime(0) {} virtual int getIdleTimeSeconds() { return idleTime; } int idleTime; }; struct MockTimer : public Timer { MockTimer(int interval) : interval(interval), running(false), lastTime(0) {} virtual void start() { running = true; } virtual void stop() { running = false; } virtual void updateTime(int currentTime) { if (lastTime == currentTime) { return; } if (running) { int time = lastTime; while (time <= currentTime) { onTick(); time += interval; } } lastTime = currentTime; } int interval; bool running; int lastTime; }; struct MockTimerFactory : public TimerFactory { MockTimerFactory() {} void updateTime(int milliseconds) { foreach(boost::shared_ptr timer, timers) { timer->updateTime(milliseconds); } } boost::shared_ptr createTimer(int milliseconds) { boost::shared_ptr timer(new MockTimer(milliseconds)); timers.push_back(timer); return timer; } std::vector > timers; }; MockIdleQuerier* querier; MockTimerFactory* timerFactory; std::vector idleEvents; }; CPPUNIT_TEST_SUITE_REGISTRATION(ActualIdleDetectorTest); swift-im-2.0+dev6/SwifTools/Idle/IdleQuerier.cpp0000644000175000017500000000037712227051773021415 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { IdleQuerier::~IdleQuerier() { } } swift-im-2.0+dev6/SwifTools/Idle/ActualIdleDetector.cpp0000644000175000017500000000161012227051773022673 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include namespace Swift { ActualIdleDetector::ActualIdleDetector(IdleQuerier* querier, TimerFactory* timerFactory, int refreshRateMilliseconds) : querier(querier) { timer = timerFactory->createTimer(refreshRateMilliseconds); timer->onTick.connect(boost::bind(&ActualIdleDetector::handleTimerTick, this)); timer->start(); } ActualIdleDetector::~ActualIdleDetector() { timer->stop(); } void ActualIdleDetector::handleTimerTick() { timer->stop(); setIdle(querier->getIdleTimeSeconds() >= getIdleTimeSeconds()); timer->start(); } } swift-im-2.0+dev6/SwifTools/Idle/ActualIdleDetector.h0000644000175000017500000000120212227051773022335 0ustar kismithkismith/* * 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 #include namespace Swift { class IdleQuerier; class TimerFactory; class Timer; class ActualIdleDetector : public IdleDetector, public boost::bsignals::trackable { public: ActualIdleDetector(IdleQuerier*, TimerFactory*, int refreshRateMilliseconds); ~ActualIdleDetector(); private: void handleTimerTick(); private: IdleQuerier* querier; boost::shared_ptr timer; }; } swift-im-2.0+dev6/SwifTools/Idle/MacOSXIdleQuerier.cpp0000644000175000017500000000166112227051773022425 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #pragma GCC diagnostic ignored "-Wold-style-cast" #include #include #include namespace Swift { MacOSXIdleQuerier::MacOSXIdleQuerier() : ioService(0) { mach_port_t masterPort; IOMasterPort(MACH_PORT_NULL, &masterPort); ioService = IOServiceGetMatchingService(masterPort, IOServiceMatching("IOHIDSystem")); assert(ioService); } int MacOSXIdleQuerier::getIdleTimeSeconds() { CFTypeRef property = IORegistryEntryCreateCFProperty(ioService, CFSTR("HIDIdleTime"), kCFAllocatorDefault, 0); uint64_t idle = 0; bool result = CFNumberGetValue((CFNumberRef)property, kCFNumberSInt64Type, &idle); assert(result); (void) result; CFRelease(property); return idle / 1000000000; } } swift-im-2.0+dev6/SwifTools/Idle/XSSIdleQuerier.h0000644000175000017500000000103112227051773021444 0ustar kismithkismith/* * 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 #include #include namespace Swift { class XSSIdleQuerier : public IdleQuerier { public: XSSIdleQuerier(); ~XSSIdleQuerier(); virtual int getIdleTimeSeconds(); private: Display* display; Window rootWindow; bool available; XScreenSaverInfo* info; }; } swift-im-2.0+dev6/SwifTools/Idle/DummyIdleQuerier.h0000644000175000017500000000057312227051773022074 0ustar kismithkismith/* * 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 namespace Swift { class DummyIdleQuerier : public IdleQuerier { public: DummyIdleQuerier() {} virtual int getIdleTimeSeconds() { return 0; } }; } swift-im-2.0+dev6/SwifTools/Idle/WindowsIdleQuerier.cpp0000644000175000017500000000076712227051773022773 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include namespace Swift { WindowsIdleQuerier::WindowsIdleQuerier() { } int WindowsIdleQuerier::getIdleTimeSeconds() { LASTINPUTINFO info; info.cbSize = sizeof(info); if (GetLastInputInfo(&info)) { return (GetTickCount() - info.dwTime) / 1000; } else { return 0; } } } swift-im-2.0+dev6/SwifTools/Notifier/0000755000175000017500000000000012227051773017372 5ustar kismithkismithswift-im-2.0+dev6/SwifTools/Notifier/GrowlNotifier.mm0000644000175000017500000000707412227051773022527 0ustar kismithkismith/* * Copyright (c) 2010-2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #pragma GCC diagnostic ignored "-Wold-style-cast" namespace { struct Context { Context(const boost::function& callback) : callback(new boost::function(callback)) {} boost::function* callback; }; } namespace Swift { class GrowlNotifier::Private { public: std::set pendingNotifications; boost::intrusive_ptr delegate; }; GrowlNotifier::GrowlNotifier(const std::string& name) { p = boost::make_shared(); p->delegate = boost::intrusive_ptr([[GrowlNotifierDelegate alloc] init], false); p->delegate.get().notifier = this; p->delegate.get().name = STD2NSSTRING(name); NSMutableArray* allNotifications = [[NSMutableArray alloc] init]; foreach(Type type, getAllTypes()) { [allNotifications addObject: STD2NSSTRING(typeToString(type))]; } NSMutableArray* defaultNotifications = [[NSMutableArray alloc] init]; foreach(Type type, getDefaultTypes()) { [defaultNotifications addObject: STD2NSSTRING(typeToString(type))]; } p->delegate.get().registrationDictionary = [[[NSDictionary alloc] initWithObjects: [NSArray arrayWithObjects: allNotifications, defaultNotifications, nil] forKeys: [NSArray arrayWithObjects: GROWL_NOTIFICATIONS_ALL, GROWL_NOTIFICATIONS_DEFAULT, nil]] autorelease]; [allNotifications release]; [defaultNotifications release]; [GrowlApplicationBridge setGrowlDelegate: p->delegate.get()]; } GrowlNotifier::~GrowlNotifier() { [GrowlApplicationBridge setGrowlDelegate: nil]; clearPendingNotifications(); } void GrowlNotifier::showMessage(Type type, const std::string& subject, const std::string& description, const boost::filesystem::path& picturePath, boost::function callback) { ByteArray picture; readByteArrayFromFile(picture, picturePath.string()); Context* context = new Context(callback); // Growl sometimes sends timeout notifications twice for the same message. We therefore need // to keep track of which ones have already been processed. p->pendingNotifications.insert(context); [GrowlApplicationBridge notifyWithTitle: STD2NSSTRING(subject) description: STD2NSSTRING(description) notificationName: STD2NSSTRING(typeToString(type)) iconData: [NSData dataWithBytes: vecptr(picture) length: picture.size()] priority: 0 isSticky: NO clickContext: [NSData dataWithBytes: &context length: sizeof(context)]]; } void GrowlNotifier::handleNotificationClicked(void* rawData) { Context* context = *(Context**) [((NSData*) rawData) bytes]; if (p->pendingNotifications.erase(context) > 0) { if (!context->callback->empty()) { (*context->callback)(); } delete context; } } void GrowlNotifier::handleNotificationTimedOut(void* rawData) { Context* context = *(Context**) [((NSData*) rawData) bytes]; if (p->pendingNotifications.erase(context) > 0) { delete context; } } bool GrowlNotifier::isExternallyConfigured() const { return ![GrowlApplicationBridge isMistEnabled]; } void GrowlNotifier::purgeCallbacks() { clearPendingNotifications(); } void GrowlNotifier::clearPendingNotifications() { foreach (Context* context, p->pendingNotifications) { delete context; } p->pendingNotifications.clear(); } } swift-im-2.0+dev6/SwifTools/Notifier/SConscript0000644000175000017500000000064712227051773021413 0ustar kismithkismithImport("swiftools_env") myenv = swiftools_env.Clone() sources = [ "Notifier.cpp", ] if swiftools_env.get("HAVE_GROWL", False) : sources += [ "GrowlNotifier.mm", "GrowlNotifierDelegate.mm", ] if swiftools_env.get("HAVE_SNARL", False) : myenv.MergeFlags(myenv["SNARL_FLAGS"]) sources += [ "SnarlNotifier.cpp", ] objects = myenv.StaticObject(sources) swiftools_env.Append(SWIFTOOLS_OBJECTS = objects) swift-im-2.0+dev6/SwifTools/Notifier/NullNotifier.h0000644000175000017500000000072412227051773022160 0ustar kismithkismith/* * 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 namespace Swift { class NullNotifier : public Notifier { public: virtual void showMessage(Type, const std::string&, const std::string&, const boost::filesystem::path&, boost::function) { } virtual void purgeCallbacks() { } }; } swift-im-2.0+dev6/SwifTools/Notifier/LoggingNotifier.h0000644000175000017500000000214312227051773022631 0ustar kismithkismith/* * 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 #include namespace Swift { class LoggingNotifier : public Notifier { public: virtual void showMessage(Type type, const std::string& subject, const std::string& description, const boost::filesystem::path& picture, boost::function callback) { notifications.push_back(Notification(type, subject, description, picture, callback)); } struct Notification { Notification(Type type, const std::string& subject, const std::string& description, const boost::filesystem::path& picture, boost::function callback) : type(type), subject(subject), description(description), picture(picture), callback(callback) {} Type type; std::string subject; std::string description; boost::filesystem::path picture; boost::function callback; }; virtual void purgeCallbacks() {} std::vector notifications; }; } swift-im-2.0+dev6/SwifTools/Notifier/GrowlNotifierDelegate.mm0000644000175000017500000000136512227051773024157 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #import "GrowlNotifierDelegate.h" #include @implementation GrowlNotifierDelegate; @synthesize registrationDictionary; @synthesize name; @synthesize notifier; using namespace Swift; - (NSString *) applicationNameForGrowl { return name; } - (NSDictionary*) registrationDictionaryForGrowl { return registrationDictionary; } - (void) growlNotificationWasClicked: (id) clickContext { notifier->handleNotificationClicked(clickContext); } - (void) growlNotificationTimedOut: (id) clickContext { notifier->handleNotificationTimedOut(clickContext); } @end swift-im-2.0+dev6/SwifTools/Notifier/GrowlNotifierDelegate.h0000644000175000017500000000140612227051773023771 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #import namespace Swift { class GrowlNotifier; } @interface GrowlNotifierDelegate : NSObject { Swift::GrowlNotifier* notifier; NSString* name; NSDictionary* registrationDictionary; } @property (nonatomic, retain) NSDictionary* registrationDictionary; @property (nonatomic, copy) NSString* name; @property (nonatomic) Swift::GrowlNotifier* notifier; - (NSDictionary*) registrationDictionaryForGrowl; - (NSString *) applicationNameForGrowl; - (void) growlNotificationWasClicked: (id) clickContext; - (void) growlNotificationTimedOut: (id) clickContext; @end swift-im-2.0+dev6/SwifTools/Notifier/GrowlNotifier.h0000644000175000017500000000215112227051773022334 0ustar kismithkismith/* * Copyright (c) 2010-2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include namespace Swift { /** * Preconditions for using growlnotifier: * - Must be part a bundle. * - The Carbon/Cocoa application loop must be running (e.g. through QApplication) * such that notifications are coming through. */ class GrowlNotifier : public Notifier { public: GrowlNotifier(const std::string& name); ~GrowlNotifier(); virtual void showMessage(Type type, const std::string& subject, const std::string& description, const boost::filesystem::path& picture, boost::function callback); virtual bool isExternallyConfigured() const; // Called by the delegate. Don't call. void handleNotificationClicked(void* data); void handleNotificationTimedOut(void* data); virtual void purgeCallbacks(); private: void clearPendingNotifications(); private: class Private; boost::shared_ptr p; }; } swift-im-2.0+dev6/SwifTools/Notifier/Notifier.cpp0000644000175000017500000000232212227051773021654 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { const int Notifier::DEFAULT_STATUS_NOTIFICATION_TIMEOUT_SECONDS = 3; const int Notifier::DEFAULT_MESSAGE_NOTIFICATION_TIMEOUT_SECONDS = 5; Notifier::~Notifier() { } std::string Notifier::typeToString(Type type) { switch (type) { case ContactAvailable: return "Contact Becomes Available"; case ContactUnavailable: return "Contact Becomes Unavailable"; case ContactStatusChange: return "Contact Changes Status"; case IncomingMessage: return "Incoming Message"; case SystemMessage: return "System Message"; } assert(false); return ""; } std::vector Notifier::getAllTypes() { std::vector result; result.push_back(ContactAvailable); result.push_back(ContactUnavailable); result.push_back(ContactStatusChange); result.push_back(IncomingMessage); result.push_back(SystemMessage); return result; } std::vector Notifier::getDefaultTypes() { std::vector result; result.push_back(IncomingMessage); result.push_back(SystemMessage); return result; } } swift-im-2.0+dev6/SwifTools/Notifier/Notifier.h0000644000175000017500000000225612227051773021327 0ustar kismithkismith/* * 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 #include #include #include namespace Swift { class Notifier { public: virtual ~Notifier(); enum Type { ContactAvailable, ContactUnavailable, ContactStatusChange, IncomingMessage, SystemMessage }; /** * Picture is a PNG image. */ virtual void showMessage( Type type, const std::string& subject, const std::string& description, const boost::filesystem::path& picture, boost::function callback) = 0; virtual bool isAvailable() const { return true; } virtual bool isExternallyConfigured() const { return false; } /** Remove any pending callbacks. */ virtual void purgeCallbacks() = 0; protected: std::string typeToString(Type type); static std::vector getAllTypes(); static std::vector getDefaultTypes(); static const int DEFAULT_STATUS_NOTIFICATION_TIMEOUT_SECONDS; static const int DEFAULT_MESSAGE_NOTIFICATION_TIMEOUT_SECONDS; }; } swift-im-2.0+dev6/SwifTools/Notifier/TogglableNotifier.h0000644000175000017500000000333712227051773023151 0ustar kismithkismith/* * 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 namespace Swift { class TogglableNotifier : public Notifier { public: TogglableNotifier(Notifier* notifier) : notifier(notifier), persistentEnabled(true), temporarilyDisabled(false) { } /** * Set a long-term (usually user-set) enabled. * This may be temporarily overriden by the application, e.g. if the * user is marked DND. */ void setPersistentEnabled(bool b) { persistentEnabled = b; } /** * Set a temporary override to stop notifications without changing the * long-term state. e.g. if the user goes DND, but the persistent * enabled shouldn't be lost when they become available again. */ void setTemporarilyDisabled(bool b) { temporarilyDisabled = b; } /** * Get the result of applying the temporary override to the persistent * enabledness. */ bool getCurrentlyEnabled() const { return persistentEnabled && !temporarilyDisabled; } virtual void showMessage(Type type, const std::string& subject, const std::string& description, const boost::filesystem::path& picture, boost::function callback) { if (getCurrentlyEnabled() || notifier->isExternallyConfigured()) { notifier->showMessage(type, subject, description, picture, callback); } } virtual bool isExternallyConfigured() const { return notifier->isExternallyConfigured(); } virtual void purgeCallbacks() { notifier->purgeCallbacks(); } private: Notifier* notifier; bool persistentEnabled; bool temporarilyDisabled; }; } swift-im-2.0+dev6/SwifTools/Notifier/Win32NotifierWindow.h0000644000175000017500000000065212227051773023340 0ustar kismithkismith/* * 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 #include namespace Swift { class Win32NotifierWindow { public: virtual ~Win32NotifierWindow() {} virtual HWND getID() const = 0; boost::signal onMessageReceived; }; } swift-im-2.0+dev6/SwifTools/Notifier/GNTPNotifier.h0000644000175000017500000000205112227051773022011 0ustar kismithkismith/* * 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 #include #include namespace Swift { class ConnectionFactory; class GNTPNotifier : public Notifier { public: GNTPNotifier(const std::string& name, const boost::filesystem::path& icon, ConnectionFactory* connectionFactory); ~GNTPNotifier(); virtual void showMessage(Type type, const std::string& subject, const std::string& description, const boost::filesystem::path& picture, boost::function callback); private: void handleConnectFinished(bool error); void handleDataRead(const ByteArray& data); void send(const std::string& message); private: std::string name; boost::filesystem::path icon; ConnectionFactory* connectionFactory; bool initialized; bool registered; std::string currentMessage; Connection::ref currentConnection; }; } swift-im-2.0+dev6/SwifTools/Notifier/GNTPNotifier.cpp0000644000175000017500000000573312227051773022356 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ // FIXME: This notifier needs finishing (handling callbacks etc.) #include #include #include #include #include #include #include namespace Swift { GNTPNotifier::GNTPNotifier(const std::string& name, const boost::filesystem::path& icon, ConnectionFactory* connectionFactory) : name(name), icon(icon), connectionFactory(connectionFactory), initialized(false), registered(false) { // Registration message std::ostringstream message; message << "GNTP/1.0 REGISTER NONE\r\n"; message << "Application-Name: " << name << "\r\n"; message << "Application-Icon: file://" << icon.string() << "\r\n"; message << "Notifications-Count: " << getAllTypes().size() << "\r\n"; std::vector defaultTypes = getDefaultTypes(); std::vector allTypes = getAllTypes(); foreach(Notifier::Type type, allTypes) { message << "\r\n"; message << "Notification-Name: " << typeToString(type) << "\r\n"; message << "Notification-Enabled: " << (std::find(defaultTypes.begin(), defaultTypes.end(), type) == defaultTypes.end() ? "false" : "true") << "\r\n"; } message << "\r\n"; send(message.str()); } GNTPNotifier::~GNTPNotifier() { } void GNTPNotifier::send(const std::string& message) { if (currentConnection) { return; } currentMessage = message; currentConnection = connectionFactory->createConnection(); currentConnection->onConnectFinished.connect(boost::bind(&GNTPNotifier::handleConnectFinished, this, _1)); currentConnection->onDataRead.connect(boost::bind(&GNTPNotifier::handleDataRead, this, _1)); currentConnection->connect(HostAddressPort(HostAddress("127.0.0.1"), 23053)); } void GNTPNotifier::showMessage(Type type, const std::string& subject, const std::string& description, const boost::filesystem::path& picture, boost::function) { if (registered) { std::ostringstream message; message << "GNTP/1.0 NOTIFY NONE\r\n"; message << "Application-Name: " << name << "\r\n"; message << "Notification-Name: " << typeToString(type) << "\r\n"; message << "Notification-Title: " << subject << "\r\n"; message << "Notification-Text: " << description << "\r\n"; message << "Notification-Icon: " << picture.string() << "\r\n"; message << "\r\n"; send(message.str()); } } void GNTPNotifier::handleConnectFinished(bool error) { if (!initialized) { initialized = true; registered = !error; } if (!error) { currentConnection->write(currentMessage.c_str()); } } void GNTPNotifier::handleDataRead(const ByteArray&) { currentConnection->onDataRead.disconnect(boost::bind(&GNTPNotifier::handleDataRead, this, _1)); currentConnection->onConnectFinished.disconnect(boost::bind(&GNTPNotifier::handleConnectFinished, this, _1)); currentConnection.reset(); } } swift-im-2.0+dev6/SwifTools/Notifier/SnarlNotifier.cpp0000644000175000017500000000512712227051773022662 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #define SWIFT_SNARLNOTIFIER_MESSAGE_ID 0x4567 // Sounds sick to pick a number, but this is windows namespace Swift { SnarlNotifier::SnarlNotifier(const std::string& name, Win32NotifierWindow* window, const boost::filesystem::path& icon) : window(window), available(false) { window->onMessageReceived.connect(boost::bind(&SnarlNotifier::handleMessageReceived, this, _1)); available = snarl.RegisterApp(name.c_str(), name.c_str(), icon.string().c_str(), window->getID(), SWIFT_SNARLNOTIFIER_MESSAGE_ID); foreach(Notifier::Type type, getAllTypes()) { snarl.AddClass(typeToString(type).c_str(), typeToString(type).c_str()); } } SnarlNotifier::~SnarlNotifier() { snarl.UnregisterApp(); window->onMessageReceived.disconnect(boost::bind(&SnarlNotifier::handleMessageReceived, this, _1)); if (!notifications.empty()) { std::cerr << "Warning: " << notifications.size() << " Snarl notifications pending" << std::endl; } } bool SnarlNotifier::isAvailable() const { return available; } void SnarlNotifier::showMessage(Type type, const std::string& subject, const std::string& description, const boost::filesystem::path& picture, boost::function callback) { int timeout = (type == IncomingMessage || type == SystemMessage) ? DEFAULT_MESSAGE_NOTIFICATION_TIMEOUT_SECONDS : DEFAULT_STATUS_NOTIFICATION_TIMEOUT_SECONDS; int notificationID = snarl.EZNotify( typeToString(type).c_str(), subject.c_str(), description.c_str(), timeout, picture.string().c_str()); if (notificationID > 0) { notifications.insert(std::make_pair(notificationID, callback)); } } void SnarlNotifier::handleMessageReceived(MSG* message) { if (message->message == SWIFT_SNARLNOTIFIER_MESSAGE_ID) { int action = message->wParam; if (action == Snarl::V41::SnarlEnums::NotificationTimedOut || action == Snarl::V41::SnarlEnums::NotificationAck || action == Snarl::V41::SnarlEnums::NotificationClosed) { int notificationID = message->lParam; NotificationsMap::iterator i = notifications.find(notificationID); if (i != notifications.end()) { if (action == Snarl::V41::SnarlEnums::NotificationAck && !i->second.empty()) { i->second(); } notifications.erase(i); } else { std::cerr << "Warning: Orphaned Snarl notification received"; } } } } } swift-im-2.0+dev6/SwifTools/Notifier/SnarlNotifier.h0000644000175000017500000000201512227051773022320 0ustar kismithkismith/* * 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 #include #include namespace Swift { class Win32NotifierWindow; class SnarlNotifier : public Notifier { public: SnarlNotifier(const std::string& name, Win32NotifierWindow* window, const boost::filesystem::path& icon); ~SnarlNotifier(); virtual void showMessage(Type type, const std::string& subject, const std::string& description, const boost::filesystem::path& picture, boost::function callback); virtual bool isAvailable() const; virtual void purgeCallbacks() { notifications.clear(); } private: void handleMessageReceived(MSG* message); private: Snarl::V41::SnarlInterface snarl; Win32NotifierWindow* window; bool available; typedef std::map > NotificationsMap; NotificationsMap notifications; }; } swift-im-2.0+dev6/SwifTools/CrashReporter.cpp0000644000175000017500000000425412227051773021107 0ustar kismithkismith/* * Copyright (c) 2012 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #if defined(HAVE_BREAKPAD) #pragma GCC diagnostic ignored "-Wold-style-cast" #include #ifdef SWIFTEN_PLATFORM_MACOSX #include "client/mac/handler/exception_handler.h" #endif #ifdef SWIFTEN_PLATFORM_WINDOWS #include "client/windows/handler/exception_handler.h" #endif #if defined(SWIFTEN_PLATFORM_WINDOWS) static bool handleDump(const wchar_t* /* dir */, const wchar_t* /* id*/, void* /* context */, EXCEPTION_POINTERS*, MDRawAssertionInfo*, bool /* succeeded */) { return false; } #else static bool handleDump(const char* /* dir */, const char* /* id*/, void* /* context */, bool /* succeeded */) { return false; } #endif namespace Swift { struct CrashReporter::Private { boost::shared_ptr handler; }; CrashReporter::CrashReporter(const boost::filesystem::path& path) { // Create the path that will contain the crash dumps if (!boost::filesystem::exists(path)) { try { boost::filesystem::create_directories(path); } catch (const boost::filesystem::filesystem_error& e) { std::cerr << "ERROR: " << e.what() << std::endl; } } p = boost::make_shared(); #if defined(SWIFTEN_PLATFORM_WINDOWS) // FIXME: Need UTF8 conversion from string to wstring std::string pathString = path.string(); p->handler = boost::make_shared( std::wstring(pathString.begin(), pathString.end()), (google_breakpad::ExceptionHandler::FilterCallback) 0, handleDump, (void*) 0, google_breakpad::ExceptionHandler::HANDLER_ALL); // Turning it off for Mac, because it doesn't really help us //#elif defined(SWIFTEN_PLATFORM_MACOSX) // p->handler = boost::make_shared(path.string(), (google_breakpad::ExceptionHandler::FilterCallback) 0, handleDump, (void*) 0, true, (const char*) 0); #endif } }; #else // Dummy implementation namespace Swift { CrashReporter::CrashReporter(const boost::filesystem::path&) {} }; #endif swift-im-2.0+dev6/SwifTools/Linkify.h0000644000175000017500000000043012227051773017366 0ustar kismithkismith/* * 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 namespace Swift { namespace Linkify { std::string linkify(const std::string&); } } swift-im-2.0+dev6/SwifTools/AutoUpdater/0000755000175000017500000000000012227051773020050 5ustar kismithkismithswift-im-2.0+dev6/SwifTools/AutoUpdater/AutoUpdater.h0000644000175000017500000000045012227051773022455 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once namespace Swift { class AutoUpdater { public: virtual ~AutoUpdater(); virtual void checkForUpdates() = 0; }; } swift-im-2.0+dev6/SwifTools/AutoUpdater/SparkleAutoUpdater.h0000644000175000017500000000072712227051773024006 0ustar kismithkismith/* * 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 #include namespace Swift { class SparkleAutoUpdater : public AutoUpdater { public: SparkleAutoUpdater(const std::string& url); ~SparkleAutoUpdater(); void checkForUpdates(); private: class Private; Private* d; }; } swift-im-2.0+dev6/SwifTools/AutoUpdater/AutoUpdater.cpp0000644000175000017500000000040612227051773023011 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { AutoUpdater::~AutoUpdater() { } } swift-im-2.0+dev6/SwifTools/AutoUpdater/SparkleAutoUpdater.mm0000644000175000017500000000127412227051773024166 0ustar kismithkismith#include #include #include namespace Swift { class SparkleAutoUpdater::Private { public: SUUpdater* updater; }; SparkleAutoUpdater::SparkleAutoUpdater(const std::string& url) { d = new Private; d->updater = [SUUpdater sharedUpdater]; [d->updater retain]; [d->updater setAutomaticallyChecksForUpdates: true]; NSURL* nsurl = [NSURL URLWithString: [NSString stringWithUTF8String: url.c_str()]]; [d->updater setFeedURL: nsurl]; } SparkleAutoUpdater::~SparkleAutoUpdater() { [d->updater release]; delete d; } void SparkleAutoUpdater::checkForUpdates() { [d->updater checkForUpdatesInBackground]; } } swift-im-2.0+dev6/SwifTools/AutoUpdater/PlatformAutoUpdaterFactory.h0000644000175000017500000000055612227051773025521 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { class AutoUpdater; class PlatformAutoUpdaterFactory { public: bool isSupported() const; AutoUpdater* createAutoUpdater(const std::string& appcastURL); }; } swift-im-2.0+dev6/SwifTools/AutoUpdater/PlatformAutoUpdaterFactory.cpp0000644000175000017500000000124012227051773026043 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #ifdef HAVE_SPARKLE #include #endif namespace Swift { bool PlatformAutoUpdaterFactory::isSupported() const { #ifdef HAVE_SPARKLE return true; #else return false; #endif } AutoUpdater* PlatformAutoUpdaterFactory::createAutoUpdater(const std::string& appcastURL) { #ifdef HAVE_SPARKLE return new SparkleAutoUpdater(appcastURL); #else (void) appcastURL; return NULL; #endif } } swift-im-2.0+dev6/SwifTools/Application/0000755000175000017500000000000012227051773020056 5ustar kismithkismithswift-im-2.0+dev6/SwifTools/Application/SConscript0000644000175000017500000000125412227051773022072 0ustar kismithkismithImport("swiftools_env", "env") sources = [ "ApplicationPathProvider.cpp", ] if swiftools_env["PLATFORM"] == "darwin" and swiftools_env["target"] == "native" : sources += [ "CocoaApplication.mm", "MacOSXApplicationPathProvider.cpp", ] elif swiftools_env["PLATFORM"] == "win32" : sources += [ "WindowsApplicationPathProvider.cpp" ] else : sources += [ "UnixApplicationPathProvider.cpp" ] objects = swiftools_env.StaticObject(sources) swiftools_env.Append(SWIFTOOLS_OBJECTS = [objects]) if swiftools_env["PLATFORM"] != "darwin" or swiftools_env["target"] == "native" : env.Append(UNITTEST_SOURCES = [ File("UnitTest/ApplicationPathProviderTest.cpp") ]) swift-im-2.0+dev6/SwifTools/Application/UnixApplicationPathProvider.cpp0000644000175000017500000000363312227051773026226 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include namespace Swift { UnixApplicationPathProvider::UnixApplicationPathProvider(const std::string& name) : ApplicationPathProvider(name) { resourceDirs.push_back(getExecutableDir() / "../resources"); // Development resourceDirs.push_back(getExecutableDir() / ".." / "share" / boost::to_lower_copy(getApplicationName())); // Local install char* xdgDataDirs = getenv("XDG_DATA_DIRS"); if (xdgDataDirs) { std::vector dataDirs = String::split(xdgDataDirs, ':'); if (!dataDirs.empty()) { foreach(const std::string& dir, dataDirs) { resourceDirs.push_back(boost::filesystem::path(dir) / "swift"); } return; } } resourceDirs.push_back("/usr/local/share/" + boost::to_lower_copy(getApplicationName())); resourceDirs.push_back("/usr/share/" + boost::to_lower_copy(getApplicationName())); } boost::filesystem::path UnixApplicationPathProvider::getHomeDir() const { char* home = getenv("HOME"); return home ? boost::filesystem::path(home) : boost::filesystem::path(); } boost::filesystem::path UnixApplicationPathProvider::getDataDir() const { char* xdgDataHome = getenv("XDG_DATA_HOME"); std::string dataDir; if (xdgDataHome) { dataDir = std::string(xdgDataHome); } boost::filesystem::path dataPath = (dataDir.empty() ? getHomeDir() / ".local" / "share" : boost::filesystem::path(dataDir)) / boost::to_lower_copy(getApplicationName()); try { boost::filesystem::create_directories(dataPath); } catch (const boost::filesystem::filesystem_error& e) { std::cerr << "ERROR: " << e.what() << std::endl; } return dataPath; } } swift-im-2.0+dev6/SwifTools/Application/WindowsApplicationPathProvider.h0000644000175000017500000000200112227051773026366 0ustar kismithkismith/* * 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 namespace Swift { class WindowsApplicationPathProvider : public ApplicationPathProvider { public: WindowsApplicationPathProvider(const std::string& name); boost::filesystem::path getDataDir() const { char* appDirRaw = getenv("APPDATA"); boost::filesystem::path result(boost::filesystem::path(appDirRaw) / getApplicationName()); boost::filesystem::create_directory(result); return result; } boost::filesystem::path getHomeDir() const { //FIXME: This should be My Documents char* homeDirRaw = getenv("USERPROFILE"); return boost::filesystem::path(homeDirRaw); } virtual std::vector getResourceDirs() const { return resourceDirs; } private: std::vector resourceDirs; }; } swift-im-2.0+dev6/SwifTools/Application/PlatformApplicationPathProvider.h0000644000175000017500000000137712227051773026537 0ustar kismithkismith/* * 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 #if defined(SWIFTEN_PLATFORM_MACOSX) #include namespace Swift { typedef MacOSXApplicationPathProvider PlatformApplicationPathProvider; } #elif defined(SWIFTEN_PLATFORM_WIN32) #include namespace Swift { typedef WindowsApplicationPathProvider PlatformApplicationPathProvider; } #else #include namespace Swift { typedef UnixApplicationPathProvider PlatformApplicationPathProvider; } #endif swift-im-2.0+dev6/SwifTools/Application/CocoaApplication.h0000644000175000017500000000050612227051773023440 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once namespace Swift { class CocoaApplication { public: CocoaApplication(); ~CocoaApplication(); private: class Private; Private* d; }; } swift-im-2.0+dev6/SwifTools/Application/ApplicationPathProvider.cpp0000644000175000017500000000256512227051773025365 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include namespace Swift { ApplicationPathProvider::ApplicationPathProvider(const std::string& applicationName) : applicationName(applicationName) { } ApplicationPathProvider::~ApplicationPathProvider() { } boost::filesystem::path ApplicationPathProvider::getProfileDir(const std::string& profile) const { boost::filesystem::path result(getHomeDir() / profile); try { boost::filesystem::create_directory(result); } catch (const boost::filesystem::filesystem_error& e) { std::cerr << "ERROR: " << e.what() << std::endl; } return result; } boost::filesystem::path ApplicationPathProvider::getResourcePath(const std::string& resource) const { std::vector resourcePaths = getResourceDirs(); foreach(const boost::filesystem::path& resourcePath, resourcePaths) { boost::filesystem::path r(resourcePath / resource); if (boost::filesystem::exists(r)) { return r; } } return boost::filesystem::path(); } boost::filesystem::path ApplicationPathProvider::getExecutableDir() const { return Paths::getExecutablePath(); } } swift-im-2.0+dev6/SwifTools/Application/MacOSXApplicationPathProvider.cpp0000644000175000017500000000202712227051773026371 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include namespace Swift { MacOSXApplicationPathProvider::MacOSXApplicationPathProvider(const std::string& name) : ApplicationPathProvider(name) { resourceDirs.push_back(getExecutableDir() / "../Resources"); resourceDirs.push_back(getExecutableDir() / "../resources"); // Development } boost::filesystem::path MacOSXApplicationPathProvider::getDataDir() const { boost::filesystem::path result(getHomeDir() / "Library/Application Support" / getApplicationName()); try { boost::filesystem::create_directory(result); } catch (const boost::filesystem::filesystem_error& e) { std::cerr << "ERROR: " << e.what() << std::endl; } return result; } boost::filesystem::path MacOSXApplicationPathProvider::getHomeDir() const { return boost::filesystem::path(getenv("HOME")); } } swift-im-2.0+dev6/SwifTools/Application/UnitTest/0000755000175000017500000000000012227051773021635 5ustar kismithkismithswift-im-2.0+dev6/SwifTools/Application/UnitTest/ApplicationPathProviderTest.cpp0000644000175000017500000000246612227051773030004 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include using namespace Swift; class ApplicationPathProviderTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(ApplicationPathProviderTest); CPPUNIT_TEST(testGetDataDir); CPPUNIT_TEST(testGetExecutableDir); CPPUNIT_TEST_SUITE_END(); public: void setUp() { testling_ = new PlatformApplicationPathProvider("SwiftTest"); } void tearDown() { delete testling_; } void testGetDataDir() { boost::filesystem::path dir = testling_->getDataDir(); CPPUNIT_ASSERT(boost::filesystem::exists(dir)); CPPUNIT_ASSERT(boost::filesystem::is_directory(dir)); boost::filesystem::remove(dir); } void testGetExecutableDir() { boost::filesystem::path dir = testling_->getExecutableDir(); CPPUNIT_ASSERT(boost::filesystem::is_directory(dir)); CPPUNIT_ASSERT(boost::ends_with(dir.string(), "UnitTest")); } private: ApplicationPathProvider* testling_; }; CPPUNIT_TEST_SUITE_REGISTRATION(ApplicationPathProviderTest); swift-im-2.0+dev6/SwifTools/Application/ApplicationPathProvider.h0000644000175000017500000000170712227051773025027 0ustar kismithkismith/* * 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 #include #include namespace Swift { class ApplicationPathProvider { public: ApplicationPathProvider(const std::string& applicationName); virtual ~ApplicationPathProvider(); virtual boost::filesystem::path getHomeDir() const = 0; virtual boost::filesystem::path getDataDir() const = 0; virtual boost::filesystem::path getExecutableDir() const; boost::filesystem::path getProfileDir(const std::string& profile) const; boost::filesystem::path getResourcePath(const std::string& resource) const; protected: virtual std::vector getResourceDirs() const = 0; const std::string& getApplicationName() const { return applicationName; } private: std::string applicationName; }; } swift-im-2.0+dev6/SwifTools/Application/UnixApplicationPathProvider.h0000644000175000017500000000123612227051773025670 0ustar kismithkismith/* * 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 namespace Swift { class UnixApplicationPathProvider : public ApplicationPathProvider { public: UnixApplicationPathProvider(const std::string& name); virtual boost::filesystem::path getHomeDir() const; boost::filesystem::path getDataDir() const; virtual std::vector getResourceDirs() const { return resourceDirs; } private: std::vector resourceDirs; }; } swift-im-2.0+dev6/SwifTools/Application/WindowsApplicationPathProvider.cpp0000644000175000017500000000100512227051773026724 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include namespace Swift { WindowsApplicationPathProvider::WindowsApplicationPathProvider(const std::string& name) : ApplicationPathProvider(name) { resourceDirs.push_back(getExecutableDir()); resourceDirs.push_back(getExecutableDir() / "../resources"); // Development } } swift-im-2.0+dev6/SwifTools/Application/CocoaApplication.mm0000644000175000017500000000071212227051773023621 0ustar kismithkismith#include #include #include namespace Swift { class CocoaApplication::Private { public: NSAutoreleasePool* autoReleasePool_; }; CocoaApplication::CocoaApplication() { d = new CocoaApplication::Private(); NSApplicationLoad(); d->autoReleasePool_ = [[NSAutoreleasePool alloc] init]; } CocoaApplication::~CocoaApplication() { [d->autoReleasePool_ release]; delete d; } } swift-im-2.0+dev6/SwifTools/Application/MacOSXApplicationPathProvider.h0000644000175000017500000000124112227051773026033 0ustar kismithkismith/* * 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 namespace Swift { class MacOSXApplicationPathProvider : public ApplicationPathProvider { public: MacOSXApplicationPathProvider(const std::string& name); virtual boost::filesystem::path getHomeDir() const; boost::filesystem::path getDataDir() const; virtual std::vector getResourceDirs() const { return resourceDirs; } private: std::vector resourceDirs; }; } swift-im-2.0+dev6/SwifTools/TabComplete.cpp0000644000175000017500000000344112227051773020520 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include namespace Swift { void TabComplete::addWord(const std::string& word) { words_.erase(std::remove(words_.begin(), words_.end(), word), words_.end()); words_.insert(words_.begin(), word); if (boost::starts_with(boost::to_lower_copy(word), lastShort_)) { lastCompletionCandidates_.insert(lastCompletionCandidates_.begin(), word); } } void TabComplete::removeWord(const std::string& word) { words_.erase(std::remove(words_.begin(), words_.end(), word), words_.end()); lastCompletionCandidates_.erase(std::remove(lastCompletionCandidates_.begin(), lastCompletionCandidates_.end(), word), lastCompletionCandidates_.end()); } std::string TabComplete::completeWord(const std::string& word) { if (word == lastCompletion_) { if (!lastCompletionCandidates_.empty()) { size_t match = 0; for (match = 0; match < lastCompletionCandidates_.size(); match++) { if (lastCompletionCandidates_[match] == lastCompletion_) { break; } } size_t nextIndex = match + 1; nextIndex = nextIndex >= lastCompletionCandidates_.size() ? 0 : nextIndex; lastCompletion_ = lastCompletionCandidates_[nextIndex]; } } else { lastShort_ = boost::to_lower_copy(word); lastCompletionCandidates_.clear(); foreach (std::string candidate, words_) { if (boost::starts_with(boost::to_lower_copy(candidate), boost::to_lower_copy(word))) { lastCompletionCandidates_.push_back(candidate); } } lastCompletion_ = !lastCompletionCandidates_.empty() ? lastCompletionCandidates_[0] : word; } return lastCompletion_; } } swift-im-2.0+dev6/SwifTools/TabComplete.h0000644000175000017500000000106612227051773020166 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include namespace Swift { class TabComplete { public: void addWord(const std::string& word); void removeWord(const std::string& word); std::string completeWord(const std::string& word); private: std::vector words_; std::string lastCompletion_; std::string lastShort_; std::vector lastCompletionCandidates_; }; } swift-im-2.0+dev6/SwifTools/Cocoa/0000755000175000017500000000000012227051773016637 5ustar kismithkismithswift-im-2.0+dev6/SwifTools/Cocoa/SConscript0000644000175000017500000000040212227051773020645 0ustar kismithkismithImport("swiftools_env", "env") sources = [] if swiftools_env["PLATFORM"] == "darwin" and swiftools_env["target"] == "native" : sources += ["CocoaAction.mm"] objects = swiftools_env.StaticObject(sources) swiftools_env.Append(SWIFTOOLS_OBJECTS = [objects]) swift-im-2.0+dev6/SwifTools/Cocoa/CocoaUtil.h0000644000175000017500000000135612227051773020677 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once // Conversion utilities #define NS2STDSTRING(a) (a == nil ? std::string() : std::string([a cStringUsingEncoding:NSUTF8StringEncoding])) #define STD2NSSTRING(a) [NSString stringWithCString:a.c_str() encoding:NSUTF8StringEncoding] // Intrusive pointer for NSObjects namespace boost { inline void intrusive_ptr_add_ref(NSObject* object) { [object retain]; } inline void intrusive_ptr_release(NSObject* object) { [object release]; } } // Including intrusive_ptr after ref/release methods to avoid compilation // errors with CLang #include swift-im-2.0+dev6/SwifTools/Cocoa/CocoaAction.h0000644000175000017500000000077512227051773021203 0ustar kismithkismith/* * 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 #include @interface CocoaAction : NSObject { boost::function* function; } /** * Acquires ownership of 'f'. */ - (id) initWithFunction: (boost::function*) f; /** * Calls the functor passed as a parameter to the contsructor. */ - (void) doAction: (id) sender; @end swift-im-2.0+dev6/SwifTools/Cocoa/CocoaAction.mm0000644000175000017500000000074312227051773021360 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include @implementation CocoaAction - (id) initWithFunction: (boost::function*) f { if ([super init]) { function = f; } return self; } - (void) dealloc { delete function; [super dealloc]; } - (void) doAction: (id) sender { (void) sender; (*function)(); } @end swift-im-2.0+dev6/SwifTools/UnitTest/0000755000175000017500000000000012227051773017372 5ustar kismithkismithswift-im-2.0+dev6/SwifTools/UnitTest/SConscript0000644000175000017500000000022012227051773021376 0ustar kismithkismithImport("env") env.Append(UNITTEST_SOURCES = [ File("LinkifyTest.cpp"), File("TabCompleteTest.cpp"), File("LastLineTrackerTest.cpp"), ]) swift-im-2.0+dev6/SwifTools/UnitTest/LinkifyTest.cpp0000644000175000017500000001351512227051773022350 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include using namespace Swift; class LinkifyTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(LinkifyTest); CPPUNIT_TEST(testLinkify_URLWithResource); CPPUNIT_TEST(testLinkify_HTTPSURLWithResource); CPPUNIT_TEST(testLinkify_URLWithEmptyResource); CPPUNIT_TEST(testLinkify_BareURL); CPPUNIT_TEST(testLinkify_URLSurroundedByWhitespace); CPPUNIT_TEST(testLinkify_MultipleURLs); CPPUNIT_TEST(testLinkify_CamelCase); CPPUNIT_TEST(testLinkify_HierarchicalResource); CPPUNIT_TEST(testLinkify_Anchor); CPPUNIT_TEST(testLinkify_Plus); CPPUNIT_TEST(testLinkify_Tilde); CPPUNIT_TEST(testLinkify_Equal); CPPUNIT_TEST(testLinkify_Authentication); CPPUNIT_TEST(testLinkify_At); CPPUNIT_TEST(testLinkify_Amps); CPPUNIT_TEST(testLinkify_UnicodeCharacter); CPPUNIT_TEST(testLinkify_NewLine); CPPUNIT_TEST(testLinkify_Tab); CPPUNIT_TEST(testLinkify_Action); CPPUNIT_TEST_SUITE_END(); public: void testLinkify_URLWithResource() { std::string result = Linkify::linkify("http://swift.im/blog"); CPPUNIT_ASSERT_EQUAL( std::string("http://swift.im/blog"), result); } void testLinkify_HTTPSURLWithResource() { std::string result = Linkify::linkify("https://swift.im/blog"); CPPUNIT_ASSERT_EQUAL( std::string("https://swift.im/blog"), result); } void testLinkify_URLWithEmptyResource() { std::string result = Linkify::linkify("http://swift.im/"); CPPUNIT_ASSERT_EQUAL( std::string("http://swift.im/"), result); } void testLinkify_BareURL() { std::string result = Linkify::linkify("http://swift.im"); CPPUNIT_ASSERT_EQUAL( std::string("http://swift.im"), result); } void testLinkify_URLSurroundedByWhitespace() { std::string result = Linkify::linkify("Foo http://swift.im/blog Bar"); CPPUNIT_ASSERT_EQUAL( std::string("Foo http://swift.im/blog Bar"), result); } void testLinkify_MultipleURLs() { std::string result = Linkify::linkify("Foo http://swift.im/blog Bar http://el-tramo.be/about Baz"); CPPUNIT_ASSERT_EQUAL( std::string("Foo http://swift.im/blog Bar http://el-tramo.be/about Baz"), result); } void testLinkify_CamelCase() { std::string result = Linkify::linkify("http://fOo.cOm/bAz"); CPPUNIT_ASSERT_EQUAL( std::string("http://fOo.cOm/bAz"), result); } void testLinkify_HierarchicalResource() { std::string result = Linkify::linkify("http://foo.com/bar/baz/"); CPPUNIT_ASSERT_EQUAL( std::string("http://foo.com/bar/baz/"), result); } void testLinkify_Anchor() { std::string result = Linkify::linkify("http://foo.com/bar#baz"); CPPUNIT_ASSERT_EQUAL( std::string("http://foo.com/bar#baz"), result); } void testLinkify_Plus() { std::string result = Linkify::linkify("http://foo.com/bar+baz"); CPPUNIT_ASSERT_EQUAL( std::string("http://foo.com/bar+baz"), result); } void testLinkify_Tilde() { std::string result = Linkify::linkify("http://foo.com/~kev/"); CPPUNIT_ASSERT_EQUAL( std::string("http://foo.com/~kev/"), result); } void testLinkify_Equal() { std::string result = Linkify::linkify("http://www.amazon.co.uk/s/ref=nb_sb_noss?url=search-alias%3Daps&field-keywords=xmpp+definitive+guide&x=0&y=0"); CPPUNIT_ASSERT_EQUAL( std::string("http://www.amazon.co.uk/s/ref=nb_sb_noss?url=search-alias%3Daps&field-keywords=xmpp+definitive+guide&x=0&y=0"), result); } void testLinkify_Authentication() { std::string result = Linkify::linkify("http://bob:bla@swift.im/foo/bar"); CPPUNIT_ASSERT_EQUAL( std::string("http://bob:bla@swift.im/foo/bar"), result); } void testLinkify_At() { std::string result = Linkify::linkify("http://swift.im/foo@bar"); CPPUNIT_ASSERT_EQUAL( std::string("http://swift.im/foo@bar"), result); } void testLinkify_Amps() { std::string result = Linkify::linkify("http://swift.im/foo&bar&baz"); CPPUNIT_ASSERT_EQUAL( std::string("http://swift.im/foo&bar&baz"), result); } void testLinkify_UnicodeCharacter() { std::string result = Linkify::linkify("http://\xe2\x98\x83.net"); CPPUNIT_ASSERT_EQUAL( std::string("http://\xe2\x98\x83.net"), result); } void testLinkify_NewLine() { std::string result = Linkify::linkify("http://swift.im\nfoo"); CPPUNIT_ASSERT_EQUAL( std::string("http://swift.im\nfoo"), result); } void testLinkify_Tab() { std::string result = Linkify::linkify("http://swift.im\tfoo"); CPPUNIT_ASSERT_EQUAL( std::string("http://swift.im\tfoo"), result); } void testLinkify_Action() { std::string result = Linkify::linkify("*http://swift.im*"); CPPUNIT_ASSERT_EQUAL( std::string("*http://swift.im*"), result); } }; CPPUNIT_TEST_SUITE_REGISTRATION(LinkifyTest); swift-im-2.0+dev6/SwifTools/UnitTest/LastLineTrackerTest.cpp0000644000175000017500000000374612227051773023777 0ustar kismithkismith/* * Copyright (c) 2011 Vlad Voicu * Licensed under the Simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include #include #include using namespace Swift; class LastLineTrackerTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(LastLineTrackerTest); CPPUNIT_TEST(testFocusNormal); CPPUNIT_TEST(testFocusOut); CPPUNIT_TEST(testFocusOtherTab); CPPUNIT_TEST(testRepeatedFocusOut); CPPUNIT_TEST(testRepeatedFocusIn); CPPUNIT_TEST_SUITE_END(); public: LastLineTrackerTest () { }; void testFocusNormal() { LastLineTracker testling; testling.setHasFocus(true); CPPUNIT_ASSERT_EQUAL(false, testling.getShouldMoveLastLine()); } void testFocusOut() { LastLineTracker testling; testling.setHasFocus(false); CPPUNIT_ASSERT_EQUAL(true, testling.getShouldMoveLastLine()); CPPUNIT_ASSERT_EQUAL(false, testling.getShouldMoveLastLine()); CPPUNIT_ASSERT_EQUAL(false, testling.getShouldMoveLastLine()); } void testFocusOtherTab() { LastLineTracker testling; testling.setHasFocus(true); testling.setHasFocus(false); CPPUNIT_ASSERT_EQUAL(true, testling.getShouldMoveLastLine()); CPPUNIT_ASSERT_EQUAL(false, testling.getShouldMoveLastLine()); } void testRepeatedFocusOut() { LastLineTracker testling; testling.setHasFocus(true); CPPUNIT_ASSERT_EQUAL(false, testling.getShouldMoveLastLine()); testling.setHasFocus(false); CPPUNIT_ASSERT_EQUAL(true, testling.getShouldMoveLastLine()); testling.setHasFocus(false); CPPUNIT_ASSERT_EQUAL(false, testling.getShouldMoveLastLine()); } void testRepeatedFocusIn() { LastLineTracker testling; testling.setHasFocus(false); CPPUNIT_ASSERT_EQUAL(true, testling.getShouldMoveLastLine()); testling.setHasFocus(true); testling.setHasFocus(false); CPPUNIT_ASSERT_EQUAL(true, testling.getShouldMoveLastLine()); } }; CPPUNIT_TEST_SUITE_REGISTRATION(LastLineTrackerTest); swift-im-2.0+dev6/SwifTools/UnitTest/TabCompleteTest.cpp0000644000175000017500000001334512227051773023143 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include using namespace Swift; class TabCompleteTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TabCompleteTest); CPPUNIT_TEST(testEmpty); CPPUNIT_TEST(testNoMatch); CPPUNIT_TEST(testOneMatch); CPPUNIT_TEST(testTwoMatch); CPPUNIT_TEST(testChangeMatch); CPPUNIT_TEST(testRemoveDuringComplete); CPPUNIT_TEST(testAddDuringComplete); CPPUNIT_TEST(testSwiftRoomSample); CPPUNIT_TEST_SUITE_END(); public: TabCompleteTest() {}; void setUp() { completer_ = TabComplete(); } void testEmpty() { std::string blah("Blah"); CPPUNIT_ASSERT_EQUAL( blah, completer_.completeWord(blah)); CPPUNIT_ASSERT_EQUAL( blah, completer_.completeWord(blah)); } void testNoMatch() { completer_.addWord("Bleh"); std::string blah("Blah"); CPPUNIT_ASSERT_EQUAL( blah, completer_.completeWord(blah)); CPPUNIT_ASSERT_EQUAL( blah, completer_.completeWord(blah)); } void testOneMatch() { std::string short1("Bl"); std::string long1("Blehling"); completer_.addWord(long1); CPPUNIT_ASSERT_EQUAL( long1, completer_.completeWord(short1)); CPPUNIT_ASSERT_EQUAL( long1, completer_.completeWord(long1)); } void testTwoMatch() { std::string short1("Hur"); std::string long1("Hurgle"); std::string long2("Hurdler"); completer_.addWord(long1); completer_.addWord("Blah"); completer_.addWord(long2); completer_.addWord("Bleh"); CPPUNIT_ASSERT_EQUAL( long2, completer_.completeWord(short1)); CPPUNIT_ASSERT_EQUAL( long1, completer_.completeWord(long2)); CPPUNIT_ASSERT_EQUAL( long2, completer_.completeWord(long1)); } void testChangeMatch() { std::string short1("Hur"); std::string short2("Rub"); std::string long1("Hurgle"); std::string long2("Rubbish"); completer_.addWord(long2); completer_.addWord("Blah"); completer_.addWord(long1); completer_.addWord("Bleh"); CPPUNIT_ASSERT_EQUAL( long1, completer_.completeWord(short1)); CPPUNIT_ASSERT_EQUAL( long2, completer_.completeWord(short2)); CPPUNIT_ASSERT_EQUAL( long2, completer_.completeWord(long2)); CPPUNIT_ASSERT_EQUAL( long1, completer_.completeWord(short1)); } void testRemoveDuringComplete() { std::string short1("Kev"); std::string long1("Kevin"); std::string long2("Kevlar"); completer_.addWord(long1); completer_.addWord("Blah"); completer_.addWord(long2); completer_.addWord("Bleh"); CPPUNIT_ASSERT_EQUAL( long2, completer_.completeWord(short1)); completer_.removeWord(long2); CPPUNIT_ASSERT_EQUAL( long1, completer_.completeWord(long2)); CPPUNIT_ASSERT_EQUAL( long1, completer_.completeWord(long1)); } void testAddDuringComplete() { std::string short1("Rem"); std::string long1("Remko"); std::string long2("Remove"); std::string long3("Remedial"); completer_.addWord(long1); completer_.addWord("Blah"); completer_.addWord(long2); completer_.addWord("Bleh"); CPPUNIT_ASSERT_EQUAL( long2, completer_.completeWord(short1)); completer_.addWord(long3); CPPUNIT_ASSERT_EQUAL( long1, completer_.completeWord(long2)); CPPUNIT_ASSERT_EQUAL( long3, completer_.completeWord(long1)); } void testSwiftRoomSample() { std::string t("t"); std::string Anpan("Anpan"); std::string cdubouloz("cdubouloz"); std::string Tobias("Tobias"); std::string Zash("Zash"); std::string lastsky("lastsky"); std::string Steve("Steve Kille"); std::string Flo("Flo"); std::string Test("Test"); std::string test("test"); completer_.addWord(Anpan); completer_.addWord(cdubouloz); completer_.addWord(Tobias); completer_.addWord(lastsky); completer_.addWord(Steve); completer_.addWord(Flo); completer_.addWord(Zash); CPPUNIT_ASSERT_EQUAL( Tobias, completer_.completeWord(t)); CPPUNIT_ASSERT_EQUAL( Tobias, completer_.completeWord(Tobias)); completer_.addWord(Test); CPPUNIT_ASSERT_EQUAL( Test, completer_.completeWord(t)); CPPUNIT_ASSERT_EQUAL( Tobias, completer_.completeWord(Test)); CPPUNIT_ASSERT_EQUAL( Test, completer_.completeWord(Tobias)); CPPUNIT_ASSERT_EQUAL( Tobias, completer_.completeWord(Test)); completer_.addWord(Zash); completer_.addWord(Zash); completer_.addWord(Zash); completer_.addWord(Zash); completer_.removeWord(Test); CPPUNIT_ASSERT_EQUAL( Tobias, completer_.completeWord(t)); CPPUNIT_ASSERT_EQUAL( Tobias, completer_.completeWord(Tobias)); completer_.addWord(test); completer_.addWord(Zash); completer_.addWord(Zash); completer_.addWord(Zash); completer_.addWord(Zash); CPPUNIT_ASSERT_EQUAL( test, completer_.completeWord(t)); CPPUNIT_ASSERT_EQUAL( Tobias, completer_.completeWord(test)); CPPUNIT_ASSERT_EQUAL( test, completer_.completeWord(Tobias)); CPPUNIT_ASSERT_EQUAL( Tobias, completer_.completeWord(test)); completer_.removeWord(test); CPPUNIT_ASSERT_EQUAL( Tobias, completer_.completeWord(t)); CPPUNIT_ASSERT_EQUAL( Tobias, completer_.completeWord(Tobias)); completer_.removeWord(Tobias); CPPUNIT_ASSERT_EQUAL( t, completer_.completeWord(t)); CPPUNIT_ASSERT_EQUAL( t, completer_.completeWord(t)); CPPUNIT_ASSERT_EQUAL( t, completer_.completeWord(t)); CPPUNIT_ASSERT_EQUAL( t, completer_.completeWord(t)); completer_.addWord(Tobias); CPPUNIT_ASSERT_EQUAL( Tobias, completer_.completeWord(t)); CPPUNIT_ASSERT_EQUAL( Tobias, completer_.completeWord(Tobias)); } private: TabComplete completer_; }; CPPUNIT_TEST_SUITE_REGISTRATION(TabCompleteTest); swift-im-2.0+dev6/SwifTools/Dock/0000755000175000017500000000000012227051773016473 5ustar kismithkismithswift-im-2.0+dev6/SwifTools/Dock/SConscript0000644000175000017500000000042112227051773020502 0ustar kismithkismithImport("swiftools_env") sources = [ "Dock.cpp", ] if swiftools_env["PLATFORM"] == "darwin" and swiftools_env["target"] == "native" : sources += [ "MacOSXDock.mm", ] objects = swiftools_env.StaticObject(sources) swiftools_env.Append(SWIFTOOLS_OBJECTS = objects) swift-im-2.0+dev6/SwifTools/Dock/MacOSXDock.mm0000644000175000017500000000121412227051773020717 0ustar kismithkismith// Fix Boost-Cocoa conflict #define __ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES 0 #include #include #include #include #include namespace Swift { MacOSXDock::MacOSXDock(CocoaApplication*) { } void MacOSXDock::setNumberOfPendingMessages(int i) { std::string label(i > 0 ? boost::lexical_cast(i) : ""); NSString *labelString = [[NSString alloc] initWithUTF8String: label.c_str()]; [[NSApp dockTile] setBadgeLabel: labelString]; [labelString release]; [NSApp requestUserAttention: NSInformationalRequest]; } } swift-im-2.0+dev6/SwifTools/Dock/NullDock.h0000644000175000017500000000053312227051773020360 0ustar kismithkismith/* * 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 namespace Swift { class NullDock : public Dock { public: NullDock() {} virtual void setNumberOfPendingMessages(int) { } }; } swift-im-2.0+dev6/SwifTools/Dock/MacOSXDock.h0000644000175000017500000000062212227051773020537 0ustar kismithkismith/* * 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 namespace Swift { class CocoaApplication; class MacOSXDock : public Dock { public: MacOSXDock(CocoaApplication* application); virtual void setNumberOfPendingMessages(int i); }; } swift-im-2.0+dev6/SwifTools/Dock/Dock.h0000644000175000017500000000045512227051773017530 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once namespace Swift { class Dock { public: virtual ~Dock(); virtual void setNumberOfPendingMessages(int i) = 0; }; } swift-im-2.0+dev6/SwifTools/Dock/WindowsDock.h0000644000175000017500000000173412227051773021104 0ustar kismithkismith/* * 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 #include #include #include namespace Swift { class WindowsDock : public Dock { public: WindowsDock(QSystemTrayIcon* tray, Notifier* notifier) : tray(tray), notifier(notifier) {} virtual void setNumberOfPendingMessages(int i) { if (notifier->isAvailable()) { return; } if (i > 0) { std::string message = boost::lexical_cast(i) + " new message"; if (i > 1) { message += "s"; } message += " received."; tray->showMessage("New messages", message.c_str(), QSystemTrayIcon::NoIcon); } else { tray->showMessage("", "", QSystemTrayIcon::NoIcon, 0); } } private: QSystemTrayIcon* tray; Notifier* notifier; }; } swift-im-2.0+dev6/SwifTools/Dock/Dock.cpp0000644000175000017500000000035212227051773020057 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { Dock::~Dock() { } } swift-im-2.0+dev6/SwifTools/URIHandler/0000755000175000017500000000000012227051773017550 5ustar kismithkismithswift-im-2.0+dev6/SwifTools/URIHandler/SConscript0000644000175000017500000000072012227051773021561 0ustar kismithkismithImport("swiftools_env", "env") sources = [ "XMPPURI.cpp", "URIHandler.cpp", ] if swiftools_env["PLATFORM"] == "darwin" and swiftools_env["target"] == "native" : sources += [ "MacOSXURIHandler.mm" ] elif swiftools_env["PLATFORM"] == "win32" : sources += [] else : sources += [] objects = swiftools_env.StaticObject(sources) swiftools_env.Append(SWIFTOOLS_OBJECTS = [objects]) env.Append(UNITTEST_SOURCES = [ File("UnitTest/XMPPURITest.cpp"), ]) swift-im-2.0+dev6/SwifTools/URIHandler/XMPPURI.h0000644000175000017500000000252112227051773021065 0ustar kismithkismith/* * 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 #include #include namespace Swift { // TODO: Implement using Base/URI class XMPPURI { public: XMPPURI(); const JID& getAuthority() const { return authority; } void setAuthority(const JID& j) { authority = j; } const JID& getPath() const { return path; } void setPath(const JID& j) { path = j; } const std::string& getQueryType() const { return queryType; } void setQueryType(const std::string& q) { queryType = q; } const std::map& getQueryParameters() const { return queryParameters; } void addQueryParameter(const std::string& key, const std::string& path) { queryParameters[key] = path; } const std::string& getFragment() const { return fragment; } void setFragment(const std::string& f) { fragment = f; } bool isNull() const { return !authority.isValid() && !path.isValid(); } static XMPPURI fromString(const std::string&); private: JID authority; JID path; std::string fragment; std::string queryType; std::map queryParameters; }; } swift-im-2.0+dev6/SwifTools/URIHandler/NullURIHandler.h0000644000175000017500000000055012227051773022511 0ustar kismithkismith/* * 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 namespace Swift { class NullURIHandler : public URIHandler { public: virtual void start() { } virtual void stop() { } }; } swift-im-2.0+dev6/SwifTools/URIHandler/MacOSXURIHandler.mm0000644000175000017500000000340012227051773023050 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include using namespace Swift; @interface MacOSXURIEventHandler : NSObject { URIHandler* handler; } - (id) initWithHandler: (URIHandler*) handler; - (void) getUrl: (NSAppleEventDescriptor*) event withReplyEvent: (NSAppleEventDescriptor*) replyEvent; @end @implementation MacOSXURIEventHandler - (id) initWithHandler: (URIHandler*) h { if ([super init]) { handler = h; } return self; } - (void) getUrl: (NSAppleEventDescriptor*) event withReplyEvent: (NSAppleEventDescriptor*) replyEvent { (void) replyEvent; NSString* url = [[event paramDescriptorForKeyword:keyDirectObject] stringValue]; handler->onURI(std::string([url UTF8String])); } @end class MacOSXURIHandler::Private { public: MacOSXURIEventHandler* eventHandler; }; MacOSXURIHandler::MacOSXURIHandler() { p = new Private(); p->eventHandler = [[MacOSXURIEventHandler alloc] initWithHandler: this]; } MacOSXURIHandler::~MacOSXURIHandler() { [p->eventHandler release]; delete p; } void MacOSXURIHandler::start() { [[NSAppleEventManager sharedAppleEventManager] setEventHandler:p->eventHandler andSelector:@selector(getUrl:withReplyEvent:) forEventClass:kInternetEventClass andEventID:kAEGetURL]; // Register ourselves as default URI handler //NSString* bundleID = [[NSBundle mainBundle] bundleIdentifier]; //LSSetDefaultHandlerForURLScheme((CFStringRef)@"xmpp", (CFStringRef)bundleID); } void MacOSXURIHandler::stop() { [[NSAppleEventManager sharedAppleEventManager] removeEventHandlerForEventClass:kInternetEventClass andEventID:kAEGetURL]; } swift-im-2.0+dev6/SwifTools/URIHandler/MacOSXURIHandler.h0000644000175000017500000000070212227051773022670 0ustar kismithkismith/* * 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 namespace Swift { class MacOSXURIHandler : public URIHandler { public: MacOSXURIHandler(); virtual ~MacOSXURIHandler(); virtual void start(); virtual void stop(); private: class Private; Private* p; }; } swift-im-2.0+dev6/SwifTools/URIHandler/UnitTest/0000755000175000017500000000000012227051773021327 5ustar kismithkismithswift-im-2.0+dev6/SwifTools/URIHandler/UnitTest/XMPPURITest.cpp0000644000175000017500000002040012227051773024033 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include using namespace Swift; class XMPPURITest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(XMPPURITest); CPPUNIT_TEST(testFromString_Authority); CPPUNIT_TEST(testFromString_AuthorityWithPath); CPPUNIT_TEST(testFromString_AuthorityWithFragment); CPPUNIT_TEST(testFromString_AuthorityWithPathAndFragment); CPPUNIT_TEST(testFromString_AuthorityWithIntlChars); CPPUNIT_TEST(testFromString_AuthorityWithQueryWithoutParameters); CPPUNIT_TEST(testFromString_AuthorityWithQueryWithParameters); CPPUNIT_TEST(testFromString_AuthorityWithQueryWithoutParametersWithFragment); CPPUNIT_TEST(testFromString_AuthorityWithQueryWithParametersWithFragment); CPPUNIT_TEST(testFromString_Path); CPPUNIT_TEST(testFromString_PathWithFragment); CPPUNIT_TEST(testFromString_PathWithIntlChars); CPPUNIT_TEST(testFromString_PathWithInvalidEscapedChar); CPPUNIT_TEST(testFromString_PathWithIncompleteEscapedChar); CPPUNIT_TEST(testFromString_PathWithIncompleteEscapedChar2); CPPUNIT_TEST(testFromString_PathWithQueryWithoutParameters); CPPUNIT_TEST(testFromString_PathWithQueryWithParameters); CPPUNIT_TEST(testFromString_PathWithQueryWithoutParametersWithFragment); CPPUNIT_TEST(testFromString_PathWithQueryWithParametersWithFragment); CPPUNIT_TEST(testFromString_NoPrefix); CPPUNIT_TEST_SUITE_END(); public: void testFromString_Authority() { XMPPURI testling = XMPPURI::fromString("xmpp://foo@bar.com"); CPPUNIT_ASSERT_EQUAL(JID("foo@bar.com"), testling.getAuthority()); } void testFromString_AuthorityWithPath() { XMPPURI testling = XMPPURI::fromString("xmpp://foo@bar.com/baz@example.com"); CPPUNIT_ASSERT_EQUAL(JID("foo@bar.com"), testling.getAuthority()); CPPUNIT_ASSERT_EQUAL(JID("baz@example.com"), testling.getPath()); } void testFromString_AuthorityWithFragment() { XMPPURI testling = XMPPURI::fromString("xmpp://foo@bar.com#myfragment"); CPPUNIT_ASSERT_EQUAL(JID("foo@bar.com"), testling.getAuthority()); CPPUNIT_ASSERT_EQUAL(std::string("myfragment"), testling.getFragment()); } void testFromString_AuthorityWithPathAndFragment() { XMPPURI testling = XMPPURI::fromString("xmpp://foo@bar.com/baz@example.com#myfragment"); CPPUNIT_ASSERT_EQUAL(JID("foo@bar.com"), testling.getAuthority()); CPPUNIT_ASSERT_EQUAL(JID("baz@example.com"), testling.getPath()); CPPUNIT_ASSERT_EQUAL(std::string("myfragment"), testling.getFragment()); } void testFromString_AuthorityWithIntlChars() { XMPPURI testling = XMPPURI::fromString("xmpp://nasty!%23$%25()*+,-.;=\%3F\%5B\%5C\%5D\%5E_\%60\%7B\%7C\%7D~node@example.com"); CPPUNIT_ASSERT_EQUAL(JID("nasty!#$\%()*+,-.;=?[\\]^_`{|}~node@example.com"), testling.getAuthority()); } void testFromString_AuthorityWithQueryWithoutParameters() { XMPPURI testling = XMPPURI::fromString("xmpp://test@example.com?message"); CPPUNIT_ASSERT_EQUAL(JID("test@example.com"), testling.getAuthority()); CPPUNIT_ASSERT_EQUAL(std::string("message"), testling.getQueryType()); } void testFromString_AuthorityWithQueryWithParameters() { XMPPURI testling = XMPPURI::fromString("xmpp://test@example.com?message;subject=Test%20Message;body=Here%27s%20a%20test%20message"); CPPUNIT_ASSERT_EQUAL(JID("test@example.com"), testling.getAuthority()); CPPUNIT_ASSERT_EQUAL(std::string("message"), testling.getQueryType()); CPPUNIT_ASSERT_EQUAL(std::string("Test Message"), get(testling.getQueryParameters(), "subject")); CPPUNIT_ASSERT_EQUAL(std::string("Here's a test message"), get(testling.getQueryParameters(), "body")); } void testFromString_AuthorityWithQueryWithoutParametersWithFragment() { XMPPURI testling = XMPPURI::fromString("xmpp://test@example.com?message#myfragment"); CPPUNIT_ASSERT_EQUAL(JID("test@example.com"), testling.getAuthority()); CPPUNIT_ASSERT_EQUAL(std::string("message"), testling.getQueryType()); CPPUNIT_ASSERT_EQUAL(std::string("myfragment"), testling.getFragment()); } void testFromString_AuthorityWithQueryWithParametersWithFragment() { XMPPURI testling = XMPPURI::fromString("xmpp://test@example.com?message;subject=Test%20Message;body=Here%27s%20a%20test%20message#myfragment"); CPPUNIT_ASSERT_EQUAL(JID("test@example.com"), testling.getAuthority()); CPPUNIT_ASSERT_EQUAL(std::string("message"), testling.getQueryType()); CPPUNIT_ASSERT_EQUAL(std::string("Test Message"), get(testling.getQueryParameters(), "subject")); CPPUNIT_ASSERT_EQUAL(std::string("Here's a test message"), get(testling.getQueryParameters(), "body")); CPPUNIT_ASSERT_EQUAL(std::string("myfragment"), testling.getFragment()); } void testFromString_Path() { XMPPURI testling = XMPPURI::fromString("xmpp:baz@example.com"); CPPUNIT_ASSERT_EQUAL(JID("baz@example.com"), testling.getPath()); } void testFromString_PathWithFragment() { XMPPURI testling = XMPPURI::fromString("xmpp:baz@example.com#myfragment"); CPPUNIT_ASSERT_EQUAL(JID("baz@example.com"), testling.getPath()); CPPUNIT_ASSERT_EQUAL(std::string("myfragment"), testling.getFragment()); } void testFromString_PathWithIntlChars() { XMPPURI testling = XMPPURI::fromString("xmpp:nasty!%23$%25()*+,-.;=\%3F\%5B\%5C\%5D\%5E_\%60\%7B\%7C\%7D~node@example.com"); CPPUNIT_ASSERT_EQUAL(JID("nasty!#$\%()*+,-.;=?[\\]^_`{|}~node@example.com"), testling.getPath()); } void testFromString_PathWithInvalidEscapedChar() { XMPPURI testling = XMPPURI::fromString("xmpp:test%%@example.com"); CPPUNIT_ASSERT_EQUAL(JID(), testling.getPath()); } void testFromString_PathWithIncompleteEscapedChar() { XMPPURI testling = XMPPURI::fromString("xmpp:test@example.com%"); CPPUNIT_ASSERT_EQUAL(JID(), testling.getPath()); } void testFromString_PathWithIncompleteEscapedChar2() { XMPPURI testling = XMPPURI::fromString("xmpp:test@example.com%1"); CPPUNIT_ASSERT_EQUAL(JID(), testling.getPath()); } void testFromString_PathWithQueryWithoutParameters() { XMPPURI testling = XMPPURI::fromString("xmpp:test@example.com?message"); CPPUNIT_ASSERT_EQUAL(JID("test@example.com"), testling.getPath()); CPPUNIT_ASSERT_EQUAL(std::string("message"), testling.getQueryType()); } void testFromString_PathWithQueryWithParameters() { XMPPURI testling = XMPPURI::fromString("xmpp:test@example.com?message;subject=Test%20Message;body=Here%27s%20a%20test%20message"); CPPUNIT_ASSERT_EQUAL(JID("test@example.com"), testling.getPath()); CPPUNIT_ASSERT_EQUAL(std::string("message"), testling.getQueryType()); CPPUNIT_ASSERT_EQUAL(std::string("Test Message"), get(testling.getQueryParameters(), "subject")); CPPUNIT_ASSERT_EQUAL(std::string("Here's a test message"), get(testling.getQueryParameters(), "body")); } void testFromString_PathWithQueryWithoutParametersWithFragment() { XMPPURI testling = XMPPURI::fromString("xmpp:test@example.com?message#myfragment"); CPPUNIT_ASSERT_EQUAL(JID("test@example.com"), testling.getPath()); CPPUNIT_ASSERT_EQUAL(std::string("message"), testling.getQueryType()); CPPUNIT_ASSERT_EQUAL(std::string("myfragment"), testling.getFragment()); } void testFromString_PathWithQueryWithParametersWithFragment() { XMPPURI testling = XMPPURI::fromString("xmpp:test@example.com?message;subject=Test%20Message;body=Here%27s%20a%20test%20message#myfragment"); CPPUNIT_ASSERT_EQUAL(JID("test@example.com"), testling.getPath()); CPPUNIT_ASSERT_EQUAL(std::string("message"), testling.getQueryType()); CPPUNIT_ASSERT_EQUAL(std::string("Test Message"), get(testling.getQueryParameters(), "subject")); CPPUNIT_ASSERT_EQUAL(std::string("Here's a test message"), get(testling.getQueryParameters(), "body")); CPPUNIT_ASSERT_EQUAL(std::string("myfragment"), testling.getFragment()); } void testFromString_NoPrefix() { XMPPURI testling = XMPPURI::fromString("baz@example.com"); CPPUNIT_ASSERT(testling.isNull()); } private: std::string get(const std::map& m, const std::string& k) { std::map::const_iterator i = m.find(k); return i == m.end() ? "" : i->second; } }; CPPUNIT_TEST_SUITE_REGISTRATION(XMPPURITest); swift-im-2.0+dev6/SwifTools/URIHandler/URIHandler.h0000644000175000017500000000057712227051773021667 0ustar kismithkismith/* * 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 #include namespace Swift { class URIHandler { public: URIHandler(); virtual ~URIHandler(); boost::signal onURI; }; } swift-im-2.0+dev6/SwifTools/URIHandler/URIHandler.cpp0000644000175000017500000000044212227051773022211 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include using namespace Swift; URIHandler::URIHandler() { } URIHandler::~URIHandler() { } swift-im-2.0+dev6/SwifTools/URIHandler/XMPPURI.cpp0000644000175000017500000000517212227051773021425 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include #include using namespace Swift; XMPPURI::XMPPURI() { } XMPPURI XMPPURI::fromString(const std::string& s) { XMPPURI result; if (boost::starts_with(s, "xmpp:")) { std::string uri = s.substr(5, s.npos); bool parsePath = true; bool parseQuery = true; bool parseFragment = true; // Parse authority if (boost::starts_with(uri, "//")) { size_t i = uri.find_first_of("/#?", 2); result.setAuthority(JID(URL::unescape(uri.substr(2, i - 2)))); if (i == uri.npos) { uri = ""; parsePath = parseQuery = parseFragment = false; } else { if (uri[i] == '?') { parsePath = false; } else if (uri[i] == '#') { parseQuery = parsePath = false; } uri = uri.substr(i + 1, uri.npos); } } // Parse path if (parsePath) { size_t i = uri.find_first_of("#?"); result.setPath(JID(URL::unescape(uri.substr(0, i)))); if (i == uri.npos) { uri = ""; parseQuery = parseFragment = false; } else { if (uri[i] == '#') { parseQuery = false; } uri = uri.substr(i + 1, uri.npos); } } // Parse query if (parseQuery) { size_t end = uri.find_first_of("#"); std::string query = uri.substr(0, end); bool haveType = false; typedef boost::split_iterator split_iterator; for (split_iterator it = boost::make_split_iterator(query, boost::first_finder(";")); it != split_iterator(); ++it) { if (haveType) { std::vector keyValue; boost::split(keyValue, *it, boost::is_any_of("=")); if (keyValue.size() == 1) { result.addQueryParameter(URL::unescape(keyValue[0]), ""); } else if (keyValue.size() >= 2) { result.addQueryParameter(URL::unescape(keyValue[0]), URL::unescape(keyValue[1])); } } else { result.setQueryType(URL::unescape(boost::copy_range(*it))); haveType = true; } } uri = (end == uri.npos ? "" : uri.substr(end + 1, uri.npos)); } // Parse fragment if (parseFragment) { result.setFragment(URL::unescape(uri)); } } return result; } swift-im-2.0+dev6/SwifTools/LastLineTracker.cpp0000644000175000017500000000110212227051773021340 0ustar kismithkismith/* * Copyright (c) 2011 Vlad Voicu * Licensed under the Simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include "LastLineTracker.h" using namespace Swift; LastLineTracker::LastLineTracker() { lastFocus = true; shouldMove = false; } void LastLineTracker::setHasFocus(bool focus) { if (!focus && lastFocus) { shouldMove = true; lastFocus = focus; return; } shouldMove = false; lastFocus = focus; } bool LastLineTracker::getShouldMoveLastLine() { bool ret = shouldMove; shouldMove = false; return ret; } swift-im-2.0+dev6/SwifTools/LastLineTracker.h0000644000175000017500000000056112227051773021015 0ustar kismithkismith/* * Copyright (c) 2011 Vlad Voicu * Licensed under the Simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once namespace Swift { class LastLineTracker { public: LastLineTracker(); void setHasFocus(bool focus); bool getShouldMoveLastLine(); private: bool lastFocus; bool shouldMove; }; } swift-im-2.0+dev6/.subl/0000755000175000017500000000000012227051773014705 5ustar kismithkismithswift-im-2.0+dev6/.subl/swift.sublime-project0000644000175000017500000000107012227051773021065 0ustar kismithkismith/* http://www.sublimetext.com/docs/2/projects.html */ { "folders": [ { "path": "..", "folder_exclude_patterns": ["tmp", ".sconf_temp", ".settings", "Swift.app", "3rdParty"], "file_exclude_patterns": ["moc_*", ".cproject", ".project", ".sconsign.dblite", "*~", "config.log", "*.o"] } ], "settings": { "tab_size": 4, "translate_tabs_to_spaces": false }, "build_systems": [ { "name": "Scons", "cmd" : ["./scons"], "working_dir": ".." }, { "name": "Scons test", "cmd" : ["./scons", "check=1"], "working_dir": ".." } ] } swift-im-2.0+dev6/Slimber/0000755000175000017500000000000012227051774015260 5ustar kismithkismithswift-im-2.0+dev6/Slimber/ServerError.h0000644000175000017500000000117012227051773017707 0ustar kismithkismith/* * 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 namespace Swift { class ServerError { public: enum Type { C2SPortConflict, C2SError, LinkLocalPortConflict, LinkLocalError }; ServerError(Type type, const std::string& message = std::string()) : type(type), message(message) { } Type getType() const { return type; } const std::string& getMessage() const { return message; } private: Type type; std::string message; }; } swift-im-2.0+dev6/Slimber/SConscript0000644000175000017500000000335412227051773017276 0ustar kismithkismithimport datetime Import("env") env["BUILD_SLIMBER"] = True if env["PLATFORM"] == "win32" : if not env.get("HAVE_BONJOUR") and "Slimber" in env["PROJECTS"] : env["PROJECTS"].remove("Slimber") elif env["PLATFORM"] != "darwin" : if not env.get("HAVE_AVAHI", False) and "Slimber" in env["PROJECTS"] : env["PROJECTS"].remove("Slimber") if not "Slimber" in env["PROJECTS"] and env["SCONS_STAGE"] == "flags" : print "Bonjour missing. Not building Slimber." if "Slimber" in env["PROJECTS"] : ################################################################################ # Flags ################################################################################ if env["SCONS_STAGE"] == "flags" : env["SLIMBER_FLAGS"] = { "LIBPATH": [Dir(".")], "LIBS": ["Slimber"] } ################################################################################ # Build ################################################################################ if env["SCONS_STAGE"] == "build" : myenv = env.Clone() myenv.UseFlags(env["LIMBER_FLAGS"]) myenv.MergeFlags(env["BOOST_FLAGS"]) myenv.MergeFlags(env["SWIFTEN_FLAGS"]) myenv.MergeFlags(env["SWIFTEN_DEP_FLAGS"]) myenv.StaticLibrary("Slimber", [ "LinkLocalPresenceManager.cpp", "FileVCardCollection.cpp", "VCardCollection.cpp", "Server.cpp", "MainController.cpp", "MenuletController.cpp", "Menulet.cpp" ]) env["SLIMBER_VERSION"] = "0.9.9." + datetime.date.today().strftime("%Y%m%d") env.Append(UNITTEST_SOURCES = [ File("UnitTest/LinkLocalPresenceManagerTest.cpp"), File("UnitTest/MenuletControllerTest.cpp") ]) SConscript("CLI/SConscript") if env["PLATFORM"] == "darwin" : SConscript("Cocoa/SConscript") else : SConscript("Qt/SConscript") swift-im-2.0+dev6/Slimber/VCardCollection.cpp0000644000175000017500000000040412227051773020774 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Slimber/VCardCollection.h" namespace Swift { VCardCollection::~VCardCollection() { } } swift-im-2.0+dev6/Slimber/LinkLocalPresenceManager.cpp0000644000175000017500000000702112227051773022613 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Slimber/LinkLocalPresenceManager.h" #include #include #include #include #include namespace Swift { LinkLocalPresenceManager::LinkLocalPresenceManager(LinkLocalServiceBrowser* browser) : browser(browser) { browser->onServiceAdded.connect( boost::bind(&LinkLocalPresenceManager::handleServiceAdded, this, _1)); browser->onServiceChanged.connect( boost::bind(&LinkLocalPresenceManager::handleServiceChanged, this, _1)); browser->onServiceRemoved.connect( boost::bind(&LinkLocalPresenceManager::handleServiceRemoved, this, _1)); } boost::optional LinkLocalPresenceManager::getServiceForJID(const JID& j) const { foreach(const LinkLocalService& service, browser->getServices()) { if (service.getJID() == j) { return service; } } return boost::optional(); } void LinkLocalPresenceManager::handleServiceAdded(const LinkLocalService& service) { boost::shared_ptr roster(new RosterPayload()); roster->addItem(getRosterItem(service)); onRosterChanged(roster); onPresenceChanged(getPresence(service)); } void LinkLocalPresenceManager::handleServiceChanged(const LinkLocalService& service) { onPresenceChanged(getPresence(service)); } void LinkLocalPresenceManager::handleServiceRemoved(const LinkLocalService& service) { boost::shared_ptr roster(new RosterPayload()); roster->addItem(RosterItemPayload(service.getJID(), "", RosterItemPayload::Remove)); onRosterChanged(roster); } boost::shared_ptr LinkLocalPresenceManager::getRoster() const { boost::shared_ptr roster(new RosterPayload()); foreach(const LinkLocalService& service, browser->getServices()) { roster->addItem(getRosterItem(service)); } return roster; } std::vector > LinkLocalPresenceManager::getAllPresence() const { std::vector > result; foreach(const LinkLocalService& service, browser->getServices()) { result.push_back(getPresence(service)); } return result; } RosterItemPayload LinkLocalPresenceManager::getRosterItem(const LinkLocalService& service) const { return RosterItemPayload(service.getJID(), getRosterName(service), RosterItemPayload::Both); } std::string LinkLocalPresenceManager::getRosterName(const LinkLocalService& service) const { LinkLocalServiceInfo info = service.getInfo(); if (!info.getNick().empty()) { return info.getNick(); } else if (!info.getFirstName().empty()) { std::string result = info.getFirstName(); if (!info.getLastName().empty()) { result += " " + info.getLastName(); } return result; } else if (!info.getLastName().empty()) { return info.getLastName(); } return ""; } boost::shared_ptr LinkLocalPresenceManager::getPresence(const LinkLocalService& service) const { boost::shared_ptr presence(new Presence()); presence->setFrom(service.getJID()); switch (service.getInfo().getStatus()) { case LinkLocalServiceInfo::Available: presence->setShow(StatusShow::Online); break; case LinkLocalServiceInfo::Away: presence->setShow(StatusShow::Away); break; case LinkLocalServiceInfo::DND: presence->setShow(StatusShow::DND); break; } presence->setStatus(service.getInfo().getMessage()); return presence; } } swift-im-2.0+dev6/Slimber/CLI/0000755000175000017500000000000012227051773015666 5ustar kismithkismithswift-im-2.0+dev6/Slimber/CLI/SConscript0000644000175000017500000000044512227051773017703 0ustar kismithkismithImport("env") myenv = env.Clone() myenv.UseFlags(env["SLIMBER_FLAGS"]) myenv.UseFlags(env["LIMBER_FLAGS"]) myenv.UseFlags(env["SWIFTOOLS_FLAGS"]) myenv.UseFlags(env["SWIFTEN_FLAGS"]) myenv.UseFlags(env["SWIFTEN_DEP_FLAGS"]) myenv.Program("slimber", [ "main.cpp", "DummyMenulet.cpp", ]) swift-im-2.0+dev6/Slimber/CLI/DummyMenulet.h0000644000175000017500000000101012227051773020454 0ustar kismithkismith/* * 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 "Slimber/Menulet.h" class DummyMenulet : public Menulet { public: DummyMenulet() { } void clear() { } void addItem(const std::string&, const std::string&) { } void addAboutItem() { } void addRestartItem() { } void addExitItem() { } void addSeparator() { } void setIcon(const std::string&) { } }; swift-im-2.0+dev6/Slimber/CLI/DummyMenulet.cpp0000644000175000017500000000030712227051773021017 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Slimber/CLI/DummyMenulet.h" swift-im-2.0+dev6/Slimber/CLI/main.cpp0000644000175000017500000000070612227051773017321 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include "Slimber/CLI/DummyMenulet.h" #include "Slimber/MainController.h" using namespace Swift; int main() { SimpleEventLoop eventLoop; DummyMenulet menulet; MainController controller(&menulet, &eventLoop); eventLoop.run(); return 0; } swift-im-2.0+dev6/Slimber/Qt/0000755000175000017500000000000012227051774015644 5ustar kismithkismithswift-im-2.0+dev6/Slimber/Qt/SConscript0000644000175000017500000000321612227051773017657 0ustar kismithkismithimport os, shutil, datetime Import("env") myenv = env.Clone() myenv["CXXFLAGS"] = filter(lambda x : x != "-Wfloat-equal", myenv["CXXFLAGS"]) myenv.UseFlags(env["SLIMBER_FLAGS"]) myenv.UseFlags(env["LIMBER_FLAGS"]) myenv.UseFlags(env["SWIFTOOLS_FLAGS"]) myenv.UseFlags(env["SWIFTEN_FLAGS"]) myenv.UseFlags(env["LIBIDN_FLAGS"]) myenv.UseFlags(env["BOOST_FLAGS"]) myenv.UseFlags(env.get("LIBXML_FLAGS", "")) myenv.UseFlags(env.get("EXPAT_FLAGS", "")) myenv.UseFlags(env.get("AVAHI_FLAGS", "")) myenv.UseFlags(myenv["PLATFORM_FLAGS"]) myenv.Tool("qt4", toolpath = ["#/BuildTools/SCons/Tools"]) myenv.Tool("nsis", toolpath = ["#/BuildTools/SCons/Tools"]) myenv.EnableQt4Modules(['QtCore', 'QtGui'], debug = False) myenv.Append(CPPPATH = ["."]) if env["PLATFORM"] == "win32" : myenv.Append(LINKFLAGS = ["/SUBSYSTEM:WINDOWS"]) myenv.Append(LIBS = "qtmain") myenv.BuildVersion("BuildVersion.h", project = "slimber") sources = [ "main.cpp", "QtMenulet.cpp", "QtAboutDialog.cpp", myenv.Qrc("Slimber.qrc"), ] #if env["PLATFORM"] == "win32" : # myenv.RES("../resources/Windows/Slimber.rc") # sources += ["../resources/Windows/Slimber.res"] if env["PLATFORM"] == "win32" : slimberProgram = myenv.Program("Slimber", sources) else : slimberProgram = myenv.Program("slimber", sources) if env["PLATFORM"] == "win32" : if "dist" in COMMAND_LINE_TARGETS or env.GetOption("clean") : myenv.WindowsBundle("Slimber", resources = {}, qtlibs = ["QtCore4", "QtGui4"]) myenv.Append(NSIS_OPTIONS = [ "/DmsvccRedistributableDir=\"" + env["vcredist"] + "\"", "/DbuildDate=" + datetime.date.today().strftime("%Y%m%d") ]) #myenv.Nsis("../Packaging/nsis/slimber.nsi") swift-im-2.0+dev6/Slimber/Qt/QtAboutDialog.h0000644000175000017500000000041112227051773020507 0ustar kismithkismith/* * 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 class QtAboutDialog : public QDialog { public: QtAboutDialog(); }; swift-im-2.0+dev6/Slimber/Qt/QtAboutDialog.cpp0000644000175000017500000000242112227051773021045 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Slimber/Qt/QtAboutDialog.h" #include #include #include #include QtAboutDialog::QtAboutDialog() { setAttribute(Qt::WA_DeleteOnClose); setWindowTitle("About Slimber"); QVBoxLayout* layout = new QVBoxLayout(this); QLabel* iconLabel = new QLabel(this); iconLabel->setPixmap(QPixmap(":/icons/Icon-128.png")); iconLabel->setAlignment(Qt::AlignHCenter); layout->addWidget(iconLabel); QLabel* appNameLabel = new QLabel("

" + QCoreApplication::applicationName() + "
", this); layout->addWidget(appNameLabel); QLabel* versionLabel = new QLabel(QString("
Version ") + QCoreApplication::applicationVersion() + "
", this); layout->addWidget(versionLabel); QString buildString = QString("
Built with: Qt version ") + QT_VERSION_STR; buildString += QString("
Running with Qt version ") + qVersion(); buildString += "
"; QLabel* buildLabel = new QLabel(buildString, this); layout->addWidget(buildLabel); setFixedSize(minimumSizeHint()); } swift-im-2.0+dev6/Slimber/Qt/QtMenulet.h0000644000175000017500000000316012227051773017732 0ustar kismithkismith/* * 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 #include #include #include #include #include #include #include "Slimber/Menulet.h" #include "Slimber/Qt/QtAboutDialog.h" class QtMenulet : public QObject, public Menulet { Q_OBJECT public: QtMenulet() { trayIcon.setIcon(QPixmap(":/icons/UsersOffline.png")); trayIcon.setContextMenu(&menu); trayIcon.show(); } void clear() { menu.clear(); } void addItem(const std::string& name, const std::string& icon) { menu.addAction(getIcon(icon), QString::fromUtf8(name.c_str())); } void addAboutItem() { menu.addAction("About", this, SLOT(showAboutDialog())); } void addRestartItem() { menu.addAction("Restart", this, SLOT(restart())); } void addExitItem() { menu.addAction("Exit", qApp, SLOT(quit())); } void addSeparator() { menu.addSeparator(); } void setIcon(const std::string& icon) { trayIcon.setIcon(getIcon(icon)); } private: QPixmap getIcon(const std::string& name) { return QPixmap(":/icons/" + QString::fromUtf8(name.c_str()) + ".png"); } private slots: void showAboutDialog() { if (aboutDialog) { aboutDialog->raise(); aboutDialog->activateWindow(); } else { aboutDialog = new QtAboutDialog(); aboutDialog->show(); } } void restart() { onRestartClicked(); } private: QMenu menu; QSystemTrayIcon trayIcon; QPointer aboutDialog; }; swift-im-2.0+dev6/Slimber/Qt/Slimber.qrc0000644000175000017500000000064112227051773017750 0ustar kismithkismith ../Resources/Offline.png ../Resources/Online.png ../Resources/UsersOffline.png ../Resources/UsersOnline.png ../Resources/Icon-128.png swift-im-2.0+dev6/Slimber/Qt/QtMenulet.cpp0000644000175000017500000000030312227051773020261 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Slimber/Qt/QtMenulet.h" swift-im-2.0+dev6/Slimber/Qt/main.cpp0000644000175000017500000000160412227051773017274 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include "QtMenulet.h" #include "Slimber/MainController.h" #include #include int main(int argc, char* argv[]) { QApplication app(argc, argv); Swift::QtEventLoop eventLoop; QCoreApplication::setApplicationName("Slimber"); QCoreApplication::setApplicationVersion(QString(buildVersion)); if (!QSystemTrayIcon::isSystemTrayAvailable()) { QMessageBox::critical(0, QObject::tr("Systray"), QObject::tr("No system tray")); return 1; } app.setQuitOnLastWindowClosed(false); QtMenulet menulet; MainController controller(&menulet, &eventLoop); return app.exec(); } swift-im-2.0+dev6/Slimber/Menulet.cpp0000644000175000017500000000032612227051773017375 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Slimber/Menulet.h" Menulet::~Menulet() { } swift-im-2.0+dev6/Slimber/MenuletController.h0000644000175000017500000000132512227051773021106 0ustar kismithkismith/* * 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 #include #include class Menulet; class MenuletController { public: enum Status { Online, Offline }; MenuletController(Menulet*); virtual ~MenuletController(); void setXMPPStatus(const std::string& message, Status status); void setUserNames(const std::vector&); boost::signal onRestartRequested; private: void update(); private: Menulet* menulet; Status xmppStatus; std::string xmppStatusMessage; std::vector linkLocalUsers; }; swift-im-2.0+dev6/Slimber/Cocoa/0000755000175000017500000000000012227051774016304 5ustar kismithkismithswift-im-2.0+dev6/Slimber/Cocoa/SConscript0000644000175000017500000000124212227051773020314 0ustar kismithkismithImport("env") myenv = env.Clone() myenv.UseFlags(env["LIMBER_FLAGS"]) myenv.MergeFlags(env["SLIMBER_FLAGS"]) myenv.MergeFlags(env["SWIFTOOLS_FLAGS"]) myenv.MergeFlags(env["SWIFTEN_FLAGS"]) myenv.MergeFlags(env["SWIFTEN_DEP_FLAGS"]) myenv.Program("Slimber", [ "main.mm", "CocoaController.mm", "CocoaMenulet.mm", ]) myenv.Nib("MainMenu") myenv.AppBundle("Slimber", resources = { "": [ "MainMenu.nib", "../Resources/Slimber.icns", "../Resources/Credits.html", "../Resources/Online.png", "../Resources/Offline.png", "../Resources/UsersOnline.png", "../Resources/UsersOffline.png" ]}, info = { "NSMainNibFile" : "MainMenu", "LSUIElement" : "1", }) swift-im-2.0+dev6/Slimber/Cocoa/CocoaMenulet.h0000644000175000017500000000146112227051773021034 0ustar kismithkismith/* * Copyright (c) 2010-2012 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once // Fix Boost-Cocoa conflict #define __ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES 0 #include #include "Slimber/Menulet.h" #include class CocoaMenulet : public Menulet { public: CocoaMenulet(); ~CocoaMenulet(); private: virtual void clear(); virtual void addItem(const std::string& name, const std::string& icon); virtual void addSeparator(); void setIcon(const std::string& icon); virtual void addAboutItem(); virtual void addRestartItem(); virtual void addExitItem(); private: NSStatusItem* statusItem; NSMenu* menu; CocoaAction* restartAction; }; swift-im-2.0+dev6/Slimber/Cocoa/main.mm0000644000175000017500000000051512227051773017563 0ustar kismithkismith#include #include "Slimber/Cocoa/main.h" #include Swift::CocoaEventLoop* eventLoop; int main(int argc, char *argv[]) { eventLoop = new Swift::CocoaEventLoop(); int result = NSApplicationMain(argc, const_cast(argv)); delete eventLoop; return result; } swift-im-2.0+dev6/Slimber/Cocoa/CocoaMenulet.mm0000644000175000017500000000463212227051773021221 0ustar kismithkismith#include "Slimber/Cocoa/CocoaMenulet.h" #pragma GCC diagnostic ignored "-Wold-style-cast" #include CocoaMenulet::CocoaMenulet() { restartAction = [[CocoaAction alloc] initWithFunction: new boost::function(boost::ref(onRestartClicked))]; menu = [[NSMenu alloc] init]; statusItem = [[[NSStatusBar systemStatusBar] statusItemWithLength: NSVariableStatusItemLength] retain]; [statusItem setHighlightMode: YES]; [statusItem setEnabled: YES]; [statusItem setToolTip: @"Slimber"]; [statusItem setMenu: menu]; } CocoaMenulet::~CocoaMenulet() { [statusItem release]; [menu release]; [restartAction release]; } void CocoaMenulet::setIcon(const std::string& icon) { NSString* path = [[NSBundle mainBundle] pathForResource: [NSString stringWithUTF8String: icon.c_str()] ofType:@"png"]; NSImage* image = [[NSImage alloc] initWithContentsOfFile: path]; [statusItem setImage: image]; [image release]; } void CocoaMenulet::clear() { while ([menu numberOfItems] > 0) { [menu removeItemAtIndex: 0]; } } void CocoaMenulet::addItem(const std::string& name, const std::string& icon) { NSMenuItem* item = [[NSMenuItem alloc] initWithTitle: [NSString stringWithUTF8String: name.c_str()] action: NULL keyEquivalent: @""]; if (!icon.empty()) { NSString* path = [[NSBundle mainBundle] pathForResource: [NSString stringWithUTF8String: icon.c_str()] ofType:@"png"]; NSImage* image = [[NSImage alloc] initWithContentsOfFile: path]; [item setImage: [[NSImage alloc] initWithContentsOfFile: path]]; [image release]; } [menu addItem: item]; [item release]; } void CocoaMenulet::addAboutItem() { NSMenuItem* item = [[NSMenuItem alloc] initWithTitle: @"About Slimber" action: @selector(orderFrontStandardAboutPanel:) keyEquivalent: @""]; [item setTarget: [NSApplication sharedApplication]]; [menu addItem: item]; [item release]; } void CocoaMenulet::addRestartItem() { NSMenuItem* item = [[NSMenuItem alloc] initWithTitle: @"Restart" action: @selector(doAction:) keyEquivalent: @""]; [item setTarget: restartAction]; [menu addItem: item]; [item release]; } void CocoaMenulet::addExitItem() { NSMenuItem* item = [[NSMenuItem alloc] initWithTitle: @"Exit" action: @selector(terminate:) keyEquivalent: @""]; [item setTarget: [NSApplication sharedApplication]]; [menu addItem: item]; [item release]; } void CocoaMenulet::addSeparator() { [menu addItem: [NSMenuItem separatorItem]]; } swift-im-2.0+dev6/Slimber/Cocoa/CocoaController.mm0000644000175000017500000000056012227051773021727 0ustar kismithkismith#include "Slimber/Cocoa/CocoaController.h" #include "Slimber/MainController.h" #include "Slimber/Cocoa/CocoaMenulet.h" #include "Slimber/Cocoa/main.h" @implementation CocoaController - (void) dealloc { delete main; delete menulet; [super dealloc]; } - (void) awakeFromNib { menulet = new CocoaMenulet(); main = new MainController(menulet, eventLoop); } @end swift-im-2.0+dev6/Slimber/Cocoa/main.h0000644000175000017500000000041412227051773017377 0ustar kismithkismith/* * 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 extern Swift::CocoaEventLoop* eventLoop; swift-im-2.0+dev6/Slimber/Cocoa/MainMenu.xib0000644000175000017500000005063012227051773020524 0ustar kismithkismith 1050 9J61 677 949.46 353.00 YES YES com.apple.InterfaceBuilderKit com.apple.InterfaceBuilder.CocoaPlugin YES YES YES YES NSApplication FirstResponder NSApplication AMainMenu YES Slimber 1048576 2147483647 NSImage NSMenuCheckmark NSImage NSMenuMixedState submenuAction: Slimber YES About Slimber 2147483647 YES YES 1048576 2147483647 UHJlZmVyZW5jZXPigKY , 1048576 2147483647 YES YES 1048576 2147483647 Services 1048576 2147483647 submenuAction: Services YES _NSServicesMenu YES YES 1048576 2147483647 Hide Slimber h 1048576 2147483647 Hide Others h 1572864 2147483647 Show All 1048576 2147483647 YES YES 1048576 2147483647 Quit Slimber q 1048576 2147483647 _NSAppleMenu _NSMainMenu CocoaController YES orderFrontStandardAboutPanel: 142 hide: 367 hideOtherApplications: 368 unhideAllApplications: 370 terminate: 449 YES 0 YES -2 RmlsZSdzIE93bmVyA -1 First Responder -3 Application 29 YES MainMenu 458 56 YES 57 YES 145 149 131 YES 130 236 143 129 121 144 136 1111 150 134 58 YES YES -1.IBPluginDependency -2.IBPluginDependency -3.IBPluginDependency 129.IBPluginDependency 129.ImportedFromIB2 130.IBPluginDependency 130.ImportedFromIB2 130.editorWindowContentRectSynchronizationRect 131.IBPluginDependency 131.ImportedFromIB2 134.IBPluginDependency 134.ImportedFromIB2 136.IBPluginDependency 136.ImportedFromIB2 143.IBPluginDependency 143.ImportedFromIB2 144.IBPluginDependency 144.ImportedFromIB2 145.IBPluginDependency 145.ImportedFromIB2 149.IBPluginDependency 149.ImportedFromIB2 150.IBPluginDependency 150.ImportedFromIB2 236.IBPluginDependency 236.ImportedFromIB2 29.IBEditorWindowLastContentRect 29.IBPluginDependency 29.ImportedFromIB2 29.WindowOrigin 29.editorWindowContentRectSynchronizationRect 458.IBPluginDependency 56.IBPluginDependency 56.ImportedFromIB2 57.IBEditorWindowLastContentRect 57.IBPluginDependency 57.ImportedFromIB2 57.editorWindowContentRectSynchronizationRect 58.IBPluginDependency 58.ImportedFromIB2 YES com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilderKit com.apple.InterfaceBuilderKit com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin {{436, 809}, {64, 6}} com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin {{306, 359}, {97, 20}} com.apple.InterfaceBuilder.CocoaPlugin {74, 862} {{6, 978}, {478, 20}} com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin {{318, 176}, {190, 183}} com.apple.InterfaceBuilder.CocoaPlugin {{23, 794}, {245, 183}} com.apple.InterfaceBuilder.CocoaPlugin YES YES YES YES YES YES 463 YES CocoaController NSObject IBDocumentRelativeSource CocoaController.h 0 ../Slimber.xcodeproj 3 swift-im-2.0+dev6/Slimber/Cocoa/CocoaController.h0000644000175000017500000000064312227051773021547 0ustar kismithkismith/* * Copyright (c) 2010-2012 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ // Fix Boost-Cocoa conflict #define __ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES 0 #include class MainController; class CocoaMenulet; @interface CocoaController : NSObject { CocoaMenulet* menulet; MainController* main; } @end swift-im-2.0+dev6/Slimber/MainController.h0000644000175000017500000000177412227051773020371 0ustar kismithkismith/* * 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 #include #include "Slimber/ServerError.h" namespace Swift { class DNSSDQuerier; class LinkLocalServiceBrowser; class VCardCollection; class Server; class EventLoop; } class MenuletController; class Menulet; class MainController { public: MainController(Menulet* menulet, Swift::EventLoop* eventLoop); virtual ~MainController(); private: void handleSelfConnected(bool b); void handleServicesChanged(); void handleServerStopped(boost::optional error); void handleRestartRequested(); void start(); void stop(); private: boost::shared_ptr dnsSDQuerier; Swift::LinkLocalServiceBrowser* linkLocalServiceBrowser; Swift::VCardCollection* vCardCollection; Swift::Server* server; MenuletController* menuletController; }; swift-im-2.0+dev6/Slimber/UnitTest/0000755000175000017500000000000012227051773017036 5ustar kismithkismithswift-im-2.0+dev6/Slimber/UnitTest/LinkLocalPresenceManagerTest.cpp0000644000175000017500000002373512227051773025244 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include "Slimber/LinkLocalPresenceManager.h" #include #include #include #include #include #include using namespace Swift; class LinkLocalPresenceManagerTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(LinkLocalPresenceManagerTest); CPPUNIT_TEST(testConstructor); CPPUNIT_TEST(testServiceAdded); CPPUNIT_TEST(testServiceRemoved); CPPUNIT_TEST(testServiceChanged); CPPUNIT_TEST(testGetRoster); CPPUNIT_TEST(testGetAllPresence); CPPUNIT_TEST(testGetRoster_InfoWithNick); CPPUNIT_TEST(testGetRoster_InfoWithFirstName); CPPUNIT_TEST(testGetRoster_InfoWithLastName); CPPUNIT_TEST(testGetRoster_InfoWithFirstAndLastName); CPPUNIT_TEST(testGetRoster_NoInfo); CPPUNIT_TEST(testGetServiceForJID); CPPUNIT_TEST(testGetServiceForJID_NoMatch); CPPUNIT_TEST_SUITE_END(); public: void setUp() { eventLoop = new DummyEventLoop(); querier = boost::make_shared("wonderland.lit", eventLoop); browser = new LinkLocalServiceBrowser(querier); browser->start(); } void tearDown() { browser->stop(); delete browser; delete eventLoop; } void testConstructor() { addService("alice@wonderland"); addService("rabbit@teaparty"); boost::shared_ptr testling(createTestling()); CPPUNIT_ASSERT_EQUAL(2, static_cast(testling->getRoster()->getItems().size())); CPPUNIT_ASSERT_EQUAL(2, static_cast(testling->getAllPresence().size())); } void testServiceAdded() { boost::shared_ptr testling(createTestling()); addService("alice@wonderland", "Alice"); CPPUNIT_ASSERT_EQUAL(1, static_cast(rosterChanges.size())); CPPUNIT_ASSERT_EQUAL(1, static_cast(rosterChanges[0]->getItems().size())); boost::optional item = rosterChanges[0]->getItem(JID("alice@wonderland")); CPPUNIT_ASSERT(item); CPPUNIT_ASSERT_EQUAL(std::string("Alice"), item->getName()); CPPUNIT_ASSERT_EQUAL(RosterItemPayload::Both, item->getSubscription()); CPPUNIT_ASSERT_EQUAL(1, static_cast(presenceChanges.size())); CPPUNIT_ASSERT(StatusShow::Online == presenceChanges[0]->getShow()); CPPUNIT_ASSERT(JID("alice@wonderland") == presenceChanges[0]->getFrom()); } void testServiceRemoved() { boost::shared_ptr testling(createTestling()); addService("alice@wonderland"); removeService("alice@wonderland"); CPPUNIT_ASSERT_EQUAL(2, static_cast(rosterChanges.size())); CPPUNIT_ASSERT_EQUAL(1, static_cast(rosterChanges[1]->getItems().size())); boost::optional item = rosterChanges[1]->getItem(JID("alice@wonderland")); CPPUNIT_ASSERT(item); CPPUNIT_ASSERT_EQUAL(RosterItemPayload::Remove, item->getSubscription()); } void testServiceChanged() { boost::shared_ptr testling(createTestling()); addService("alice@wonderland"); updateServicePresence("alice@wonderland", LinkLocalServiceInfo::Away, "I'm Away"); CPPUNIT_ASSERT_EQUAL(1, static_cast(rosterChanges.size())); CPPUNIT_ASSERT_EQUAL(2, static_cast(presenceChanges.size())); CPPUNIT_ASSERT(StatusShow::Away == presenceChanges[1]->getShow()); CPPUNIT_ASSERT(JID("alice@wonderland") == presenceChanges[1]->getFrom()); CPPUNIT_ASSERT_EQUAL(std::string("I'm Away"), presenceChanges[1]->getStatus()); } void testGetAllPresence() { boost::shared_ptr testling(createTestling()); addService("alice@wonderland"); addService("rabbit@teaparty"); updateServicePresence("rabbit@teaparty", LinkLocalServiceInfo::Away, "Partying"); std::vector > presences = testling->getAllPresence(); CPPUNIT_ASSERT_EQUAL(2, static_cast(presences.size())); // The order doesn't matter CPPUNIT_ASSERT(JID("rabbit@teaparty") == presences[0]->getFrom()); CPPUNIT_ASSERT(StatusShow::Away == presences[0]->getShow()); CPPUNIT_ASSERT(JID("alice@wonderland") == presences[1]->getFrom()); CPPUNIT_ASSERT(StatusShow::Online == presences[1]->getShow()); } void testGetRoster() { boost::shared_ptr testling(createTestling()); addService("alice@wonderland", "Alice"); addService("rabbit@teaparty", "Rabbit"); boost::shared_ptr roster = testling->getRoster(); CPPUNIT_ASSERT_EQUAL(2, static_cast(roster->getItems().size())); boost::optional item; item = roster->getItem(JID("alice@wonderland")); CPPUNIT_ASSERT(item); CPPUNIT_ASSERT_EQUAL(std::string("Alice"), item->getName()); CPPUNIT_ASSERT_EQUAL(RosterItemPayload::Both, item->getSubscription()); item = roster->getItem(JID("rabbit@teaparty")); CPPUNIT_ASSERT(item); CPPUNIT_ASSERT_EQUAL(std::string("Rabbit"), item->getName()); CPPUNIT_ASSERT_EQUAL(RosterItemPayload::Both, item->getSubscription()); } void testGetRoster_InfoWithNick() { boost::shared_ptr testling(createTestling()); addService("alice@wonderland", "Alice", "Alice In", "Wonderland"); boost::optional item = testling->getRoster()->getItem(JID("alice@wonderland")); CPPUNIT_ASSERT_EQUAL(std::string("Alice"), item->getName()); } void testGetRoster_InfoWithFirstName() { boost::shared_ptr testling(createTestling()); addService("alice@wonderland", "", "Alice In", ""); boost::optional item = testling->getRoster()->getItem(JID("alice@wonderland")); CPPUNIT_ASSERT_EQUAL(std::string("Alice In"), item->getName()); } void testGetRoster_InfoWithLastName() { boost::shared_ptr testling(createTestling()); addService("alice@wonderland", "", "", "Wonderland"); boost::optional item = testling->getRoster()->getItem(JID("alice@wonderland")); CPPUNIT_ASSERT_EQUAL(std::string("Wonderland"), item->getName()); } void testGetRoster_InfoWithFirstAndLastName() { boost::shared_ptr testling(createTestling()); addService("alice@wonderland", "", "Alice In", "Wonderland"); boost::optional item = testling->getRoster()->getItem(JID("alice@wonderland")); CPPUNIT_ASSERT_EQUAL(std::string("Alice In Wonderland"), item->getName()); } void testGetRoster_NoInfo() { boost::shared_ptr testling(createTestling()); addService("alice@wonderland"); boost::optional item = testling->getRoster()->getItem(JID("alice@wonderland")); CPPUNIT_ASSERT_EQUAL(std::string(""), item->getName()); } void testGetServiceForJID() { boost::shared_ptr testling(createTestling()); addService("alice@wonderland"); addService("rabbit@teaparty"); addService("queen@garden"); boost::optional service = testling->getServiceForJID(JID("rabbit@teaparty")); CPPUNIT_ASSERT(service); CPPUNIT_ASSERT_EQUAL(std::string("rabbit@teaparty"), service->getID().getName()); } void testGetServiceForJID_NoMatch() { boost::shared_ptr testling(createTestling()); addService("alice@wonderland"); addService("queen@garden"); CPPUNIT_ASSERT(!testling->getServiceForJID(JID("rabbit@teaparty"))); } private: boost::shared_ptr createTestling() { boost::shared_ptr testling( new LinkLocalPresenceManager(browser)); testling->onRosterChanged.connect(boost::bind( &LinkLocalPresenceManagerTest::handleRosterChanged, this, _1)); testling->onPresenceChanged.connect(boost::bind( &LinkLocalPresenceManagerTest::handlePresenceChanged, this, _1)); return testling; } void addService(const std::string& name, const std::string& nickName = std::string(), const std::string& firstName = std::string(), const std::string& lastName = std::string()) { DNSSDServiceID service(name, "local."); LinkLocalServiceInfo info; info.setFirstName(firstName); info.setLastName(lastName); info.setNick(nickName); querier->setServiceInfo(service, DNSSDResolveServiceQuery::Result(name + "._presence._tcp.local", "rabbithole.local", 1234, info.toTXTRecord())); querier->addService(service); eventLoop->processEvents(); } void removeService(const std::string& name) { DNSSDServiceID service(name, "local."); querier->removeService(DNSSDServiceID(name, "local.")); eventLoop->processEvents(); } void updateServicePresence(const std::string& name, LinkLocalServiceInfo::Status status, const std::string& message) { DNSSDServiceID service(name, "local."); LinkLocalServiceInfo info; info.setStatus(status); info.setMessage(message); querier->setServiceInfo(service, DNSSDResolveServiceQuery::Result(name + "._presence._tcp.local", "rabbithole.local", 1234, info.toTXTRecord())); eventLoop->processEvents(); } void handleRosterChanged(boost::shared_ptr payload) { rosterChanges.push_back(payload); } void handlePresenceChanged(boost::shared_ptr presence) { presenceChanges.push_back(presence); } private: DummyEventLoop* eventLoop; boost::shared_ptr querier; LinkLocalServiceBrowser* browser; std::vector< boost::shared_ptr > rosterChanges; std::vector< boost::shared_ptr > presenceChanges; }; CPPUNIT_TEST_SUITE_REGISTRATION(LinkLocalPresenceManagerTest); swift-im-2.0+dev6/Slimber/UnitTest/MenuletControllerTest.cpp0000644000175000017500000001127312227051773024063 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include "Slimber/Menulet.h" #include "Slimber/MenuletController.h" class MenuletControllerTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(MenuletControllerTest); CPPUNIT_TEST(testConstructor); CPPUNIT_TEST(testUpdate); CPPUNIT_TEST(testSetXMPPStatus_Online); CPPUNIT_TEST(testSetXMPPStatus_Offline); CPPUNIT_TEST(testSetUserNames); CPPUNIT_TEST(testSetUserNames_NoUsers); CPPUNIT_TEST_SUITE_END(); public: void setUp() { menulet = new FakeMenulet(); } void tearDown() { delete menulet; } void testConstructor() { MenuletController testling(menulet); CPPUNIT_ASSERT_EQUAL(8, static_cast(menulet->items.size())); int i = 0; CPPUNIT_ASSERT_EQUAL(std::string("No online users"), menulet->items[i++]); CPPUNIT_ASSERT_EQUAL(std::string("-"), menulet->items[i++]); CPPUNIT_ASSERT_EQUAL(std::string("[Offline] "), menulet->items[i++]); CPPUNIT_ASSERT_EQUAL(std::string("-"), menulet->items[i++]); CPPUNIT_ASSERT_EQUAL(std::string("*About*"), menulet->items[i++]); CPPUNIT_ASSERT_EQUAL(std::string("-"), menulet->items[i++]); CPPUNIT_ASSERT_EQUAL(std::string("*Restart*"), menulet->items[i++]); CPPUNIT_ASSERT_EQUAL(std::string("*Exit*"), menulet->items[i++]); } void testUpdate() { MenuletController testling(menulet); testling.setXMPPStatus("You are connected", MenuletController::Online); CPPUNIT_ASSERT_EQUAL(8, static_cast(menulet->items.size())); int i = 0; CPPUNIT_ASSERT_EQUAL(std::string("No online users"), menulet->items[i++]); CPPUNIT_ASSERT_EQUAL(std::string("-"), menulet->items[i++]); CPPUNIT_ASSERT_EQUAL(std::string("[Online] You are connected"), menulet->items[i++]); CPPUNIT_ASSERT_EQUAL(std::string("-"), menulet->items[i++]); CPPUNIT_ASSERT_EQUAL(std::string("*About*"), menulet->items[i++]); CPPUNIT_ASSERT_EQUAL(std::string("-"), menulet->items[i++]); CPPUNIT_ASSERT_EQUAL(std::string("*Restart*"), menulet->items[i++]); CPPUNIT_ASSERT_EQUAL(std::string("*Exit*"), menulet->items[i++]); } void testSetXMPPStatus_Online() { MenuletController testling(menulet); testling.setXMPPStatus("You are connected", MenuletController::Online); int i = 0; CPPUNIT_ASSERT_EQUAL(std::string("No online users"), menulet->items[i++]); CPPUNIT_ASSERT_EQUAL(std::string("-"), menulet->items[i++]); CPPUNIT_ASSERT_EQUAL(std::string("[Online] You are connected"), menulet->items[i++]); } void testSetXMPPStatus_Offline() { MenuletController testling(menulet); testling.setXMPPStatus("You are not connected", MenuletController::Offline); int i = 0; CPPUNIT_ASSERT_EQUAL(std::string("No online users"), menulet->items[i++]); CPPUNIT_ASSERT_EQUAL(std::string("-"), menulet->items[i++]); CPPUNIT_ASSERT_EQUAL(std::string("[Offline] You are not connected"), menulet->items[i++]); } void testSetUserNames() { MenuletController testling(menulet); std::vector users; users.push_back("Alice In Wonderland"); users.push_back("The Mad Hatter"); testling.setUserNames(users); int i = 0; CPPUNIT_ASSERT_EQUAL(std::string("Online users:"), menulet->items[i++]); CPPUNIT_ASSERT_EQUAL(std::string(" Alice In Wonderland"), menulet->items[i++]); CPPUNIT_ASSERT_EQUAL(std::string(" The Mad Hatter"), menulet->items[i++]); CPPUNIT_ASSERT_EQUAL(std::string("-"), menulet->items[i++]); } void testSetUserNames_NoUsers() { MenuletController testling(menulet); std::vector users; testling.setUserNames(users); int i = 0; CPPUNIT_ASSERT_EQUAL(std::string("No online users"), menulet->items[i++]); CPPUNIT_ASSERT_EQUAL(std::string("-"), menulet->items[i++]); } private: struct FakeMenulet : public Menulet { virtual void clear() { items.clear(); } virtual void addItem(const std::string& name, const std::string& icon = std::string()) { std::string result; if (!icon.empty()) { result += "[" + icon + "] "; } result += name; items.push_back(result); } virtual void addAboutItem() { items.push_back("*About*"); } virtual void addRestartItem() { items.push_back("*Restart*"); } virtual void addExitItem() { items.push_back("*Exit*"); } virtual void addSeparator() { items.push_back("-"); } virtual void setIcon(const std::string& i) { icon = i; } std::vector items; std::string icon; }; FakeMenulet* menulet; }; CPPUNIT_TEST_SUITE_REGISTRATION(MenuletControllerTest); swift-im-2.0+dev6/Slimber/FileVCardCollection.h0000644000175000017500000000107412227051773021245 0ustar kismithkismith/* * 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 #include #include "Slimber/VCardCollection.h" namespace Swift { class FileVCardCollection : public VCardCollection { public: FileVCardCollection(boost::filesystem::path dir); boost::shared_ptr getOwnVCard() const; void setOwnVCard(boost::shared_ptr vcard); private: boost::filesystem::path vcardsPath; }; } swift-im-2.0+dev6/Slimber/Server.h0000644000175000017500000001070212227051773016676 0ustar kismithkismith/* * 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 #include #include #include "Swiften/Network/BoostIOServiceThread.h" #include "Swiften/Network/BoostConnectionServer.h" #include "Limber/Server/UserRegistry.h" #include "Swiften/Base/IDGenerator.h" #include "Swiften/Parser/PlatformXMLParserFactory.h" #include "Limber/Server/ServerFromClientSession.h" #include "Swiften/JID/JID.h" #include "Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.h" #include "Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.h" #include "Swiften/LinkLocal/LinkLocalServiceInfo.h" #include "Slimber/ServerError.h" namespace Swift { class DNSSDServiceID; class VCardCollection; class LinkLocalConnector; class LinkLocalServiceBrowser; class LinkLocalPresenceManager; class BoostConnectionServer; class SessionTracer; class RosterPayload; class Presence; class EventLoop; class Server { public: Server( int clientConnectionPort, int linkLocalConnectionPort, LinkLocalServiceBrowser* browser, VCardCollection* vCardCollection, EventLoop* eventLoop); ~Server(); void start(); void stop(); int getLinkLocalPort() const { return linkLocalConnectionPort; } int getClientToServerPort() const { return clientConnectionPort; } boost::signal onSelfConnected; boost::signal)> onStopped; private: void stop(boost::optional); void handleNewClientConnection(boost::shared_ptr c); void handleSessionStarted(); void handleSessionFinished(boost::shared_ptr); void handleElementReceived(boost::shared_ptr element, boost::shared_ptr session); void handleRosterChanged(boost::shared_ptr roster); void handlePresenceChanged(boost::shared_ptr presence); void handleServiceRegistered(const DNSSDServiceID& service); void handleNewLinkLocalConnection(boost::shared_ptr connection); void handleLinkLocalSessionFinished(boost::shared_ptr session); void handleLinkLocalElementReceived(boost::shared_ptr element, boost::shared_ptr session); void handleConnectFinished(boost::shared_ptr connector, bool error); void handleClientConnectionServerStopped( boost::optional); void handleLinkLocalConnectionServerStopped( boost::optional); boost::shared_ptr getLinkLocalSessionForJID(const JID& jid); boost::shared_ptr getLinkLocalConnectorForJID(const JID& jid); void registerLinkLocalSession(boost::shared_ptr session); void unregisterService(); LinkLocalServiceInfo getLinkLocalServiceInfo(boost::shared_ptr presence); private: class DummyUserRegistry : public UserRegistry { public: DummyUserRegistry() {} virtual bool isValidUserPassword(const JID&, const SafeByteArray&) const { return true; } }; private: IDGenerator idGenerator; FullPayloadParserFactoryCollection payloadParserFactories; FullPayloadSerializerCollection payloadSerializers; BoostIOServiceThread boostIOServiceThread; DummyUserRegistry userRegistry; PlatformXMLParserFactory xmlParserFactory; bool linkLocalServiceRegistered; bool rosterRequested; int clientConnectionPort; int linkLocalConnectionPort; LinkLocalServiceBrowser* linkLocalServiceBrowser; VCardCollection* vCardCollection; EventLoop* eventLoop; LinkLocalPresenceManager* presenceManager; bool stopping; boost::shared_ptr serverFromClientConnectionServer; std::vector serverFromClientConnectionServerSignalConnections; boost::shared_ptr serverFromClientSession; boost::shared_ptr lastPresence; JID selfJID; boost::shared_ptr serverFromNetworkConnectionServer; std::vector serverFromNetworkConnectionServerSignalConnections; std::vector< boost::shared_ptr > linkLocalSessions; std::vector< boost::shared_ptr > connectors; std::vector< boost::shared_ptr > tracers; }; } swift-im-2.0+dev6/Slimber/MenuletController.cpp0000644000175000017500000000255612227051773021450 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Slimber/MenuletController.h" #include #include #include "Slimber/Menulet.h" #include MenuletController::MenuletController(Menulet* menulet) : menulet(menulet), xmppStatus(Offline) { menulet->onRestartClicked.connect(boost::ref(onRestartRequested)); update(); } MenuletController::~MenuletController() { } void MenuletController::setXMPPStatus(const std::string& message, Status status) { xmppStatus = status; xmppStatusMessage = message; update(); } void MenuletController::setUserNames(const std::vector& users) { linkLocalUsers = users; update(); } void MenuletController::update() { menulet->clear(); if (linkLocalUsers.empty()) { menulet->setIcon("UsersOffline"); menulet->addItem("No online users"); } else { menulet->setIcon("UsersOnline"); menulet->addItem("Online users:"); foreach(const std::string& user, linkLocalUsers) { menulet->addItem(std::string(" ") + user); } } menulet->addSeparator(); menulet->addItem(xmppStatusMessage, (xmppStatus == Online ? "Online" : "Offline")); menulet->addSeparator(); menulet->addAboutItem(); menulet->addSeparator(); menulet->addRestartItem(); menulet->addExitItem(); } swift-im-2.0+dev6/Slimber/LinkLocalPresenceManager.h0000644000175000017500000000256712227051773022272 0ustar kismithkismith/* * 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 #include #include #include #include namespace Swift { class LinkLocalService; class LinkLocalServiceBrowser; class RosterPayload; class Presence; class LinkLocalPresenceManager : public boost::bsignals::trackable { public: LinkLocalPresenceManager(LinkLocalServiceBrowser*); boost::shared_ptr getRoster() const; std::vector > getAllPresence() const; boost::optional getServiceForJID(const JID&) const; boost::signal)> onRosterChanged; boost::signal)> onPresenceChanged; private: void handleServiceAdded(const LinkLocalService&); void handleServiceChanged(const LinkLocalService&); void handleServiceRemoved(const LinkLocalService&); RosterItemPayload getRosterItem(const LinkLocalService& service) const; std::string getRosterName(const LinkLocalService& service) const; boost::shared_ptr getPresence(const LinkLocalService& service) const; private: LinkLocalServiceBrowser* browser; }; } swift-im-2.0+dev6/Slimber/Resources/0000755000175000017500000000000012227051773017231 5ustar kismithkismithswift-im-2.0+dev6/Slimber/Resources/Credits.html0000644000175000017500000000007612227051773021517 0ustar kismithkismithhttp://swift.im/slimber swift-im-2.0+dev6/Slimber/Resources/Icon-128.png0000644000175000017500000002025012227051773021136 0ustar kismithkismithPNG  IHDR>asBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org< IDATxyxՙ[[kvɒ}m`&,@a`nev7 I2 ` fȐf@ L̰-ۀ7le[mUQ-s[ݒ>OI]u:U[!$N\c][$ 8q'8N?*R`KAasğ[w!۲YUKD3, Hr% h6-~-+lZ" |}\.b`>0 灒ay-(3XP(4hI@/ l^^2MT8SsL GUKIxxh]LkXRʶp|(@vrx0] fW&pJYBalQ!N'xxז<-(`u ܽY%A(:Xr 4Ͱ76|Mg %a!X*; -47y~MӁwoUPh.b1n6M1]UKD9Y,K4Z+lL5aJ~ m_,hl^Be8_r4F5 biS`(# >v|kX@JgZ`~ m–MؒwҪZG8Fh 4o~ -kkB@MY!* |-wh?KJ'Mr2x!E? iĊBΪaJM  )5aزŮ}ɛw-w.`a/<9̫ko(mӻ) 沬q"3&2\x1:yus+oO߀'Z䟓ELJ5 li NI\?˛8}N-E^ ca=l{$i|n>M+JٴionÛ?q'Ws) :/xv.?p4Y..["sJI54_ήʳgQ=mGyO[5T_{{o#jBל;9 e-%Ͽ'^F_F{["h roy\ ys=)s )%Ϯo_@B0EòKSM_݉WWx21?;=-v-a@zI|G8M%֔p'N!@[G/?(a .e> yh:*/+z]&{_#o?unY]E!~bfRcϿßu-T( I qJ97\_R/mt7O3yїvٓ>PHt ֶU6~*SgUsMiW+;Bwo&^{UKDVѢUp ~۹%'1o!»4yW|ہۀMOPa~Ut$\1|7Y<|e. bJ"PQ~trpDa$Gz` Eۇ`>'t9yE(2mM-KϘQ}QEoy4)Ĉ>,폽VU޿E}!7){;TƧZLCp/4p Ml<q(_k*,^ !X:zXBtIU$"[;I~^uAb][P5zԕ,{[qG|KKv3:.>C2L[5O(2/XD`$\b:gT`G=soz^{(̊ lkecV\VT{H))-sf늛6\!\)|FjB0c'!w8Sjtz0Be}[Tϒ1ŽpJMNExgVS[TYz~Q¥P3}Zmlc5V@oIS8|gQ`žF3l'\ ?ez-ò(ucνV ^wTEomr1"4*v^3*l[)tZa PEvdjwVܑ[olX"EƍMF?§'c15zA#8RDE%I IN^o_ޡ[1Rg4 ,k(b4RV\P+TS]9bɃ;e*B4lx(WU򬩀IJэ$"$ΘSóo& K8}"mzUJ#UANW_TV6.[:r{ ! &F8= )}˲eː|Q\01g:o֚  GIJIKP(bN7u?#)P%U[D.1\9Di?(H7%;,aBPbjK,[_s'"ELpcӓk٬rvQe|x(&@ms\;bAKX#L's)K)W "6-M@^u$MPnJL Ć__Xl<"|W/&/"Ubb aܪfPJD&#'ʙB"[W`J,l(fNy@9dzu)C6A{<|IB3wP' *y6!"mb௅ lscaŠk>UܦpRrJ$!&+4& ҵ%]n9\CS |yUۄd* %>l{@qFϓ Ƈ%^4_@dD~)9bᮁ^2:&2PX >R $A 0`( 8#KW 8>, LF>$`J[w }I@LK9_90`j1 1ASմujHZ BE) [R{T:Z@}Ė̪\ZZ' QW 1+I#Ex$]ئW=6-6ւeK;( cYWAƜ)bO\ EkJi['`9>l;JpQ懗%I~ Av ]P&Ͳuu8BhaQ^p鳊!_|X?['PcRYm5Zg0n0[hmK=E0ux{[MN2iAÝ05%yAa"7S kuTcU3yP$O<,H$@MkG?E$H"+Ī+;j ȡDמy=$:tIt[uzA1"@m%F9o@j$ZH>HWoėP*aM FXyJ#yIHŖq֥1Cs2I<|)>EkgP[X2mj-fTKH[aSI.1#~? +a0>衣Wf蓘ڰ NǑeI=q×Iud0OJ$#?ҍH&y(Rw-.Bk!vhm{p[rFq R堍OHjgVPBq}C89vpw@J/&*TqǾ6 clF@K#j> a|9 ppt=}wՍ!xQz P3JA:kaG^ Ei8wKm9Я-0z Yzf٪,ۄMB෸+́PUÙ{0G"`dV}nm r\ɜDE.T|&8!KPF> ^ Iz1V 2Ȗdacz9»7]Mܚ&q hDlxtxtQ%3 vv@l׬(MѳYH~~&'c BWBh9{ՠ߂ݰb ۆiZw1Sq{cXd/x:UK0"-@s \SE_PaD)%!@i.9_V0,ݝp7ey0&fg~p}4qi;UI<$zr^ZtCw} pZ>9P;:^l4G{Ž;IpXmt6~7= BKG!qC8-"%d~lr{_}42e̪%"zէI]H>I3dzϠ3V>Ӷ窃[6 s4g*f՗QYOo];u a(qc;_;&;73wp(z}ۛ N?άB8{$X1St۲t|A -4Mw)"BȮtJgσL(k/he?zT?کNne3Mm;42zLf%Nహlʦ-ѾyU'8Mή撟Q>ÂiU<ն$LH6.ကB~ڪ VO^K2`\`xp9>>3Eno0Ҡ[X>fmBY\QS C!(lRЎhM3I(] IixmnwVُvv1g՗r  UŬd!k_K\*m~F28xc'HPlM°g4۲W|٢lPƵkD#zd1cEϢQyl!9~c}3⬙I$FcM?4H&Q#O3ga'$8gQ='n`w}&N;lBcy/Y }VO#3ݜ.}yj HS;c8м@,C4 r\~洌|ۖʣ,DžKC>?L"э+o.=}*y9>*jAX'+9{uHygƤ8nFш:j95iNPōڃ]-:!KN2,lE]|-+r x|@Uy8)m㲪I4HF6n2S@ھ)U D xRԀЬM8Ƴ*H@9̭6 WY iP1GG7ސilϥW%kRUM* %BdY>JWlg4v&Y۔Ɗ5[|nuJuH)jJZp&uRy.p4͌Nw`P QkKZt,LA;nPZ6mZp^G~;xV/UGUa9Ydt̐)AJ9VlS#m'XUSCւ͕@-!3k Ks-3!3%%=_ 0M0h74 \<\ғG/ZmGq8ހ*yǁ1ާ#[*|g bN6Jj{OjfP8;>*d^P^gFӤAݏ[sifl= 2N4/xPo<"TջW)W§ޖ/b]yZ*Q85 -\TUtz>FgUrx8\ x)#[wKIa74iNjMd o-",4@(*`vg +UO'P(tEe~w|F#?h& M'&Qɒ7S?蔡ע`-<Yw9AώD1MW+P;swLB7!a:ٮ}J[` A۶ ɖ[?ΌDFi$e~+m<~4HӫqGGƨ;D7M78F{3`g<a(r%@4ͷaL<⛦yp.T$0˦>'r3"$f[-`E}hn3M+Wj=1LӴq쁄μ KwKLHm?ym֣&@l㐟7!(?ćU5.S9ԟ{hp?picxw1MPwmHe SR3` iz&sSy3 ٣yLҨ#G{ Tn:3y84Gd1.04 0 ==6x=r|hŦi1JEqE4LMɤTQ,!aA6nRgR٘?Rd|L" ]v{1Y<Fszvi] X;[5i8%qafaFqToá1Z(??`< ƹĚTu"y(m!/Tr:O%xWt&{-as;sߴ`}eF9e 4ˤ3G^[uìRz=O$m3{"&MnqP['@.>49ے#%788zwH2-'= uװ{=cmL8. I| x',~g[\sm?lC{/Nvap2qqwo;#ƸH{6ȗ b62 /ϲYpkhi+hVg Kq&qMc MyFp 0#|xuP >)%^!W image/svg+xml swift-im-2.0+dev6/Slimber/Resources/UsersOnline.png0000644000175000017500000000144612227051773022212 0ustar kismithkismithPNG  IHDRasRGBIDAT8˕kU6ɚG! Wnj@^K+R X¿  \QQBP+"bM ZP4TNdfg/- }lQ,,0T*٥R)V,` YStR2V|21{!^BYt2̍VG3,Ɵ eM% \|KɍWG:*Y\v'4յia =j Ehz̧/+?+_||t6|#@=}uߌ ʡ5Zkյ br+VYGJaHj ㅯVc[95ZF)CoKBU.HCnRrF-h Ѐ2=)0b'u@N6ap''kO]y{gLV̳7neYlӲr #ōj o sՏ#|s~Ҵѧ_~z<{DvPN /#3;痉l`H{z w\’g"j: =oJ5n ׭xM ŷz}r2ۻ0e]UwvPw~+8BTŗV;~;E7388vh%{TzNz;]FpO۩,% yw)C`@^9C^IENDB`swift-im-2.0+dev6/Slimber/Resources/UsersOffline.png0000644000175000017500000000075512227051773022352 0ustar kismithkismithPNG  IHDR7sRGBIDAT(M;kQ;nDQ\_S"nc X 6VBPIXXXDMN md3̵XVs9фNhi4% \hRӂf˫}dQ\8y<3Q M>-†w2Hxϛc lê`<Q+ӥπx7?1016w'LP(:1 ԫQW"A<MTo-f۸)U,ѧmO`@# Źzm %owj+kF9[,ڷ>6 8u7}EDDQz4!V,iϼ+sRPregkh >\,ՕmGCJJ4B3Ty\4䈏aePN5 j"^8IENDB`swift-im-2.0+dev6/Slimber/Resources/Slimber.icns0000644000175000017500000053273412227051773021522 0ustar kismithkismithicnsis32 ݦz{旣وބ{ςսfnyurtyjppƪ٫魒ͨԻ|{z~uw埒mnjiC{PQRQ~C߬H2 4 eЦ+!# @ $<GdChs8mkVYzo^Y+V< -2(ND.%"il32 QޝxowԒohqTcgPҪړPlgSP[hf\JOPjlʶkkQzz_́`u\XՙW`Y݆ec[[ηeof^iʿ\ׁs^\¿ƴYdqZkn`ewhxcd]hzmZ`Zpzg[{ob``_dafף`dc^^_co|ʨwf]ïƦW[֯ijԱ*[\ѱΣ}p݀`ȭ⥫e}v޽^Քfͯաaʝc ^ ^diktmqYZVSWdzybYӓtnv vHn{!@LA$iάf'IUG$zmaj2/kbpk)6̱41+iG5wRP z0R"[;|bzweW(c|uxvvuutvpx>/,A{tzxz{|}}b Wpjlj@iihgj[)4*_nlnnpqrsvs}V$2%BXVYY[\]^_ ](/NJIHIHL;$=NJMNOPQQSUX*6"#AKNLKJJMLG3,4GLLIIKLOLC!(Q$/;BEF>9-!%"/9@GFC;/#SV>*$!!*'*b~W%*( ")=VέB&oYϑkV!ٯ3:&Pӱ*ƍl["б΢S*|y{ z{vxƮ ⥫/Exosr sox4G޽ "Tlgji jfoE0DZ ֟!Va_a` a^dJ)PXVW XYV[G$ ﰓ)>QMON OMR739.JHJHJ)EEHGIAdCMONOL@wo!/4.~hA9Cm r['%WJQ]GͮD  [Q&FMNMI)*JLMMG'T-EA@?E7έ8E@@ABBG,,>7;:967.42/88<=>?@=C7402/ 0--!eb#//2 3475=(&%%$$%"& (%(()*+,.,2  !"#%$)!!X  p -0;!3'<Ѱ5LHK,7փ*D<@=Eۯ=5:996;8ӱ+'1121203ϲ΢9-'*) *(+dů ⥪ & #" #(+߼  òמw   z  ,  H  ]PbJP l8mk     6Ο?zĬc Eף;tU]T!7/"H6-&n c   |  \J* qU#*  D٥9 it32`ȩ}lfhg zͼ Ѥod]\ [agtƕn][`bhi ca\]iй ܠp_\afjmkie^[by[^gmmlkighhjlmlh`\gۺІa\flmligeedefhjmmkb[lvZdmlifeddfghgfdfhlmi]d۳]akmigedegijkjhfedegkmg\k [emjf$gjmmljihhjkmmkhfefhlj]g뺣h^mkgfjmmkfdbehlmlhf hmg\~eamifgfimkd]\`eiljea\]bjmjfg li[Ϩ~[ilfgkme]]dpzvj`\`imjfgfgmcd,_hkehghmg]ayտf[cmjfhfgo]qebmgghfkl`]uԳi\emhgieki_Wmgfifki\m|]bmhgielfe_hkeighma_v\hlfiggmWYpehifl`g[hkfjfijbelggiflg\k_mfihepX^meihgl]j`mfihgjccifhhfm_n]lghiem``jfihgk^5]lghigjcckgihgl\^kghgjcjgigj`8]khhigjbżbkfigij^_kgifl_\mfifk`2bihgifjcʾbkfigigf!_lfjen\cjgifl]fgigifjbȁǾcjfifjfl ]mfhidx^lggl^0igighgifӬbkgigjenakgfm\djghigoghgkÀȀзfighgifm fjhgjdx\mffm\dighgjdvĀpfighhgi\mffm\tejfjd|(akfifkaτbkfigiemhifl__lgfm]^lfifkbÀǀќ`lfifl_]mfh\meign%_lfihhiζeigifl^bkgfm^bkel_#bjggkbydihfk`xdjem\ogifl^khgkaј`kghifr^mdl`djggjd akfhhgl̸gighgjc]mejdy]meigo߄ޅ݀_kggka΄bkgfl^ހ߀bkghih\mejdx߃ޅ݄܄ۅڂpfihgifϬcjghidxۂ܀݀ށ߀fjggig\mekc~܄ۄڅل؄ׁݭakggjbydjggkcڀـڀہ܀݀ހ߁iihgig\mdkcل؅ׄքՅԃׁbjggifӥcjggk`ց׀؀فڀۀ܁݀ހ߁jhhgig]mekc|քՅԄӄ҅тԸeihgjbzejghhiӀԁՀրׁ؀ـځۀ܀݁ހgighih\nejevӄ҅фЄυ΃ ӊakfiflhfk`ЀрҁӀԀՁր׀؁ـڀہdjggje^lfigmЃυ΄̈́̅˃ζfighghgḱ΀πЁрҀӁԀՀց׀؀`lfjewfjggjë́̄˄ʅɄȅ˅bkghhihhgkàˀ̀́΀πЁрҀӁԀՀց_lekc}mgifl`ȃɄȅDŽƄń ɫcjghhghigȀɀʁˀ̀́΀πЀсҀӀڂbkel_`lekbȄƅńĄÅ„ueighgjcŁƀǀȁɀʀˁ̀̀΀ρЀkhiem\\meiglƅńĒakgh gkbɿÁĀŀƁǀȀɁʀˀ̀́΀إ`lggkb`lggkaȽfighj€ÀāŀƀǁȀɀʁˀ|cjfkcteigjeurfighgjd{€ÀāŀƀǁȀЯcjhfn[\mfgkbcjgigkbÀĀŁ}cjggjedjghifobjgigkb€̡akggk`^lfgjbcjghgieĹlghhfl]bjgigjcdjghhif {djggk`]mfigifdjghgifÇbkgifkcvdihgcjghgiebjfjen[`meihgcjgigjcbjgifk`XodjhhidrfighgjdzzbjgighhmZndihgjcpcjghgifkdjgihfl]Zoehigjfd{kghifqzcgjgifhl]Wmhfigijdf{nfihgieuzedjhhieji\_fmfghhjider hfigh gielqddijhhgfm`lm]kkfghhjjfcgs pbiighhih gigcvsgcfjjhhgfkk]~_amkffggijiecdju yjbfjhihfgihhghjecm~vkdceijiggffkla`lԅ\`llihffhijjhecceiot|zqjecdhjighihfhlmmlifgihhgijgccglt}|upjeccehjjihffghll`__^gijigffghjkkjhfedcdfhijjhghhifhmg_\\^dljfhgghjjigecdefhjkkjhgffgijih^^Бndcgijjhgfghijjkjihghflg\nw^cmgghgghhjjkjjihgfefghjjigcej֢}necegjkkjihgfghgl_m\jigihfefeffgijkjjhecelvkebbdgijkhgk\cfigihklklkigcacekuxnhebabahj` odjfihib_``a`cfn} lghgja)sdjgigifկ飼͘cjgigka+reiggjcӬߣaigifm] gihhfl\ڶߣugihfl^`lfigkgЀ ڹͣœ`jggjb ]mfhjbπ ۾ɣjhgie}cjggk`Ԁæʣ Æbjgfm] djghies̀Ԁǧʣ­fihgjc}]lfgk`ԀȧʣÄbjggk`qfigifqԀȧʣfihfk_]lfgk_ԀɧʣÇakggjc xdjgietԀȧɣjhhfl`^lfgkaԀƦͣĒakggjc wdjfjbрԀæߣreifka]lfhjdԀߣdjggk`lgifl_Ӏں …bkfjdwߓ _lfjesρՀشmgifl^ߘbkggkbծ Ÿbjghifߜydjel`ͩɣ‡akfkb܅ے_lfjet¦ݤqfifl_ؓلdjggjcضejggje؞wdjel_ѫ•akfjdxԝݓ`lekb֣bkel`чґۭ`kfifpֱ rfifl`ϖЃihhgjdȧihhgifϟxdjfl`ֲᥬ djgifp҇͠akel_Ƨakfjc|ʊɑҗ`lekaծأ Œbkekașǃϥakfjc|׼ bkekaƟʭdjfifrɨؤ ckfkaɿž dzgigignů xdjfkaǽ ĵhhghgm䥬 xdjfkaݳihiɤ xdjfkaùihj|djfkb gighgl६ ckfkb figignѣckfjdz cjfifrdjgignbkfjdz hgje{djfkcrfifkblgigjc﫩cjfifqcjghhi﫦djggjdzdjfjeu󸤮pfigjdxeihgjccjggjd|djghӤhgifqdjggjb~笩cjhgjcmgihiǝghgjd{dighieyakgihigcjgigjemghidjgiem\]mfighgg~cjgigjc|Zndighidrdjgihhie\odihhjcpgfjgigfl]\nehigjfdvlciigjfhl]Ynhfihijecnyhcgjhekh\(\glfghhijgccgmquvtpjecdhjihhfhn_om]klgfghikjigefhijjhgffhmgZ`]jmifefghijiggfegkmh[pӒf[gmmkhgegilmkb[l ߠp\\ahkmlig_[b ɦkb[\ [^fnނ հ첅[H0(+* +DTxͼ S5&!*;ij3 $+.-%!-Yйy7!).20-'#LC*230/+*.021,)lۺU")120,*'(*-12/#1>&21,(''(**+**(('(+02-%l۳g!/2-)'()*,-.-+*(''*/3*/1Z'3-(()+-//.-,++,.//.+)((*1/*뺣2*2/)()*-//-*(&&'(+./.,))(+2+J/&!2,(**,/-(#"%),.-)&""',/-**))1,ZͩJ,1(*./)##'18>?<5-%"%+/-*+(*2$&,0'+"/*#&8Pgz|kU=)"'/-*+))45.Ʃ*),*-.%#4Pn{cD,"(/++,'/,.2)),*.,"/U~a:#'/++,'1)(y,/',*+/&$Dra5"+.)-))206'+,)/%+[r="+-)-(,-"s0h#0*)-).*"@yd-%/),+&73&,,*.#Cg-%/),**/ ka /)+,*/%0lF#.),,'2 /(,+*-$]?B#.*++)/!f[ 0),+*.#G^$-*,+**.K%.),*-%m6[#-++,)0 gż\ 0(,++,$bk$-),)0Y1(-)-%o2k',++,)0 oʾc 0(-*,**s!i$.)-'3%.*,).#bw)++*,)/!~Ȃm 0(,*-). Z#/)+-&Al1)*.$K-{,++*+*.&ט!/),*,)0A&-+(2%.*++*0{+*+*0ÀȀӬ(,*+*,)/ ),+*.%A 2(*.#gt(,*,).%BĀ 9(-*+,|Y"/*(2<'-)-(9&k&-*,(0 eY!0),*,)t.+,(1h1)*.#b]$.),(0 ǀր0(,*.%gS#/)+,+~3',*0L%.*+-ѩ'-*,).$Vw'-+(2Q"/(.%G<'-+*/"TG%.**-&D7(-'35),).$_}t-++,)0 y0)++)2{M$/'0 [%.*+,(o~}|}y_&-*+,)3Ϯ*,*+*-(g|~~a#/(.%C2(,*0w}~}|{zyxzv~F%-+)0hY!/**.$Nx|z|}~m'-**,+2'-(6z{zyxwvuvp0*++*-(Ԛ#/)+,(5vwxxyz{|}~t),+*-*2'-':zwxwvutstqxY&-+)/!UG$.*+-(ayswvwxyz{|}~v,++*-*2'.';wuvutsrqpnr;'-+*-'ۏ"/)+-&Cwptsstuvwxyz{|}~~u,++*-)2'-(8trsrqponmnkp^),+)/"XH%.)+eroppqrstuvwxyz{|}|q*,+*,+3'-)4oponmlkjkhn@'-*,)2x*++*-&Hrknmnopqrstuvwxyz{x~h(-+*.'1(,*.gnlmlkjihghei[),+*+-ckjklmnopqrstuvwwxu~]&.)-&?(-*+,)\mikjihgfedebg='-*++,++*-'Dkfihhijklmnopqrstvr{M%.(/$H2*,*-&Mlehggfedcba b`dR(,*++*+,*[gdeefghijklmnopqrrsqu<'.(1d\ 0(-'&,*,+BLIJIL>),+,*BLJKLMNOPQRSTUVWVVZ>(-*-'3 2',+*>LJIJ IL9),++*,)),+,*CLIKJKLLMPPB-),*,*)1}5'+,+,*)3DKLJIIJHLF,+*/IKIJIKMME3)+,*-(+1~3+(,++,)*4AJLKJIIJIJMD-*+,*1HLJIJIIJKLJ@3*),++,'/,)2(),+,+))0:DJLLKJIJIKLI:+*+,*->KLKIJIJKLLJD:/)),,+,)(3 1 20/(),*)+08AGKLKJJIJKLLKF;/)+,,+ ,+)1>HLLKJJIJJKLKGA80+)*, +)(//I{!2/)(*+,,+*)),17=BFIJL KJFB;3,)*,+*))**+,*)-5=DHJLKIFB=81,))*+,,+*((/1" S!11,*)*+,+*))*,.1468:;;::863/,*))+,+ ,+)+.//.,)*+,,+))*-1569;;<<;9741/,*))*+,+*)*+01 TS)./,*))*+,-,,+**)*+,+ ,)+/*%""#(.-)+,,++*)**+,--,+*))*,/.+Ui9#")./.,*(()*+,,-,+ ).*"/GWYM6#'/*+,-,,+*)()*+./.)!$/d٠U7%!%*.00.,*))()+*.$/dt<"-,*,+)()*,.0/.*%!%5Y}dF1$ !%),.0+*-"Oe'*,*,+*/101/-)# !$1Edq\F5*%! !!  +,&f0(-*,++*-" #)5K`j]üwog_[\]^gt|3),++*-&r3(,*,*-'spg_^]\cktٴ飼ƀ|"/),*-&r+2),+*.$UѪ ߣ• -*,).#h),+,(1t̀ ۷ߣ A).**.$Sr%.),*0)փٹͣu/)+-'<X#/**."P۾ɣ ¬/+*,):',+)0Ԁæʣ ^ /)*.#_ |(-**-'<ʀԀǧʣš(-**,':T#.*)1Ԁȧʣ[!/)*-%p2),*,(9ԀȧʣĠ(-**.%Fb#.*)1Ԁɧʣ ` /)+-(t 6(,)-&>Ԁȧɣ/*+*.%Ga$.*)0 ԀƦͣr0)+,(p 5(-)/"OЀԀæߣ='-)-&@X$.**.&Ӏߣ Œ#.**.%c~y.*,(1rӀں ^!0(-(5}|J%.)-'<Հش 3),).$Pz~}~{h'-+)0!ծƋ!/)+,)j~x{zz{zz7(-(1jͩɣ ` 0(-';zwxyzvP$.)-&>¦ݤ:'-).%P|swvwtze),+*.$ضÚ&.*+,)dwruts5)-(1}ѫz0(-)5qqrspwG&.(0!W֣W!/(.&DtmpopqnuW&-),(7ֱ <'-)-&Srknmnmpd,++).%ȧ-+,*_njlk 5)-(1ղᥬ đ#/),*0ejijgm>'.'1sƧ { 0(-(8hfghelH&.(0!Yծأ j 0(.'?hbedefciP'-).$Jּ ] 0(.'Dg`cbcdbfT),)-'<ɨؤ R"/(-'Id^a` _bW*,*,)4 K$.)-'Jb\_^ ]`W++*,*2䥬 I$.)-'I_Z]\[^U+.ɤ I$.)-'F]WZYZY\T+/ R"/)-'B[UXWXWZP*,*,*2६]!/(-((-)/#N+,++,*EPMNONP4)-)/!a?&-),(:OKLMNJ-+,)/"|﫩_!/),*/JIM?),*++,﫦#.*+,)@LIJIK3),).%D󸤮:'-*,)2KIJIKC*,+)/!nc!0)+,)@LIJIL4),++,,ӣ,+,*/IJKIL@),+)/!U読_"/*+,)8MIJH.+,*ɕ-+,)?LHJIL3),+*.$Pu0),++*BLHJIL7),*,*-(3*+BLIJIJL8),*-'2x2'-+?LJIJIKJ5),*,).%G3&-+)8ILJIJIIJLD0),+,**-&{5&,++,)/@KLJIJIKLH9**,+-*)0W3'+,+,*)1?HLLKJKLLKE:-)+ -(+1*|3+(,+,,*).6>DHJKKJIGC;3+)+,+,+'/+*1))+,*))*-/120/,*))+, +(+35201*(*,,-,,+**)**+, +)(+2*]o .2,(()++,,-,,+*(().2+5e(*22/+)('(*,02/#1 x6"+/21.*#L 봁P/#)4]̬ځ ͠m9" 1^ͼ e1  Jw L  8й_   &x  Nۺ1    o  M۳G   w 8z뺣w & 8̪ &  x%08<=<82(   .˘)6?CFFEA<1#xe 3CIJIHGHIJJE8! yh,AIJHFEDFGIJG;! ek >JIECDCDHJD, _R 0GJFCDEDCCDGJB#x :JGCCEFEFEDCEJE%UH(FIDCEFEFDCEK< HHCDFFE9FFECEK;OA>JDCFFEFFECGH-(KDDFE4FECGHPżB"IGCEFEFDCK'?w*KBFFE4DDECCI'[ʾL,JBDFDDEFBK' "KBFDCBABB?G.mȂY3G?CCBCDE GBK!TF@CBA@?+@=<>:A/  Ȁբ3@;>==>?@ABCD EDH8" o$C<>=<;:9;7>*#;/=8<:;<=>?@ABCAH :;:98'7785;%M=*;6989:;<=>?@C9L!=7:87654629}ǀm":37566789:;<=>?=Ci18565432305 Ӡ6143456789:;<<=;@.1725443210!/0/0 8(3/21123456789:;: i5/210/.-,.'xf*/./012345678896<=%1./.-,+*+)-ѧ .+-,-./012345674;").,+*)()&*P>-(+*+,-./0123537% *)('&%&"Ӿ֎&''()*+,-./01214) )(('&%$#!%:('$%%&'()*+,-./2*  &$%$#"! !߁ $!#$%&'()*+,-/(  #"!  =(! !"#$%&'()*++*-%    c  !"#$%&'(*(+  !"#$%&'&*  !"#$%$() !"$K> ! i     ,  xo     =T   @b   w  6x  e   e   e   x  & _ g 0 0 / 1L  F ۟w9 #>HJJIB-  =p |L$?JGDCFJE# #L|{\@$ $IFCDFECDK3 (CUz!żwcXNC>?@BM^h ,LCEFEFEBJ< [sWLA@?>FQ\jܷ飼ʀh,KBFEGBH<6Щ(ߣÉ&KBGEGCI8_ˀ ۸ߣ HBEDCEAI+ٹͣb BABAC@G3Ӏ۾ɣ Ħ 3C>@A@? Ԁæʣ D C ?=A1рԀǧʣƙ <=:ArԀȧʣA)>9;:<6Ԁȧʣ Ƙ;7989:7= pԀɧʣ H*957675̀Ԁȧɣ§835639ԀƦͣ^'6243 20ЀԀæߣ 2012/5Ӏߣ ȇ2.0/ 0*\Ӏں E,-.,1Հش .*,+,-+. ծ ~!+()*)Rͩɣ G ('()'+¦ݤ)%'&%(ضő&#$jѫ g"#!%:֣: " #ֱ   ȧ ղᥬ dž [ŧ i  ;֮أ S'ּ A  ɨؤ 2   * 䥬 )  ɤ )   5  ६D  ѣX v1 I m 﫩G  﫦w $ 󸤮 [ NԢ < 驰Fʐ7c b *k  e   6   b   k    <Q    D    o^ &v i-   5\^70r˘\9Q knxUU<@ mC ;AP R[0 [b2S˦DQ_W\O=.aC}Vi;- m8t7 IZ[UK0Rl(ni.n^'S  u&2y4 V"+wd 82$%Zp  !*6APY hr         g Py 7i K'  u> LSn5HRRIK-yc/[ .Tb j m T8^#|7"rv - ^z+9|0^,y=b{V6v֠a QP]p魪ŐY{wϭZtKy//+P0o s?3xπ !C 4 30VH(^ti8d_D||> @/,M.#))!Г (!V}{Vc1!w=CȠGw?N| ]l)Wd-Cʙ5s"D 1yr4F\+MH`^CaQ;^4 |A %we&l_$laLH4oL"й#Vf94tQ/o*d9wNDpjװ@^ ҖB0#ˣ`j?>`94:o$̿~ٌ B{h^lޱkj5 ,z4†/RL_ULez,V_uP~i(A:<3iW#! }ihsC^SOp(bD``&ޏ1GMAx _$7MkY=9A֍M&!*+\p-4vZzo"O~0Ѝ RLU66|XXxF]L'n*\JJ`?=iJ_{cfXUq MC>Ԙh f.+F :)s9\Ǒ}@ˁaJ˙ j{^ ZbJA2JB䣛SPL(1dkC|:.z#p"ƦD5O }s؂Jf4iu+?P0>(a:եΉ⮝+Dmc\MjşwWLI^hVT}3\ Քf t}hM?0tqCleY_;tXN9d72߶I+zrvF<"6GUNSoz -Mfp2G!8ø#FG0?% $,kZ&i[ +J:QH(uxO +EpKտǸN7!N"_F 8'7ڽ)9E^~@O'ȽٵD-1*څV&ѐ}]:1(LQ˦ "q4*Ϛ\$v]t<^uR,tYnd/>}&a$؂xWT˜‰ţTc´Ȫ ̮$vJ̃i>֦䋎֘bj%D8wԙ{LX5+XYd-IoB> Y Y|0XI t{ֻ{%, x_5^=a^%#U!=teEnWH2K Twn`ཱིzP;+=L}qX'N[f4遶R\ςG m3qZ>T^3sH6iԈc CEHNEd:|N5i >YvMp΍]:6gM{tFnk4E8if}jlrXScIcӴ*sdJ?MU zB[F>>~`h~>="U#-P3l:0K$HTe-j~߬dfى-9*b&U~33Ŕ Aƨ(}x& ]X'F+dL05)^gńiN; , ]{_!~#"[ױnR$c;o s>G`w﮺r=#W ZCbǃbsi5ۗr)WF@F! ^ŗd*u qzIN ( #ЅT Sn;49֦hߤ|!.y,~àu6`s[^*Dmn/5n,yԻ ҭjX 9aV".S\:O//SJ/":)'ٸ΁|yw74MPXCwyޭ8"k9Wr<&@HH&d$fo_gF)8IGrBT.!&ŜA紇t5(UY@1 B2et|52= kK|Pl9鈺.~*3=ΆjӖcڬ^', J7/7tU|񸂕ҁڟR30RzHCB&,u`$DMIwG0XWOݒS]i m@D4.?¦YI 9 ^HX\]3eʶ &> A]џ~*kZpdUF'[hgޘo## |]ZD1Ev sQ9uTZÍDR}>TH( z#n2tw"#"ryitaLݨ#5lC|9B&&ov7K)K! ,"Cʅwj-+TyFf1堬>iSN *ޭQplZj .AHV& ߮A8!.03Hb=?R i+;N:d6ͦU(&|ܹ̾5sMyl~a+ZHoa3wΥQArdKs]fQRɻ@:9ZWmO4pr"< NX ڥډ~g9Q ^ *8Y -!&-=NC'}M>}ÝaIÁ|*˧3jt&Z`0!R~ҁ%Z{x`+ڪ2}hϑ|W%-`_B0sQ$u~qv#ͲQ-p\$ı3MC .Uz^b2M,K9lmBg-C0A~dBV{ad#uKZD ޫ)N2:ȾtUaǷeѮF$+) p1]ʈ=d/]y2ۤفߠ9ҫeȃǁ? CZ/x_zДGb]f)`8,@9~v~K$1c]ḡ|ׯ"g hٯKgێ HܹFUuk ؾ}###GeJ-јĸש/fߜ'6RkUq8C4m d.  0s37f^jTl- DYհ& _-gCCݖ-'^>m%.a_}/ ]/vsɑ(Ym#>6NG/NQA_KcBYk JT :jSoVD+0 aKYROcq?GSNi^3/j$V9•$-A/T! O2)9w%lanNftV܅ T ";6g~= Q!S!K%i0ʯ3,%@ ~g0 xwANF`MͲ d0/Vh< e.ñW''aT6(ҵgOziRcK:z?vD]Z{ylt: A@cl[ iɢ[w-MXZ[ZCDoF#,Y$h0oWBIs.-5\Φ2Ȇ Y5vh}ga=Zf) m^}t=tU@_qKvKj03izXf驀F3}nE+ʶj*|+Ɇ;^ &S%XYY6oqj9{[f t\bA{XR!0 uT8 (.rT6ӈ;@s07hT\XL*ulD)bÈ)=N7'OZvCt(ȝ(|#V$ 8ɛFR0|8+/+e&٫ݮ0*}$`ָ<-pƨ&l簞+;%ߧ ,_:Yz`>7{#>akLշY_f$ %nzV\HFtw4K{O!ݵ(6DsoYuVu2MT_T|$\MxCbK|4C%Y40 $t@\/WK@?_>?y uߥnWh+{=4p AN"EWطAsDE[/U`j֯9U١_Dft9 \du2_2>eDU.wv[MaTq?hT")?ڪ>/ߢ]1"{O^fK c*,nd{")vݤChR>CawGX]#&a /D4X=ſzG4byh_ڼahl 0*) Ԍoy6`#HSʻWk `;$-[+CT|;-Rґyqzwxu{=7Ɩ#HOT7(%P6㑼sG"XKS i@KŅ8Lɫ&&d4~W~m]u"ʣ 6,lE?1hQPAᇦA T#5G N퉀)>qdv^+tx_ڸ NVi*$D*GE{4X?{x3Q4Vqq;+dY&JABo 8PWTz<*`d'!ceW= X\+X~6@:Jx/=.[ WS!Hy~D~3,cD '&OэԳO"\tـ- "z#Q uy^ʓ|8* "~(&SϹ~/#>*O`'SܓTw*D&LhNl 6v2zF_|*ףP`֜ǧ6g⎚Dע+Uobm"Cm2=䉡DoA~E@h BLwXkL6,-=ωvKK&:eDzõ y'I鰂Q_uy 0䢈l^ِ^  ϱKr5LAwR+UW(_I%[&R "3Ҭ[&N[4H#aÉ 5mCXE(1t>~q5<젼G-s (?¶@&"Ӭ*Yeؗ:{RMY#kM.hvkk*VH@8] LT ,v HjmxkHψŞ&q +k@?u+L;VTn_{zg6l]d qj#%w] wD;Mt@s/x[Y1i}HyBZþԼ^ z^II<#*VA*=maACE j+ ~" ! rhdݢBW$k\/6y`S Y%4md#yNat\٩SP0a eit!m/kny"wPOe0@grxZwz!z/4Gl߉~ѽ`f52U qj}0e$ O(ԄKC3viKr<=@v̈ej5٪<@kߪ?Ί| llLD:.k`pǸC %T—KbXz ]L$6̆`Tfn.2G(:%ς} 5> ^o@BNMknWwSo} ҈&'"0&Wr=ϊ޿)EظSN0PwT6̨6WM#?+OL}(ˠi> *U>k%rttx 2HW5cn}m0'L)޲g[[&00dM~!O{,d Fptf5rx4Doґm\}c/p[w!6b!̞֏"sG1t*bӨ$P~e9j0U cKZa u?X9v7g*'+/EU; KnZߵyJ`L&Kv &z#Tx\~X]ψUQ͒XqBfrIn?@8"QF1\7&EC֚)q*>ݩXN܄^G!BVmEuL Wb:i$2L^`fcxkq~-K"["w]`<``>ďu>@`-m"ˍ$9g/k.dk *#C5Τ0ƒ.s33-ŞN\`s(*웍Xh蠌3>IdOl9bއ}m q4s1lM)8߾H<ؒ\r&rHXvZMRHF92J eq4yn6](S{An~ %uv'Re2uI#Oc[iG#BJwR#-˨n(Ql]6f@p*IM} #*haLtnzO{tڬY 2OjacF}So34p2Ucr&O<nArӻ\9L]B#)'d` (i*WH . C U+M?A/6kQդcy,'PM%Rl^G aA<2 n΅P$=n2ʼyil}ݫ~aQjU;TWFWR V{¶XAi9R'ȿx(.9.^u>AXkf(ĥ6iâ|4*5ʈ:hآ u2b,9"ፋS$5Sp0`Zt pIQ/~f۱]h%҃u,$S/,Κ: X=7KfU5KB"<=x|:&^H ;vK7DkuXlBY oELW bKU(z,@;|I-=3ɌigoIpOd]n5Dר"YN&HZ9wyhxvWzRbP%<|O.q"?K Id/1HKKd@-()GIjq ]{D 9:L -LRqzljV]]%<_4b/?ЂnYw (#5g6nTk1HCaa{fv"_SnQaUn,TX|o9,0( ~ \\;ϱGW~64 it:-O .K]L=|ʃNP}T^VW! *i'ATJCRXbCa缗=d t5a>Tt"L)EmdICk}9xaҙ0 u2 ;@Ȫ nK56xQ.>6l^(2{o]d2on2!`эp: (?oPos4W"*o^7vYOBf9|j*вߴˣ m>K_w6_Y4\o|.vMV2bqi.(Kܵ1M>YiA[bI%S Jɢt-|B$kPΟ+ٛ7+,{ce T150VAJZo?HR8z# @dSߚ ?ӗ =1εE9خ2m ӓ|Gj,>/P/]8aZqC]M mzo/8$-\jӃ)OϑrT{ "%LJgJmvTPF1w۱_"@;&]?٨ؤ,~ao XK|7 iZj(3 3(:H V%Ӂx*x!>N h_E]d?5B9MԱgU56~#n`ME wf¡װTZ8YyCp-eS9Rx,Fc(nz`W\} }Z 8r'y, STax)ؒ.iPiSXo)R>h95Ӌja WeQS5Dxm_QH{]}hKwV|B6(ɛ)$N MbabaneIOTǟXG SXn0bd)bHMin|}ίs =W6 d Qq%5yvG1IIYQPl9ђbe;?: # P$9fK^;xS?#': 礀>J]$SeoAhb"@ {Bt+b3n%{)tP }xZ80}:VB5$ݼd2#3fAZKU_݃H,@('cA ̾c%\Xڄg~1Ƚ|8 z ! 7|tN_\flcQ^kQ?LƆRt&ܙf-S!1մA:/) \܂ Q?]UzgF>]l  O|-QMZ1F=l~7Ǔw5%ǥc>>IȂ\@TQz*>qzj/]<^Xvt 79l\Y씺Ʃ[u5[; &,JLsXb_҅ 鯾jIi#\M/;(hzb袞$)$v54'XfE?4'TSA=UICZu d yy]iF½.x q\\ H/`jZvHu4\퇳x`E__r=P!ad.ZX\v! FR娶1BNOt Fl|}!a`"lN8ĀڠRQv vk=[ g`A`tG Xe8H.(I/Ï]^W9Jև 2Gjem uK/5## LWeKXkl6iUD`r*L:<1)_N Ć z3?_ ʲ T,6,"#Xh*/kiNQ=a zm_̒8%[QB }p Z)҃ ZckB4>I>qplۺPD|L!O@՚~;3:H".@Nr^H$U}]r_1pI1!a#W!P-4Yl~mbW? E=8 Q!,XF>'WuA  榠+Vt=Ӈc[þra=ĉy#v_eʾwiw=o!Mq4 {AօHÐ-(0fP7ePaKoL(҄-:e۳r -[ceG|$fD h;Y#o+Aѣӯ V?psSƪB-@ iTQnBrAX?Wf[an,C4JtBS7]bS[3,u|.|dv:y:ܠ !U9"NK&V*F0fr0`>+r"Frnjy ރ4Jwp"S"b<.n@Zu8ȿi@[(A^xU* ~ڌ[ tpٷ@GhDfѝ:A4kձ6~'O̬v>zڐJ3]Ձ)7F]e!8f ~ 2$>#bQ͎/8tkh7™TU>,&3*42ۧD+";Q<` Lo10$.s4Yb)^⭉*\S@lܐPv/ .A!-Pӹ?`3Hm ]ڵl^ ɠ"6&6/A5T}~ruI^Z'}N]P ԇ}}5o{Kz5ߖv?6>tAd&4Tٜh$fP@T:/FDgۢ꒐"X4X6f;N9OH61Z/^Cu|aNڳrTxC-5zs5URKfYCemICC5M!6oOA4ީWYGs;FlmspuBc,A8v4M71.~OFE׳s_#%o|o꬝c7`-C|QbkVhc Ȱ+cN t_XYk O jB,? ?&U#'$VqG#Alpe0Ӆ[:8XF(RgaKS@4x5ULRCjEls[ڬro Ct;,k!mلv6^DZqŲt-35A\ލUfϣ:)áJ\q)l*[L/ 20-edEb+wH7cVa>`7Dž>,0dI5".~DJIȥcWnΞZʣX#74so'd a4ԡM"keyWX?`*Gj ]Yl9mƭ-aF^dT~E If .Y|h>E$Pwu= uߚ,6[EMܴ5Kɸo٘ 8 Zdإ#aZbvOpG5g! SLf_r6HH˕G͘ E^.U9t 9l^jKh6sI?6G0eY_ˎtD`sқ7 "E]YU[wRfTULPxc8D.)1N;bBN]>arP'hB ctkG.Z<]\AW8J.Z;X E3 g+NVd&2~}ȊИcEӊPXeoIHЬ7ee gt; yp2Eje\R|Cy?mi1nsF, |Vv>)Zk36O;a)I ߫! pBKx?hAF6bWj=Mi:!2̩/u t?HtmdnPOe(rUL/MA’^ȋO˞10{ ȡ]>L9}+x6mm5v !DN5껝UOqK"Lo>#G$vv`MT~A%fkt: Xs7=m;=OSѯ `]QY[xN{|ek@W0VWfx*<*<R,c->ogfyϟ/[CR[((i\/<€*7-sG1z"ժD#|FrJjOp1$ ) .s.)HpႪZU`p7aFz$[dwPZjbs{cFّ(A"8&FL1ޕ.0[t )o#1^L]asj U4<*?&i4نRWqG1Ŀ貌Yhy "$te4ZyiPfv?)-k-ūHg3 $-GNzt)rsT܈,X#(dΫZ@@p/K QMI@qgRpt>nt;1LP/;p5h?ej0eW[3qDPܟc \)l|Nݛ|0bW/n)*Uqm5"kȸ>~_/ Q4^G-kgաPImV7&)lY刁/3#>ߊQ#blYnA[I.lJ0HRl1-{!ޙ}?oܯ۽8~o{?y7@߬Ź{uz4.Q34U_ Ta\듺T^4?C+XЗwT*8-GXR2XA>pI˩Cb:t%Cu_4ӊ)):4pWzZOg7]љo|⮗A]J]ocekalI2HS| KotfQ6ZX=*)|C/בMmPL[*֐|+qdY\Zz8›z\Eah,%I訞]<ݼdJ+ c߿Zđ @ENڋ&pahSǕT/j6*G߷!.T{so($ yOy{km(@L5z?ɩؐ禋X<@ƬyQHd,z]y'ZAl}"úLL舞@f@bAmJF K J 2~29| o_+s.$Sonԯ)#Α"~.ے[[iU,LMn[5O.c3N6*I]$:qf˨WwðdvDڂ#4Vf6=vs56=<6va5߽Y[/ob& C%'(A(UaҳJ$ n" 5T.cQ&9 ]MUsy׍j[u"70)?3q˲MH*u._ *j(\m)\9LҽUo`'[WF] 5K6d쳹 _tkyl/m*r-iȕ#7{͂%~4v1U.J / 16zB{M[*k~KYsxax>~=e˽QxCN7!~Uu? ݧv L"z@OA3cDqhC'YVR;e?qVeGSyװd+?|:Y=qKzH"t償ñ1)[fJqso@ܪc5=,}hU`$Ic(eu.׮'T.Yi;I3R.k7k6$ =z2KRh񰍏opMub6xqCvNrS)c ZU2!kF TaǷvWѵ,̽w?A._[ZĸEi s'ত+k@V(PSerxTW0bW3vo%am-VkO I KBxTv i *RU&FǤ`vOkxE' MBZ?An 7"8B?Ewd*PI?`]m3'_?RSB $ߣNeNUL W= Bu[52ю[*|a@+'~E$8 y?-m2߲&>:;'JVxg~_bg!`FXbfOځM4ݢG R#mJYbB9MFj=ەh 4j`, w;v0$:C-}R֔ay Ѩ#@gv@,к ]{ ^h@_|PXTG?L^LŤ:vpuV35F쌽>|'L 3M{ݿ;3_8onsZ8jw^8a܍kSkThMe*.ÚrlQƿߊWLb1keOIU-쉣anN7Xv;b98G=]6= ?Q'b)IgK_|dk 0"H T:ϻws7\mgy4G`+WTҫ޴+[,O)eqHK䆥ڰ6Rpݩfa{[(ci{ `k)JQ'7zoc<+͊/+RƓ5Z%]0ƹœ_^_̘Z?ЉVfE\m缭_!]=+c%y /I> 7Bn>{6$[>-j*#1>K|YAKO-%41N 0Iܺ=' 6&Tc:@;HAz-*r DBk33*w%K28Rg#KGƱ?_jÒR!)h^:ƱǼ|4y&u[X|?  {^H,,f7^h?4G*FˑXӄXtaa=uŠ͗2RB]u;8UlFfHs]EߓWwws+]h*EO-1,pdkdu6$+ei#\ W2Sʼn$czvPi)*"Dee砇PO٨YD'"^Y/'6Tۊ!]p8D=._ `A>kp,Zmr&b;<C׈NgjW.WǶ5~=7F- J3ѽrawSueAnBdnmjρփ|:ꃯY _cXwiޑ?TM~W6h\,c4G r72HuЬ!4H96liCVƷΘȮkL7qh9Wĝ"MF>럒j\3kB ͤ4;|XOh|fb aQW`q; uko[XT+GLӃY<^BPcCş rpJWr:alDo=&0yq06]Vf6G$8Ɔ5[X僔u/nDj,>\EC{sڭAA#W \/eͬKCfqqdߪa&Rڋv7Ct,& bzV:};-seY`mɜ'/S w H g:KT{;  ޺< `D\ Úд`X\dx*σ[)t,Ze̔ѵJ /z&OM?#(e|`jU<>Mp3A3øog8P-~.G >i"5K.CG[|sŕzgy0?wQ΅\&?0J:KYXR^W_އ7oW5M $Bsɤ"to%?b`xV/4pb֧u$ 6;Jrnᑱ Hl|T}[kek#CM@Qǘ@7Nzb2~>ml2H]9w}{J1FXBĹ@hġȄւ $o,8{i^6:>]T#907Mh;V~)Z>.pCe nAY`@[1e!>Ҵ `FQx/gkN_HE$~یiN (vcr5#Y`6$ӌ¢B{ϰ,*mǪ iDk/?=iMo ^mBiy  Ǡq`3i.޵,Är#c':3' aяqU=h/W0 gM Aw\` 6nWϵЀ.Rh.UQ}2Jer6 -֏V~8+HNG >jV{+ ENq5.ID[S"udX_$re.Oaq✈8 Hj|RL>HZ^^]P1Өd,Lw4c3v7Ĥ&tٖ QW> 2_ 5@ YGWf0{J8Z8a~ϫ(*U3]2ΕT0+DInO`dj9s6XkI KlfoF M_FJV>%OYҚ8|[Pu`GE4=b6GryWBqD:d1B{Gg+ҁZk \|c1'R³ʮ@.u<2W\#vE/9xCJꩼ>z˹6JJ|7>1ky>y!UU!Ŭ@wI;k~kt{,5 Q5fW.GcޙB@ey ^o;)NH a6?n3(*S6w \U,8а3Jwul3vL*޶Ts"WƸ0Zp[ 3lD|vݸ .خ 7[^%G| e{ ~ "2g["deB5lSmPo$s [@aD,u՛*PjN-!`o Y,B?j^+;BpW # 3MG /+RBol.fYqb]R4婡Bk@3«EKj׌jgv3;k2I.w(*|_tmW3_^Z]^*0B9(F <+QX ϪI6a9Jon7KV::A7"nȨQ)k_YcE\3hN{xXP1q]-R63P( ;Lu`v2u~7 x@zhzm;BY6dD(e;vfN- 3БՋf'l0X  ZŮ1b-D'VK;ƕqrC $lCZLVZ"y ``);:LJ3푈=Dߡhoݞq*V!xwl*C42cÿHnҙ X5R)q7 FKJl*sp*|\7Zz}-==b-Z/|[S͛8y’jV޲Ӭ?TeWҀ >}}aYD-Iy;xK65f+.%9_;2pvߔ?ViÂ5*.W (,}7.痚,˫L/xIdžDcs $svW'0n[S2CJP +,*8ϣDFNgC >b7?~!XͯLDϷhiwN*llliʄ}E;2Pu93hFeFRTLs%qnSD(0:™wmb0d+ހAʅj=8O[')X[gI}Z\ZfY(My@G3Ivd\oq~1˾|-Y?W#m#|89> 札5[jQ2$ͼ);x.BfS N U ^7V"(l'߳͢/xw5c !n7L^G3$x[KHC;?=.Uy8~$i:[dK$LX ZLH.@O|ޖ^ԖZ7JɳHaȣ#QR ޛ+r5c.{ OAF e~qS~D]Fh͎ފWG HUDj8MTa  FM:d 4wRPX' xMϢn^LN04SQdKNE2˙vp ҃FS c];SO-; u2 ${5:'L XW3+ j@qةiR؃kUyʰt XC{g(ǺrIuZ]ezw8 ގ$;)~h\͑%wfͅ #11Eg0*=Y>C6.h>Xa‡wcoT7=P8Bɰ???lW_Gxhߟ_2njsSفOJfd5E{v=A4X'mGe~])1:+Y=2O3Ew950g]E&FϿ-a%0,h&qI4a^1(tNP2Cc(]ŵAV7e#&ErRr^P=dI2ݭ }8}PdƑ5:D:WEL ~18bx LeBm31Qw 9#5M<.cw:o$÷VBy PA"՘{L z{0/YwRe6O#v$&-#(5!XZ />䭊;@ڝ7NѧkEdø=A& &DSŽvб٣dK-h.a u.FdG%A sT32~X7W$< HRȤwC: F5 A*TJ |uL,x3x;/,^'ψx~d9S}Cj Q>BT.`uԾs;/F~׾sTp&2;Jacw3{P(KGiWaAc:?y[DCIԭ{-X9DD/eiKʘ8k1o؎6"O\w >Lր?e4A&p?sI|JM`Ta~ֈ<´ɝ:XF8d@m2+8[+I`ZW+Fs'ڔN񟽻Z(F߂R1HK4V-[Lzҹ4!_ه^h8WF yMYZ+%ܼ,P8w.zU>[M6Pfsiɼs庿6Rʖz Ā@ o$ZrI&2<J5Έ!RطXdH30N(>ug[/ 1b a_5Dʲ\|F\ЏӜez:&!0wb)CD\08Ihz&*MBmԭ +lP]~B zd@SSNe f"TIyX`̙tN5|( V(%2OPC2G.ztp-BZQ q%je.v4WRi@?ӧɳn5"[r;?SHnNkJ¸OEQ'2Tmtz+!>]Oa/C Soy#ë͉}-1GN-Ɯ&{- dok;% $2|a#QR{(&KClC9-Gc6_o<\:7>~h0gRe6Йz2_u+=W+S c+fo+M@W3=pʼnfa4V|w÷bgƊZ&+Y+3AZÊ^Bh7/yBUmIFxٸY[d+:6w_vG3 3F,Ҭ1*H/Gp4lJnwL[Oբ&5yJ Tv>.[]ks>rvf}cޔMa;ؕ)m%!Eků4;+0#6]BF f@ &VO!Wv=o-+~{6q DzLw!'CB*Eͣ`G[!>Fer.ͥg8>У7_΋so_0;!qYwzPT#e@aTߖ (TF# }|ts*͢;a" Cؚ14 |:?Iɏ'ʇDy@~UE4W_cmiqxݢZAUzv>9EJk6^9. !C57 !\b8v#+'tNo g4oҘWIsÉ9؇"u+ \bȺEJ`$a5Ұp ΢IVߤ߂sRtwOpH&%;[fAXO#NrFd:0dG?G^IIjm?)_9W/̮s MGRn8"DžKEvYDYNP QRYMKIz.. ܱR s0ʇ26MV2ZKD$o[z(PeG7iԄU7=}Q (q0W(|-:lȡBr\ݠ!nh34eUr+l-PtR&ٖLB/Ѐ)ցZ*c)#3zqU++uucXWoaA> Q(q͎:lt|P}jm1m?AKv댰A\5p@ r[@WY%ںd~t+c"0_o18FHD6"i/\`h J[gnj^ʓ3s$6ܦjӚEڡ<*)9?PҬ])ֽx\E'5U`>"osoJ +L|^i7%AT[N4ے}~Y.gjs嶥I|/#G %oB+.'g{ {*-;@L1>0r 9PfDloqk;DwDX 6}WQ9+y[k,]CW4*6K?AȮcѺ. flzhC/TQCPu&pXaM]f} 'SײgQ.l&ƶ%'l%y׍b=hhD|7P3,Z]_SCAW4 jDS u7b"VU&v 媅B Cѻ'uU y d^0H8+ UUT砖6wF'}vq$УzP^/MږnJj"qm2Y9jM4#T/$ExW[X)Hys(K;zN3sΰ|As,oTq=q[alE~{ ,ٌPzFӗDioBM.?&c/hѕqWi&xQ/r!6+Ad 8~_vדx`{kA'̹Lg-txIF4{9‹WAi)M lDrN#;3y[zMQ2ĥ(AWb"+*D퍋CVvٳq|uJYV5VLK{%(>~M?W>۶*IJ|Z>%n= p[<,O \1=$;=F_wj%?铫 *0k djK>܉DMUr"@&GmA'9x%M%!{gW+KͱO\`୭-!^yKt9 IhHŷ; X)bA8 e2s#4dK~vll4; W ¸4G@m';?iNt@ANbX9;Ns_J?jq(8 ~]^MUw.)V r˸X(,_sxR"h<"ڕǏ珢k$^wHUNђ se# ڈϮUة=C&xl; QKK $@Eeщhnz;|2I31(!f/9/S BPWiPt&o>VrBn}@]X#M&qࣃ LpMtUW??՟,ٿee~jߟ`ߧ48S Ά ( _ ^XZmz/L`aއa.'~^9^Qc@r^oDcpGr ][3ɾP4mA!R-T33mZ:PJÊi ~ G]O3!K&ԢNk\YB/Gpfoa'G QH/\7V@5l wUGSٿ '$Wy}g/ R]wCH aN2&`KEC^twkZCY͐x IpB_4 fb^JKc_kU3Qǂ&9}ObU=n_&UЃbN i5Gm+v,j,O7Ff80}r:GR|*>:p\)Y5V5 xȌxC%: 쾜iG$a-t%j 1aVFZosB/*:`##yMNzzd7DZQ`; '#v,dN757vо4{I#8}vAjVZ&nE+"#| KB ,"lْ-x`aKlNqժvP7bчhQNKsn eyh6]n=Mou7t[M3TʈlFgݠoh75r}OP0z <L*\`|"BOc2%OR~; a JS;FC="ah)>-^ЧRBE69'q@7sIr2 ?C^TeYfs(I c(BYo9]Nz>d9|ԕ40Nɨ9PBv320ثCZm^t.\BAx;Q(ith!7,kZn3˜3ٟ=]Wcx2z'T?;B6Pb <-X@K>$[sM03.tKz\r/Yg Kg(.q.7\p_׆2A^@DAcvβ@F+wri3Bmˋ>E?amnɻ$5Cf u8.9U+x"_%FݚJ2[ w1~.oXLpذ5cL=vl*z=!ėF1Mz6{X!;p\nZZ?󑔛M%YJ'\:i}jx j_s*{M5 :G=s?{roщiGoBx*Qs76gof@)E0^wf&Zźn;& hK6SyLkKBƈj0_хVv; ):l&w`Oz5uj%|&ME~r[ !{?"ۀ!|& %)՞Jͥ_ͻGanE!@PiE^ |tKyQQۇrVOLVzC~e=J"Bxcu9s.e} `X|yEd2{E`/phK~NG]G]{+i/WI>g!3R]Nc{&y%̴֤Pr۫Mo8 L3r_CԽvu۟{aSDmb<I恓.1(4;0KJFW^7s8w<9`l\.G#.$r@GDq &1GUzr.+9*@Y0%i0f1 T%:.JYA,Ro]1.+IgI_ߚ"x0aJ(p0|ArV ]DG>󕇍Pڋ%ӉÚ s{[$u{q?*<> x%s#E—mO^_dJ~sDY5\u;Z>W:)Ma wmaYT0{")4/:?&L eNQDVG,l ̪ێ\}FGZk3FkNӃ!b!‹.DtgںjGӫ}*KKnB K-#9m?٘l7Gs'y0:ߗ63e<+!Y݌2+ Ъ]~5'!$HS6'&r+3c;l槮.OC%ePdѳfEsU[hmջ-JSg9ĻJH&-?zl!^.iѱy({SuE|-5iC̖4Wx &O\Ɍoۚہxlkl;;n1jqSEye[Mn'Y[[Ÿv  ؒI؇S3H3Z{%@Ly@W;mI]ȅihWf Bup*Yn'uX(M &3?U6]VWw S9~ {EZZ"NjaW6M)i>rnSI9wא w3NBVHk\8ttE)j-s&-5gї )cԮ@1>p9``#|ф^Vu=ZEdCKU[ sn~@rY",5ʼJb٥+M4ikqVuH`8zk-p_oWՒM`[b>C*e-ĐS. SA|G|1H C? KTJꟺDRհBT9OfyGwЕ݂K%K#shodD.!Alf`$VlFiuD}NQ?G\T4LEuɯO0%.tc`ߵ]OQ8uPzz,I:BHc8?tXE;AWa-yƹ5L100'W}&#z6>Afd @mjR "3](H?"HiϦ)!:#wRnI/3ts3k)hzU CԘaVF3sƖ;0!5uǚ|\ÅA $ R& NѤ'Z>L`Q_rSe8rG ?,Yw_30XR!0Qc錫2˯o<+;I; lA%zEzϷ\Ϻl:wJ,.=y ޸Xw6pUc, De]Byڀ,d!Us>[)i܃HRW]H~qJnMYǹj1!2[F(E6In/ղyx1oz6AlI(-F8'h7+u~R 2 BƄOcQ}T_?MΆ>ض LfsqSzN]xeP6|G[+%aX%B`& f9_B6uRo*ڻgwՔ2r;5;Pd-9|^ؽ?W&HI큡ٶ 1WVLDJQdYۦt ȉ'%ۿ}SfA)-X0g[9aovakLX}پ!ñSZHGAxfou gI<1!CCڑeD[ m)L?ۮ R+c\`v0n꓋/\/B쫑.HwĒ 䀺oCxUW.>B⯉܆@zbdrJlP{.SZ[K&m}n$9ׯIn/WG9eq'S~y-޻0%qܚۇ+d-a*MFc-P~B<6Ё?ͩe\b(\r!Q«*sff8nR|J%Fv|4f<пAʟN v+NbG(IGJ{fy`P{C#N l7oɵ e^?*JYA\5BzH͇_ X%W9&5`}Lesa ЭdM{2NWspS;@G0I2!1k_ 3PgG 6Ems+]2#59FM}v'{hc4h\=x*vY+"ߴRJRڷ;BǖtaG(c@Tۆs;{ԻK5Я~g0/piw3)p=9Uv_H՚$4YYgv<ۏW'n_uYtlG* Ro؄ᎪGܲҐ(٤6l^;LQ5DK+4|QD.NH-M<jDnl3=4~KCKm n ^2)o6FgcRb}Mz:-]~i= 2vL C;We ixS9WGiɩ;g o C=ңMeĞ1\蚨vЉZ$eˑ[4?e^_skOpfWo}R-ߺ fYsrxlS\II[ZYB,RT7MFn=!:1@䴸_h $F}#)k&] 0ڳ4P2fj(dar8w`6l%({#\~Jx-%b3+=Kz6 d!۲,j1 @է^t,^1z{ey۠[xu[vuO6kzWI`ȍ'Ϛႂȧ~!^̷5/J[3`PwRH8tt}gg8[v{Gʄ'^&(w,pAswo'?B&4, m)O.a1Ceg/9J r)^ ־stϕfp޸Wp9ܮ? %*f{Sdo2۷b'jH舮oe`x]ܵRoW˲fֽsw9?X\6!,]+u`qܷ}zO&dΤ4EBu['6ʢiPr!'~\qΉ*K*stx,A1LiHE QV6fōfQ$(}>#>o-$a8]~d.uT|O@ f#pM5'!Dd)dK<} 6@ ͛v8r. UE[e6i$,frq" XɓGl 2q&$LY <'a.Ũm6o߲8:R=L(ٻR-rhZ&J갇4DH6[$}z㰜aDA:ltdLb}DOZ;us_j:&؞߶bYZq}\:{fXR@@6(hLRFgiUZI T(*kAz*7pU`a~AӼ~27P6_&߷jPkAˈ?(cxGZr~4w0N!K-ݻ0H^4ͦ5`OK-3[ =q9?R>ӌ߂QكV@}9#{YM+ |˞S]H'vK_2+ۉ8fhTf\vFQvCBp͙}{c9+>1`αU7btL#~9+N=yPSŅƨ5KcF266Ұ 5(` #qWXIl s m*:Lt&вx]m#_sS_ 3}Xo}{̬cHc*IYyОun2rV VfYk &YssoZͺ/+R_h+Á5zQQ2^ A~[m ; ,{)eX=UG#_ܐ8>{B`9tCM!DZ='vo v,|{)n]96y0b9T7&F'8A+`\975ܬ.H3g ߽,V8$1{脪jc'ɴ`m>,vwLP[P?dQb]}VK狵Er 4md@妎B^?Ъmofb‚0 6!b)Lwyw  54r*&)&@^$ fSjMdoψG<ړ<{.[_WPJc]]1 yC6ɖ?#ML93~o x*Ȣ}/^8?/iL}ކq#{z-u؟|?كV8ܗGw454DW63]l@!a޼t2U7xr4P۩Ƃ$j]|IK"(Os&,tSیhIw]['tH[t0pի[2Gm GX@Hvb?hFJH+Kp=٦c ^;z$g/Ai;Í(;_ jXSQϲ-P7v(<)8|-`ѸMr:ҟ787oԗ9 )T  /,.AG*Q= p_; ~m#w+p;KIrJ;8'Z> bn?Un9`3D8L8- NQ+11.^{+`N^Zd/N,:2~?'&,8X_IO+ T[PHk؋jRWYwYR 5af=7D4#减x%y}.L?F3ZZ ʂF-|4RrQ\I+F-+z+BLv`7֒E(|&B$Xkd1"|sgK5Úpe-1Ԯ\&&U} >;4b~٫Ch|bڂhP5R{(ic09UF jP ftypjp2 jp2 Ojp2hihdrcolr"cdefjp2cOQ2R \ PXX`XX`XX`XXXPPXdKakadu-v5.2.1 TY@ aUsGڤw\58N=s/h0mav;e:)=3o$(c*T3:=ێ*ؒj[a):1,DEWP>} lV;x39UُQ~]9",mawv>Z鰍<)_C}K.0j1POu<m̡>6MYY zK`Yh(sZ`3|¡0wURS@%:hTqy (@~א^z 8OrB8bpM ::AML%w?t-_dZ.sKYHHE"'& C8hxJ\`)ۢˆ+p?mrʱ-"PE^yX*RN6gxW0!(MriSjr L=)xr5*Wa@d\׍,`_E|bo$Qejc"~/WқʕD12F}xT`}p xXG~ۭ{Bܣbن 7y>w`qa즭!g|TEM}U;!Ǎ,;g' G eTVs] 7^xSP(DV0R-4'g^$ާ)YQE9 5+d{(f+NvəgŤ]v/rTV@ioZeaNn² = ?PFu"g I*s lp_ f1M wNf?薳YƓ.(ջ;P}T ma:եΉ⮝G)Q 5.IJYqǟUZB-z5|mRb"YƢ'Ϊ-ڀ.ψod0R(b̬O@I}oQ&dˏ\ Ք7mpߐܫy!sqT=$Ӷ ^dbޭ*\hO$`I#Z !([kj÷Q|m;(^~G` h{`t֚Ja}]:1(LQ˦ "q4*Ϛ\$L+[4۠W؀'sxLxr4L>4-|ly;4Z\Do>~˻#v<|y0jd$̡i g 7.2D/F>1@06 VP;:>xYpș(o LY@aFnN>$ٓ=cRMN( _/A]#S2K 'ڰa:zgOY ZdXH$ aT,ċ"]W ewiHaŝ;ӢٙVCmx3 =ǡFŪlxH^[#ƍO/XCHΑ]:6F2WC6极opmV!!eiL*W{7cKaXOf #YXͺ X .E Sl|t|6:7fU@4[P|[QƸAWn"~*y5!(9 i$QnT*8^ٮ@ʳ.p ʠSpvJFkgԨ?Jk p93#W4-?,5>bPq+km i>\*||fwsB;X1ySzNNKۼ>Nl=~]6WLhgL".YiJg®"ϡ8: @-_rw'BqڣV="NR3l94<Yݐq4$м!5O Sk V9߀dIb2A *Z2Q Vn<(Tv*gyp- 35W0{L8hdH/Jw9 BZ_)u=$tCz R[5 H7ɈxG'Iu klO5C[kKOֺ&uM ?[Y ;QR{MT4ǶQ)R!hef)_*dNͬ5~.m-ѧnHpW=gdXkr3%Dx;7d:ccڢ鼧hJ[;>oIG\s(fqAJJ Kp|iK G T[_Wp!\!``1n.86 g>=HcKQ%F'fq u (c瑞rb5j0)|*ۇ|v)Ԁ(D:ֳ^Ւk= yc.[d(+MJa5D+eтZ~>y}y_,־gD/n#&LW+Ul׭vdRKgS^WP U&3+<)Vb;EfBp/! UeXyVFV@FXԝ^ԧ!7pcQcF{p,h|,U;!G4^3dd`K!';a^Ew} pǩZL 7w ק%R//Րl{1$rU A'uZb{ {6@kЭ[wR̦H7yDeG@@bb.ZbA2Gt嫲Ҝ{0}Kᦲ7S:"q&ϴY"t BtU(SW?Qk\jtV̭4\VTӔĝB雫=h2(I}FC0i- ^Rh=FYs3*.Ҽm0r\1r ;kZJZ (;xK*F`A\{I;n-ےj"{ྚï;v qOi6v`^Ĥ^n )5=/rHҢ܀qi ݃rm,ctزr slij J p:`b,)P<'ðҍ; h¢18bkkDr'4UyJ:^|𐖦OIhS"IkEHTBŒ9u]T!& eo s= m6y7H;NLwŶ_e7{qkŝC#FCM6H:,JPXb!}ԄlGnT Ӡu o=5`;IF ?Ƨ 2g*[{D i_tʃ8%f>i礚g毑a^+as z$N+oH `3XD]8YlzH4w5$ Cu$0Kܤ-eQ (宙* U3c{&QҬGF9?=4򼘇jU7=cGh,-x"7vtЏ8.LqZtIXq?aF 'R[v#L0lLapΐ~y] } ^ EI|Þ݄~_Rv*s;yVժ,Ar7a?Mqфy@3dpYzu9/O+]c: (p?.DoiE6f}}M 'wH,L-"w>怫~{M=3|&8Ȳn>V /_<;!nB=ep2w]Pk{;r '6[g;a$+,dݪ=F鬞ƴX?g >jLMTOcxRA9\snu-Mxe]>\^"޳ 1ΈZ8MP΅KNZDPzG=u냯NU$zϙnj2u\TC>"[7Dv 9u\2F+: zμv|Y~%Twi@ UlocQ@>^pzMȍv>Lf:(4;ӖVbj %`)]H pt$ @Y5cvǨ1u2{>1k0 [AޝX*a`N9Ge qŗ:'SDR|׉")vݤChR>񅂒lXVy"$xxwl+*:(vВᒝe:`q"+ξ4I!~5cGQ1S pɁ@b [yERХ^| Xe<|8Yn&,Ϥ2Ǹix38ca0Y[b-$Dz,WH@|lmg24N?E:ّ315C ֡yv"a?Ϝb)ilY,!F'[7k@jȻw*nd?Wu6 4xf.NƢn3S)?{8c\PvvqlI<-E^? W|Ă CX]>r)|8Vgˣ'"ʼsKi -ffB:)X K65-͵U]qo%Qrzc~愢u_$]/I?asORH)ʳ[9I{h|okI @Z3+gvϳgW7BZ<7 к36[ ízRБ2#8Bgd"WqkOb8qNK{wh 0=k8Q-Nj;[U ~ho{dd9T.-7gdG6MTK*PYk%VF#шT3,S˪i*hܖbhָ (~2P}v?4ݔX:!=4uw˅b4 uDtTPMr%Vǫq0 GrG O*C*Ҿ]iɟ"M$裗}{OCK:lA6]z #݈y!!wN׾ryI^5B\ݏ |3ؤLleĔ}T˭{v//PGT5v:~5Bd Mqۨ9_f!Zh؜u ?ID@0ђ;P n Qwy80pr, .Z3̙U>Mf!iP_Ǣ)`FWnP*2O%iU$8L-;*^RpsXR`BA0e뒁oAK 'R檑_$p×$̈|tkAhM2O'mWomF֝Zz% & }N?VHFɗr5cuVc|bZ\@Xa:ږ~ʺF _gH ab5qruJh 0RxzڥzjqHyZIOwx";_DwhfLu0L5$>(̧p9efrm騽>R aC>kWkGH;Df绘$rs$x1flcVN8h 1ٖX!U77b@*6weʲ Vv?A$7X_ ʷ@27nFcbRRQ2.B+<<3 % UW."7A(~ E>,PyxU*fdl 3 "*.}͞4cUcw W"|*5;5hMb61]a:Fr,T悹Sk3T^w'3Ɵ;8So%%J9,x!8f &_R[5IPY%hW*jw_tsX3[~k9%NjA>t $fMepD_`X79"K\(m]kwau^YxuB>8!^pgjނNTrtp1ny5Sxf(y](?sp?T {$coe{8{' sl)y3GAᅃGB*c;j`C)fK/M Q $١\78&o!3y[Aq(Ј?ΤaJXx5eV2'EV +nQxƈr!ͬ[6ةԠGpydbg,lH *L7 ȔR}uErcKG]r= ^Ye9% +Y+0&K"w-QfJ Ek"كKhL41sl_Nml< 24-Cɟ)oӻ O&8b[F3J^<*OT|bC5EYZٙ>F4 3oֳ_{o\,"W)rf!1q3M)س\R5]|h NTӥ4dѫ_{_24g1GaNDŽc~m#5hfS_QUX4HѣS WNjog4G>F "*c'g]sOaªk>cO\HTZwOn={xI9Nkͺ1 l! "mRYhP{:vK[=@iW~Vð&zCϥK:]Qkl{W7q{$$5 U{GAݨX R)OBa[͌25e$oѤG!*Dx~VJÛEˌTi=^@_5a5}x͊v jvD6e^)D;|djLԌ8<&uswBNKT_/B}0<+h zC Gh`N+E_78 pL_Bn4 or"laP扐t笺:OVp_;~r9ܛQbtm1 uЕ{~}seSth1Y(ϪqBhJόELfe`0ܸO}XΡ-o zcSoR{G J'j :n_\veo=}V/TEx7BX!H! $W6[/5rt**W B I('4&Zo@戒WD?'/eI鵑1EVŸc%42/bVD(fo>ؿ7o{"ŏ'ˏ'Pb7 Tv4cUYLǘ͕+?ym)/z:yfz[XӵJ >0D-}uT̓Ty/N&glзu&e1jI:#"H.Dr%#;Sɱ-晪Ra ? )kLS-'PjGv'Trt#@lLN ࢠR;*27SY<)g@ ƶyO6͚=<<#;zmb\9 3de{{^oQu=CM/Uq#'XƳ 3zik8־8GlM){%CɠGezgd(WuP81:pM_4EǸZ'-6a۬T9I8'x. W#F4{Dwln[j& ̒J>"@ 1#c"q3E)q|. %_^O oh,+ 7Ľ)6zȯI& yU}'וٟ1j| H?@B8T<0H;*XgDs.aݺ{rW2V};Pc_ܥޗ% @ d<5M$ի="[%Dz1[y(Lt@fR:tj♖&|e'0wxLz] *JTLqA0咪I7r{6!Zl,%\X Aey,-, 麵Ţ]g"%fU\ ͬ]$M赱-Ech݃tsiזgGC;J,J5 YoSQҗI ^7J%`![]k1F%/wzp#՜(]@G \my[bc ݺ%0\ ұddL1r6?C:mal*텁nfNf' $9HL:,4`E)ɲ53H`tM943+(0o1eCtj=p?_RӑV((NiOaz(CeQúUEӗ>m*$9ˮ@Jjy&:Wywf@3x"w[6!o 8X̙s֟uhI~VDW Ü9B9՜Y>X>)݇0͋ljjV߸h*$D1=dn*HQr^fk0m`d a-vF!`=$J8Eƍص;ݻLP帜t|`._7T~Zpm) Lpy+*x%όh0w P}suc90Z!?6^`=L[wqd<IWeE󻬆_;GlߞWZ)Qb<|6C-T&|lO) EV tƱ_'['*y_ǖf7I-픚Ƅ׃iÎ,.vǕ'DŽM+4ʑ̓ О8+?:3M4vrt-#zh(bt_'wC&yBiA* ek-:˟%WJ >:bcP UNfyU6+OK4o!@~!ExYSU7Ӵ]&#- l}Ͻ4+y *&^ll׮bYAVChYȢ3,.ۗ' sr7 e?$5t^2<;MͮhI0=NvTHF^3@3 '?r,0^ɣTť6/Y'n_%rvGAV?oXЩj&#_&vϯD!E710|?8ܾgZ'Iq#_T7僽Ϟdw)xqhG%,R(YƠz\)eaQ ՞1G~f7r~u8z$ )k<)=OjB Ry[D+2+ M+֗è N0mQs(Q~5-bQ>}I˧߱w<~;qxc=k!yQ\0P ߑ 2V7> Ѥ=J#끷E}*含@ǒ)\-p)mnbi֯ ;[U%.= ޏB?/DGn@_~%2^)'c[D ܣLßNu!b֬eGCVNB;uyt3ǣ1pr:4٣75 } QZ|Nn֚zW,HKLA$"DBHm]ͦ8]X/!wcVh8Z6uEoF}SH~6BP"Ǥf2:*6vmt][xumT8s_D.M_typ&W*OpS; T eU ^Iu׈qK@ȊدNp'<Ӏ7>?hFeoIʪ+5<]D18dު4}RvY2wcOh;prZh8${4n KL*>ZQw5i<_3||J~SuML24+j[hѯMfޖ=%(껲(MQ֥SS*zbyĎ: 4(De҂$x-Iҏ$"6^{l,xUY: SWGVll?!&qdx ɖ2/;}}5mU&9N$c˿X\ʏq`TbܘEqVWvW3GRs"Tn0D kX7~hflW %+Yf׾?e89LH,xVYXTZ1uk8\L*p !yq 04mVLFOx2c8;b:|!T?fQdվCrscAU6,qTd `a>EG:}x86J!2,Z1:tܕ 8y_e_慈peNX};]&߬趸*C()/Q$"^+7C0@<Mi]UΦ+TS6!ɛ&3Rd]l=|eEDY'‘dqedb'*"FOhgl;0Kgi8?*P9ޝ!>EUX.&,>#)I dXgו K졎u y-nj*F,F`&Omeh˱%R1<ڷaYiL'p1>JVZ+w =8ޠjvkzvW=t\.eSIjf!FzZ,ot86 e5z2t\]3=$\!`éEhц02^HC@7xTLx{ zE?g|l/MncWU ˰w{, Kc,VֿB= W46>xLsLۊ O m_+a:=D}9* %‹XPlffLu Hs;}}`LzZH!ͩL=+VOT_?g筷EL~ճ寘6g6Dhj+.3gK.4o-7\}%=m^I6CC!NZqG:C#aWLvY^:WMl\nmg'5-aRqc08bk8A<`*Xxjh$ChMUgaRP)p͎O5yn5%a;Lz9 Rd%PW NCR$cG^04~&=).5YJ!.K 6˘/ \t{q~2{!m Z;7u%m!uSoz$<ʡokv3ׂk'IO,&:d (¤2$WuEHH"|г>m-s.q+iҭf2ڜ{М%G:Z&^4?|x*|Rm+rº揚JM"iAa *2Bv#c 7-<ꙴ+%г=]JS-Y,ZM1D[=ywLr5T8\T^Z,ŷTZgϘXH R4 { rSBpc5G:)WpL)hj$Y/Y6 zM9C^7 |_ J/dejy)U MO5S3'9CSϞa9:X+JoFO &jր7q,۟: JەL~@UDqoXGGZYOLI hs>% b13Ш< Z*BTLP/zq Uh%T\08QDC]Ïҿgn>0^hE}j\5=q| g }QcLR C ;1J`n=%WLt3_]SdH+RyهMoȋ/.U<ΠE0ySW>q _I|aC8t!Ý|#L+;K Xq@sG{ےCϤ VIDτdR Vs0GxpJSL:݄t( %# V}f/p?]`bv"77V *",IX^Do}G>&X#;#b EI )'4GxCQhԶ]8\ k-3AE$_2#9av17 P"6LXWؐczO<>Ij |$|Ƭ {̏1J ֊DY8n4=-'ѱã@Dw*>T!-rto 2#19J}ɾB$/IoG96Q'Jqwnw'iLb[,m9ޅVįep?&5dyNlv+DPX$"XroRYکͺŵȀHu M^imu !K %~Aߤ[dINjwnQ~ A$w;Ǿރ/\/xC?wV'E%u>tEo8,ϼ + ]x&E|-YJܯDIrEH'`ڕ[B˜s+DWP#_7??4d߸cO mӼ̨<^u>_8&:ʦ.zo̍zf)b97$g^iX&Bkn惊`fS-A}Yj}Pq$'4W!3|ѲU[J9>Qsw B|~ sD Κ!jB ``42w; c3X12mrHx2IcC7#Ux3P ޡјn 4A͌?n'ڧ#/d'aӜlxR+L\V1\ Mh)ج{k0^&ʥ")$9uH}.XnbHRJXCp~gPQ۾8i7Ӕh׌Oi"(mH qn6E@k--'4ϿowkvLTDEz> 1gMLNUI{R6l*"6b߉.Ъ>E 3q Dkj#xVca^&ۣ'!k=×!=NtWVax>-W:~뭼'BQw@ NG{z?ůZV|ʃV2$ЦxqO}4m.WL0}_@AxᅚRW .)K{gOKTS׉!%F OGI,Ůw)%eoaa&Nm˅1ܭܜ{îJ|XjCB5븎 pAIdYjhIݯΠB 0&Xrv!9-Ybώ?,`9$ u qNmi4rI6J <(lj"3ӣ{1&^Kq0%D_|=DkVK=X4'EPnyWvdm/erN8\ڳG{\Fr>ޥW"n<)xsŘf3G1̓g*'jLQO{ڢ FOV;{,IZMx6r&ADi_"`O}KGV`⬩]ϴ#ߨ`SC}ǚ` O[ bm{OK閘BNKqeD"pnufG'\綗essz̹AKe]}#rjLWLnUٞF6(\z?^` ߄K2뺄Ho~ L˔w) ";эz2d Dk Q@Yj"EnW3'-LHPu|Σ->eC#{%\@J nO nW:7c19jl$^uBit w#gꀤN%8KZEw~}!2EޝT~XEa<9dhF=^TV`"{@R*AN}/Mq=i-}6aMtJBayU8I*vp P!Z ,ۦ{7Nq t/<6  ۂ)TQfw}BX`ZZn!ReQq8^j# >TROljn2E3-cjˋN)Opgfy;xQeA5 *ڢ6z8$/3* O*`(jȇbva@c*M!ԊpJPs wnBM+u=`bZbKz  & Z&w_nE VخztՍq$:IXհTI5zvLHL06:mv#aŐl+$lg"R8_wh\e`B"?ׁ\6"—4+V:2±-FƳMD:$SB;R-IC&8>Aċv/CSi"5z8F<&Imjq<)~Ȉ=j!&Kq*>K9"@wqrtV~ds6 SX"%p;c6& s {{Z `(5|#O} .>Ŋ\Qu٠ V&(;gy9S9dsdGILcRPaLOծz] P-X붑uIT5 -h,k 4 j |A d}JbnA.r\8VI] 3Uɍǘ*{9 g ~{rElF9ee!(\7׶FѝiBA!w:G6e^W*U=+F[ߕ;J"~ۀ-I2Ajр9::}j^CJj'𬜽^5a> þRc$n?}/N[Zo{O Dscs ؜O-kju`Yr۫F;ݟCmyKXA9IؗTUWΰXW  ;-rwP]'v1i㲋/#RaTΒm]Fq-V$p_evoY:kwLVmR[܉LgBۂB @+PD'`AVzl,~R0-{hWi&~.`I4"Rhx^ϿEg3KPC2a{?)L&ԴL0]x39 :{}wNRiq /Lq;*FގOߌ mF?Dx4K'T> ɝE#HicCT 5-y 5``'awRbyR?QT%#Y9*6w9&,y~Hm~@Չ邊 $`хnyo=ݩghbbsq ֻcd@!sX8=S MwV; o3+З{vmv=V; FQ՚P} l8Œh {evKpEBd=Bd $oZP"Ѫ>C& MJ}G3MOJv"/nWl$9rK9:OIbe&HX#_o(6ZOY8? >, ĩA*=.>?V|iܳҟ"̇4pϽE?CǏ.d#Aw,fHba\5wv9Q-cDKWm p-#w{E oI]jHߢ+õuZk! ׺H:a! ~vDVAF>KI8GG 5U'dTMY$w)RXA=! 87ΕjѼ#cυЩAih/ hKpP!`ҶehxiBWv$`q* ؂0ij<*5i]pidoN(P zB0nd(2z [e i@oNu%#0I5zVcHP֟|LGPP_/+E^u|6?Y`4?JaA[tI:jM-&O$؝H`37E\D^ЮIm(+qI&8S$kX󔿾K k:ǁĉ=9ʌV `h愁Zq|j! m=P!c^ֲܚ vCMD,M\n@]x z9jἶ8_^p;J=?oZ1Eh ?%'e5IMpԲ-iFG{"2%aJ;r Ф DMTNroz. 5A3 lYpRɄE (/5Fb-(66Mکkose bШO,?Q-sQL^BvB2CnDw1 XA:E6zNsbYwD &Լg`/ρ]70iE ltk(g4ֻ[ ⚴nϓ_?iZ6/P땟G.Bu`Z eIDi.rOe ԜuHr;HX23skl&V⎙&sCB(r"y|*,gƗ$)l%άG*xؾ e~\ .}8>쭟.pT|IDL,_UP?.Kf(M{5i^4en?ꋧXltp\ZH^s?ګ4Xi[lU[o7j+dk |dMzmUC` !_đ5Ǡt!I*K) 3;E 1"}&$+164"oG:] Dj@ҫ/b3;}#U*rTgak{;kJMz]O1X. p& 0sked*_~˫WQ1-„7%$%Ӆs/wG<`Aux2]v0Y}zMz/n11;XT=M%*T?@`yh;qҹLtyhW5\e1eJv b;; yQDd0֘v^>R0&PwW 5>ef XԻIzmQAK?sR@N&Q0 ^\!_^Rx ՝Mu5*6z7q5Mw=K8mn!GtmH;zZjk'L4L2jn7LٖS~> <+>& >7],Oܞno:sh+2 @_bƻ{sYW~g)k @|$6dA鮺$!9HX Xq>g4%GͧީP~X[&&K LφQ_wd|hlKނWaI=])L]F5z׃' ǰGO{[L(k b7") *2-3c%?ykT/^~Poy}~֟h߬"Nk+ !a cNcͶDn}apQQeǰL7Z*.'{yGxhaK:r{I%7# o>WJylɉhbVu>'D Y^|v0@6* Y&NZɁ?#K,A>dG]I]-E/)y#/)aF͡!d߷U;_av AM3V׿8 魧(5FpNJGpkA:d]# r͑xVZ?SwFs TPsXiAmzC8fyGN K+@ i#y]Gaݝ h]bM[vKW#1@Ll0τB崲5ޮB+T@>5NyĠ}(d,Ev;b%ͪ{LٴzkUatp_I`-i⁰'ĈWG f9yA$Бw!UE"fyP~DpսZå .~HIdPGCu^j:[qeqrl%z<~?Td>>{{ebEс5-"u Mj^ɗ Hbj~"a3u\t^ sݤ ;QM ]{mQ|hrTC\3<. qdq9ᰮ(?c77rtT.63LHhN8td/fvkyef._t>Z+{ɽ6_1=>~C:)9!h ?˝q@FS$'HF—*_ INroOƼS.P^5 P E?[%iykVu,@4jggӵ[0Z4C'ėm&n_)qh7Km<܉II>=`5eUƙ,ր=4~t&I"=]tA3 B)T!f:SQۘ >#si %mN|角 3(ߖꊜi {p"f@ǽx_ܑvPf z\3NS)>Kد 9荑ot[cm5F÷/umkw+^/];դ'!Fpޑ<!GWl|f&aTx6[.`B0^UW\2Ow͏fۼ[%Z1pAIXU&gdv Kd){Żӽ0ww0A7|G/Ƶ%rIw.IďoQ Î6XeaRpD aS[s=%У=rۈ&%GxOŹ (k!6H!~2#f3C@0 ◥>iб+p:UU%1h'Bnyɩc:BȐ%k@q@5sX#ZVbAU8U䂂 stƒ\{\)rt0`fĺ!k<^&?lM`ߌ>#?B!*)1ck& OL3^'O(AU<>CDQ4uUo}xK#T _w3yύ*g^i+˴cʨ' 5 /9y F s ߣXYre%i9SiO.D+3[5oB)Dv}>/gc:Nm$`7p:#լCw ˊ[oeƛ$t%hI|柜-=̧g`c_:y,\H 1*/8 9Ԁ\:Y/z}5 Տ]Y dyn:z:/VV;OgWBF`(}LlN0;p?m,ozPaܕ4r[n_x45c%UcɛR#CH9:|ު1=*z'ZZ5 liTrRkcv o[v<Q|{dMg(ٳڦ4uܘ$o6FAU"#bء<]:S;!{תW+)iƪ`cX5ZMExq^~&|~->o)YGDM|_J%.;d _bQS/C>0'Isa-rذґ]K q'b)1lxoZ8$iu/g!iiG9+8̘6!y4-EF>;$%%i&1DeSh_`\@fFZ VɧbJ*@0|qPMk(7f>an8[ vAaœ_^iO%Teo]a H^ r 1_M^k 5XWC+[%Y! =t4x{ ؊=v<Kb1qNXn vu]x̤gphZ58D#\hޛ.%K1f#pPYT=B tMb^l::e(L24Dϵ+ظD;bW:)YFz1I5[eڪ{\,Wۼ:LYK[}l6.&vɪjg.lrѓ~ŢVB7.eLtY¾ \7Rv~K8 %>hΫQBS;6 mHv Mc[eHqe(t?ά)b.6]=ŶF9H,`gk9 i^"['7qC~Ƈ ͗$a DF傉lyF=>/ _S< N~ 7K'Įjσ9JF(@w`"M>mH)sfWyWx"(5gP-xP2o߾BFuYS ODJT-f`pkx] }MFA!>9GvX-n~4u7br{  g 0*^HTSd@Lp@uuS,I`5nZKAΉPBEdEe]K47hXJݿ\Dfϭ?XRSƒh}t?T>([!7:8'BIQk\˜<@hM ya̦T/ۘT5Pkq@|~ʡ>5+htJ}_5N-.JU/KqX(yA^-rpQ1=i)2}1pҳM{wֿD䘎|1 #𼐫4 6f0ǯw ]ovO ܦWS΂/3Ζ "*9Ȝ<^*jgoM[P5 %]]52K%b*$g(~؈9&Kdd83ؚ&<rm Yid{1i=xg-cSHU[v*[Rl_.jC>I r R67v^{IQo0!S@MQUu\)= ՟I6O+ nS;ĈҸAMbsp<Ƒf2DY՗BTS*(A(ݕF[v\ɲ/)K&+0E1RX`=leWLy]Gғ 3LvsA q$-Q}ꕥJccO%ц,Q.H~͘ Qڛ$FLg?vX=K;I" ;O^_׀Hg M&+8yN+iUh. ضILOOp W扥wJ@ /Ȝvn,3yz-9pz;F-js 67d79Ui r-;48),ꐀ<F9y^`ۡN77dLܖ<4жF:dSLEWrcW[DZMC8e"z^jg2\vYlsF ǣ"*Gd7K}#q'Sy˻ZZΚzE< :t4kdKmjTaA`*VʒMC6N鄚;ڋB^SD?C=0xPRh2#QIUk3dODG 3Ě!f6(gŖ a%}-N%jHZ@G?7s`c*zCJV!8ǹ8U`ׄ<084>Џ3w鐆25vr}q ZAT9GnpҠzJ*r ABUd&5Bgfvk?,Gܟ22[N<˒\j$=(_yrpׂE/9$ ՌJ`͝>¶PK*ǽmgEK`^/&'@ 0۸k>ʑ}u}R uw|x ~U"GL<r6^R՞ϑ4ėEYCå1y"E tGgтX iL TjMG)#{L5kqT~؁+۟7Csp e˃~.0٠ng !FpÇAqC?=K'cjTR4Nf!t 56Eta(#;ұD@tn@V~[Y%=@^T˯Cmnntá 0 *_^A$Gn<JkIqFÏK^P`V.<9=Noڲ[:]L~V~<]pZuZ@Ng7Zu(:,wܟ=K { $H75Ȭd4Îkjt35Wg,;M[7"r(MQ#`O hVFw7x_ ,u37&_ wy4qꞳevb| 2WfJF :DEH%[+:u xu[܅ ;'\A&56qGl֪IGN: U+Ib47:LǦ֑y=8qk *;psؕ\)1AY.gRsXF .lnDa WTy O5 Uǐg`ssI0 :]YXxòab"F=x}PD_*/O x9iamDc7m693M|3e=H?`; R 0{%9i,SzJrL#:$+-3%BcKb4%Cjt+_64E.MۘAR Uv?dl^0EݰY="M!ĝQfT㢧}_$U*ͣFٲ^Aoqq~QlNVYqM\yݰ]3Gds~F^ȡoB(d^\79)J%-pf[ԛ9HƋddp,7}>0+\^͟Œ u'Wl1S;5W|/Pɢ| y kz4S[hA)-j6pNc#76Ɓ"Vuك+-l5,rIs+geڛnꭾ#C0\Mmo%ʀ0=i&{Am0f Dw[h7]=tQ>Nt';¾hQɷ@ܷcIֆ2WnNx1tk)?,gy'Mr/V?Z0PKQJW~R΀a0]w̤v =pQ}z6t=%@SyΟCFnF__:cͨd e:l|OrZ' Ru%k XW%cUYNZ Uд}" ۏS62 OW/ b0䀦\@Bi**j" w1T&y|FؖLɃ\fI[*j#[Y3ߴ$zեzgps8k\9Z J OʾYؼ.8Ƞ!>{#w !8]sas*}AˍuP4gl:PIsQ@DR@[g 3L^^lX8*Rę{?B0ۻ7(Z|sYWǵkV7xnn&o4.`{. wT:;# ɠO)4gZ\W<4yS0iEw-P˱~I"}@݁〥eУWZfp ^W{)GS+q`luNr&w%ek;ZTy(Á$/j69"x;&C}i/6C~nz Q"g]hRK#L*Hmau5wQpQQ,yR)c&0G8'|< $w8h )XF/Oɖo10}L6\J1/_zg9 JF i%+~Jsn.Ê#ZjK,a.WNkp V1J d)Hi^DoS^3ݷRB92>45n5ploUM}_pGۆUs=]Fvp{玅Lc1㺄ԡC;obpv@Sϧ1ԑ]Gň@Ѝ󰉀[w$UdڎrQҴZE+ݸk(㢙c@4&>Gi"Zg]ʑ3Aqhhީ'_=2eAw>7h%w'CyDjuC[DbNBo: 8Uu8U. Gy't2յ:(8y ݬk11yT.zd).SW)k9 s3qPfp4j^h8^-\P0 `t-'NI\V/V .1UxSU:Vf_3H]k ̚Hhr!Șzc>THC0PbwPI~ס+*iιf|Jl VOr$ }}i7\2N\%H۰lhy\-+1}jdg|m<кݻ\f^2$Yt:06^ڠP0.R!jb}fn=IyѾPDDA7Dr5[JǾ_;gcQO4D/0܉Kw%H]}&4HB 3]O!ަcW`g-ĝ7L.VhZT|OUH .P EQuR IM7PHzKz4,1RN3T0gjBahhp70,i(klX~dhҸ-gTCAW@A*0y,]W5cim 9/D1.*В"]p䊖e:|G"ӤP!(i1vEug^|;dԩpgi:>%m>`cvxo)i.N'{^Pep+`D9,h= 'gVrf93$?qIϧp\ H&^K{ڹ]0w@\1ʷyŤtfi;dQY@ӎy.O1r' g2zCB*Sğ Xج31w:譍AY@b7b%P:2r :ʼn`nҒ.Q7\ڋִOB5tGQ_.OFfd YE~S7;fT+opn6!hحyi~3QjKApΛ(B__(O0}ח "RZ3{ K0|\ ۟U/O7g3NHABC/qO[oުeQ{+:f&Lt㋘@B)X*2o|"E>eÔR p8hΫFhZU=`nM5 ZўHovfa@|!W{}Τ;e&nh{]T٦K (08$(^[~}>^o@@)9SPt": G*&1 wӑ~_ѯӫFّ0CTOH,A;Xh _DOK. SpŤ(#4xCдQW{nAC;CZA\=կOcv˸IKnޣpDȾci >yPD^}(V +l3޻iAIނw a;*lFqDU7aA_SޘFL`a :!wK}w947Zc-Ec(OsL e!*tc'a `dqπA;>^K5N8J_/Zd牞.(L$v#;Xxd+Z3dpNܢPSBܲ魩ňEg"f=:H\-4V5u;bƘ_KyR ~g\׮3+U>^=F1rױC'eۄt2Q-sV7g~yK!l'a=ם,YK΀jW_v W-, A-k}I,rߛ.CM"{V7e*b&! XFn \eOE"ة]*4\0/m4K4,h'GrnR!H㖋3+U<vCl6 3^J[ p_^A ʎТ|~FI1 C<&͙v@3 {owV@g~[FS+s w2iuXk 댚ZPXƕfYָ7AM:I: " JR {E㽫zA?hqSv Ãve ^9JiP6^5էD;4ya! (1#utrs3v2e/8wAD(,w1i 5 S΀K1u,Wkk4J5UMѧU}RJEYh qYv]>j|JY'gE.&gI|LtJ=/g@dAv+IOz)q6>I]fk(Ũ #D8Fm^ч54q&eL\kSe*H%9C<'"j2SL 3l܇sժ~[iȵG+QhR*rqBNXvM{koSZ%]]˼nŢ@Ttt0CMxO**<8t$=o`\Cy,ͱK@ b`Ȏ맡3dJ>5$YpC"]΀qA3C2>ܔ~A`gGؘ-JHe FtF+$ʋZ0#q]eGl_\[2JmJX- !ʔWV}oͺeTxug]{ZRRrkM^ u7Z{9ItͽA[tѾzOs 0p/#or>f_bkHRm-{YZɆx@&͕(,#<58&:w.: _T!=aֻ$u{0@}Ż9w݇xV9i"/yvU*f.xϴlAٗ7A{pȞy4AċB ,V7:MiH,Q[}0Jۺ͸ ƨz:zi4oRŠȤ6)1fNa mm}+Le}Yq@? gA߫m? HxR_7RO+ T[={HqDM~;hxbX> =+5%o[U TKZp=v=*Gi$#)AA.6Gl' ȡPݓC@X9/tC\`)Y#vU"ϾF<:) =q#=}rE魜8m_268H*5vށCgϫHc@Z۪SWu+e6o:6vwz?Pc> NG?fpݏᬟVբS?ᶝϪՠa??BΗ)v0$T͡)xkiy6g.1`2D=d`1D\<8{js ؚsS\ vqCgX{P9`\e'FsnTM@tl c.^ ʗbe9pH5TĴGKKo %{> ;.rOr1/_/ NS o:;}6VQNrW(]Ya_2z ?w Z<2PF^`aE5_ɲ 3zcMW+ [^涄2K:}%0:LsX}>o.r{28PN?nDf[~ЁHke:'kr вgދeသ,̂µ}?`}5tptW{%h2?@P!4ň'm{^EԾwѳyagH5zyLHǍ8V(_ ~0[-G/v@έ7Ei'nbJ0#?`N"6&ti۰EPJag${rv~lT ۚaz:!i|i#N:Z&} `A|Xik =gbvHCҦ6W M~{ElL0O;D 5& v= f<@lY_ eS~61.zI㠷/u$[IN ~wkm'ռqP.Yh8znR FуY!.G"FN!RC|T}J1 |w'ЂΠz&^56$pt[+c\ 15qc(߈F0hk4t!#߁E{rqA z6CzX-66/iwu+ί9ZU勤 40`z"pJn fWoVZXH\"\s"0N`eׯ)"mo`Q]]߹y5q Tuу>\=б =EF 1A􎖱 RYJ]P~9aӻ-Y"Y^MR[QXoWl.0cka\RAD6ߥDYZ!]WѨă-E}@wB#%̀rhE o\}I QƯ~ cy |WwٔfS&'@1 pӻu`3a?3͡&s xЉY ]T#>x0Cx&А8޻tT{"'a.i-p:kE3 ,չ" ~ os|@$hj.{TfH[% Yn쒒`&2 x#N `Tj 1od:4FZz< çŏiyT8lPsD&Lz_"#! R7]~۹+8Vo-;Ѐvj7pxGY&0 h {b{-^^Q5'[? i6)%j4T]._}2^3ncM}$3d~[Zl2A}O6SbBA׳0 sy^s:(Neݑ_TC9de_K%QfU[gGYK< 㱅|j΀y;;}@aew=4Ҿ(w~4\J hr ơu.> <)baHOhHNI9`1MLV. VA`Kj-GP]#؏╸fWtUb ,g?n$&`{#/!|,l b3b=[) U) lG+Y hsi"6_z""J(= 2CdNҷn8 2TM?9f̙ǛkD xUY',xU~n̒iZ^Eњ6FdF1g]T!6b s}Z]\|^.0EC\f2)oQ7sA Hc\m(ecmwDFLt-);kh!]n _f j"3Q8Rɽ| *'dfpleHߘgE5 u_lY6Rӣ F )zF ~uv)6`&%2ם:g~`a"̯tUyxȏ)7AW,("%u]88NnfCj/^ RTD7V[9.; h:qa,~@Yh-![fwC"qw=Ú;&ꄝS| -$oӄgNz-RjYnUSi5#bPsZڲ+Lm"JM.FPV#P_Vi~N5X"!=4\<B{QBy}G}]@/y/ŌC;%M 8o*}-n0';e[)cvZ:TFײ6ca])"Sl;(A}60 ;Y>8,ɣMOk%@1~m)Պ@ʃ0g Hу\"=G͠21 JɉڧB":_LN4,#oNASgTgҿ=A&: _9v+W=[0,#MRpX c͔j&rQhYס8,7fxmjn HI(R16GӨ[;L:\{.v\ܖҡPS,VG@b~Pv4\a|ऌ%5X]˵rٰF9LmAnTmf9o¹CQ$;h)_]Vup4Ș/uHǶ^a-ͮ< -7}=hC2 ku5aVeϐLd+zVDC#{QFE=Q뷭SlCFc!]UfC؜ P|旾 u_ 奤s4 L*% o#n)nC +ځ0Q>Z87ڀ%Q# S0 {#NVgaE\LԔ"Y?y%&G0p#a1!gEVrdOLY& b=yVsp`hڽjNz>X'GLduk;pHq^gH^r WuQ6׆TS#6z"sQѥ8N^z#`OPiB뇚''^p( 7avvBV^xǛpv^nPys fh6F!,+ raF"2 %}۫.֕Rj>ɽNq] ntjMT$AM8j],z|d=GJ/ k5(ZE~/ LSy{kfdDӋoUn8G*wmw0fʟ {?;(ζ$ m4VvH>ƅ`N'܋n}A1n9[t4n)3: M+v$]m,8NazO* )x?ٓf(mbM$e?˵^S\o 9;DpZ YF= hO[瀶V栱U`5<1%$h]joo6x0/4æU4xnM%<ХBrg4̤!M/1\%EE Q_&6u 722kLMa⛋XI ڄ?<Դ xN^hg,wc56 i:YB7jTC9˃ #"jXII[n)(K8?LL/),C)L"QP(7Hle"CJOmGZ,件= T],8b +vCDKn0?05wxRJi̱aB|#lݷU#"ċUݩǿ饠t { ve!0ѮhmD-a hPFRM렃R2^( <'WuptΊچ7^ĺT vpz!pN< 1MDR5A7cJ_Bw.O$>y$A_gga0nf`'j5P`sif۠{O0[/Q.0e+EB"/8qzkXx ~SlcȂSm\HP8,\Zg:raꂽ(9e9z٠ҫ$0G=] +;'sO+ut5}wEӳ_Z![ N}zCsH#!5r(pn^ۖH瀧c~. O1E>+@S{<0㕃87-ZR=0ފ؝C$Rdӵ&~~ZR$wJ>э̰)йd SsDG2MLǍe'B+nE#Zt=([H0pط e!QEYFiPR %aq)x_>oTCBN +5"xn$rYDp0Yԝ&=wu Wvp͓bb1uuqo951_(,b_zk*DZ*gX2c{JyC\<)o!B(zn9զnG[Vk-&TJ~A.F`ak6 MO^e=5\nW6 MxrդKn<. i-`zctrDksT\防 V.G+}ȐˆH3}8PrD`SvUu\%1=jK=- P/.".z(6 :#>F]פ _?-bךFLQrw.+ueٙǜFT;Ds 70r)os, w}hV{!֤Y n}h hmN-ɡfDʒ 9hUK,;"윙.4Xt 9N&Y^d)vV9BwjDvO4}ݿi$>S&O󹑬Qg @r;\}@_iJ;}:,D_Nú 1)5 2zJ; ;j,\&ۦZ#x3Ya-V~/CP_ȠPk?,&,/$zfĄ]={-T`=oGZf>yXL2ϴ.Š1ppZP(=Ln ʼn*$.bh|:8;T'e> U4;%A$ˆDq3xY݁| 2 Hl tO 4X6}$ \!YO[!iLxxNbކ sjhiqD0JxB݊~>  'w]J R㲎tZыF)pe޾6p͋ xUPNK|5̭"x,XFF2nl)sz5|βᬱB,Ѵp~5Tݱ:_$b}1fmC2TYm^$Wsb,xb3no(a'ϙ\U~qȲNioUœ1:Q@qT_㥂3~xj  p,5Ʃ쀌Z@0 "v%M3Sg ,%"*Fe,KE&L/tzʜԥɔE sZ7$?*wl`:͠Za)ؽ}!2eLڀOu\hkxA8i0 `0 bZ( hN8uRV3d)6l|];Rsq'$|RdTn!K.滀[ݿұ#rkX//#.\r$xp dӯ^̿QN^ab/wpNjO,tSuJop?VǡעG%9LkVuEx}ဦ'|J/xڙVfm6x2ܤk1?l#z6#h=sC\ Ti D1ۓ"< },OMfڬPs O b]=RmKR62Ge"M@WKJ]Ys3ߊGBL-)TmDn_ JD<ڙY`YўrXmm;!5bۭ͵*_Jx!_8>{q2F!z(D]Zj;;UiB7ueA= c!(C j7~ΠZ8xϓ0Zk;Jb4۽_b0h\ Z,ުs( A]s :8L)2?:#3(d蕟: 3O.C MV8#0k@C w?|TŐi8YC2i'K:Yo=:9Cc-MN0J#7 @pV}\BrYBD&5X rcDfx!ōJ~RfΤ=ѐ6iQS29ߔ'ft%Nvmdj]˻C{9Ah8%ޫAc^}]S5jU"E$1xg^PY"77.4u0!JXEx7kml 5#옅/94,~숏(\QT]*s| B]]0!R福/RZGO!A*ګzpIaJUTY#tGp@g3HHax*'H.8&8< V\I=⦬2@'gi9Qbεs޼kn Osg3K11~lSn)a\$X.4jMoR&f+-fv-ZeKi9f7,X[ w1wb+9*+1{Ц;~R6=惘n Pݢt'+W" sMnIk 2\~kST%=^s)*.)8$]Wգd5ΕO+0oRE5uvWr𒜂{l`0Ơkom_&+olH` ׂ'vkf,ƚ+(|E^'.]" m7sJR#}@ZL c4a9'K#Gۍo11}M D,vO  rā]osf]F`chdY3 SgAZ8D۾'9P"<}6K{+L\~nK UƄ'^qRݰn.FZ Ҫd~{ "%5؛t\1˖>9B.Pb Ű5,،\XK"ŜYo@t 4hbNHE]iLe~c+~ī2.:iG$ T[Wpպӭ m~]A~kQfFCh nI^hUV+k%F!cf`ScdU!JhUVfcȽbY7|aH>EkIVu.]Ͷ۩~=.8 h(~.SѝZpnE9c1>Ja9|(| 2+Jz;?`ps%7wOUxE{}T  ϵvEn^-@g;" bƶo󆁘ϣՌ.T4s=k, ׋YΡiZv߈aVW~`I-%gO`Y9"m%#Ǜ } q̱4WmH@Zm/X>]6;UOvA8!>ۜN0YYڥ][$bZ B׭F΄I3)w,pl'8&ѩͳ{ Q)}_QaFs>bi25ub0IkLxM1S+Ėҋ[a[jSa?Ih_rx9P\?e inetٝv|V`\q "|`e#r=kc՘d+!u4Qs zߴ7mY"U=a 8 sWVQs)Z[״G敤iٹkSZ\]Qx4 3'6KJϱ6k@AI, `H<9Ñ{#ޕp6[|cF\H7(̜悶/>9>4Dꪑّ%y,!i3d|p~)J*9XӞʉ-϶jX&ե*T88>ek#y ţc{uu[F`0hs ]]`n V&hr:{8n85 y4oGqSk(LN -=nQZߒB&f21i2<8)W*erրLkpmsT3ެi)_"ٰ U8)gߎ-Nr2[#\2gq$7IAْIK`i$-q@"r7ilCNlC ԻL>1q 軈J.*}ICw~pǤr:OV8Uxw.hۺQQfskPiQ`>u>YL0%cw~(~$v/b?/{87Mtʺ `,i¹'901m?E[Z :-EKԷYtN`b1) G8;[hi'K,xoÑ%d6D/#Vx;گ“APrHq0RO6-|D5e}neO:CڑP/2 rjz߃D֤DKib8惿޷.6MhKkϘP;+h0'ҏ6aG'mQ+74?7[ଡ?I?G_ԟՏgsv}}~}[~~~~~~~CEnOϼ\դQխT?V_?J/pcJ3a!38+Pʾ<pԯB>j;&vn u~ʂ CN=_2l$m[弆J ޸Z@&ƒKlK)mV-QVnvnĂ(7o=(KCxw{oED$rl ʸF-3{|,ġ'B]&SvPQ/o-FӺ rUԾW0{'7dЄ(Okb'4/9'.ߍŖM7#ۊj.^ ʗbe9o4`A^=JZ(k+4 ^hL:RI2'o&6Ma"lQ+[W[mC^# ,cs}f2cn=?6 hYtCpn;[VsM(ƕe cmlP?R'[;Y>YsT"U1Of`_d<|m$3k?7 yڸyY a$U1Fap,YVV cY_"#Cb'd9Mre`®NNZj¼~C'Yhc!@$"_rHcZoõbjQyGr.|vz@v5n˳|ӱMEKCDx۾7eRJ ֽ'F/۬c=) $-K!HLIv.EmdQ |WM%KN^s:,g,u(WIȃl>Vފ_eQ˨jdJ?@7o 4p8 1x!|kG{Y]RUC(5 A{Ʌe$w3ϋ15ajRiH<蚥~FmI5w2x-9ȋ<fy*LH(P`sOXlip+DgB{\( mM!҂rKY}6l_Wsvͻ /`((/?]9\eWA!_cŝq$z=b'&C+`H!XYJ;598C'򲢁mKUSAZǟdLVMz]CP x;He1+&UJ%?ĮO?TF8$}K]">oYB 4g|r@Pց<] ĸ)tS+n>̴k3KxNJ}Ygi*C>y-X8zLD_,x]\Ğl"yS8)?{?eZ (˙0nWk(@;"dOK/Yu,Ϟ8cRVff4PyXs?8ֈAt}{p),Ԯt w[FwHC44h(qi)3J7 \3r_t^4;@(9y5(a@t\3X[7ĆwB6֔F~ʀFOPԬ]Ƃ:dUXSxeM` vu3ו]ds2 Wxz9&r؅NxAaKſK,HU-QX~5 \rH2!C"z|=rJnP*ϥ%u}ˎUysgoP  p4}!CC'ƫZ`fǚrQqWBBhc9r/:J_t7U͙PK.,a w9X~:yKA OT9]s='- #}X~* {L+ٕ8y8&YǧqW툣g_?|ёgK,߶Ljx OTRېe)QU%W-$_]9>xfo,r/AoFVWFd,ͿӢ.k痒`<qHh-\C G/[=^CH_+\9MdFPɂRʨbj(^F'9IXPL}#z|oGe^qrp}A[pۤU@ "@Y̑#"!Xn>w>LA'@-vl+78ܘ9QH?fw[lo]]wc Ō H?b<-REȉeƉ-u#]x'Vֶlyq D')BST9I'Rg#STR93II0qZz>T0}3PRXWygB 7XA,zhUGwz[^/R~ .Z;bA!9é Cц0CMç9±;2z^ȪٹWr{ f#ڍö3;^E{}:nxcݭ/$6 w7\:a[.VhKBaD8c=cK;+OC* f@yҔ$etS S PȾ [.v$i.+?aDJ%dwBMlwIS0DůuȀx/clf2.du &zb("1tCƹڢ|I]l"B}˾@2$F.0GBVD 筕Pz`7Rvfv(-2ȑl^3}|$`ٛF{fpn||,oXL6JG qDʹ I]A1:)GTnxw54x@h+9Ƨ dsyj8hIlBӑ^h$iO=hGn掊n sl5joM N{=J = C6qok܉JOɶ Po;Eq&{q(]bǩU8]Fyl<%9ŏ6&bs)XQ 5\Xّ"*0?= v (5hHE'B1QXqeKxtϩ'~/%\8MO—/%(ݣ~1H9Y14։P4w[X)~Gx#/K0~<c1v*tǪ8pK8/c-+iff> J iYqiQM:c4٢t#.sR++Ry3(jZ\A (w=%dCD}?(‚\ꊽiɈ4C`LxP≀5~~Dbɉ C\>T@4i}TYM#$71g `m/琄`* 6 $Nmyld~[!`$[`/'0#$XO[}5zPE)qٓEF_3@EJ^x@PكbŎnZFƖIF:1GޭB^I4@_o$C9zܴa_#HԼ|KEyfurXdN#{e66=F V+4R!_//. HqOL &˫zt+"} #*F >7>#-W{ Zi[cs|s!;n9mpϹ;_0[/@&αK>?+#%ʹ?ħ{%B!#6c`z&i7e\Ūj|5!x%<(+n=OYr"\?FDɂ27(.Ft梵==CwO?RE*}/ȳ ֥&3uXV%RPpVR@qGYC![ Hr|Y7p籲p$T/{O/i T-/7SuRuFOvsgj MBypzΈޑ)r]Sr 6/dss "%6_,|>^&g˖A(1=!\Z \nuRSkN0Ég hX-[7 O}-Gci؉%~Cֹ|5b7N]n:]Oj5W"`j#HJۙF)E8̙rk#@3e<O0Ud#B W}OkI0X6(BcP`UB*17@%O,`i| fN8Rx 4oB2jdlti1|Bdi8AA?;g#K`$=/)4(/v(<; |D)SZ%Lgä3ZV-2GWAY-p$O^1 ˨1 ,!nkɜ߃8iEi^m6n\UYQ #(p+ZtgaP8g+]z; 5LCZD iNUFH}$ 24=o[˩y\$ L{]Z3+sq •[x8E6)::Xj;] p6D(hjs(kAq%I Ӟר5ғf&D`&`;%6!`.e 0QO3ʩ oIy<a?X'8 Vi2 y5GD֢r(?ˑ%}+BáE`yJp C4Y=DIRV< ?"53S ?~t =Pʸ`Z5pZcmZ$ $]OetŋU{H6a a*`p"WuYVN~'+{icRgVRFLoF9Fr ?2)nmn(TmY˯ ]X$h_ʕF#Xx0kJo`";(loV^YzaYgY,6fRh~ @{>Lz6FKWywDBPLTՌM,6iEU/bj>wA4_njio ?zH?׵?Db~XV^ٙ֟7lBm~g-3rBʽ [4R/ҡ-7nVPvK8j#<8z./{ET`&2pِlZEKQsq6l2D#(䉀7QBոrDpehD*-h" 6nnPAYv@V԰!Z OQ(ĿQn%_ @i G| A=I(~~d 8%3NLk_Pp*>*@[w)w@=h-LZ|Oa HH()4al OpЇgX5E~-|0{qzO^YlTCJUqbAk`]3F.Iz*7H|~ Ep[gTa3i)>]R9_1cBWvjj,-oҰtw #~#/?5 .8j1q0ˎB/'F_ƲZ^vCq(NWډb/֭zwduI?o躠{?73k}X6-:= +U&Xp>rs菤'KjUixyg 3VqVzF CC&2,bC>#%,SNh\fK^p(Pծ:N!Oxg^";`{_P:E*ÈL7Գ5xm>AlyYhU# (~jh6餃i~WDzr59,uSv,a+CLbMXJԒpZh:v!W^Q NU#|6v=io-wfowr*t D%%JtFReqLT}6##* $F> S͇bH>F~H5)*[Đ-V eh1h97AOy]%;ŲDB2#,7?&=dd/ F&̄S*f'9ZcFeؓXn1aBLa6b6@!$"f„FsХ.Qu\v$9ϝ͌ K5PtG5aWcg$m`.EǂQl>֙4פ]3ʗC1PHR9a癙*Eś`,2IU]sVT#ʵ ;qzǺ}E*嗟>_$b}1fmC2Tgw_#,'tύ$y_XtH `B_%ϙs#fj:]!D=t-߉5Gs:\<^~⣞7̙>(Z$cӀ~ǵD0砺Lu@Nn }YaW Wj!)4^2;nV$hhpH>c[=!T%ݭF3QѢLZf,u{Ѝ{rv&_B"2vޓxhV$y L8(#'6QӖ#ֹ捂'ʮL7u $ {q2F eXu6na~dǹi8bJ92m tB|(#-vf[1Px< (g/Ş@ l7 H+%nr?I8bht庄ڒ jG,]ȊXϣ͞}%ȅk];y[.FTvYJ8RzS KE;ZƲKeR<~87 hؽozss _4k&ԫ~$:iK^0/.[YnDLr"G@sgq1(l}WG-EalդWg^PY"77.4u0!J9 0YvzA-~GwGJd؇1}0<0pKM_0d[K(2x! yCaCHGBš$RXO ݇XdeE`|l+2n'6GдU`thN 1.h-Zaw52/Y4k, o: g0(8 QR9+H\'O2POtÛ~IKdo۶G18\Enͤ9s7zɰGJ]f)ok%>hbC\28z/=惘n Pݢt'+W" sMnIk 2\~kj7c-K1?ڏ!Ei3n)_Vg^&;.(wI؆SAs6z&Pg6C"A"1}BDKHEz4l[IX} ]*%uIaMYhlDHɫ/`0g[blmr\\9ߴ|^Զh[%1QZs0Q>~x2ol+pFGX1_-oV— CJKVR raGLM6J?-6&]llw5*~ڙ-t$K~ {){HCɒbh6PF`ӔYy8^V"fmCveWJWZ |}Hcoa>n9 (K"75}w緒3s$׫/[ɰݕHO,ˣ|>iꃿRS/J9 'FT{MړE=5.R)\eG&`(ؼNtvWS|- 7pf= G?ܞ.ﲻh`f\]QAm^jd-yknf*tD{O~V+5͠Gf76J9)ݫsm'I}@cGLC&4>Ea>>NS׹+C+,O޵.7Mhw?͒V3HA#RbNHE]iLe~c+1 5r<)JWܶrxo"3kKy`bz%ۆzG/5"F=ӗx e 8QY#fTV'g).`14gyS *ӧ~Q¶`>qNo1@`@Q2@Od{ [Q/UiN`*[dAvBq> nD;A}Iݥ٫w`B-cAeѦ׻Qvӭu.Np@59HA~ o^LJlVuaL\`X֯LnߗI+& 1h*  {&͕$;MYnf}ZsʹdwO50b]:$4:,h֋BC)I[=K >&Xh,"^*,Yޗ굥IZیRx?nYP =0Y3GR, Q,8# !kQD ft 0 =@Մ)3CjAҀ9F'~=) ;Yl'h(ʀerրLkpmsT3ެz2 Z=r&Lyj74$B d?BIJ WÏ*fWHL8Vr!eYoBf>> D^N5T'flx@4\4zס2ag}ջoq4L:J4Q i~<?7D<%]bL)Vi쵾&:]ǥPC&1l1[@D!GEcڴ|{>.T.~Tz;/³"R? > .s[1lRnlKq3nd %ɣZY*?RR):juvųWqr˾̍D*71j"" \ &,kU0zHתx2Y mks߭{du rim>Li\,֊̔f ?9x0#_pol >y(?3~T9ʭ ~B! P:d.gmK I PR(.c9VY^QQACVχ!Jee+?PZB+z^H"nqrSur+9t`v-aR݊W~b|TNC9S~aa:rWG)䓩"b[[ }dkU$ɸ;. 6e/erfts`R-iFB{&Ntq臅@7&fd+2A,\4dP/0/H1[L)tj0U~R= \8^D|,`e7}R9[f:W 1_2w /0v+TdM^)`.(bs˨׼R_u@pO׳|lxH]b fagM]""mU; 3s}KKa%pV薦J'\ݦqڱuqv|C̋b|=_@?~@ YSYΫRg\p{A.`{buhKE,-2-o.!lb!IrD4 xYkJ 24%\nV`^-4#``Jbx]V?\ן9:%ZݷFC,M$A vA-@nUk[Wty~IZ-CJ4!1?m\IoՋ'Y>u|~{_չa2Ѧ S/?)oQ"5Z/7L; }= ;%SoܪEǟT)kaKӒ߳_Z^4vo"h c:ve79,섂P0QJSbZ;\"r:8hTHqh.r,K^ I7KHOz*e(%\nil_<[ѝ.A+Kj,]-4sAz ӓdgT%otXRܡAe C UMʨp,J5=ǫ E!"5twuc{{r+'ܜa!:+:ӏSOBBOFWZ@0,ApNJ@8Ϧ\ Y iȮRĽ_(\JaWd;n!gV".~Bg-6 uBē@2zbILCD3Wo +hFpD$1Y 9xz.O)u -K)ya? 7ۜ`;h#~b`ց:K?P6WQ]uPL*EK9H45Ⅶ-oG¶#d+ e{i 3'MMuΟVt }Jxh jIK nY7`'ʗtuGW1HAډvĿ0Uw3Fh $|k׳.]~HA[!ﳀ:w3![ĊBSÿ, :pFw'Sc˸kPy_m[֜GrGEj~DVb?L2L8W啃i@B<8Aeˮ8qv=VJܯ)֊ W69hNB> Aם{KmxFSSB' hLS;!`s-? \7Ȱm7-Q؅&|~3ԧa iLOj-=1>㦄UQ&f-%3R \<ņH%8%zK&Eg16 0mW}/_c맺uoh jQ釬LTuy煉VI5?q٧͐V'so l },YB wG\Iќ1?~HX*@#zāO_~"|1Zd`0g?؜k](w#:r8{c!c-T(ZBok+nHUX bbFEYeWmW^2Nɛv7xw+BNȭZA|bJ?`'gwTy;JC, 0}OvB&b {%ƚϴ:@#p]gv脁T$nVw(B= ΙR: Iٶ&-"_~2ΏEj5f }WlD|6Ț@ga~bRɰ.WW|Z,xT `yBҤ2\`L{?vxxu)SQ?+ pgc HJi3gArA-r8!0|F[hC& MΧSIܼ@ձzs.7͹x?=??,<ЅsnW^[~ &rNvNzZf6q|P (V00T6 ySlRF޾NMnÁd_)]AΓQ3Gsl,0=$֘l}MgzhdEa7Meg;Lk!8B`TF?c S؋< f_l.ߴ\@&WIHsZ[3ʎp^iG_b{I`yhʟ "Ô8x*vuĪK MOZ16EIFdǵ"7P; AHB7gVmel8RFr8&}i6$w}R~9l,vyDWa9lqL>usg6UݹCj8ћ3J]b2.Q8ixRd5Yc[д伳FM2P,&ؓiʨשHMOTʄ6`Y*Kʇ>y\?~Gb_޹@^Ey^ΪPpìjH9ۆZa}ɓ] ʦsIa 5-(Hݔؾ;M>&KgWYiUu &BdZnlzK2`Iyv~ _B s'wa.tlAUa2I8y-=S[E" _BԽ_FQR:p 6}(n<Ѿ KQ5SʮD`>>87f Cs MCK\G?ڥq^~ ɢjۭ?|@+~CWyB=vgAUSx ;֘ɸiJu%GC:~how,UPA ~Ĕ `ppDkVW_syPX=No0ŵoA%ԴDa,sLk4Hdk!L3{,u1)XH3o_L5;Ff(Tmn(Dx{>q!3Np_ݬ( .$ܨ3GeZo-xĤZǖLBuaz*ą\A߻"n4+6bS+)W{W@RY}s[^)S`ۖ;}a}Wy<WQ$4#? a+Y- L+ .}+R1-]-aKx]dZ&8<29A_pa𷏚/Dn-Ç~ܧ!;vO!i|ޙh뇊&d !y E +ku⾦Z9#7݆aӼAYo j*A";AS *mLPh SSX0Px:}˷,)T Vd~|%"j?)"SҖ4ӄPWb.}X%U)Xڕ̗F:ql\!گlqm;V0S;COj#׶]2A,BV7'o2̑s־'xIiŕ$%DM+X5ЗXd33%jYlKEV9;=?1r7q^d&몛8%GM]Ji>nb5h*iSq8$iTYQ#vHS6FBF]"/h_h{Ųλ,K9PY!in R[vo'ttl?4x^(C*N3{7_1 ͪe#W=Y{ #>?|C+>ANG6sȡWUxFn >p.NHU"rPw&Jc}rmLks eqmL2zV+ O!~ ^UJ`*vƊ1L@$l(L;ò ů\|u[l)iўNҫhgTV5{0yv hM:'_RBPim%M Ê&K[Y\M3_M)8Yu=-Ln:B l RA.WFse[c n DhbY7_ Y[s/j !" s7naE|ڋtt7. 5;6A冐,u0\WX9GUbo-'žc)%ޏkɛst}w| |d*rrޠM"e: "e*nd"[TuՊN#(fM(ad=pf?ShkHvM{[7))9n*a%qL3)?Շw 5 z(̼C`m*`{_psGoʷ>'x=/ ;sr.-@BRdD3A.dt+N{"7C'k`=ڬ-`[orhݱ_6qiMщT%T#ՏT&4B4=Ђ*\ a./.4xTK!W)UKx?!D@0;o1fn<0X5(30(x~Gkb>uO-~ )w!1XuQ>;AnxA5C1e4Yy=?1 a)љ!5X"Rb !(- ,q-o.ӓޫ&G <KTd bj'\VCښl]/+Xj41ۘSսeYH`̲#ѷQ%/Q(8VyRӌO~\ɌDZ/ @/&j)O!citiaIGr?vA=MqW=;H&79?yus+MEr\o>GgQ(8 ^QO"q@?~W:R1&jyc΍zO0TGHd%8_-]td )j6}HWF΀K$ڃ3}dKChJ I6T2ݚ\^OFoHmG#{^y k6odixcgprHnnxz5ՃdzhE,oU;#Rډ<􍔂s-g֓:0bLTr]ᕁb8S-QGgeЌ֚0dh[\tgKqٱ{ۅ?=1)?c:U&Ŗ(,gM"/(Xwn$ ZCB$FP:YznV8~bRqE}M'O^Ԩۜ %NqH!~$Qi~a Z#_eDbܕxG=VsA? oza6_ z.'1g82[5K5O-G“ }`>R?߸H@&:wz*%|Ԩ);Qe:KϤaI{$yД@c UR.[δ)ǠGbP+c_?VG{1TJ1 .n8Lve S. RR40ȇk\:@E9| #QN[VԐnFFsW&|Jr5_i!H\(|:Q0o#P 9=h d!.2c^76wjEuL2ECa2@MJV *CW-ZX\Ӎ'Peo IlkhX/ݖ?g;I9Dz|=D]4lA." ms~Aw\qn}b!ܐG9['WeƄm+xzGaTCM̆Jnv 5͕zKu߻7g_~|2H28.Ά^l@?\Ȟ"R=cHs=Z惘n Pݢt"b:yGej*Sy~6C,6J+$NyK \$y'zz*@mE+f8^-,%lܝaG4 " fFGO[jLmFBe&.-]XH`Ow=qXM^?E>3VٯX`諲%|qTpH͆yk? t ћXI -̡G|,(tNrJtBm ̺wĦywiJ] Q#p9ʿcBt6:J HSzUzK#Ȣ8 *5 hm\ UALcs?b<\rhbηcdۄ hࣸs;ou} ix~Ό*4@ʜ.*?`. 1Kc}±k2Ky<^v]O9s_ys)Ԃ*MC@ 6.4 ɗAAU(7szdrHޘNiE>.Thfׂ.'jeAGgXָ"QxUW`v@1AJH-R?$\`۟gd@Oj 6soܣ8σWj{J'UU}_򊈇F"c`\KM$PYnʬ987Mho6w6w9Me@ko0*ynhs/{KxRaba"9>bV-bNHE[\z9f#RufDkUbPu& jr@.ӑ})ɝnus@~j.s1}ݦ?d<1>FCsBN3ۅ9au€0%d ػ%OO2evsqʧ;vܧU{BMIk )!IEK_jMK6N\ %s'\zAb#*z$ch9 ɐc}2F^X49p(yE xֆWl( C%T4Q DRR DaOit J;/txᩬvECяDB% [+ @F0T=ל2KRdأzW_4AYm0M6p!?;׎ム4Ws,rΰQJn@ 6F'S_d4%dv=|Q(d'x+dN:Ox A*Dƴ;W9qdndB Cs<ɫ&ew˟BUo^2-Bw{6Xnif321!#b!BbvfYhWM2l=7T}%nA)_RG Nuqo(n9gU:KoPuсՍy7I2꧿mls_?mjjb=SOX߶ľ>XSmX~ڿY4l Di/ 2ŊxM_ejbO(4=:]*'> F`o]\tx ?#h%C%d]cw@-s9[G0mk혦Oѳ~ݒS FV5}+$I3E@>0( KUYO?97l)7&vgG|JMdݞ}-TnW:LP=I9m̈GH"yĵ$qI|88Ħ髛%-MsX2P&2^t?RjWwQ{i:LˆjA^ve =?Y#7ut`Z ĕ%DIA b"T VbM 1b6ՀY h: ]~݅-/5@cIP?БXqyLlJJ]Lr!;SYѼQ,cv1GoykC)8`QQ/^@5:b8ГDHk_.r0#+3Yoۙ1E\,ȧT–g P PBN=q-%ZYn2[(u ;r$.p!p4昒49D$^>lb_5*N7A2Z'=8;Dc2%h,*BFݧX!Y{ʯXys2nknN>U54@KePUVNdmDah:q Rʺܸ 7o nY5>>'eR wmSz 1鶱G݋!u`oAFo!F5^Mv5P-%ìu mq#a5,d/yW YPEgj V/>bKQȲ^&/<49Q-Xe"d=3k¶z[ UՅ릨bh6HZ$6%4QtXeߟU&m;縏 '/܁)9o]BBS^9FQV mD򶹸`ꇈHB w$ebHd m(wshw}@<7!cK%bR٩g>`j%˅ẁO KLk/bX } '1 azMy꣥ @$~Fr$te;@'-<[Vi̧2- T+b,yt{7n>1mҞG# 3Չ(GFЙʩ L ꖍU jxa2trZb2(a^w`ya]C_Wp78-G-9T ѪsLn9Hk^$ܜ (vs_e@1Co3Pjj>gfD+t0sձ)ABy0O!<~ iBm2?*f"S$c!Ln,gͿO: ,aA%K6BѦqBt`_ߍYc9Qpiȁ Cv^*^4v@"{L?^8IBaQf+JSY' lJtfR4Fz8"hP. B5qU=H:~$uG˘wpԻhxG{Hۙ& Qdt:!v7?T"r`v:{frVR^ܰ NP+$JЋȬcݕ}hJp(tft2{ݮNxqot[fC(& KYtN-e:pЧE{ q!2҉\%YkxCdN*78%8Ia#`%3(/N|V[QvQq5oewÀn2l}b^"=_ݣ [؟5tD4ƉooCʝ9A=͉>VzX&lX!DP$T 6C)~ktC~iK[vwwT!Mb#&!zN~ cneѭ0Iz,9o5M>&{:r(T~=VAP/E!A\̔C5ԽUH$ b>դjSx{@1{s-H xnW3:o| sDTAj٢,fpsGX|)*~):V!e9m֒ks.A(83"jHsK) ́ƱE?GʅD>a8Q`579}ڀxc3C?pf#XgbgrfM ck*UaIrHiOn-r:sXFeca!M TjʥjrdwXNrri?iaG|sB9$$T#5$%p-,?A8=~l\>s ϱ6m,߽JKN_S!v)qӸbd>jpa3~j[wOE }`|u%(moNV4rO-JzSK͓<(aZI=4U%\_i&?9\ibi )F.5)VpKcUR }Uc!Y,eX1o30sXvl9C݌րK$χ8J 4%x놜ij;fSd{;AÇWL lDe؍Z -sb(p &c*xp uC:\B 6ϸZ*yY EfjC/OuUv$40 @ G8mi/zd>B[^|d* \@p k?65tI9'5hZ󳁙kҳ||n난wwϏu UkΝXn ոL.<q4C)b%޹GW@?a8iڕS 4WUk=khw2o葅fd73'J Ld{<2XEޮS3kuXc}oYsz*i>g 䵋: %e\bgE]oSf: 7$iO*adom&B(O3ixt?LL" 0Q(t%XՀ#=PiW_v<dCc7h$•蹔 ͹Uohc.;ƁO~]H{eG:(o6p?N41Y8qbt둹G_2%ǧ4^-Uʱn&BmQ#;1oy/9VIа,D9`7J -4L4" E,zB,brۊtQ 4H}[Kʿ* 8!f(3:M!HB(T*\HVݴ8Iֱv,2|0݅^d2`QsS5hV8.+\}93UVGV~y {eMA㖢!֫Dw3 Mxڨs`Qm[J/ xu?Qŗ>)BstȺC$^;X.~1!d(َZeVBl"#Ya@Ņ VHUĂ H(gAZU\8ܧ}zy&j9R<:OHɽH gyx~t?op.$P&W " R.TSd ly|B" I>ةآ(G$@`UR,@".Y2GvX@`B, 8C L0ҿ_pH˕͗K3w!lBa)f "#HL 8?flŢko">!N_puk[Vh]3 Z zy8@P< %b0>3o~@zq@qanvRB1n#Dž)4\,XP"MyRD!ɕ2 w ONl~Xv@~- g42y@+͗\LD*A aD@ $<B AT:18 \p` Aa!:b""aH4 Q"rBj]H#-r9\@ 2G1Qu@Ơst4]k=Kut}c1fa\E`X&cX5V5cX7va$^lGXLXC%#W 1'"O%zxb:XF&!!%^'_H$ɒN !%2I IkHH-S>iL&m O:ňL $RJ5e?2BQͩ:ZImvP/S4u%͛Cˤ-Кigih/t ݃EЗkw Hb(k{/LӗT02goUX**|:V~TUsU?y TU^V}FUP թU6RwRPQ__c FHTc!2eXBrV,kMb[Lvv/{LSCsfffqƱ9ٜJ! {--?-jf~7zھbrup@,:m:u 6Qu>cy Gm7046l18c̐ckihhI'&g5x>fob4ekVyVV׬I\,mWlPW :˶vm))Sn1 9a%m;t;|rtuvlp4éĩWggs5KvSmnz˕ҵܭm=}M.]=AXq㝧/^v^Y^O&0m[{`:>=e>>z"=#~~~;yN`k5/ >B Yroc3g,Z0&L~oL̶Gli})*2.QStqt,֬Yg񏩌;jrvgjlRlc웸xEt$ =sl3Ttcܢ˞w|/9%*IDAT8˅kSQǿBbK'th!CJ$ \]\]\\I,}`$ޛwuІg_Ի\pr/^_JqONMѤc"zV Mj/`uuuej]v>٢Z^||lRP@P,=99iNOO pp,'brmnnrR)y,lh C\O6NSbQ(@D`fqMD(DDr !(ʙƙ@*Ɔ$~Ͻ^ﶔg"t]k 9f44$AZC)0 &f~0 e9nVj* x8RE빣#ιŭNyj5B4afa_55'7qIENDB`swift-im-2.0+dev6/Slimber/Resources/Online.png0000644000175000017500000000106712227051773021167 0ustar kismithkismithPNG  IHDRasRGBIDAT8˅kSa$76JۘVpREѿAGt:,EPT77_uhIjaWȱ8+7H'oc,)bkn{ 9crqTw|[M>c>Q?IܺD,J܉05~rnV T<P^<ϵII%" image/svg+xml swift-im-2.0+dev6/Slimber/MainController.cpp0000644000175000017500000000757112227051773020725 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Slimber/MainController.h" #include #include #include #include #include #include #include #include #include "Slimber/Server.h" #include "Slimber/FileVCardCollection.h" #include "Slimber/MenuletController.h" #include "Slimber/Menulet.h" using namespace Swift; MainController::MainController(Menulet* menulet, EventLoop* eventLoop) { dnsSDQuerier = PlatformDNSSDQuerierFactory(eventLoop).createQuerier(); assert(dnsSDQuerier); linkLocalServiceBrowser = new LinkLocalServiceBrowser(dnsSDQuerier); linkLocalServiceBrowser->onServiceAdded.connect( boost::bind(&MainController::handleServicesChanged, this)); linkLocalServiceBrowser->onServiceRemoved.connect( boost::bind(&MainController::handleServicesChanged, this)); linkLocalServiceBrowser->onServiceChanged.connect( boost::bind(&MainController::handleServicesChanged, this)); vCardCollection = new FileVCardCollection( PlatformApplicationPathProvider("Slimber").getDataDir()); server = new Server(5222, 5562, linkLocalServiceBrowser, vCardCollection, eventLoop); server->onStopped.connect( boost::bind(&MainController::handleServerStopped, this, _1)); server->onSelfConnected.connect( boost::bind(&MainController::handleSelfConnected, this, _1)); menuletController = new MenuletController(menulet); menuletController->onRestartRequested.connect(boost::bind( &MainController::handleRestartRequested, this)); start(); } MainController::~MainController() { delete server; delete menuletController; delete vCardCollection; linkLocalServiceBrowser->stop(); delete linkLocalServiceBrowser; dnsSDQuerier->stop(); } void MainController::start() { dnsSDQuerier->start(); linkLocalServiceBrowser->start(); handleSelfConnected(false); handleServicesChanged(); server->start(); } void MainController::stop() { server->stop(); linkLocalServiceBrowser->stop(); dnsSDQuerier->stop(); } void MainController::handleSelfConnected(bool b) { if (b) { menuletController->setXMPPStatus("You are logged in", MenuletController::Online); } else { menuletController->setXMPPStatus("You are not logged in", MenuletController::Offline); } } void MainController::handleServicesChanged() { std::vector names; foreach(const LinkLocalService& service, linkLocalServiceBrowser->getServices()) { std::string description = service.getDescription(); if (description != service.getName()) { description += " (" + service.getName() + ")"; } names.push_back(description); } menuletController->setUserNames(names); } void MainController::handleServerStopped(boost::optional error) { if (error) { std::string message; switch (error->getType()) { case ServerError::C2SPortConflict: message = std::string("Error: Port ") + boost::lexical_cast(server->getClientToServerPort()) + std::string(" in use"); break; case ServerError::C2SError: message = std::string("Local connection server error"); break; case ServerError::LinkLocalPortConflict: message = std::string("Error: Port ") + boost::lexical_cast(server->getLinkLocalPort()) + std::string(" in use"); break; case ServerError::LinkLocalError: message = std::string("External connection server error"); break; } menuletController->setXMPPStatus(message, MenuletController::Offline); } else { menuletController->setXMPPStatus("XMPP Server Not Running", MenuletController::Offline); } } void MainController::handleRestartRequested() { stop(); start(); } swift-im-2.0+dev6/Slimber/Menulet.h0000644000175000017500000000120012227051773017032 0ustar kismithkismith/* * 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 #include class Menulet { public: virtual ~Menulet(); virtual void clear() = 0; virtual void addItem(const std::string& name, const std::string& icon = std::string()) = 0; virtual void addAboutItem() = 0; virtual void addRestartItem() = 0; virtual void addExitItem() = 0; virtual void addSeparator() = 0; virtual void setIcon(const std::string&) = 0; boost::signal onRestartClicked; }; swift-im-2.0+dev6/Slimber/VCardCollection.h0000644000175000017500000000071512227051773020446 0ustar kismithkismith/* * 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 #include namespace Swift { class VCardCollection { public: virtual ~VCardCollection(); virtual boost::shared_ptr getOwnVCard() const = 0; virtual void setOwnVCard(boost::shared_ptr vcard) = 0; }; } swift-im-2.0+dev6/Slimber/FileVCardCollection.cpp0000644000175000017500000000252212227051773021577 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Slimber/FileVCardCollection.h" #include #include #include #include #include #include #include namespace Swift { FileVCardCollection::FileVCardCollection(boost::filesystem::path dir) : vcardsPath(dir) { } boost::shared_ptr FileVCardCollection::getOwnVCard() const { if (boost::filesystem::exists(vcardsPath / std::string("vcard.xml"))) { ByteArray data; readByteArrayFromFile(data, boost::filesystem::path(vcardsPath / std::string("vcard.xml")).string()); VCardParser parser; PayloadParserTester tester(&parser); tester.parse(byteArrayToString(data)); return boost::dynamic_pointer_cast(parser.getPayload()); } else { return boost::make_shared(); } } void FileVCardCollection::setOwnVCard(boost::shared_ptr v) { boost::filesystem::ofstream file(vcardsPath / std::string("vcard.xml")); file << VCardSerializer().serializePayload(v); file.close(); } } swift-im-2.0+dev6/Slimber/Server.cpp0000644000175000017500000003500012227051773017227 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Slimber/Server.h" #include #include #include #include "Swiften/Base/String.h" #include "Swiften/LinkLocal/LinkLocalConnector.h" #include "Swiften/Network/Connection.h" #include "Swiften/Session/SessionTracer.h" #include "Swiften/Elements/Element.h" #include "Swiften/Elements/Presence.h" #include "Swiften/Elements/RosterPayload.h" #include "Swiften/Network/BoostConnection.h" #include "Swiften/Network/BoostConnectionServer.h" #include "Swiften/Session/SessionTracer.h" #include "Swiften/Elements/IQ.h" #include "Swiften/Elements/VCard.h" #include "Limber/Server/UserRegistry.h" #include "Swiften/Session/Session.h" #include #include #include #include #include #include #include #include "Slimber/VCardCollection.h" #include "Slimber/LinkLocalPresenceManager.h" #include "Limber/Server/ServerFromClientSession.h" namespace Swift { Server::Server( int clientConnectionPort, int linkLocalConnectionPort, LinkLocalServiceBrowser* linkLocalServiceBrowser, VCardCollection* vCardCollection, EventLoop* eventLoop) : linkLocalServiceRegistered(false), rosterRequested(false), clientConnectionPort(clientConnectionPort), linkLocalConnectionPort(linkLocalConnectionPort), linkLocalServiceBrowser(linkLocalServiceBrowser), vCardCollection(vCardCollection), eventLoop(eventLoop), presenceManager(NULL), stopping(false) { linkLocalServiceBrowser->onServiceRegistered.connect( boost::bind(&Server::handleServiceRegistered, this, _1)); } Server::~Server() { stop(); } void Server::start() { assert(!serverFromClientConnectionServer); serverFromClientConnectionServer = BoostConnectionServer::create( clientConnectionPort, boostIOServiceThread.getIOService(), eventLoop); serverFromClientConnectionServerSignalConnections.push_back( serverFromClientConnectionServer->onNewConnection.connect( boost::bind(&Server::handleNewClientConnection, this, _1))); serverFromClientConnectionServerSignalConnections.push_back( serverFromClientConnectionServer->onStopped.connect( boost::bind(&Server::handleClientConnectionServerStopped, this, _1))); assert(!serverFromNetworkConnectionServer); serverFromNetworkConnectionServer = BoostConnectionServer::create( linkLocalConnectionPort, boostIOServiceThread.getIOService(), eventLoop); serverFromNetworkConnectionServerSignalConnections.push_back( serverFromNetworkConnectionServer->onNewConnection.connect( boost::bind(&Server::handleNewLinkLocalConnection, this, _1))); serverFromNetworkConnectionServerSignalConnections.push_back( serverFromNetworkConnectionServer->onStopped.connect( boost::bind(&Server::handleLinkLocalConnectionServerStopped, this, _1))); assert(!presenceManager); presenceManager = new LinkLocalPresenceManager(linkLocalServiceBrowser); presenceManager->onRosterChanged.connect( boost::bind(&Server::handleRosterChanged, this, _1)); presenceManager->onPresenceChanged.connect( boost::bind(&Server::handlePresenceChanged, this, _1)); serverFromClientConnectionServer->start(); serverFromNetworkConnectionServer->start(); } void Server::stop() { stop(boost::optional()); } void Server::stop(boost::optional e) { if (stopping) { return; } stopping = true; delete presenceManager; presenceManager = NULL; if (serverFromClientSession) { serverFromClientSession->finishSession(); } serverFromClientSession.reset(); foreach(boost::shared_ptr session, linkLocalSessions) { session->finishSession(); } linkLocalSessions.clear(); foreach(boost::shared_ptr connector, connectors) { connector->cancel(); } connectors.clear(); tracers.clear(); if (serverFromNetworkConnectionServer) { serverFromNetworkConnectionServer->stop(); foreach(boost::bsignals::connection& connection, serverFromNetworkConnectionServerSignalConnections) { connection.disconnect(); } serverFromNetworkConnectionServerSignalConnections.clear(); serverFromNetworkConnectionServer.reset(); } if (serverFromClientConnectionServer) { serverFromClientConnectionServer->stop(); foreach(boost::bsignals::connection& connection, serverFromClientConnectionServerSignalConnections) { connection.disconnect(); } serverFromClientConnectionServerSignalConnections.clear(); serverFromClientConnectionServer.reset(); } stopping = false; onStopped(e); } void Server::handleNewClientConnection(boost::shared_ptr connection) { if (serverFromClientSession) { connection->disconnect(); } serverFromClientSession = boost::shared_ptr( new ServerFromClientSession(idGenerator.generateID(), connection, &payloadParserFactories, &payloadSerializers, &xmlParserFactory, &userRegistry)); serverFromClientSession->setAllowSASLEXTERNAL(); serverFromClientSession->onSessionStarted.connect( boost::bind(&Server::handleSessionStarted, this)); serverFromClientSession->onElementReceived.connect( boost::bind(&Server::handleElementReceived, this, _1, serverFromClientSession)); serverFromClientSession->onSessionFinished.connect( boost::bind(&Server::handleSessionFinished, this, serverFromClientSession)); //tracers.push_back(boost::shared_ptr( // new SessionTracer(serverFromClientSession))); serverFromClientSession->startSession(); } void Server::handleSessionStarted() { onSelfConnected(true); } void Server::handleSessionFinished(boost::shared_ptr) { serverFromClientSession.reset(); unregisterService(); selfJID = JID(); rosterRequested = false; onSelfConnected(false); lastPresence.reset(); } void Server::unregisterService() { if (linkLocalServiceRegistered) { linkLocalServiceRegistered = false; linkLocalServiceBrowser->unregisterService(); } } void Server::handleElementReceived(boost::shared_ptr element, boost::shared_ptr session) { boost::shared_ptr stanza = boost::dynamic_pointer_cast(element); if (!stanza) { return; } stanza->setFrom(session->getRemoteJID()); if (!stanza->getTo().isValid()) { stanza->setTo(session->getLocalJID()); } if (boost::shared_ptr presence = boost::dynamic_pointer_cast(stanza)) { if (presence->getType() == Presence::Available) { if (!linkLocalServiceRegistered) { linkLocalServiceRegistered = true; linkLocalServiceBrowser->registerService( session->getRemoteJID().toBare().toString(), linkLocalConnectionPort, getLinkLocalServiceInfo(presence)); } else { linkLocalServiceBrowser->updateService( getLinkLocalServiceInfo(presence)); } lastPresence = presence; } else { unregisterService(); } } else if (!stanza->getTo().isValid() || stanza->getTo() == session->getLocalJID() || stanza->getTo() == session->getRemoteJID().toBare()) { if (boost::shared_ptr iq = boost::dynamic_pointer_cast(stanza)) { if (iq->getPayload()) { if (iq->getType() == IQ::Get) { session->sendElement(IQ::createResult(iq->getFrom(), iq->getID(), presenceManager->getRoster())); rosterRequested = true; foreach(const boost::shared_ptr presence, presenceManager->getAllPresence()) { session->sendElement(presence); } } else { session->sendElement(IQ::createError(iq->getFrom(), iq->getID(), ErrorPayload::Forbidden, ErrorPayload::Cancel)); } } if (boost::shared_ptr vcard = iq->getPayload()) { if (iq->getType() == IQ::Get) { session->sendElement(IQ::createResult(iq->getFrom(), iq->getID(), vCardCollection->getOwnVCard())); } else { vCardCollection->setOwnVCard(vcard); session->sendElement(IQ::createResult(iq->getFrom(), iq->getID())); if (lastPresence) { linkLocalServiceBrowser->updateService(getLinkLocalServiceInfo(lastPresence)); } } } else { session->sendElement(IQ::createError(iq->getFrom(), iq->getID(), ErrorPayload::FeatureNotImplemented, ErrorPayload::Cancel)); } } } else { JID toJID = stanza->getTo(); boost::shared_ptr outgoingSession = getLinkLocalSessionForJID(toJID); if (outgoingSession) { outgoingSession->sendElement(stanza); } else { boost::optional service = presenceManager->getServiceForJID(toJID); if (service) { boost::shared_ptr connector = getLinkLocalConnectorForJID(toJID); if (!connector) { connector = boost::shared_ptr( new LinkLocalConnector( *service, linkLocalServiceBrowser->getQuerier(), BoostConnection::create(boostIOServiceThread.getIOService(), eventLoop))); connector->onConnectFinished.connect( boost::bind(&Server::handleConnectFinished, this, connector, _1)); connectors.push_back(connector); connector->connect(); } connector->queueElement(element); } else { session->sendElement(IQ::createError( stanza->getFrom(), stanza->getID(), ErrorPayload::RecipientUnavailable, ErrorPayload::Wait)); } } } } void Server::handleNewLinkLocalConnection(boost::shared_ptr connection) { boost::shared_ptr session( new IncomingLinkLocalSession( selfJID, connection, &payloadParserFactories, &payloadSerializers, &xmlParserFactory)); registerLinkLocalSession(session); } void Server::handleLinkLocalSessionFinished(boost::shared_ptr session) { //std::cout << "Link local session from " << session->getRemoteJID() << " ended" << std::endl; linkLocalSessions.erase( std::remove(linkLocalSessions.begin(), linkLocalSessions.end(), session), linkLocalSessions.end()); } void Server::handleLinkLocalElementReceived(boost::shared_ptr element, boost::shared_ptr session) { if (boost::shared_ptr stanza = boost::dynamic_pointer_cast(element)) { JID fromJID = session->getRemoteJID(); if (!presenceManager->getServiceForJID(fromJID.toBare())) { return; // TODO: Send error back } stanza->setFrom(fromJID); serverFromClientSession->sendElement(stanza); } } void Server::handleConnectFinished(boost::shared_ptr connector, bool error) { if (error) { std::cerr << "Error connecting" << std::endl; // TODO: Send back queued stanzas } else { boost::shared_ptr outgoingSession( new OutgoingLinkLocalSession( selfJID, connector->getService().getJID(), connector->getConnection(), &payloadParserFactories, &payloadSerializers, &xmlParserFactory)); foreach(const boost::shared_ptr element, connector->getQueuedElements()) { outgoingSession->queueElement(element); } registerLinkLocalSession(outgoingSession); } connectors.erase(std::remove(connectors.begin(), connectors.end(), connector), connectors.end()); } void Server::registerLinkLocalSession(boost::shared_ptr session) { session->onSessionFinished.connect( boost::bind(&Server::handleLinkLocalSessionFinished, this, session)); session->onElementReceived.connect( boost::bind(&Server::handleLinkLocalElementReceived, this, _1, session)); linkLocalSessions.push_back(session); //tracers.push_back(boost::make_shared(session)); session->startSession(); } boost::shared_ptr Server::getLinkLocalSessionForJID(const JID& jid) { foreach(const boost::shared_ptr session, linkLocalSessions) { if (session->getRemoteJID() == jid) { return session; } } return boost::shared_ptr(); } boost::shared_ptr Server::getLinkLocalConnectorForJID(const JID& jid) { foreach(const boost::shared_ptr connector, connectors) { if (connector->getService().getJID() == jid) { return connector; } } return boost::shared_ptr(); } void Server::handleServiceRegistered(const DNSSDServiceID& service) { selfJID = JID(service.getName()); } void Server::handleRosterChanged(boost::shared_ptr roster) { if (rosterRequested) { assert(serverFromClientSession); boost::shared_ptr iq = IQ::createRequest( IQ::Set, serverFromClientSession->getRemoteJID(), idGenerator.generateID(), roster); iq->setFrom(serverFromClientSession->getRemoteJID().toBare()); serverFromClientSession->sendElement(iq); } } void Server::handlePresenceChanged(boost::shared_ptr presence) { if (rosterRequested) { serverFromClientSession->sendElement(presence); } } void Server::handleClientConnectionServerStopped(boost::optional e) { if (e) { if (*e == BoostConnectionServer::Conflict) { stop(ServerError(ServerError::C2SPortConflict)); } else { stop(ServerError(ServerError::C2SError)); } } else { stop(); } } void Server::handleLinkLocalConnectionServerStopped(boost::optional e) { if (e) { if (*e == BoostConnectionServer::Conflict) { stop(ServerError(ServerError::LinkLocalPortConflict)); } else { stop(ServerError(ServerError::LinkLocalError)); } } else { stop(); } } LinkLocalServiceInfo Server::getLinkLocalServiceInfo(boost::shared_ptr presence) { LinkLocalServiceInfo info; boost::shared_ptr vcard = vCardCollection->getOwnVCard(); if (!vcard->getFamilyName().empty() || !vcard->getGivenName().empty()) { info.setFirstName(vcard->getGivenName()); info.setLastName(vcard->getFamilyName()); } else if (!vcard->getFullName().empty()) { std::pair p = String::getSplittedAtFirst(vcard->getFullName(), ' '); info.setFirstName(p.first); info.setLastName(p.second); } if (!vcard->getNickname().empty()) { info.setNick(vcard->getNickname()); } if (!vcard->getPreferredEMailAddress().address.empty()) { info.setEMail(vcard->getPreferredEMailAddress().address); } info.setMessage(presence->getStatus()); switch (presence->getShow()) { case StatusShow::Online: case StatusShow::None: case StatusShow::FFC: info.setStatus(LinkLocalServiceInfo::Available); break; case StatusShow::Away: case StatusShow::XA: info.setStatus(LinkLocalServiceInfo::Away); break; case StatusShow::DND: info.setStatus(LinkLocalServiceInfo::DND); break; } info.setPort(linkLocalConnectionPort); return info; } } swift-im-2.0+dev6/COPYING.gpl0000644000175000017500000010662012227051773015503 0ustar kismithkismithSwift - An XMPP Client Copyright (C) 2009-2012 Kevin Smith and Remko Tronçon This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Additional permission under GNU GPL version 3 section 7 If you modify this Program, or any covered work, by linking or combining it with the OpenSSL library (or a modified version of that library), containing parts covered by the terms of the OpenSSL License, the licensors of this Program grant you additional permission to convey the resulting work. --- Start OF GNU GENERAL PUBLIC LICENSE --- GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . --- END OF GNU GENERAL PUBLIC LICENSE --- swift-im-2.0+dev6/.cproject0000644000175000017500000060472112227051773015506 0ustar kismithkismith swift-im-2.0+dev6/Sluift/0000755000175000017500000000000012227051774015131 5ustar kismithkismithswift-im-2.0+dev6/Sluift/SConscript0000644000175000017500000000402612227051773017144 0ustar kismithkismithimport Version, os.path Import(["env", "conf_env"]) if env["SCONS_STAGE"] == "build" : lib_env = env.Clone() lib_env.UseFlags(env["LUA_FLAGS"]) lib_env.UseFlags(env["SWIFTEN_FLAGS"]) lib_env.UseFlags(env["SWIFTEN_DEP_FLAGS"]) sluift_lib = lib_env.StaticLibrary("SluiftCore", [ "Lua/Value.cpp", "sluift.cpp" ]); myenv = env.Clone() myenv.Append(LIBS = sluift_lib) myenv.UseFlags(env.get("LUA_FLAGS", {})) myenv.UseFlags(env["SWIFTEN_FLAGS"]) myenv.UseFlags(env["SWIFTEN_DEP_FLAGS"]) myenv["SHLIBPREFIX"] = "" if myenv["PLATFORM"] == "win32" : myenv.Append(CPPDEFINES = ["SLUIFT_BUILD_DLL"]) elif myenv["PLATFORM"] == "darwin" : myenv["SHLIBSUFFIX"] = ".so" if env["PLATFORM"] == "win32" : myenv.Append(CFLAGS = ["/TP"]) else : myenv.Append(CFLAGS = ["-x", "c++"]) myenv["SLUIFT_VERSION"] = Version.getBuildVersion(env.Dir("#").abspath, "sluift") def patchLua(env, target, source) : f = open(source[0].abspath, "r") contents = f.read() f.close() if env["PLATFORM"] == "win32" : key = "Z" else : key = "D" contents = contents.replace("LUA_RELEASE", "\"== Sluift XMPP Console (%(version)s) == \\nPress Ctrl-%(key)s to exit\"" % {"version": source[1].get_contents(), "key" : key}) contents = contents.replace("LUA_COPYRIGHT", "") f = open(target[0].abspath, "w") f.write(contents) f.close() myenv.Command("lua.c", ["#/3rdParty/Lua/src/lua.c", myenv.Value(myenv["SLUIFT_VERSION"])], env.Action(patchLua, cmdstr = "$GENCOMSTR")) if myenv.get("HAVE_READLINE", False) : myenv.Append(CPPDEFINES = ["LUA_USE_READLINE"]) myenv.MergeFlags(myenv["READLINE_FLAGS"]) env["SLUIFT"] = myenv.Program("sluift", [ "lua.c", "linit.c", ]) myenv.WriteVal("dll.c", myenv.Value("")) myenv.SharedLibrary("sluift", ["dll.c"]) if env["PLATFORM"] == "win32" : ssl_libs = [] if myenv.get("OPENSSL_DIR", False) : ssl_libs = [ os.path.join(env["OPENSSL_DIR"], "bin", "ssleay32.dll"), os.path.join(env["OPENSSL_DIR"], "bin", "libeay32.dll") ] myenv.WindowsBundle("Sluift", resources = {"": ssl_libs}) swift-im-2.0+dev6/Sluift/sluift.h0000644000175000017500000000053612227051773016613 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #if defined(SLUIFT_BUILD_DLL) #define SLUIFT_API __declspec(dllexport) #else #define SLUIFT_API extern #endif #include SLUIFT_API int (luaopen_sluift)(lua_State *L); swift-im-2.0+dev6/Sluift/SluiftException.h0000644000175000017500000000716212227051773020434 0ustar kismithkismith/* * 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 #include namespace Swift { class SluiftException : public std::exception { public: virtual ~SluiftException() throw() {} SluiftException(const std::string& reason) : reason(reason) { } SluiftException(const ClientError& error) { reason = "Disconnected: "; switch(error.getType()) { case ClientError::UnknownError: reason += "Unknown Error"; break; case ClientError::DomainNameResolveError: reason += "Unable to find server"; break; case ClientError::ConnectionError: reason += "Error connecting to server"; break; case ClientError::ConnectionReadError: reason += "Error while receiving server data"; break; case ClientError::ConnectionWriteError: reason += "Error while sending data to the server"; break; case ClientError::XMLError: reason += "Error parsing server data"; break; case ClientError::AuthenticationFailedError: reason += "Login/password invalid"; break; case ClientError::CompressionFailedError: reason += "Error while compressing stream"; break; case ClientError::ServerVerificationFailedError: reason += "Server verification failed"; break; case ClientError::NoSupportedAuthMechanismsError: reason += "Authentication mechanisms not supported"; break; case ClientError::UnexpectedElementError: reason += "Unexpected response"; break; case ClientError::ResourceBindError: reason += "Error binding resource"; break; case ClientError::RevokedError: reason += "Certificate got revoked"; break; case ClientError::RevocationCheckFailedError: reason += "Failed to do revokation check"; break; case ClientError::SessionStartError: reason += "Error starting session"; break; case ClientError::StreamError: reason += "Stream error"; break; case ClientError::TLSError: reason += "Encryption error"; break; case ClientError::ClientCertificateLoadError: reason += "Error loading certificate (Invalid password?)"; break; case ClientError::ClientCertificateError: reason += "Certificate not authorized"; break; case ClientError::UnknownCertificateError: reason += "Unknown certificate"; break; case ClientError::CertificateCardRemoved: reason += "Certificate card removed"; break; case ClientError::CertificateExpiredError: reason += "Certificate has expired"; break; case ClientError::CertificateNotYetValidError: reason += "Certificate is not yet valid"; break; case ClientError::CertificateSelfSignedError: reason += "Certificate is self-signed"; break; case ClientError::CertificateRejectedError: reason += "Certificate has been rejected"; break; case ClientError::CertificateUntrustedError: reason += "Certificate is not trusted"; break; case ClientError::InvalidCertificatePurposeError: reason += "Certificate cannot be used for encrypting your connection"; break; case ClientError::CertificatePathLengthExceededError: reason += "Certificate path length constraint exceeded"; break; case ClientError::InvalidCertificateSignatureError: reason += "Invalid certificate signature"; break; case ClientError::InvalidCAError: reason += "Invalid Certificate Authority"; break; case ClientError::InvalidServerIdentityError: reason += "Certificate does not match the host identity"; break; } } const std::string& getReason() const { return reason; } virtual const char* what() const throw() { return getReason().c_str(); } private: std::string reason; }; } swift-im-2.0+dev6/Sluift/linit.c0000644000175000017500000000340512227051773016415 0ustar kismithkismith#include #include #include #include "sluift.h" // A built-in table print function // From: http://lua-users.org/wiki/TableSerialization static const char tprint[] = "function tprint (tt, indent, done)\n" " done = done or {}\n" " indent = indent or 0\n" " if type(tt) == \"table\" then\n" " for key, value in pairs (tt) do\n" " io.write(string.rep (\" \", indent)) -- indent it\n" " if type (value) == \"table\" and not done [value] then\n" " done [value] = true\n" " io.write(string.format(\"[%s] => table\\n\", tostring (key)));\n" " io.write(string.rep (\" \", indent+4)) -- indent it\n" " io.write(\"(\\n\");\n" " tprint (value, indent + 7, done)\n" " io.write(string.rep (\" \", indent+4)) -- indent it\n" " io.write(\")\\n\");\n" " else\n" " io.write(string.format(\"[%s] => %s\\n\",\n" " tostring (key), tostring(value)))\n" " end\n" " end\n" " elseif type(tt) == \"nil\" then\n" " io.write(\"nil\\n\")\n" " else\n" " io.write(tt .. \"\\n\")\n" " end\n" "end\n"; static const luaL_Reg lualibs[] = { {"", luaopen_base}, {LUA_LOADLIBNAME, luaopen_package}, {LUA_TABLIBNAME, luaopen_table}, {LUA_IOLIBNAME, luaopen_io}, {LUA_OSLIBNAME, luaopen_os}, {LUA_STRLIBNAME, luaopen_string}, {LUA_MATHLIBNAME, luaopen_math}, {LUA_DBLIBNAME, luaopen_debug}, {"sluift", luaopen_sluift}, {NULL, NULL} }; LUALIB_API void luaL_openlibs (lua_State *L) { const luaL_Reg *lib = lualibs; for (; lib->func; lib++) { lua_pushcfunction(L, lib->func); lua_pushstring(L, lib->name); lua_call(L, 1, 0); } if (luaL_dostring(L, tprint) != 0) { fprintf(stderr, "%s\n", lua_tostring(L, -1)); lua_pop(L, 1); } } swift-im-2.0+dev6/Sluift/Watchdog.h0000644000175000017500000000146212227051773017044 0ustar kismithkismith/* * 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 namespace Swift { class Watchdog { public: Watchdog(int timeout, TimerFactory* timerFactory) : timedOut(false) { if (timeout > 0) { timer = timerFactory->createTimer(timeout); timer->start(); timer->onTick.connect(boost::bind(&Watchdog::handleTimerTick, this)); } else if (timeout == 0) { timedOut = true; } } ~Watchdog() { if (timer) { timer->stop(); } } bool getTimedOut() const { return timedOut; } private: void handleTimerTick() { timedOut = true; } private: Timer::ref timer; bool timedOut; }; } swift-im-2.0+dev6/Sluift/ResponseSink.h0000644000175000017500000000172212227051773017726 0ustar kismithkismith/* * 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 #include namespace Swift { template class ResponseSink { public: ResponseSink() : responseReceived(false) { } bool hasResponse() const { return responseReceived; } boost::shared_ptr getResponsePayload() const { return payload; } ErrorPayload::ref getResponseError() const { return error; } void operator()(boost::shared_ptr payload, ErrorPayload::ref error) { this->payload = payload; this->error = error; this->responseReceived = true; } void operator()(ErrorPayload::ref error) { this->error = error; this->responseReceived = true; } private: bool responseReceived; boost::shared_ptr payload; ErrorPayload::ref error; }; } swift-im-2.0+dev6/Sluift/Lua/0000755000175000017500000000000012227051773015651 5ustar kismithkismithswift-im-2.0+dev6/Sluift/Lua/Value.cpp0000644000175000017500000000301412227051773017427 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Value.h" #include #include #include using namespace Swift; using namespace Swift::Lua; namespace { struct PushVisitor : public boost::static_visitor<> { PushVisitor(lua_State* state) : state(state) { } void operator()(const Nil&) const { lua_pushnil(state); } void operator()(const bool& b) const { lua_pushboolean(state, b); } void operator()(const int& i) const { lua_pushnumber(state, i); } void operator()(const std::string& s) const { lua_pushstring(state, s.c_str()); } void operator()(const std::vector& values) const { lua_createtable(state, values.size(), 0); for(size_t i = 0; i < values.size(); ++i) { boost::apply_visitor(PushVisitor(state), values[i]); lua_rawseti(state, -2, i + 1); } } void operator()(const std::map >& table) const { lua_createtable(state, 0, table.size()); for(std::map >::const_iterator i = table.begin(); i != table.end(); ++i) { boost::apply_visitor(PushVisitor(state), *i->second); lua_setfield(state, -2, i->first.c_str()); } } lua_State* state; }; } namespace Swift { namespace Lua { void pushValue(lua_State* state, const Value& value) { boost::apply_visitor(PushVisitor(state), value); } }} swift-im-2.0+dev6/Sluift/Lua/Value.h0000644000175000017500000000131212227051773017073 0ustar kismithkismith/* * 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 #include #include #include #include struct lua_State; namespace Swift { namespace Lua { struct Nil {}; typedef boost::make_recursive_variant< Nil, bool, int, std::string, std::vector< boost::recursive_variant_ >, std::map > >::type Value; typedef std::map > Table; void pushValue(lua_State* state, const Value& value); } } swift-im-2.0+dev6/Sluift/Examples/0000755000175000017500000000000012227051773016706 5ustar kismithkismithswift-im-2.0+dev6/Sluift/Examples/EchoBot.lua0000644000175000017500000000143212227051773020734 0ustar kismithkismith-- -- Copyright (c) 2010 Remko Tronçon -- Licensed under the GNU General Public License v3. -- See Documentation/Licenses/GPLv3.txt for more information. -- -- -- An XMPP Echoing Bot -- -- This script logs into an XMPP server, sends initial presence, -- and then waits for incoming messages, and echoes them back. -- -- The following environment variables are used: -- * SLUIFT_JID, SWIFT_PASS: JID and password to log in with -- * SLUIFT_DEBUG: Sets whether debugging should be turned on -- require "sluift" sluift.debug = os.getenv("SLUIFT_DEBUG") or false c = sluift.new_client(os.getenv("SLUIFT_JID"), os.getenv("SLUIFT_PASS")) c:connect() c:send_presence("Send me a message") c:for_event(function(e) if e["type"] == "message" then c:send_message(e["from"], e["body"]) end end) swift-im-2.0+dev6/Sluift/Examples/RemoveUnreachableContacts.lua0000644000175000017500000000216512227051773024503 0ustar kismithkismith-- -- Copyright (c) 2010 Remko Tronçon -- Licensed under the GNU General Public License v3. -- See Documentation/Licenses/GPLv3.txt for more information. -- -- This script logs into an XMPP server, iterates over all roster items, -- and checks if their server is still alive. If not, the script asks you -- whether it should remove the contact from your contact list. -- -- The following environment variables are used: -- * SLUIFT_JID, SWIFT_PASS: JID and password to log in with -- * SLUIFT_DEBUG: Sets whether debugging should be turned on require "sluift" sluift.debug = os.getenv("SLUIFT_DEBUG") print "Connecting ..." c = sluift.new_client(os.getenv("SLUIFT_JID"), os.getenv("SLUIFT_PASS")) c:connect() print "Checking for unreachable contacts ..." for jid, _ in pairs(c:get_contacts()) do _, err = c:get_version(sluift.jid_domain(jid), 10000) if err == "Remote server not found" or err == "Timeout" then print("Delete " .. jid .. " (" .. err .. ") ? [y/n/q]") answer = io.read() if answer == "y" then c:remove_contact(jid) elseif answer == "q" then break end end end print "Done. Exiting ..." c:disconnect() swift-im-2.0+dev6/Sluift/Examples/Wonderland.lua0000755000175000017500000000340612227051773021514 0ustar kismithkismith-- -- Copyright (c) 2011 Remko Tronçon -- Licensed under the GNU General Public License v3. -- See Documentation/Licenses/GPLv3.txt for more information. -- -- This script creates the wonderland world example. -- require "sluift" --sluift.debug = true characters = { {jid = "alice@wonderland.lit", name = "Alice", groups = {}, presence = ""}, {jid = "hatter@wonderland.lit", name = "Mad Hatter", groups = {}, presence = "awayAt the Tea Party"}, {jid ="queen@wonderland.lit", name = "Queen of Hearts", groups = {}, presence = "dndExecuting"}, {jid = "rabbit@wonderland.lit", name = "White Rabbit", groups = {"Animals"}, presence = "Oh dear!"}, {jid = "turtle@wonderland.lit", name = "Mock Turtle", groups = {"Animals"}, presence = ""}, } clients = {} for _, character in ipairs(characters) do print("Connecting " .. character["name"] .. "...") client = sluift.new_client(character["jid"], os.getenv("SLUIFT_PASS")) client:set_options({compress = false, tls = false}) client:connect() client:get_contacts() client:send(character["presence"]) table.insert(clients, client) for _, contact in ipairs(characters) do if contact["jid"] ~= character["jid"] then client:add_contact(contact) end end end print("Confirming subscriptions") for _, client in ipairs(clients) do for _, contact in ipairs(characters) do client:confirm_subscription(contact["jid"]) end end print("Done. Waiting ...") while true do for _, client in ipairs(clients) do client:for_event(function(e) if e["type"] == "message" then client:send_message(e["from"], "Off with their heads!") end end, 1000) end sluift.sleep(1000) end swift-im-2.0+dev6/Sluift/Examples/Login.lua0000644000175000017500000000145512227051773020466 0ustar kismithkismith-- -- Copyright (c) 2010 Remko Tronçon -- Licensed under the GNU General Public License v3. -- See Documentation/Licenses/GPLv3.txt for more information. -- -- This script logs into an XMPP server, and sends initial presence -- Useful as initialization script for an interactive session ('-i'), -- or as a starting point for scripts. -- -- The following environment variables are used: -- * SLUIFT_JID, SWIFT_PASS: JID and password to log in with -- * SLUIFT_DEBUG: Sets whether debugging should be turned on require "sluift" sluift.debug = os.getenv("SLUIFT_DEBUG") or false print("Connecting " .. os.getenv("SLUIFT_JID") .. " ...") c = sluift.new_client(os.getenv("SLUIFT_JID"), os.getenv("SLUIFT_PASS")) c:set_options({compress = false, tls = false}) c:connect():send_presence("") print("Connected ...") swift-im-2.0+dev6/Sluift/Examples/CollectVersions.lua0000644000175000017500000000120612227051773022526 0ustar kismithkismith-- -- Copyright (c) 2010 Remko Tronçon -- Licensed under the GNU General Public License v3. -- See Documentation/Licenses/GPLv3.txt for more information. -- -- This script logs into an XMPP server, and collects statistics about -- the server software of all contacts in your roster require "sluift" c = sluift.new_client(os.getenv("SLUIFT_JID"), os.getenv("SLUIFT_PASS")) c:connect() versions = {} for jid, _ in pairs(c:get_contacts()) do v = c:get_version(sluift.jid_domain(jid)) if v then versions[v["name"]] = (versions[v["name"]] or 0) + 1 end end for name, count in pairs(versions) do print(name .. ": " .. count) end c:disconnect() swift-im-2.0+dev6/Sluift/README0000644000175000017500000000013512227051773016007 0ustar kismithkismithFor example scripts, see the 'login.lua' script and the scripts in Swiften/QA/ScriptedTests. swift-im-2.0+dev6/Sluift/sluift.cpp0000644000175000017500000005446612227051773017161 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "sluift.h" #include #include #include #include #include #include #include #include "Watchdog.h" #include "SluiftException.h" #include "ResponseSink.h" #include "Lua/Value.h" using namespace Swift; #define SLUIFT_CLIENT "SluiftClient*" /******************************************************************************* * Forward declarations ******************************************************************************/ static bool debug = false; static int globalTimeout = 30000; /******************************************************************************* * Helper classes ******************************************************************************/ SimpleEventLoop eventLoop; BoostNetworkFactories networkFactories(&eventLoop); class SluiftClient { public: SluiftClient(const JID& jid, const std::string& password) : tracer(NULL) { client = new Client(jid, password, &networkFactories); client->setAlwaysTrustCertificates(); client->onDisconnected.connect(boost::bind(&SluiftClient::handleDisconnected, this, _1)); client->onMessageReceived.connect(boost::bind(&SluiftClient::handleIncomingEvent, this, _1)); client->onPresenceReceived.connect(boost::bind(&SluiftClient::handleIncomingEvent, this, _1)); client->getRoster()->onInitialRosterPopulated.connect(boost::bind(&SluiftClient::handleInitialRosterPopulated, this)); if (debug) { tracer = new ClientXMLTracer(client); } } ~SluiftClient() { delete tracer; delete client; } Client* getClient() { return client; } ClientOptions& getOptions() { return options; } void connect() { rosterReceived = false; client->connect(options); } void connect(const std::string& host) { rosterReceived = false; options.manualHostname = host; client->connect(options); } void waitConnected() { Watchdog watchdog(globalTimeout, networkFactories.getTimerFactory()); while (!watchdog.getTimedOut() && client->isActive() && !client->isAvailable()) { eventLoop.runUntilEvents(); } if (watchdog.getTimedOut()) { client->disconnect(); throw SluiftException("Timeout while connecting"); } } bool isConnected() const { return client->isAvailable(); } void sendMessage(const JID& to, const std::string& body) { Message::ref message = boost::make_shared(); message->setTo(to); message->setBody(body); client->sendMessage(message); } void sendPresence(const std::string& status) { client->sendPresence(boost::make_shared(status)); } boost::optional sendQuery(const JID& jid, IQ::Type type, const std::string& data, int timeout) { rawRequestResponse.reset(); RawRequest::ref request = RawRequest::create(type, jid, data, client->getIQRouter()); boost::signals::scoped_connection c = request->onResponse.connect(boost::bind(&SluiftClient::handleRawRequestResponse, this, _1)); request->send(); Watchdog watchdog(timeout, networkFactories.getTimerFactory()); while (!watchdog.getTimedOut() && !rawRequestResponse) { eventLoop.runUntilEvents(); } if (watchdog.getTimedOut()) { return boost::optional(); } else { return *rawRequestResponse; } } void disconnect() { client->disconnect(); while (client->isActive()) { eventLoop.runUntilEvents(); } } void setSoftwareVersion(const std::string& name, const std::string& version, const std::string& os) { client->setSoftwareVersion(name, version, os); } Stanza::ref getNextEvent(int timeout) { if (!pendingEvents.empty()) { Stanza::ref event = pendingEvents.front(); pendingEvents.pop_front(); return event; } Watchdog watchdog(timeout, networkFactories.getTimerFactory()); while (!watchdog.getTimedOut() && pendingEvents.empty() && !client->isActive()) { eventLoop.runUntilEvents(); } if (watchdog.getTimedOut() || !client->isActive()) { return Stanza::ref(); } else if (!pendingEvents.empty()) { Stanza::ref event = pendingEvents.front(); pendingEvents.pop_front(); return event; } else { return Stanza::ref(); } } std::vector getRoster() { if (!rosterReceived) { // If we haven't requested it yet, request it for the first time client->requestRoster(); } while (!rosterReceived) { eventLoop.runUntilEvents(); } return client->getRoster()->getItems(); } private: void handleIncomingEvent(Stanza::ref stanza) { pendingEvents.push_back(stanza); } void handleInitialRosterPopulated() { rosterReceived = true; } void handleRawRequestResponse(const std::string& response) { rawRequestResponse = response; } void handleDisconnected(const boost::optional& error) { if (error) { throw SluiftException(*error); } } private: Client* client; ClientOptions options; ClientXMLTracer* tracer; bool rosterReceived; std::deque pendingEvents; boost::optional rawRequestResponse; }; /******************************************************************************* * Client functions. ******************************************************************************/ static inline SluiftClient* getClient(lua_State* L) { return *reinterpret_cast(luaL_checkudata(L, 1, SLUIFT_CLIENT)); } static int sluift_client_connect(lua_State *L) { try { SluiftClient* client = getClient(L); std::string host; if (lua_type(L, 2) != LUA_TNONE) { host = luaL_checkstring(L, 2); } if (host.empty()) { client->connect(); } else { client->connect(host); } client->waitConnected(); return 1; } catch (const SluiftException& e) { return luaL_error(L, e.getReason().c_str()); } } static int sluift_client_async_connect(lua_State *L) { try { getClient(L)->connect(); return 1; } catch (const SluiftException& e) { return luaL_error(L, e.getReason().c_str()); } } static int sluift_client_wait_connected(lua_State *L) { try { getClient(L)->waitConnected(); return 1; } catch (const SluiftException& e) { return luaL_error(L, e.getReason().c_str()); } } static int sluift_client_is_connected(lua_State *L) { lua_pushboolean(L, getClient(L)->isConnected()); return 1; } static int sluift_client_disconnect(lua_State *L) { try { getClient(L)->disconnect(); return 1; } catch (const SluiftException& e) { return luaL_error(L, e.getReason().c_str()); } } static int sluift_client_set_version(lua_State *L) { try { eventLoop.runOnce(); SluiftClient* client = getClient(L); luaL_checktype(L, 2, LUA_TTABLE); lua_getfield(L, 2, "name"); const char* rawName = lua_tostring(L, -1); lua_getfield(L, 2, "version"); const char* rawVersion = lua_tostring(L, -1); lua_getfield(L, 2, "os"); const char* rawOS = lua_tostring(L, -1); client->setSoftwareVersion(rawName ? rawName : "", rawVersion ? rawVersion : "", rawOS ? rawOS : ""); lua_pop(L, 3); lua_pushvalue(L, 1); return 1; } catch (const SluiftException& e) { return luaL_error(L, e.getReason().c_str()); } } static int sluift_client_get_contacts(lua_State *L) { try { eventLoop.runOnce(); SluiftClient* client = getClient(L); Lua::Table contactsTable; foreach(const XMPPRosterItem& item, client->getRoster()) { std::string subscription; switch(item.getSubscription()) { case RosterItemPayload::None: subscription = "none"; break; case RosterItemPayload::To: subscription = "to"; break; case RosterItemPayload::From: subscription = "from"; break; case RosterItemPayload::Both: subscription = "both"; break; case RosterItemPayload::Remove: subscription = "remove"; break; } Lua::Value groups(std::vector(item.getGroups().begin(), item.getGroups().end())); Lua::Table itemTable = boost::assign::map_list_of ("jid", boost::make_shared(item.getJID().toString())) ("name", boost::make_shared(item.getName())) ("subscription", boost::make_shared(subscription)) ("groups", boost::make_shared(std::vector(item.getGroups().begin(), item.getGroups().end()))); contactsTable[item.getJID().toString()] = boost::make_shared(itemTable); } pushValue(L, contactsTable); return 1; } catch (const SluiftException& e) { return luaL_error(L, e.getReason().c_str()); } } static int sluift_client_get_version(lua_State *L) { try { SluiftClient* client = getClient(L); int timeout = -1; if (lua_type(L, 3) != LUA_TNONE) { timeout = luaL_checknumber(L, 3); } ResponseSink sink; GetSoftwareVersionRequest::ref request = GetSoftwareVersionRequest::create(std::string(luaL_checkstring(L, 2)), client->getClient()->getIQRouter()); boost::signals::scoped_connection c = request->onResponse.connect(boost::ref(sink)); request->send(); Watchdog watchdog(timeout, networkFactories.getTimerFactory()); while (!watchdog.getTimedOut() && !sink.hasResponse()) { eventLoop.runUntilEvents(); } ErrorPayload::ref error = sink.getResponseError(); if (error || watchdog.getTimedOut()) { lua_pushnil(L); if (watchdog.getTimedOut()) { lua_pushstring(L, "Timeout"); } else if (error->getCondition() == ErrorPayload::RemoteServerNotFound) { lua_pushstring(L, "Remote server not found"); } // TODO else { lua_pushstring(L, "Error"); } return 2; } else if (SoftwareVersion::ref version = sink.getResponsePayload()) { Lua::Table result = boost::assign::map_list_of ("name", boost::make_shared(version->getName())) ("version", boost::make_shared(version->getVersion())) ("os", boost::make_shared(version->getOS())); Lua::pushValue(L, result); } else { lua_pushnil(L); } return 1; } catch (const SluiftException& e) { return luaL_error(L, e.getReason().c_str()); } } static int sluift_client_send_message(lua_State *L) { try { eventLoop.runOnce(); getClient(L)->sendMessage(std::string(luaL_checkstring(L, 2)), luaL_checkstring(L, 3)); lua_pushvalue(L, 1); return 1; } catch (const SluiftException& e) { return luaL_error(L, e.getReason().c_str()); } } static int sluift_client_send_presence(lua_State *L) { try { eventLoop.runOnce(); getClient(L)->sendPresence(std::string(luaL_checkstring(L, 2))); lua_pushvalue(L, 1); return 0; } catch (const SluiftException& e) { return luaL_error(L, e.getReason().c_str()); } } static int sluift_client_get(lua_State *L) { try { SluiftClient* client = getClient(L); JID jid; std::string data; int timeout = -1; if (lua_type(L, 3) == LUA_TSTRING) { jid = JID(std::string(luaL_checkstring(L, 2))); data = std::string(luaL_checkstring(L, 3)); if (lua_type(L, 4) != LUA_TNONE) { timeout = luaL_checknumber(L, 4); } } else { data = std::string(luaL_checkstring(L, 2)); if (lua_type(L, 3) != LUA_TNONE) { timeout = luaL_checknumber(L, 3); } } boost::optional result = client->sendQuery(jid, IQ::Get, data, timeout); if (result) { lua_pushstring(L, result->c_str()); } else { lua_pushnil(L); } return 1; } catch (const SluiftException& e) { return luaL_error(L, e.getReason().c_str()); } } static int sluift_client_set(lua_State *L) { try { SluiftClient* client = getClient(L); JID jid; std::string data; int timeout = -1; if (lua_type(L, 3) == LUA_TSTRING) { jid = JID(std::string(luaL_checkstring(L, 2))); data = std::string(luaL_checkstring(L, 3)); if (lua_type(L, 4) != LUA_TNONE) { timeout = luaL_checknumber(L, 4); } } else { data = std::string(luaL_checkstring(L, 2)); if (lua_type(L, 3) != LUA_TNONE) { timeout = luaL_checknumber(L, 3); } } boost::optional result = client->sendQuery(jid, IQ::Set, data, timeout); if (result) { lua_pushstring(L, result->c_str()); } else { lua_pushnil(L); } return 1; } catch (const SluiftException& e) { return luaL_error(L, e.getReason().c_str()); } } static int sluift_client_send(lua_State *L) { try { eventLoop.runOnce(); getClient(L)->getClient()->sendData(std::string(luaL_checkstring(L, 2))); lua_pushvalue(L, 1); return 0; } catch (const SluiftException& e) { return luaL_error(L, e.getReason().c_str()); } } static int sluift_client_set_options(lua_State* L) { SluiftClient* client = getClient(L); luaL_checktype(L, 2, LUA_TTABLE); lua_getfield(L, 2, "ack"); if (!lua_isnil(L, -1)) { client->getOptions().useAcks = lua_toboolean(L, -1); } lua_getfield(L, 2, "compress"); if (!lua_isnil(L, -1)) { client->getOptions().useStreamCompression = lua_toboolean(L, -1); } lua_getfield(L, 2, "tls"); if (!lua_isnil(L, -1)) { bool useTLS = lua_toboolean(L, -1); client->getOptions().useTLS = (useTLS ? ClientOptions::UseTLSWhenAvailable : ClientOptions::NeverUseTLS); } lua_pushvalue(L, 1); return 0; } static void pushEvent(lua_State* L, Stanza::ref event) { if (Message::ref message = boost::dynamic_pointer_cast(event)) { Lua::Table result = boost::assign::map_list_of ("type", boost::make_shared(std::string("message"))) ("from", boost::make_shared(message->getFrom().toString())) ("body", boost::make_shared(message->getBody())); Lua::pushValue(L, result); } else if (Presence::ref presence = boost::dynamic_pointer_cast(event)) { Lua::Table result = boost::assign::map_list_of ("type", boost::make_shared(std::string("presence"))) ("from", boost::make_shared(presence->getFrom().toString())) ("status", boost::make_shared(presence->getStatus())); Lua::pushValue(L, result); } else { lua_pushnil(L); } } static int sluift_client_for_event(lua_State *L) { try { eventLoop.runOnce(); SluiftClient* client = getClient(L); luaL_checktype(L, 2, LUA_TFUNCTION); int timeout = -1; if (lua_type(L, 3) != LUA_TNONE) { timeout = lua_tonumber(L, 3); } while (true) { Stanza::ref event = client->getNextEvent(timeout); if (!event) { // We got a timeout lua_pushnil(L); return 1; } else { // Push the function and event on the stack lua_pushvalue(L, 2); pushEvent(L, event); int oldTop = lua_gettop(L) - 2; lua_call(L, 1, LUA_MULTRET); int returnValues = lua_gettop(L) - oldTop; if (returnValues > 0) { lua_remove(L, -1 - returnValues); return returnValues; } } } } catch (const SluiftException& e) { return luaL_error(L, e.getReason().c_str()); } } static int sluift_client_get_next_event(lua_State *L) { try { eventLoop.runOnce(); SluiftClient* client = getClient(L); int timeout = -1; if (lua_type(L, 2) != LUA_TNONE) { timeout = lua_tonumber(L, 2); } pushEvent(L, client->getNextEvent(timeout)); return 1; } catch (const SluiftException& e) { return luaL_error(L, e.getReason().c_str()); } } static int sluift_client_add_contact(lua_State* L) { try { eventLoop.runOnce(); SluiftClient* client = getClient(L); RosterItemPayload item; if (lua_type(L, 2) == LUA_TTABLE) { lua_getfield(L, 2, "jid"); const char* rawJID = lua_tostring(L, -1); if (rawJID) { item.setJID(std::string(rawJID)); } lua_getfield(L, 2, "name"); const char* rawName = lua_tostring(L, -1); if (rawName) { item.setName(rawName); } lua_getfield(L, 2, "groups"); if (!lua_isnil(L, -1)) { if (lua_type(L, -1) == LUA_TTABLE) { for (size_t i = 1; i <= lua_objlen(L, -1); ++i) { lua_rawgeti(L, -1, i); const char* rawGroup = lua_tostring(L, -1); if (rawGroup) { item.addGroup(rawGroup); } lua_pop(L, 1); } } else { return luaL_error(L, "Groups should be a table"); } } } else { item.setJID(luaL_checkstring(L, 2)); } client->getRoster(); if (!client->getClient()->getRoster()->containsJID(item.getJID())) { RosterPayload::ref roster = boost::make_shared(); roster->addItem(item); ResponseSink sink; SetRosterRequest::ref request = SetRosterRequest::create(roster, client->getClient()->getIQRouter()); boost::signals::scoped_connection c = request->onResponse.connect(boost::ref(sink)); request->send(); while (!sink.hasResponse()) { eventLoop.runUntilEvents(); } if (sink.getResponseError()) { lua_pushboolean(L, false); return 1; } } client->getClient()->getSubscriptionManager()->requestSubscription(item.getJID()); lua_pushboolean(L, true); return 1; } catch (const SluiftException& e) { return luaL_error(L, e.getReason().c_str()); } } static int sluift_client_remove_contact(lua_State* L) { try { eventLoop.runOnce(); SluiftClient* client = getClient(L); JID jid(luaL_checkstring(L, 2)); RosterPayload::ref roster = boost::make_shared(); roster->addItem(RosterItemPayload(JID(luaL_checkstring(L, 2)), "", RosterItemPayload::Remove)); ResponseSink sink; SetRosterRequest::ref request = SetRosterRequest::create(roster, client->getClient()->getIQRouter()); boost::signals::scoped_connection c = request->onResponse.connect(boost::ref(sink)); request->send(); while (!sink.hasResponse()) { eventLoop.runUntilEvents(); } lua_pushboolean(L, !sink.getResponseError()); return 1; } catch (const SluiftException& e) { return luaL_error(L, e.getReason().c_str()); } } static int sluift_client_confirm_subscription(lua_State* L) { try { eventLoop.runOnce(); SluiftClient* client = getClient(L); JID jid(luaL_checkstring(L, 2)); client->getClient()->getSubscriptionManager()->confirmSubscription(jid); return 0; } catch (const SluiftException& e) { return luaL_error(L, e.getReason().c_str()); } } static int sluift_client_cancel_subscription(lua_State* L) { try { eventLoop.runOnce(); SluiftClient* client = getClient(L); JID jid(luaL_checkstring(L, 2)); client->getClient()->getSubscriptionManager()->cancelSubscription(jid); return 0; } catch (const SluiftException& e) { return luaL_error(L, e.getReason().c_str()); } } static int sluift_client_gc (lua_State *L) { SluiftClient* client = getClient(L); delete client; return 0; } static const luaL_reg sluift_client_functions[] = { {"connect", sluift_client_connect}, {"async_connect", sluift_client_async_connect}, {"wait_connected", sluift_client_wait_connected}, {"is_connected", sluift_client_is_connected}, {"disconnect", sluift_client_disconnect}, {"send_message", sluift_client_send_message}, {"send_presence", sluift_client_send_presence}, {"get", sluift_client_get}, {"set", sluift_client_set}, {"send", sluift_client_send}, {"set_version", sluift_client_set_version}, {"get_contacts", sluift_client_get_contacts}, {"get_version", sluift_client_get_version}, {"set_options", sluift_client_set_options}, {"for_event", sluift_client_for_event}, {"get_next_event", sluift_client_get_next_event}, {"add_contact", sluift_client_add_contact}, {"remove_contact", sluift_client_remove_contact}, {"confirm_subscription", sluift_client_confirm_subscription}, {"cancel_subscription", sluift_client_cancel_subscription}, {"__gc", sluift_client_gc}, {NULL, NULL} }; /******************************************************************************* * Module functions ******************************************************************************/ static int sluift_new_client(lua_State *L) { try { JID jid(std::string(luaL_checkstring(L, 1))); std::string password(luaL_checkstring(L, 2)); SluiftClient** client = reinterpret_cast(lua_newuserdata(L, sizeof(SluiftClient*))); luaL_getmetatable(L, SLUIFT_CLIENT); lua_setmetatable(L, -2); *client = new SluiftClient(jid, password); return 1; } catch (const SluiftException& e) { return luaL_error(L, e.getReason().c_str()); } } static int sluift_jid_to_bare(lua_State *L) { JID jid(std::string(luaL_checkstring(L, 1))); lua_pushstring(L, jid.toBare().toString().c_str()); return 1; } static int sluift_jid_node(lua_State *L) { JID jid(std::string(luaL_checkstring(L, 1))); lua_pushstring(L, jid.getNode().c_str()); return 1; } static int sluift_jid_domain(lua_State *L) { JID jid(std::string(luaL_checkstring(L, 1))); lua_pushstring(L, jid.getDomain().c_str()); return 1; } static int sluift_jid_resource(lua_State *L) { JID jid(std::string(luaL_checkstring(L, 1))); lua_pushstring(L, jid.getResource().c_str()); return 1; } static int sluift_sleep(lua_State *L) { try { eventLoop.runOnce(); int timeout = luaL_checknumber(L, 1); Watchdog watchdog(timeout, networkFactories.getTimerFactory()); while (!watchdog.getTimedOut()) { Swift::sleep(std::min(100, timeout)); eventLoop.runOnce(); } return 0; } catch (const SluiftException& e) { return luaL_error(L, e.getReason().c_str()); } } static int sluift_index(lua_State *L) { std::string key(luaL_checkstring(L, 2)); if (key == "debug") { lua_pushboolean(L, debug); return 1; } else if (key == "timeout") { lua_pushnumber(L, globalTimeout); return 1; } else { return luaL_error(L, "Invalid index"); } } static int sluift_newindex(lua_State *L) { std::string key(luaL_checkstring(L, 2)); if (key == "debug") { debug = lua_toboolean(L, 3); return 0; } else if (key == "timeout") { globalTimeout = luaL_checknumber(L, 3); return 0; } else { return luaL_error(L, "Invalid index"); } } static const luaL_reg sluift_functions[] = { {"new_client", sluift_new_client}, {"jid_to_bare", sluift_jid_to_bare}, {"jid_node", sluift_jid_node}, {"jid_domain", sluift_jid_domain}, {"jid_resource", sluift_jid_resource}, {"sleep", sluift_sleep}, {NULL, NULL} }; /******************************************************************************* * Module registration ******************************************************************************/ SLUIFT_API int luaopen_sluift(lua_State *L) { // Register functions luaL_register(L, "sluift", sluift_functions); lua_createtable(L, 0, 0); lua_pushcclosure(L, sluift_index, 0); lua_setfield(L, -2, "__index"); lua_pushcclosure(L, sluift_newindex, 0); lua_setfield(L, -2, "__newindex"); lua_setmetatable(L, -2); // Register the client metatable luaL_newmetatable(L, SLUIFT_CLIENT); lua_pushvalue(L, -1); lua_setfield(L, -2, "__index"); luaL_register(L, NULL, sluift_client_functions); return 1; } swift-im-2.0+dev6/QA/0000755000175000017500000000000012227051774014164 5ustar kismithkismithswift-im-2.0+dev6/QA/SConscript0000644000175000017500000000006312227051773016174 0ustar kismithkismithSConscript(dirs = [ "Checker", "UnitTest" ]) swift-im-2.0+dev6/QA/valgrind.supp0000644000175000017500000001312012227051773016677 0ustar kismithkismith{ ZLib doesn't allocate its buffer. This is no bug according to the FAQ. Memcheck:Cond fun:longest_match fun:deflate_slow fun:deflate } { Not sure why this happens. Memcheck:Leak fun:calloc fun:_dl_allocate_tls fun:pthread_create@@GLIBC_2.1 fun:_ZN5boost6thread12start_threadEv } { Memcheck:Leak fun:malloc fun:__cxa_get_globals fun:__cxa_allocate_exception fun:_ZN5boost14copy_exceptionINS_16exception_detail10bad_alloc_EEENS_10shared_ptrIKNS1_10clone_baseEEERKT_ fun:_ZN5boost16exception_detail13get_bad_allocILi42EEENS_10shared_ptrIKNS0_10clone_baseEEEv fun:_Z41__static_initialization_and_destruction_0ii fun:_GLOBAL__I__ZN12_GLOBAL__N_12_1E fun:_ZN16ImageLoaderMachO18doModInitFunctionsERKN11ImageLoader11LinkContextE fun:_ZN11ImageLoader23recursiveInitializationERKNS_11LinkContextEj fun:_ZN11ImageLoader15runInitializersERKNS_11LinkContextE fun:_ZN4dyld24initializeMainExecutableEv fun:_ZN4dyld5_mainEPK12macho_headermiPPKcS5_S5_ } { Memcheck:Param socketcall.sendto(msg) fun:sendto fun:getaddrinfo } { Memcheck:Cond fun:BN_bin2bn } { Memcheck:Cond fun:BN_num_bits_word } { Memcheck:Cond fun:BN_num_bits } { Memcheck:Value4 fun:BN_mod_exp_mont_consttime fun:BN_mod_exp_mont } { Memcheck:Value4 fun:BN_num_bits_word fun:BN_mod_exp_mont_consttime fun:BN_mod_exp_mont } { Memcheck:Value8 fun:BN_num_bits } { Memcheck:Value8 fun:BN_mod_exp_mont_consttime } { Memcheck:Cond fun:*mersenne_twister* } { Memcheck:Leak fun:malloc fun:__cxa_get_globals } { ZLib Memcheck:Cond fun:deflateEnd fun:deflateSetDictionary fun:deflate } { Uninitialized value from boost uuid Memcheck:Value8 fun:_ZSt13__int_to_charIcmEiPT_T0_PKS0_St13_Ios_Fmtflagsb fun:_ZNKSt7num_putIcSt19ostreambuf_iteratorIcSt11char_traitsIcEEE13_M_insert_intImEES3_S3_RSt8ios_basecT_ fun:_ZNKSt7num_putIcSt19ostreambuf_iteratorIcSt11char_traitsIcEEE6do_putES3_RSt8ios_basecm fun:_ZNSo9_M_insertImEERSoT_ fun:_ZN5boost5uuidslsIcSt11char_traitsIcEEERSt13basic_ostreamIT_T0_ES8_RKNS0_4uuidE fun:_ZN5Swift13ClientSession13handleElementEN5boost10shared_ptrINS_7ElementEEE fun:_ZNK5boost4_mfi3mf1IvN5Swift13ClientSessionENS_10shared_ptrINS2_7ElementEEEE4callINS4_IS3_EES6_EEvRT_PKvRT0_ fun:_ZNK5boost4_mfi3mf1IvN5Swift13ClientSessionENS_10shared_ptrINS2_7ElementEEEEclINS4_IS3_EEEEvRT_S6_ fun:_ZN5boost3_bi5list2INS0_5valueINS_10shared_ptrIN5Swift13ClientSessionEEEEENS_3argILi1EEEEclINS_4_mfi3mf1IvS5_NS3_INS4_7ElementEEEEENS0_5list1IRSF_EEEEvNS0_4typeIvEERT_RT0_i fun:_ZN5boost3_bi6bind_tIvNS_4_mfi3mf1IvN5Swift13ClientSessionENS_10shared_ptrINS4_7ElementEEEEENS0_5list2INS0_5valueINS6_IS5_EEEENS_3argILi1EEEEEEclIS8_EEvRT_ fun:_ZN5boost6detail8function26void_function_obj_invoker1INS_3_bi6bind_tIvNS_4_mfi3mf1IvN5Swift13ClientSessionENS_10shared_ptrINS7_7ElementEEEEENS3_5list2INS3_5valueINS9_IS8_EEEENS_3argILi1EEEEEEEvSB_E6invokeERNS1_15function_bufferESB_ fun:_ZNK5boost9function1IvNS_10shared_ptrIN5Swift7ElementEEEEclES4_ } { Uninitialized value from boost uuid Memcheck:Cond fun:_ZSt13__int_to_charIcmEiPT_T0_PKS0_St13_Ios_Fmtflagsb fun:_ZNKSt7num_putIcSt19ostreambuf_iteratorIcSt11char_traitsIcEEE13_M_insert_intImEES3_S3_RSt8ios_basecT_ fun:_ZNKSt7num_putIcSt19ostreambuf_iteratorIcSt11char_traitsIcEEE6do_putES3_RSt8ios_basecm fun:_ZNSo9_M_insertImEERSoT_ fun:_ZN5boost5uuidslsIcSt11char_traitsIcEEERSt13basic_ostreamIT_T0_ES8_RKNS0_4uuidE fun:_ZN5Swift13ClientSession13handleElementEN5boost10shared_ptrINS_7ElementEEE fun:_ZNK5boost4_mfi3mf1IvN5Swift13ClientSessionENS_10shared_ptrINS2_7ElementEEEE4callINS4_IS3_EES6_EEvRT_PKvRT0_ fun:_ZNK5boost4_mfi3mf1IvN5Swift13ClientSessionENS_10shared_ptrINS2_7ElementEEEEclINS4_IS3_EEEEvRT_S6_ fun:_ZN5boost3_bi5list2INS0_5valueINS_10shared_ptrIN5Swift13ClientSessionEEEEENS_3argILi1EEEEclINS_4_mfi3mf1IvS5_NS3_INS4_7ElementEEEEENS0_5list1IRSF_EEEEvNS0_4typeIvEERT_RT0_i fun:_ZN5boost3_bi6bind_tIvNS_4_mfi3mf1IvN5Swift13ClientSessionENS_10shared_ptrINS4_7ElementEEEEENS0_5list2INS0_5valueINS6_IS5_EEEENS_3argILi1EEEEEEclIS8_EEvRT_ fun:_ZN5boost6detail8function26void_function_obj_invoker1INS_3_bi6bind_tIvNS_4_mfi3mf1IvN5Swift13ClientSessionENS_10shared_ptrINS7_7ElementEEEEENS3_5list2INS3_5valueINS9_IS8_EEEENS_3argILi1EEEEEEEvSB_E6invokeERNS1_15function_bufferESB_ fun:_ZNK5boost9function1IvNS_10shared_ptrIN5Swift7ElementEEEEclES4_ } { dateFromString warning Memcheck:Cond fun:strftime_l fun:_ZNKSt11__timepunctIcE6_M_putEPcmPKcPK2tm fun:_ZNKSt8time_putIcSt19ostreambuf_iteratorIcSt11char_traitsIcEEE6do_putES3_RSt8ios_basecPK2tmcc fun:_ZNKSt8time_putIcSt19ostreambuf_iteratorIcSt11char_traitsIcEEE3putES3_RSt8ios_basecPK2tmPKcSB_ fun:_ZN5boost9date_time20gather_month_stringsIcEESt6vectorISbIT_St11char_traitsIS3_ESaIS3_EESaIS7_EERKSt6localeb fun:_ZN5boost9date_time18format_date_parserINS_9gregorian4dateEcEC1ERKSsRKSt6locale fun:_ZN5boost9date_time16date_input_facetINS_9gregorian4dateEcSt19istreambuf_iteratorIcSt11char_traitsIcEEEC2ERKSsm fun:_ZN5boost9date_time16time_input_facetINS_10posix_time5ptimeEcSt19istreambuf_iteratorIcSt11char_traitsIcEEEC1ERKSsm fun:_ZN5Swift11DelayParser14dateFromStringERKNS_6StringE swift-im-2.0+dev6/QA/UnitTest/0000755000175000017500000000000012227051774015743 5ustar kismithkismithswift-im-2.0+dev6/QA/UnitTest/SConscript0000644000175000017500000000202112227051773017747 0ustar kismithkismithimport os Import("env") if env["TEST"] : if env["SCONS_STAGE"] == "flags" : env["UNITTEST_SOURCES"] = [] env["UNITTEST_OBJECTS"] = [] if env["SCONS_STAGE"] == "test" : myenv = env.Clone() myenv.UseFlags(env.get("CHECKER_FLAGS","")) myenv.UseFlags(env.get("SLIMBER_FLAGS","")) myenv.UseFlags(env.get("SWIFT_CONTROLLERS_FLAGS","")) myenv.UseFlags(env.get("SWIFTOOLS_FLAGS","")) myenv.UseFlags(env.get("LIMBER_FLAGS","")) myenv.UseFlags(env.get("SWIFTEN_FLAGS","")) myenv.UseFlags(env.get("CPPUNIT_FLAGS","")) myenv.UseFlags(env.get("SWIFTEN_DEP_FLAGS", "")) if env.get("HAVE_LIBXML") : myenv.Append(CPPDEFINES = ["HAVE_LIBXML"]) if env.get("HAVE_EXPAT") : myenv.Append(CPPDEFINES = ["HAVE_EXPAT"]) if env["TEST_CREATE_LIBRARIES"] : lib = myenv.StaticLibrary("Swift_UnitTests", env["UNITTEST_SOURCES"] + env["UNITTEST_OBJECTS"]) myenv.Program("checker", lib) else : checker = myenv.Program("checker", env["UNITTEST_SOURCES"] + env["UNITTEST_OBJECTS"]) myenv.Test(checker, is_checker = True) swift-im-2.0+dev6/QA/UnitTest/Unit Tests.launch0000644000175000017500000000273112227051773021143 0ustar kismithkismith swift-im-2.0+dev6/QA/UnitTest/template/0000755000175000017500000000000012227051773017555 5ustar kismithkismithswift-im-2.0+dev6/QA/UnitTest/template/FooTest.cpp0000644000175000017500000000103212227051773021640 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include using namespace Swift; class FooTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(FooTest); CPPUNIT_TEST(testBar); CPPUNIT_TEST_SUITE_END(); public: void setUp() { } void tearDown() { } void testBar() { } }; CPPUNIT_TEST_SUITE_REGISTRATION(FooTest); swift-im-2.0+dev6/QA/Checker/0000755000175000017500000000000012227051773015527 5ustar kismithkismithswift-im-2.0+dev6/QA/Checker/SConscript0000644000175000017500000000104412227051773017540 0ustar kismithkismithImport("env") if env["TEST"] : if env["SCONS_STAGE"] == "flags" : env["CHECKER_FLAGS"] = { "CPPPATH" : ["#/3rdParty/HippoMocks"], "LIBS": ["Checker"], "LIBPATH": [Dir(".")], "LINKFLAGS": ["/SUBSYSTEM:CONSOLE"] if env["PLATFORM"] == "win32" else [] } if env["SCONS_STAGE"] == "build" : checker_env = env.Clone() checker_env.UseFlags(env["SWIFTEN_FLAGS"]) checker_env.UseFlags(env["BOOST_FLAGS"]) checker_env.UseFlags(env["CPPUNIT_FLAGS"]) checker_env.Library("Checker", ["checker.cpp", "IO.cpp"]) swift-im-2.0+dev6/QA/Checker/IO.h0000644000175000017500000000114512227051773016210 0ustar kismithkismith/* * 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 #include std::ostream& operator<<(std::ostream& os, const Swift::ByteArray& s); bool operator==(const Swift::ByteArray& a, const Swift::ByteArray& b); std::ostream& operator<<(std::ostream& os, const Swift::SafeByteArray& s); std::ostream& operator<<(std::ostream& os, const std::vector& s); std::ostream& operator<<(std::ostream& os, const std::vector& s); swift-im-2.0+dev6/QA/Checker/IO.cpp0000644000175000017500000000313012227051773016537 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include std::ostream& operator<<(std::ostream& os, const Swift::ByteArray& s) { std::ios::fmtflags oldFlags = os.flags(); os << std::hex; for (Swift::ByteArray::const_iterator i = s.begin(); i != s.end(); ++i) { if (*i >= 32 && *i < 127) { os << *i; } else { os << "\\x" << static_cast(static_cast(*i)); } } os << std::endl; os.flags(oldFlags); return os; } std::ostream& operator<<(std::ostream& os, const Swift::SafeByteArray& s) { std::ios::fmtflags oldFlags = os.flags(); os << std::hex; for (Swift::SafeByteArray::const_iterator i = s.begin(); i != s.end(); ++i) { if (*i >= 32 && *i < 127) { os << *i; } else { os << "\\x" << static_cast(static_cast(*i)); } } os << std::endl; os.flags(oldFlags); return os; } std::ostream& operator<<(std::ostream& os, const std::vector& s) { for (std::vector::const_iterator i = s.begin(); i != s.end(); ++i) { os << *i << " "; } os << std::endl; return os; } std::ostream& operator<<(std::ostream& os, const std::vector& s) { for (std::vector::const_iterator i = s.begin(); i != s.end(); ++i) { os << *i << " "; } os << std::endl; return os; } bool operator==(const Swift::ByteArray& a, const Swift::ByteArray& b) { if (a.size() != b.size()) { return false; } return std::equal(a.begin(), a.end(), b.begin()); } swift-im-2.0+dev6/QA/Checker/checker.cpp0000644000175000017500000000371512227051773017645 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include int main(int argc, char* argv[]) { bool verbose = false; bool outputXML = false; // Parse parameters std::vector testsToRun; for (int i = 1; i < argc; ++i) { std::string param(argv[i]); if (param == "--verbose") { verbose = true; } else if (param == "--xml") { outputXML = true; } else if (param == "--debug") { Swift::logging = true; } else { testsToRun.push_back(param); } } if (testsToRun.empty()) { testsToRun.push_back(""); } // Set up the listeners CppUnit::TestResult controller; CppUnit::TestResultCollector result; controller.addListener(&result); CppUnit::TextTestProgressListener progressListener; CppUnit::BriefTestProgressListener verboseListener; if (!outputXML) { if (verbose) { controller.addListener(&verboseListener); } else { controller.addListener(&progressListener); } } // Run the tests CppUnit::TestRunner runner; runner.addTest(CppUnit::TestFactoryRegistry::getRegistry().makeTest()); for (std::vector::const_iterator i = testsToRun.begin(); i != testsToRun.end(); ++i) { try { runner.run(controller, *i); } catch (const std::exception& e) { std::cerr << "Error: " << e.what() << std::endl; return -1; } } // Output the results if (outputXML) { CppUnit::XmlOutputter outputter(&result, std::cout); outputter.write(); } else { CppUnit::TextOutputter outputter(&result, std::cerr); outputter.write(); } return result.wasSuccessful() ? 0 : 1; } swift-im-2.0+dev6/README.txt0000644000175000017500000000005412227051773015357 0ustar kismithkismithSee Documentation/* for build instructions. swift-im-2.0+dev6/.settings/0000755000175000017500000000000012227051773015600 5ustar kismithkismithswift-im-2.0+dev6/.settings/org.eclipse.core.resources.prefs0000644000175000017500000000012612227051773024012 0ustar kismithkismith#Thu Aug 19 16:27:10 CEST 2010 eclipse.preferences.version=1 encoding/=UTF-8 swift-im-2.0+dev6/.settings/org.eclipse.cdt.ui.prefs0000644000175000017500000000016312227051773022240 0ustar kismithkismith#Sat Aug 21 19:42:38 CEST 2010 eclipse.preferences.version=1 formatter_profile=_Swift formatter_settings_version=1 swift-im-2.0+dev6/.settings/org.eclipse.cdt.core.prefs0000644000175000017500000003070412227051773022557 0ustar kismithkismith#Sat Aug 21 19:42:38 CEST 2010 eclipse.preferences.version=1 indexerId=org.eclipse.cdt.core.fastIndexer org.eclipse.cdt.core.formatter.alignment_for_arguments_in_method_invocation=16 org.eclipse.cdt.core.formatter.alignment_for_base_clause_in_type_declaration=0 org.eclipse.cdt.core.formatter.alignment_for_compact_if=0 org.eclipse.cdt.core.formatter.alignment_for_conditional_expression=80 org.eclipse.cdt.core.formatter.alignment_for_declarator_list=16 org.eclipse.cdt.core.formatter.alignment_for_enumerator_list=48 org.eclipse.cdt.core.formatter.alignment_for_expression_list=0 org.eclipse.cdt.core.formatter.alignment_for_expressions_in_array_initializer=16 org.eclipse.cdt.core.formatter.alignment_for_parameters_in_method_declaration=16 org.eclipse.cdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16 org.eclipse.cdt.core.formatter.brace_position_for_array_initializer=end_of_line org.eclipse.cdt.core.formatter.brace_position_for_block=end_of_line org.eclipse.cdt.core.formatter.brace_position_for_block_in_case=end_of_line org.eclipse.cdt.core.formatter.brace_position_for_method_declaration=end_of_line org.eclipse.cdt.core.formatter.brace_position_for_namespace_declaration=end_of_line org.eclipse.cdt.core.formatter.brace_position_for_switch=end_of_line org.eclipse.cdt.core.formatter.brace_position_for_type_declaration=end_of_line org.eclipse.cdt.core.formatter.compact_else_if=true org.eclipse.cdt.core.formatter.continuation_indentation=2 org.eclipse.cdt.core.formatter.continuation_indentation_for_array_initializer=2 org.eclipse.cdt.core.formatter.format_guardian_clause_on_one_line=false org.eclipse.cdt.core.formatter.indent_access_specifier_compare_to_type_header=true org.eclipse.cdt.core.formatter.indent_body_declarations_compare_to_access_specifier=true org.eclipse.cdt.core.formatter.indent_body_declarations_compare_to_namespace_header=false org.eclipse.cdt.core.formatter.indent_breaks_compare_to_cases=true org.eclipse.cdt.core.formatter.indent_declaration_compare_to_template_header=false org.eclipse.cdt.core.formatter.indent_empty_lines=false org.eclipse.cdt.core.formatter.indent_statements_compare_to_block=true org.eclipse.cdt.core.formatter.indent_statements_compare_to_body=true org.eclipse.cdt.core.formatter.indent_switchstatements_compare_to_cases=true org.eclipse.cdt.core.formatter.indent_switchstatements_compare_to_switch=true org.eclipse.cdt.core.formatter.indentation.size=4 org.eclipse.cdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert org.eclipse.cdt.core.formatter.insert_new_line_after_template_declaration=do not insert org.eclipse.cdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert org.eclipse.cdt.core.formatter.insert_new_line_before_catch_in_try_statement=insert org.eclipse.cdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert org.eclipse.cdt.core.formatter.insert_new_line_before_else_in_if_statement=insert org.eclipse.cdt.core.formatter.insert_new_line_before_identifier_in_function_declaration=do not insert org.eclipse.cdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert org.eclipse.cdt.core.formatter.insert_new_line_in_empty_block=insert org.eclipse.cdt.core.formatter.insert_space_after_assignment_operator=insert org.eclipse.cdt.core.formatter.insert_space_after_binary_operator=insert org.eclipse.cdt.core.formatter.insert_space_after_closing_angle_bracket_in_template_arguments=insert org.eclipse.cdt.core.formatter.insert_space_after_closing_angle_bracket_in_template_parameters=insert org.eclipse.cdt.core.formatter.insert_space_after_closing_brace_in_block=insert org.eclipse.cdt.core.formatter.insert_space_after_closing_paren_in_cast=insert org.eclipse.cdt.core.formatter.insert_space_after_colon_in_base_clause=insert org.eclipse.cdt.core.formatter.insert_space_after_colon_in_case=insert org.eclipse.cdt.core.formatter.insert_space_after_colon_in_conditional=insert org.eclipse.cdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert org.eclipse.cdt.core.formatter.insert_space_after_comma_in_array_initializer=insert org.eclipse.cdt.core.formatter.insert_space_after_comma_in_base_types=insert org.eclipse.cdt.core.formatter.insert_space_after_comma_in_declarator_list=insert org.eclipse.cdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert org.eclipse.cdt.core.formatter.insert_space_after_comma_in_expression_list=insert org.eclipse.cdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert org.eclipse.cdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert org.eclipse.cdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert org.eclipse.cdt.core.formatter.insert_space_after_comma_in_template_arguments=insert org.eclipse.cdt.core.formatter.insert_space_after_comma_in_template_parameters=insert org.eclipse.cdt.core.formatter.insert_space_after_opening_angle_bracket_in_template_arguments=do not insert org.eclipse.cdt.core.formatter.insert_space_after_opening_angle_bracket_in_template_parameters=do not insert org.eclipse.cdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert org.eclipse.cdt.core.formatter.insert_space_after_opening_bracket=do not insert org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_exception_specification=do not insert org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert org.eclipse.cdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert org.eclipse.cdt.core.formatter.insert_space_after_postfix_operator=do not insert org.eclipse.cdt.core.formatter.insert_space_after_prefix_operator=do not insert org.eclipse.cdt.core.formatter.insert_space_after_question_in_conditional=insert org.eclipse.cdt.core.formatter.insert_space_after_semicolon_in_for=insert org.eclipse.cdt.core.formatter.insert_space_after_unary_operator=do not insert org.eclipse.cdt.core.formatter.insert_space_before_assignment_operator=insert org.eclipse.cdt.core.formatter.insert_space_before_binary_operator=insert org.eclipse.cdt.core.formatter.insert_space_before_closing_angle_bracket_in_template_arguments=do not insert org.eclipse.cdt.core.formatter.insert_space_before_closing_angle_bracket_in_template_parameters=do not insert org.eclipse.cdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert org.eclipse.cdt.core.formatter.insert_space_before_closing_bracket=do not insert org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_exception_specification=do not insert org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert org.eclipse.cdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert org.eclipse.cdt.core.formatter.insert_space_before_colon_in_base_clause=do not insert org.eclipse.cdt.core.formatter.insert_space_before_colon_in_case=do not insert org.eclipse.cdt.core.formatter.insert_space_before_colon_in_conditional=insert org.eclipse.cdt.core.formatter.insert_space_before_colon_in_default=do not insert org.eclipse.cdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert org.eclipse.cdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert org.eclipse.cdt.core.formatter.insert_space_before_comma_in_base_types=do not insert org.eclipse.cdt.core.formatter.insert_space_before_comma_in_declarator_list=do not insert org.eclipse.cdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert org.eclipse.cdt.core.formatter.insert_space_before_comma_in_expression_list=do not insert org.eclipse.cdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert org.eclipse.cdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert org.eclipse.cdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert org.eclipse.cdt.core.formatter.insert_space_before_comma_in_template_arguments=do not insert org.eclipse.cdt.core.formatter.insert_space_before_comma_in_template_parameters=do not insert org.eclipse.cdt.core.formatter.insert_space_before_opening_angle_bracket_in_template_arguments=do not insert org.eclipse.cdt.core.formatter.insert_space_before_opening_angle_bracket_in_template_parameters=do not insert org.eclipse.cdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert org.eclipse.cdt.core.formatter.insert_space_before_opening_brace_in_block=insert org.eclipse.cdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert org.eclipse.cdt.core.formatter.insert_space_before_opening_brace_in_namespace_declaration=insert org.eclipse.cdt.core.formatter.insert_space_before_opening_brace_in_switch=insert org.eclipse.cdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert org.eclipse.cdt.core.formatter.insert_space_before_opening_bracket=do not insert org.eclipse.cdt.core.formatter.insert_space_before_opening_paren_in_catch=insert org.eclipse.cdt.core.formatter.insert_space_before_opening_paren_in_exception_specification=insert org.eclipse.cdt.core.formatter.insert_space_before_opening_paren_in_for=insert org.eclipse.cdt.core.formatter.insert_space_before_opening_paren_in_if=insert org.eclipse.cdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert org.eclipse.cdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert org.eclipse.cdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert org.eclipse.cdt.core.formatter.insert_space_before_opening_paren_in_switch=insert org.eclipse.cdt.core.formatter.insert_space_before_opening_paren_in_while=insert org.eclipse.cdt.core.formatter.insert_space_before_postfix_operator=do not insert org.eclipse.cdt.core.formatter.insert_space_before_prefix_operator=do not insert org.eclipse.cdt.core.formatter.insert_space_before_question_in_conditional=insert org.eclipse.cdt.core.formatter.insert_space_before_semicolon=do not insert org.eclipse.cdt.core.formatter.insert_space_before_semicolon_in_for=do not insert org.eclipse.cdt.core.formatter.insert_space_before_unary_operator=do not insert org.eclipse.cdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert org.eclipse.cdt.core.formatter.insert_space_between_empty_brackets=do not insert org.eclipse.cdt.core.formatter.insert_space_between_empty_parens_in_exception_specification=do not insert org.eclipse.cdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert org.eclipse.cdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert org.eclipse.cdt.core.formatter.keep_else_statement_on_same_line=false org.eclipse.cdt.core.formatter.keep_empty_array_initializer_on_one_line=false org.eclipse.cdt.core.formatter.keep_imple_if_on_one_line=false org.eclipse.cdt.core.formatter.keep_then_statement_on_same_line=false org.eclipse.cdt.core.formatter.lineSplit=80 org.eclipse.cdt.core.formatter.number_of_empty_lines_to_preserve=1 org.eclipse.cdt.core.formatter.put_empty_statement_on_new_line=true org.eclipse.cdt.core.formatter.tabulation.char=tab org.eclipse.cdt.core.formatter.tabulation.size=2 org.eclipse.cdt.core.formatter.use_tabs_only_for_leading_indentations=false swift-im-2.0+dev6/Swift/0000755000175000017500000000000012227051774014757 5ustar kismithkismithswift-im-2.0+dev6/Swift/SConscript0000644000175000017500000000113012227051774016764 0ustar kismithkismithimport datetime Import("env") SConscript("Controllers/SConscript") if env["SCONS_STAGE"] == "build" : if not GetOption("help") and not env.get("HAVE_OPENSSL", 0) and not env.get("HAVE_SCHANNEL", 0) : print "Error: Swift requires OpenSSL support, and OpenSSL was not found." if "Swift" in env["PROJECTS"] : env["PROJECTS"].remove("Swift") elif not GetOption("help") and not env.get("HAVE_QT", 0) : print "Error: Swift requires Qt. Not building Swift." if "Swift" in env["PROJECTS"] : env["PROJECTS"].remove("Swift") elif env["target"] == "native": SConscript("QtUI/SConscript") swift-im-2.0+dev6/Swift/resources/0000755000175000017500000000000012227051774016771 5ustar kismithkismithswift-im-2.0+dev6/Swift/resources/icons/0000755000175000017500000000000012227051774020104 5ustar kismithkismithswift-im-2.0+dev6/Swift/resources/icons/Status.svg0000644000175000017500000000622312227051774022113 0ustar kismithkismith image/svg+xml swift-im-2.0+dev6/Swift/resources/icons/lock.svg0000644000175000017500000322271012227051774021564 0ustar kismithkismith image/svg+xml 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 e+5/ZM8sZYzx7QhQDp3d88j999x95wdu8m1y/Y233vnh+6bZvgNHF44dO7494dixhaMH9rHp+z58 5603Xu8a5Obb775vz96jpxbPvry2vr6xHWF9fe3ls4unju7dc9/dt99sm+S6G2/dcc/03qcXN77w 5he/9OWvbE/48pe++OYXNhaf3jt9z45bb3SE3HbX/ezo4pW3vvq1r7/9jXe2J3zj7a9/7atvXVk8 yu6/6zZLyPU33fGRR/ad2njr99755re/893vbU/47ne+/c13fu+tjVP7HvnIHTcZJrn+5s49ew4s fuGr73zr+z9494c/2p7ww3d/8P1vvfPVLywe2HNP5+brDa/esuPe2aNn3/zaN7//Fz/+6Xvv/Ww7 wnvv/fTHf/H9b37tzbNHZ+/dcQty63U33LLjY3Th5S9+/ds/+PFf/+L993+5HeH993/x1z/+wbe/ /sWXF+jHdtxygyXkgx9nx9a+9PZ33v3pL/7v3/5qe8Lf/t9f/PTd77z9pbVj7OMfTAlZ//I3vvvD 997/2//36+0J/+9v33/vh9/9xpfXS4QcX//KO9/70Xvv/+rXf7c94de/ev+9H33vna+sH08JuY8f 30BCfvbLbSTklz9DQjaO8/smhEwImRAyIWRCyFYSUlTxo7I1aPSOj9z6/s1f/fBP/qDsBtCnL/wu +CM/+fk2+iM//8mf//F//devRMcIXMWPkafO/6vf/9b//suf/fxvtslD+5uf/+z//Nkf/ee3zs1n 3lW8/pY7f6t3ZOVf/Kc//NN3//Inf7VNPutf/eT//Pn/+u//4Z+eOTRzz503ey/+N3fPffaNf/f2 H/3pn/35tnnxf/5n/+t//Jd/89vPPb4zDCduuv3DU3tPXHjz37/9h//zj/9kewY23/2TP/6j//5f vvrPzh2TD959mx/pfeBD92WHnv/8m//2P/7+f/uD7RnqfeMP/ut//g//5p+tPTs3c+8H/ZDzhlvu /OjDav75V17/5//yd7+8PYPfL3/pX7/1T3/73LMH2YMfvuNmO0Ny3fU33fahe6flwU8+v/zShe2Z DlhfX3vl3Jnnjs2xqd/84AfctAQ2yR133ftwlj95+BNPb9v8yML8ocflzIO/+Ru33eynjK67/sZb 7vjQRz/+0HQv26YpI5wwymZ2Pnjvhz8Y6bCU3Hbnb/zjj/4WzqFtxySam0K7+4N3fA== 4OYb0tm862+46ZYP3H7Hnds4q7hjx51mUvGGwlTrdUDKjTdt80QrTrPeUJhmjTPP2zn1XDfxnJIz 4qn40YepqUPrK/ise+f0UfuuPHy7V+WPda64/wg+SJ91j3cy94B8hi/SSyF6UmrdzXPZy2mWd9dS mOgRlemuFLwHwsoAwmwqKahP4yDLHcGzHiGaRNiFjmB5j8s8jzDBlE/lsCcQGXB5mM7yXq40N7g8 jGQ9QTXrBuyaEJsq0BAgy51Aa4Bd6IQaBViodcAeIZ6G5Zr2utDZfwYad+ex1de6Ry4urZxfXb/c Pfna0uXll7p82rQzVdjmttXt//efg26YoXkvE7nszhBKe0IAspmsl2EgULgqEX16rWN/BeBMTjXp Mc3FgMxQxR5VjHf3L2OJhIN6h6q1K3E/lNjN+ifOterlAiAmcbsCCnmGrVJgJKjRWShy//7OKUPm 6ZWO7O6c7p5+FgDa/TU7N0/Ip1Zfv9zdC500e/ji6uqp9ZWN7h7IAl9La6vz6yurr8M37c6evLxx 8Q3//WinGWvdX1OLV5KIgja1WCddBehQ8jW1WCdhAN2kjGHOqpSl0OHkDGtWlTSEbk7W6toP8YG8 TS1uQuKwV4aXOch1VVJnSh1a7jDXEJLXvpBSrquRvqnFs6ZgkMCpxVOOZJAX6LcgE0kPsigBCTc6 6EKR71xDLKQY6qEFDOuWMebWN9a70AfKEYG0RTFNVQLSnCoF952oBUCYlTTD1OKjEOvre8oUcPHy wfPLl89vrC9dfMOUAqCLS/bPnXPz3bkrlze61kKf/53VacR/8fz6OYsb6F/ZOLO6ODevF4+fubR6 8bdXVxafWn1j0Sa6NB1J8HXVnJjyr4DKAisPHN9TwjE/GHnjExw+f2F1r/1z/+q58+vOQQDCIc2R zvNBWgQ1mqT7Qpcw1eUKK/lS7e/m26Zx/+RdjnKXm8Qc/ss6U1OmOPRP9napUEJ3Ts/fNmqfaRL+ wYZRb4ebxPHaSjjqOk3i+PLNqOmexPHmlVHTOonjzSejpnESRx/7hVHTNonjE5vCqOmaxPGJdWHU NE3i+MVyGDU9kzh+sRxGTc8kjl8sh1HTM4njF8th1PRM4vjFNIyalkkc3+jDqOmYxPGNPoyajkkc 3+jDqOmYxPGNPoyajkkc3+jDqOmYxPGNPoyajkkc3+jDqOmYxPGNPoyajkkc3+jDqOmYxPGNPoya jkkc3+jDqOmYxPGNPoyajkkc3+jDqOmYxPGNPoyajkkc3+jDqOmYxPGNPoyajkkc3+jDqOmYxPGN Poyajkkc3+jDqOmYxPGNPoyajkkc35iGUdMyieMXy2HU9Ezi+MVyGDU9kzh+sRxGTc8kjl8sh1HT M4njF+vCqGmaxPGJTWHUdE3i+MR+YdS0TeLoY5swahoncbz5w4dR0zqJ480faRg13ZM4vrzRL4y6 TpM4PrwwCZMwpmFbX7preOvuOvP43za+/Vf/+h8QceON2/waon0P8frCe4jXXX/jzbfedkdnxzY+ EPnBHTs65oXI9KHKG2669fYdd33knns/tj0vZoY3M+/acfutN4W3O6+7/qYP3Hn3Pfc/smd2m94Q ta+Izu555P577r7zAzf5Nrn+xlvv/PB902zfgaMLx7btWdVjC0cP7GPT9334zlvd+67XXX/z7Xff t2fv0VOLZ1/enndmzUuzL59dPHV075777r795uvdG8C37rhneu/TixtfePOLX9qel3fx7d0vvvmF jcWn907fs8O/AXzjbXfdz44uXnnrq1/7+tvb8xbxO+984+2vf+2rb11ZPMruv8u9inz9TXd85JF9 pzbe+r13vvnt72zP68zf+953v/Ptb77ze29tnNr3SHgn+ubOPXsOLH7hq+986/s/eHfbHqx+9wff /9Y7X/3C4oE993TMy9n4lvi9s0fPvvm1b37/L3780/e26Qnv937647/4/je/9ubZo7P+LfEbbtnx Mbrw8he//u0f/Pivf/H+Nj1q/v4v/vrHP/j217/4cnxd3b43v/alt7/z7k9/sY3PvP/ip+9+5+3y e/OjePj+/fd++N1vfHm9RMjx9a+8870fvff+r379d9sTfv2r99/70ffe+cr68ZSQ+/jxDSTkZ7/c RkJ++TMkZOM4v29CyISQCSETQiaEXDtCxsbWjJH1HYE/8vOfgD/yuxeeppGQUXhof/Pzn/3l//7W 7/+r80+RjzlXcTQ+61/95C/f/dM//E//YuVI77fuNISMyov/8z/70z96+9+98dm53b9pvfjRjGu+ +yd//D//8O1//+aFE3unPny7HdeMZKT3jT/4b7//H//tm59//lB234c+cOPoxr5f/vLv/st//vor z8+rhz965y12hmQUswHr62sXXlp+/pMH5fS9H7rNT5CMZn7k6U8cfjLPHr73rjtu8VNGI5gxwgmj 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 Y3ep63j8PfzYXeq6HX+Px+0r8Xs8Zpfn93jcLs/v8bhdnt/jcbs8v8fjdvl+j8fs8vwej9vl+T0e t8vzezxul+f3eNwuz+/xuF2e3+Nxuzy/x+N2eX6Px+3y/B6P2+X5PR63y/N7PG6X5/d43C7P7/G4 XZ7f43G7PL/H43Z5flc/l7o+x9/jcftK/W76XOo6HX+Px+0r9Xs8bpfnd6/Ppa7X8fd43L7Svhf6 XOr6HX+Px+0r5XvQz6Wu5/H38GPWPpe6vsffp/a51HX/avwe5edSt+Wr4fvl/Fzqtl6O3+PP8ecr /PO0I//sXdAVV1xx5RF/iOSGIp92xZVXXXX1Ndce+eeaq6+6ikqct+vKq6657vpnPPPGG2860s+N Ny6ecf1111yli6OGXfP0Z9z0rOfc/PxbXvCCF7zwiD5E6pbn3/ycr7vxhuuuvnIU97Qrr7n+pmc/ /8UvPTFZ55w/og+Rsre97CUvuvlZz3z61b11KOxrnvviE/GVr75refc9R/e5++7lnSdf4b7xhc9+ 5nVXXVFLu+Kqp9/03Fvd7Xff++YH3r577tz5I/qcO7f7tvtPvfHOl7/shc+64ZraOGraM579YnfH tz/wyPe854fe+74fObLP+977g9/33edPvf7l33jzjdddVUu76rqbnn/i9m/fffePvv+nPvChxz58 ZJ/HPvgzP/Fj73nHqTvdi77u+l7a9c96cbz7gXf/2M/+3C9+5KMf+7Uj+3zsV3/5Fz78r99z/o2v eMlznnG1DNwVVz/jOS995b2P/OjP/qdf/81PfPozR/f59Cc//hu/9OEf++5TJ1928+KaWto1z7z5 xKvf/D3v/7lf/8Tv/N7nHv/8UX0ef/yzv/up3/iFn/i++++87fk3XsvT5GlXXHvj86e7HnjPT/3i b/7O5//wj5744lF9nnjiC//3dz/+yz/zg29b2ltuvJYZ/GlXXnvjLXb59h/6wEc+8Xt/+Mdf+tOj +3zpj7/w2U/+6gffu3u3e8FNvbSbXuDu3n3vhz766c/90Zf+7M+P7vNnX3ri8U9/7LH3nVst7Z5z 73vsY595/Ik//fO/OLrPn//pE49/5tc+/CPn7lkr7Uc+/Guf+fwXj7i0L34epZ2/x79QlfZCf8/5 49KOS/v/qbQvLwdcPO7+/EbuFsl1EWTJuuSCVD77Q//2I5/87BeOWE7+0ec+9dEPzQ== pLJonLe856d/6eP/6/e/+CdHqAP+5It/8H/+56984AffPjRO06bv/vH/+F9/67O//4UnnjgaDUd0 vvAHn/vt//6ff/L777+ra1NYCn/5pX/jO975zx/7L//jt36XlPfRaO/HH//cZ//3b3/8o//hX/6j v3fHsBTYCkr3vO373v/vf/k3fvMTnzoqy+TTn/rkx//br/78T/7wg3/nrw8rCBbeLduv+o4H/+m/ +uDP/+KvfPRjR2R1feyjH/mlX/h3/+af/cM3/y2vLDxYr3813Pmdf/97f/T9P/mzHzwii/Kxxz70 gZ/+8X/xA4/e/y2v+KbndeuVLfObX5Je+6a3vOMff/8P/PBRGctkKf+Td79r99Q9r9zeGpY5GnfD 1z7vJe5vLr/91Jm3nT13JJ4AUTn7tgdOf8e3vKaceJH2OsDgN3zNc//KN/tXvOq1r3v9UXk5d7/+ dXfe8cp02ze84Ou1R0UMftU119/49c970Td887Y5Ig8Ozpu57WUvffEtz/naxcxbhHN69XU33Pi1 z37u8+CcHoV3CjK3PO+5z3nWTYunX3vVquN9xVVXX/v0GxbPPErH+8ZnLm6A133lFaurCk/DqsLV 1xztqsI111xNRa2VJeVhweQoV0yu2LxaMi/zoi8Efbk+t9766nP34Z2LxZvulIc36Hd9duPu+YNU pj5J9aZHFybY7YxXdopP/K7GrobwNOsk7yNOxiZCHCcyPrckAuwsYtzGIyoNOLuIfjsamzsSbU1Q ifbfqVGoQAyNQgPSdsG7O51iLEKxFll/4mnKVq0KnV2MqldotK6RVUhohFZ75Wx9L/uN586d3j1z 39YD7c1sL49l27Thtexp64SNWyeK2y6lhC39zrSf1/NNuwv5I79Snydp+H45c0p4p8UHvCd98IJe deiC2vCNZ6unrTf0N4fK1uuOp9BBp9BD6uGmtTedbj2lcrjaj7ee6k0UaKmgWpvlLOtGUGU+t/nZ H4IePF1fALr9rq3b3/Hw+S2RHW/9B2fGQ0D87M/WS2+/7/ybz5y6/a5y6p43P3TmwUfO3HfqdWce PSWJHtLvN1Hac+fPbRUX6pNdt91+lzHfduZdD1NZ09Ztr3nwzJk3nrvvPGXhX8Rcd52778y76Lf1 KPb8g4824K8t0GfbMQzptdKHhyAeNhE3e1LWL26tveuHWllVK7zrZvAyVgl525bg+c3ZuD2lyQ0M jxSWnCtieV657RxqvqlEu3VyMTDMLORbaiwTIxjH3BUNHjpPk6TiecxpGnKS+WsGLcFhZjJxQNGs kB5A6HQa5PFKFr8jTROsUOkkNPyERwiN2U5T0o1d7ZI9nwekobqLBCtNQzNt7Zzf/a7z7zh339ZD bzn9XWe2ds/fd2aPt+FnoxccNcfkvGXxPts04d2wOAU8mTig5YAKXq+b8FjtyLkJG1nP4V1C09+f e4A6ZZInw9xEMsZlvLwWSCr3n0v+iSfwsuG+DyFum4xXuTTmqYRI6ayl+ZLBtCGT/PGM2OQ4Fd7A 8sbqnL38TZiqxP38at5aZ+xubOWm3tjQaRu6VtTl3WfeufXapiq/9Z2nH955y5YVjVk2KMymfjye bA9EdStnaUD/Q7B+9A2pTcnioqEs0W3jEbBNWU7kyVPdMol+UmStHLtFoM3ElfuXA61JNdyciEp1 4IZX7R6Y7lqWC9a/D+BQw99EKgRi5wGeNMZgihiaF474b7dPmoaoaWS2fS6uTjbJtwkbOe9fedq0 P2+ap7QdI+YcJY6T48fpBxZJOTpq2BS3naXOsCZIqozWSJqKkOouJCEsWR4dI+1d8GR4SQOLubRU lbpCcqfVMU/6Ilq2BAZm5V3JQd07STXq0JCdxahrw84uRosaNlrdqCtkGrRW++vsU2MUqilpioD3 tmmuBjzkWP+CrkorHarYpWxljwdxCz/7vmfWE1keFjSaa2C7bU+WpstBCq28s19SW2hugzN3D1nG WsYDN6wP3OCnDRaZGq/Q5jfZX2PGVXQ5Qxvp5YzCZlRTONfNJUvCasXsOIRpEzfaTXu9VNztmfWJ vruAkxGptrr6NBu2veM3cWv3LyVd0oy6XKwz+XKxSRSsi4tVJjm5WGem5WITy62z5WqbTu7DcBfy 5SqrnXAkoQsJedIIU1mh3zw5w9zjMx6n9nljnpyIYsaUFReuzfh96Vd22phGscOFyOmkB6zu4GjF MmXj29dr6oEM1gJ7UKsH8k54sMaohUlSjVFriFYPDdPqoWGDCRt1hbhOq2MpSh9p9ZCSTOVBPRVJ NerQEK0eGqbVQ8NGqxt1hcRBa7W/vizqoXXek1EPhrrEW5cPrx9aqQfQDzEcXjXoPAfXCm28DqgV WhvmWqGhc63QSM/l/2ZUUzgqrZAuqBWULljnNtIFFr5i8bp6iQx/eec8QrxzEjYeR7vWmWa52MBa 6+y3xqQnF+vMvFxsYPl1sbDWnAOoAWasE8JT9D+Tpkxs7QsxE5UA49h71gxtWq4riOEPJBtXeqYp CuKdExlVZnr7Zc28uBMdOwGj1DU5v1+pq+pjU1phnUMWoDIdvEVDCHR+2+NRardtvAnFXehN6tt0 zq3b7j7/8BvO7Jx/8L4z9/GfLzSct73hzOmzrz9NJbyLUm+99OTtd7XZ8W33n39wV/70jevrYFTn b3340bNnTo3y1ToFP4x9H6acvfCUWzM8qFfWDbfxr74O8cCCZEecKgvutl/eYSEGDQyZAeIGD8Ab 9fvkoiR0hxnQEhC/uT1yRTIHU2CgeAFCycgUtrGERWTIhPAuEkLWmw8QC8VjoSszVMj02KI0kytI U7adCwzYGAUIkU0+gkqGHDDk5eYIi6/gffaYGQrGYUF2AJmsl62RyUgvnETh8PWM3c7BRKnNZB31 jXHk3VODC5lWweYZ4K0pIxNRIfvWFcvQhFX3JSA/MWTEiiO7mCc6gJTwVDkWwXJgoEBJkiGLx8u9 57JMtYlpJHyQ4k0Bn+Mteydpgo1zIPuQhE6FMBDGC50KoTNhRiUqlTsjEB3eM9imqrgBEB2MFga0 QUTHb2ebjMrmZaNhEO5ALfzkYkC1issZJM3wMNOlnTkz3WlKtd+LdA8NbJYejPxwPQGOGJURH6mX SJBlGmIGgiXlnap5yr3ueQommmaT8TxYwXgMOkExcSdbSgUZlImwTzNgSimOTESHBISLnqcXeTis 4UhyTUkgGseypQGTqBE9E03l4hPUFqk0kzCfCOJFUDhMNHmsY5bgRTQA0QuP+EhzmgBnmA6Na2aV hbIC0wGzuSL1mWL0gyFTIEa3imUJoGmaNKcnYgmfNaMnUqc2q0QJ7kZWAkQDlbMGRKOFSbScQY4U 8tYgbKii1IW97PqbyPQKVmi5GI1oudBMmAnThL0PBky0kYEwkQThXg5YjQ4ktYyLSXo5BWLEgKVK qjk6leaKZcB7mmno9kSCB4C1rZcjfKBAwoRNW+5lSp0YMoXGdABgFpJ/IxN14DRFmYUkYQtBkCLF 11noIyPRGZ7ME82MGUCiKY08zBK8FUDKbzuZyhLWCVBcgtsryivILt4W1vms4Z/ZxyTcyZzG5QTv hDvBjEBCFOZkbgVg3RQUQMLLsOc8IPCDCYrJAXnshDXpAYBaT3SqhOkA0alyqEPLRZNVI1uVZoOw ArjwIQR7FZcKas2AdLVFNbRKYOmLYkW4k11refBIBhgR7i5xJ9J8RecSkLCgC8DAPqXeJa/ZSa8H UX0O5pmMDE+T5YKHZJJ+Lom3P7dd5LYPIORURqaT0HPOGSm8Cnfo2ZCNzMDImq//Do7+MfIEsRFZ g7LcxdwOkTcECHKTSwyl4ljvep4GadtOrD9Jy0X+TbaAlcrECRqUOY2FzrACKjcOO6Hy67AlAPCm UTc4KrJcDJukQqUJDhqCouTKyZlh07YwYL45zCsiY2iEd3maQyZ3KNuqElH3mL0CwF8Ru1ADwkT1 opZAJZIXX8hNrOW0nzuYY9Ea0xHM0MTGS6UwgF7KgFpNKpFR15Xm7Dxl15/G0rktW2RidDwZkdGd 1YbfDwFJ0sM5eHQb85FIOkGqzEhD504/8VOMe2XUBVYnZM+Etd9WnP4LkV/LdYDmsCFRzZzZzspT 3ZnsRvkIlaCJ/DreGg7WeW2nzzBWtkBsYH1X9TF5PyIKlBo3ntgvsRLsihxY8DnrnGQ/k98+TAQg JC2jMtoNOWkxJm20AyK5a4fVzkh0w2oHkIo22o0rsMytttqBsYXVzfYZwna7ytcNd0pJgt55bbsD Cz75YasDMQm1GgixpQ9Om+/AxF4Y9jsJLtKfNg4DHghXsFvwQKwzRZvwVM1tm6rdWm144+jPsdhh xANJ4ptU83eGVDNeYV3UK6za20A873W1uQrEOJ+1KW8sbNxJzeglYy5MUeXEIgX0zaA+kG7OK6zb 8zNMWmQTdVzww6QHQhV12qY3vG4UgrLqjSWqdbTZqgfgLSy+ZtYDqe5Mt+uxcufZehyGPTBTqmfG k4qmtthkM4SUidG2vTEFFlbRxj2w6KYyrPsZwub9yDfse2Mn4rXgtIEPrNiQh4XPCOI9uokPJLuS tY3PJSam1Y18YCGXrFiYEBtCUWxuiEudMzMBYZKYSlpAEOaDNToncVCxWvxopHHjwLq1P8PY3FfU qyWg6tAtflXXbvKrFvWc1OoCv7yZGdw3eWb0Y2gNAl+U1Q/MppyH2c+ItWnY/YZtu27zYyRMyVYb /SiNLDQ7rH6FVLNf5et2PwrjQVKGP89SKM9u6csq9BTmSDUfu/EPLoimxGH9M+vAqu/mP/jLBrbu xf4HQLPXaw+gM6ZyATr7dtu4s3g3nxXS3QCFdT9ghrG93kVPV/tdQClXoAsy5Qt0cTdyNpE4qGuk ugMK6/6AwlqLWFRHp1rdBLpyCSD4o0lJ+wTAqDZhOAVQK9Werl4BkOBc0m4BVBTpwqj9AlZkxqXh BwBhXagQkgriJ3XXAIoyBx+0bwB9PmUTunMwA9g7UNm6ewBswj6I8g9QZHXfxEFgFS8+q3gIAIqZ eQhiTzS2FIk1jI7Gu8Mwafw9zJfuJCgzp7sJyhiqmDKYqrcwQ6rPsG5qLRcPsedA5rUEsCnfgRkk asyQAS16uNnsCumGvcKq9d8pNf+gFzdcCEw5TtzTaA/EzD0Uoz2YnlPVc7U1O9TOI7Bh39GsWJPg FrKlR8J8chzhq7EC1Q3Eko4UbYagQsM+Yv1BA4K1H5HfFVsyNvlYdCaH8DVBLBQzEDLGBPGkMJkW CUueFDAhaT4xLbCvE6yUiEV+hC/UFVhWCUCyd1lMDyemANF3Lvtq5HGUHDBfnKwPkkaXWnSEk6t8 ZJcUm2q9jK9GEo0Xy42EhWEnxiHreyDJ8dKsQgJYtuc7yfkmH4SWqVZ5gsWcpRa81A0kYr2HEZ8y I3Vl2oHDWW5EksY51zpgXi0ZI9NZDFLwNIBpcmIJwkyaAS76RqliNF2NqEqNOWopSQ== IZIjrq49GyNIbIaoEbkRg0S6dWzJWJp4lbhnhOVqFOnxm4sXQgLVWi5nkLSEcnteSEZbK0IiyFQT t/BMiDSrSqwuh5dZRVjEtiebk1ivAOJsqQYmPBQgNPKShiaCtA6SKMqIsjpbMkYTRUaBGIeYlxAy N9wcmSrA2RADjEVvI96LjdJRhFHRsknB5rVCjPg9Kh/VpSqQiM1RK6nImOBa0QyyPDbENxGGDpDo BInkGzESonhsHkas1CpFxNAzW4bcajXFoJiXEEzKwd8Bhp4WEyAFi9j6mWRAoLD3RufMbbG7iRyN sGASWhWL25PhCFqFIYgZvTyoV0Wv6lARaWKta8WkidKgnhFtllV5z/wHIPGSGbzHmGu3p4BYA5Lr xUkLCcuRNwHgqEDjA2G2IcSz5YqRSEb0AW9YCC1jRR+SDGz9XmIWDHGzM8CBE3QusihznaJ1jwHL IqJtMUWTgWFShC2AUGviHMkplZFPWMdHJ1jIyTfWMbwMOLUGUusR08pI9kDgKPLSwyShZsLPzJpc YgxNMDADMzZxTmFxACGtAI4QJRmgGkMS70BBnKqLHCAmZRExLJg6Akoivzq0XHQhpzJWOaiIK4Rr oCRor+hyjtXWZInFUg2uMly6RaoF6Z9c3Rlx4npCR8SKFWwtQpOQOSQmWplQU0JsKWLcsHIVfZON lyG0EvrLuithqQZDnVkbkwyy3swRl4of+USnhlTXez1W9kWnkl0rrmCoCrQD2YEtdbYCSSmkSL8J 64SqUUk8R55YNMMwmRmxEFDgrzxJrWT7c3LoyFarFCrzWicia9gbjX+HTdJYfFguAaJStHMYP5cL Zf1UTFlIEDpsfHSkiqaTG2yrbqXGOLbyeKthVzgjhjAw8I9siKJJCabEQCLWbixMyIFhGX6q6k8o YcGhYPL28iqyw9PSllh0qiJek6LUEVVex3qtOqVe89X2HZHd2qzWAiWWxFJmb32XMZpfjllowm4h EJvYCSZZbwWYjBcuI2ub5w1ijJx3Qkr0b87Cg2J1wxnMWDANsSbCeBPC2yBcmhc5maNQZcaYxIHL JG9s9XoCCW0AdWGI2ACrTkgRsvCAqetqsmNQeII36U2YD8ycrq7ucZQLr6I4eHmFkSmzOwJTU5Rm JjPJOVlhIrOaWT/TnBG29tXTyODOYGXtiFVyrkYKEPKIWbxlJ40FfU/yn2mBGb3Uwk4BOW2LHAF7 Y4ZkmFXcMw7aWkQSzSOHXXkwsS9MC5ukIQotYzOkBvmkWNUBQpZZZCQHVsEOi0jC/Bn+YN3Thj2+ ZMzDXuM2YvkJSB0fTywDxZlIzKdJltnJePCVa7P0Ks1dmulVAoQ8SV1LxIoftra9rT0d2SESf5y7 ZpJhBKmYRHKFSRbpCCtspBFmeQkTqaAqWFTSvxiJrm6lkwfa5BvbK5hwXkwfSM9sZA7aWJ2iJD5+ wfIKAx5RWKyPJm+rLohTFragxjc/xskiAukf60WLTD6LjkrwU6F7ipuEezxmrOioyfJSV+otJAFB hkCRdQXPfTPJKUssPRTP3UesGYss49leLTsxT9O0xmqJVIsXkXk9T7oZpreVzdDiiW+lObL8EWP1 +kzbHoVNkmwlFWRJhPSKhVGNNvsie/aOmRptzm2n3NqmhWkmyk50TrIlENl2SYwlLC8BSW6SLXX2 YzmNDAUWKKdGK2D2BwidIt5MYnuc0uFwkIUYRj8TOzOSyiQ1taggkJzl8CC8UbYhchn6jkbb41hR xjZO1d8Q7ZzIlqo6MYGBsFUi09R47OKR+KBp1HSew7YRMOK/NuMhKCHmrMxS9pUCRBpWe2QdoExc IP0jxbYOkDgZ/R+dKolMYcQb7C2gBth/AsK2C0glcgK5PET31lolqhXTIm7A0qDhmNKWKjvHSMo1 TXTisBHnkyuWmX5APIDIBxei1CJDlBuOJMKCIjVosjzlM2SztCZ7EfJGHEPGpqmJwAmHlhmDdckC L2QhnkRhWIjvKI0OsoyV2SctXNVAwrvSysVFaSKsNwhd3oEBYkxOVTTDkEcaGjqmRcOThFQJsn9C EB8lRJFUv6p8Uq0Wy3ROk8Ga2YtUqKRcCdwPvCUntDgDDzbCtjiV56FG7FIRWjElmTPOiSlVZ22o etVkrg+vZlW9Cs9fpjr/5jZkGVRRz9HxPITXGaW72fcVliHfx7JiJ8fH1VQCOByU5LKt2MDMiEFW rYlHuVkFMoD3GBBpA2sTqZyTVXESmIFplZSFt7mhJ9nYIFklEiAgtqxWy7J1AZccljgq7+uaO9Zx GMipChjYjdLCJEvUYEPTSLHlLnsWxnNOnolY5OVlURg3FvOEEBrAWFsocStRGLo2MHrB2CsGQpJa ZC9prMiIn6KI5zyJ2FuzsGC7bo7gnbAZQFrgQgG8NV72DuYhMrAkJAjewi5rfuawwIMbhbPL5CWU iLUoENsW/JPHYqhJ1AZbanAWNkGQyGOBkJe52bQh0uSAiD7y8FB3uAa82oLmTXBpwVMywbHbBM8W htPk6iYR+/o7bEwlUyQOiBUm8lFdZHbwwKw2budoOm4v6/nAVJ7U4euVk9DWYT1JLCgyVXFoDueN vDFmYEvGaOjKwCw2iCI7m0H264CEUB2vkNiOszaK+oBP4hGYsGSMvGDxXZIjC4gRXqMjFoD9goOM bKvwElZkpQZM/EisD2c2CVGkqVEBgRhV/2abTGWyonFBqG4N8XpjYbEBjI/sAuNNKyDkI2aNeKhx M/KBFk0p3j8wrp2mttj1m1KsS7ponuPVUF5wTNxTTswnWSzmZTWL/dJYV0FJxHCvEyanXwkLONwM RKK3sP8figagvI2rpCoWJdhjOcd4r5vPDFuEOZlUB5AQhxWcjoBWECesY0vGsL2kMlYjQRFXSI03 UVit6lJjrT1e5iO3mOoCwKYsiI+JR5BmBYe+8dIvlluWjE1ZOjla6kcLL9ikimBdFAgv4fBAOLZ3 La+H1pCUbCbuK6yHwvZDkSRGPeWkaTQhCF4jIZg88oEW+iqZGqQipKipJtVYiUSG3gwxIbgtlY3k YuG9ZOICaDkVmbxkLCFMh/17bLUC4f0BIDTCiRHZREEkAya5VMuJ0xKkT6VeNk1BcS+7UCYqDod7 2U4U9O5yMvRKNGDzC1EnKqOtV77A9eS7BjTCo3lyoTBY2OwSzzCW2Yo6llsNuqtVoQIg1WtaseVC tadnDHInA696OiYeRJEAqZvcGNhpEm3jYpbOSnL3AzDy3ROPPtZnec+W5IuMRJrEi2EbWvpdLHNS ZRwKI/2eJi/hogYhbQphvbOlsjnZgJBKcdeydWLYCscsFRMJeirg3gcEhGHGz5BarZoPtLAybETp kt/pG/OULJ5TcgVnfifZZ+YAB5iHlmfn1CKiY6oszcPJJWY2wxsHs3GVfRyygPX+pOUFG26e418G BlfKZ6+lA1uTmaWBiB4AvLXR5VNHQKrKsY4tF13aqZxVIiriHalVULK0V3U5x2qDihx9UI2u4px7 RvQXbPwaXpyLNNAiCjlItMHE4gEBIpZHGjrCJUb4vgcem1CaCnPYGWUftpSuwqZJ6hCx6wy1youx M8Sm4ka+k6xDOZiHI1CwUyB6tUY81LWmGRKYB0Y+3GHCUR1Qt7VesGr5zB/r8jIJxmY3I6YIP7HH AYS4XcKfUxJparNoDmHOFGq9qtHRWHgYJo3Nh/kCJDD/dCunQkuGSnZ+YNZVu4KFD+J9ZwiLKJDq WJ37yzkmAm9Qb0Jx1dA6WUOj+Sy9EVvdhcAWGIyJFtnBGJjP140eiwnTAAk9CLhIY0DoVSiWRoaD QIWMlNUBymURCMIrdC0RJuqMTgdGWRUa9WlkVJVX2vXUg6TrwT9nZS16/AGzIWixoMKk4xaZlogx ohm0R07cqka4k82P1dPRrDsPUGaNlN47Ze+/1QPSFyhhPd+BWqUttCONl36HPsZocfdUzMokoPkb k6zEaDMhxnrcr3NEjBJl0E0QAsLk/cyBiB5tn/kPuBcuVZ9C/AdC2NcfDgQhJN7izIGIoW6ZKQci 4qYvk4cHoQFxIUa24UJgi8YbN3MhCAu5nrQTh4EQIzvzHbGyUKZdCMJ4D0O7ENjGDfUUIbsQCD2Q OObmQ8Sp7jIoHyIkiUvQPgRhNuRmUcOHCDQSySibewDDhxjY8CE0JmZ+wFEqjqVorgAhjqN3hwsR sBaWFLZkzASnfYhgZaAH7QE0D6JDw4HoUGuLrdOxOxCh7YEqB8LnEQ3UHAjCSJdn5UH4JDH03YEg wE3Nk6sWsScp6ayZORCE1Tj35i74IKfbZogv1UtsDoRHsHaN96gOhMfeUA2/FgdCI+JAjGzDgfCY AZOdORAebFLPNooDQQivDw0HwmPZxbuZA4HbGIyb+Q+ohIlOMa+32BbWGs9jq8LYmf/gTR13JRk8 rrOrkc8154Qln6j0qUaaAzGw4UBoTByIQb0ZCKMOw4MYdR0exGhRz4lWF21+oGtsdU+aB+ER7dlU YXUhaLR5p2q4EIRMMWflQ3gvm9vahyD6RVbyhg+B62V8KcqHGEj1IUa24UNQgTl7N/MhMFF9s1LZ Y/DMbXOg1mq4ELglM6c4cyHAPr6e1RMXwmeJChsuhOc7dFpktbgQjT21C9GYeJjTlc2Hxa2A5kE0 SDkQHWomfpM7ww1o0kk7EFWIaf+hSTqVsUpDRbwjw3/omPIfNFZbAyHtvW5wFeXDf0AAUN3cau4D 1IQxXrkPHNzmnXIfEOvjgpm5D6SYbC5x5j5AffnolbMQnUSYKcTL5ZfafSCFaSeTZu5DxM52Ccp9 0Ii4DyPfcB9I1dZ6Dfch4v6BGJX7AN0eg1PuA+IjXLIz94GNiXoMubkP3eJo7DuMksbiw3QZ3sMw cYb7EOvm3nAfIu/IKe9BAc156NDwHRQkom5Q7obSqonVfQdc/ButsrrJ+AowFep2gFji0HaSqlnr DdEm/cCa3d8oDdeglacdCMTiBRtVKszUOaWBjPIaNmrVKI2ar7XvIjoRLovq2OREJDK3fZG67JGT RAg5EWhUWj1suaeFv1bknj5ES3lYH2LvfAdqlF4ePlIfgu8cg5DLs2mxqzEjh8UgLoMsFxRJpaZF 6dMpODH9VSqzRsmslLezUJjHGcq01SnBtskr0z6MfLVWKlVao7Tavic5faetuGX4GG4br97KTZMU m+YjtWPvNkxyrQBfX+f2otYu+Jr9Vc+d9bz7ziy3nrr21qaL6eqVVivHcO3WOyUWzGFJD+t9seAO XF4UxW5PgF3q6qJowxAMXTieyfKhROyINwyLhh4nVzryCC/qsRs1UmHbBmGKoM+G4KML3DJr2KIw VWNbrKQib0iJyTi5xztYHIBhZ7xVycICIGVzr6omL9rJxvVqC/fenz3oBUvCaY2whyOVODSxl+9t bNc9KswgttLprutY77qOqK4bqVrXgb6DhaO6jnLL4Sl0neejESR4VNd5g6gd3XVA2A== YVFd57FF4iTeeK2JR9R3ox1R9R3ilDL1CSSA49NmA8NBgmLaXgYHd3TM8LUKZKZ35BGqOu8/z1K1 xhB9DrR9lDf9+Uocj8hMHDQ1CBLA3RQe4Uy4h4c6EEG+2DX1CQenp7A16oW4K5hK96q6pjH2a+08 6g50eAFhpQNdtmPyNQy7/W2/q3Zgx3oHdkR1oEpVOxD06+TrHehSO8XeOtDB3qozsHUgKdM2A1u9 EFLeZmDHbJuB6+08og5UT4EYkoMcA4e1I0ibXfkJ6YQwNnYi4ED5UsURwmT6jx1xtA1mUU/gdH6n aFNq/MQhbm/kKlfOGqyi3X/sLHrhI0HU+eOM9lO04RxpoZmpotq8qg/rYxkr6fHPQGrRw+fGZtjc YFtLP+g3C23l771xc7tsndB6wn0rOvpTv+nBM6K9aGBxPBVhxoGXvITHfKmOPcTlZGXdoGEGJzE5 OA3WD4e2NAimjkHAZAMeEdcXMYgjTZF4zsCnfRyEe8h8XQCUF7EJ1uGCxJcHvirYCyEnp0mDybKO oqqU5Ojpvaqa1kiM33JDE5d7vUhwOPbSV+GKSUoTFUIwQqQKg3lqG68yd/5Cp+HvzF/th+avnsCp 7E5RFvbyqZ4LHjmzJp0H6V52TxB1/jijfRHYq7XokOyFQNwDsVejvwd79cZdiL3WE+7PXr0/92av Nts9lEJjryQLWR4n9CYry4Udcy3ADmtuFouPHZNFRaeQR3jekHmpU7X5DvqsIonFHMJbYWXhsJGs 3uI8PpnYPiJ+OwgtOR4Ks0Aeg+jVSoig9MJiHXPk9tUFrtVmXjQWw8yHyYirIHbrz4nvGBl8wMfA Q+WD9kPzQU8QVfaoKO8I89oaIjhyOk3aDdK97J7A6fxuRvtgLHai8RjRzbb6corNTsymY4y1WV7d ODtmbw6r6UN9CCoFXBmAwdOXzZq19Ip+v1t2NQE3sSbYl9Jqwn1rGmuveneh62MPdins6NFLdQvs vt66ugV2g7LmbpD+S8wBPkusiMf9BHVjUjD4VLadO+T1OByo6Bg0asTBsI6IujbWz1Lhjs3gmD4C WEVf801/nhQvH7+HwnY4AgEfipeCHpF1+Iy6BrkEZ9TKsNHIsqRDtlvua43cW5aciNiLJcfwyQsT kp3yoJIIExcjh1t2hnahyN/B8f2HEiYjQVTZo6IswgQORZkJE5c16TxI97J7AqfzuxntiyRMpFmH FyY4SnAQYdLp7yVMpIkHECZrCS8gTKRXj4VJY2zpvypMknC9yyPKwddNDfjIuQoT3m20A+KXOYIf APE/1AqMpp6mMTaITyJJcBUObt90fOdGrO/4YI8NvrCtkoSG0pN4Yf+7iCjpdTJsH9+rqomn+qoo WWvixRAlEY8UIqIvOSNPeu0uInlCfFlR8jWaZslYRNgGMJ95Ny/y44uupsMmHEnR7TSROZY8jgPh 1Geom/CdFh5QxF0QuC8Xhh3Ho+PUPzb0E27A48tRkA+7vgn33018gGbQAiY3oESPm38M8YylSnse dGAZV+wzVnypWCQfirGIoiL2+3EFOA3hNm924zUs8osHKaoq1XAyCYkS31oVPU4OIheuhMJtJ5It Sg/ygU3K1kkxht110ld8tWbCjjVuYbt3wZglBzfJLrb0Kf0TNxt7LAkiwBlO1BSQKInFiu5zgS8p rqTQpThoGHEBcpHLoZDKZ65CkL7lfIaHGtuIfNRq0KJUk9yKFEn38e2KCQcBSiq1WgXH8dA3E47c nKW8Ti4xSs7x1RIVwglAhixvPTJGTRHMJ37lKvK1KYmLRVRmxCUFuEwM0ySh5wZ17+VCuh2eALwE m3w9dIgp6HAEOWGzHqvrqD2HsCeEGTgZDmDY12DMcBwOMIPQlk6fquVwc1pBi3CPLmm5szwDOKSW B5yv8HbgNmoH5oTx2FXt+RBJ57NMASd38iWyBPhkHKag4euTcfUfB8Lz1oThbA4HhVBVghzP3Xq9 +1IwnN7t5NGDNMPIzuHB5AO2Z3kw+dKU1O+MjBitUHhaQGqNbDRDObocRWbZz0FPyJVQoR4axAzj owUIFcDeNbIVagRncxIXy6TqC3tjllfyeB0V15Bh0BzOUpfI2KrUeepPyODoOYRxDaDrzzhQ57Th kjAC9YBM2MoOsRpYmtk74wkSbTTSVMvVm2QLiZ4DlNhc+70T0j/4IufVu2QvVMBavoO2CdLE4ez1 Bd+0wA0o5GXlA6qWtddaZseu+ubW2xcBd7bh1B8WLvisD16iRIS+TwPDOirfT+RD9UAVMsn9djuL gfl2+LtTcrzqSPl6eQ2hfB6xJvAYeiroNIQiDkodUeUNrNWqU+o1X2vfTo1dUDWhaRUsP4To6tOo HcMGWUYsjvdWrlAcCAQCrv5AyzsW5S6LwOHWNH2wr8IXegUiyTLF401eiexQWJDbdZaM4Zy0R5hJ 4PsjgveiM0DMGL5xIXCUV7G8RENy1/R0WZaF+GCJIhf6dtUoFa5a7MkqhuXCLJiTOwVJhJAfhlO+ Dlf1Gaw6+hpUP5DRGx3zePEHF1U7X6nTeAY8YDl6vyGYBxLUPRLRcFrYhINQR1RxAzMS4hSghnDv gUf8Fc4NA+EgAq544Au8gE2ywOpbbC8w0u6ZMXiVgLC4JR3oMl/rBKgPd5QrqAKrjCKYtV4wslLB +b4pyMBv72Kji5UhVpkHMlrUsT7pEDyHFaA+L9dmL+b1UV5GvdeDxZjrEyLtPWuXHOXtWulBj9sk +WGFgFtwnHS4HEmHTQXm5g7Co9TSUOKEwjxhorSTn2OaMzDOQSoGZrGCE44zAZWMKywK+KS4EkYF 8dMIWgLG9/143+7itE4egsCLX7zQud68I+rY2RHYmQyevUYcOQSAeBUuABZScfMom87T7N+SZqnS 9+d6yTJKuPPEI4oNFjQoFbI4eB8pdAO9YWQ/yCUpy5F3E6byHkmP1BdpDd9vYhGpUaPndhd8rQcO zzdoSclMveQP+0C8a1uyGJQGR75gAvIbChzFZnCiLPGlfAOKcq0tv/xQEIm9EeoZca8Ctq4Uebid uIhm1AFXFEBQqJrizhncprMBsXJ/mc42oEobF5/glPioAK6QtRxf0mqJe4SK0YCV+31GczcgLVPr t0G3d+4ofAxBr6EaqU1Yb+79EkSD4X3twuDhb4d7Asbw4upWj1hKPb4Qx96prsWlrMml2QDwPbe4 w0yNk8L6cJKSJJcP95NsxEZei+ulccWjKgNBMRaXboyquElcHV1hy5sXeQ9s9NDIO7BWxOjvXhM1 Kq3CfeQG0ke3t34T1PO1rlTEa3+rGoxRGRUdo7cJ6w1/SEzLytE5yi2ABvduZj6Tw9eVJCds6Gy9 b6jI/YEGt9nKbe8IMElSnLFy8yHO0008NbPch5wnvAmDTsatwHKbSJbb6yAhJZwJN6jgfa4g97zK HVUJ0cOYsqWRWq3p3ss7hzLBFQOQliZrxNWZbWUDD/5itrVceRcXBy0jHBUjbyZsyQHUUHCpnEqG K2WxXNPJLTm2yTseOb6wMGgMMflOTifYKMeGNkA651qF9w4maX7Swa6cmPshMlmy3BUdsKtY5GkW I23tUIo1CU6eyANK/Dsk+fvOoiOwj6m/OgkcMECSVkr7vQOpyMuTHWKpqGj0372UhrR6dBKtoqtt ab5GL52PrOJ6kmzlytMO4UIhsFKAYYhruQYwWtkgWEtWnqgRKhEXxeE5uFaB+nsHjxPxE3sd4qtc giLSAdXQDtXKdCqtuqstOiIjKWc5nRRwCSuZa+ipBmG1amJhw9fU4bTzBghWetXMieY/bGcFdfId KmHtVy16uV6b1eft2+2BzsoDWbz+x4EbuNibr9BvEDQDH2hBv/MrMwOhjncuyA3hHavSvBNCT2dc dtUKa8AOXxA/gyDJ5QR6JzOQUVjHepUaoV7r1ZbtLM7JjTvwDSHA0HkepyF2uRv4JYyO1fv5OdR1 E4bbVIO8CdDpbcJCNVjqZfG4E3AzpspYrd+Sak4zdF0AGSzE4pQAsy2Msl2F4Va0eoujrddabsRa 3iW/YcIXQ6PTCswXa+qDDgjYw8XdA9FD3zGcwcHlTnj6NCA6qomoMfZdjEHXe36ioCeCG80Xg3VC A1GD37BeKey2QDH3eq91TDv3sTYH1YTvc3DDVN5vwg+2WOWc9Tm4Pk/Xp/K+E75XaY1z1rmLBdv6 XNzdY85umtubeGATr6zP2Y1zexMPbOCVzbIKYarwGYe+lXsLtd7CdauSqGq6DgwdMaCqLweZPhNb WWq64qa5oqcwTLc5nQ6MshrU69PJ9Cqvtuupn9TBHvzY9zXRzxujQtGgvnFVIh+fWssS+HR/7KdY qDSzP+n26NmmNK1XOM1ehFSig9Vx9NuRHrOZZktJfGsi35871PvAhjLnS9iw1bIRG9qa72oUO0Vh vYyB5Xpryyaoq/b12u3BQrCsw0zbW5PknvAG4SwBR6x0eTSQIY8UVuVRJ9TETytriCPcVaIRXP5B JpoSagoZRXWsV6jS6VVeaVXT9BxazvfuDE2PtAnrdkoqWTyc5YvfjA1pOOhtwob0woUl5Jjshaky Vuu3rulxNw1vaCsVP7ChznGSJOdkN2NDxeMUyoSbIpqqxEEVl5JS8QPR492xqplxSYncitpYvw94 F5l86Qyfqe1pnJPVo0FnIGrIG9br5LBGHYeGX++X/d66h9TEVeSWl4ML1i/zlLErvrcMhXyieUot UreDlNoLPsozjau3pWCTn48Ob85ZIE9Tfc1H5Om0HQ5SUJere6YMvMVYU16IrEp8mPpjlqRaIS1s 58JynSd29+CdTTy2iRc38ew672zksU28uIFn5+LyHdrfX9lz5JtGPS7KGnuOE4f65baiX59E4eBa nPXvAO6wbjuOFXNY9+EA/UrH4SnhtuHIhXVEdoz4DtORqm0lDkJ9c1EV17FWp0ZoVHutbX2/MbgV grsDc6W+2sNbV8jm+BLceQNibwDcDoQbjFRtu2NQ6hsgur/s6FRn63EZpoRAzNUWpL7h0mo1UvXW dEpr7aumc50CY1MLa83Y6NrVmON7erFhhju1cbhumrBh5hHUgUBRHCRBuMzOQjCOJgYWJFXCmwtk fsoN9o9yKn7SC1jKSTZI6yybsmyGnl1AjyJshnPiBH6D8oCwKyWdhDN/2D0E+SBXSDDG94X7IJeb AeEQuEd5RzZj4xIYb1EvOeeECzc8Xn/AicSzjPFSEjC+2F4w4i8nQdNRzEPZ6MSRV34YeiC1T2Ws GHO4dxZrzMFb2QDA3Oi7rzKnGrLDu70szFWquo87KPWdXVXewEK1/8m3KbgBDR3It5oSue2I2/c9 JCCepMLed6x7wrgQsO04dwwCsiCaSWG4TJjXroEl6eokr4ic5R1sPgSKrNiTbVCqEK+21etScaCT g4VliPi9+I4BSYjTAMKhdTLc3mMwcAvT2G6v6aiDaYrxoHl5PcjzzUmhdCworFfM8A== SitP11Z9hnC4qbeRj/pyktYTBneU8zspCosSqqZ7DOfybd3zj3LxPZ9+lQtmeBsVxzfwzBUErK+7 2lM95KmQMdwNc3z7i5FpQm1A/HEem9NVZOW2Oc38guCnkaox/6C0JiKqCFkLaFnZTL1n3/1qXM5J /Yh5zdb2Lu9h88qGw337hmUXX22KOeaNiDd+oxFTh28a2eEdfn7oF/2VZOnYy8MzHMWO11C55X5S iAhr3pIeqayVWQvhyY9RBH7zMwtjeceGGDC+VxvsVxCEht1qCX/FBdIWl5esNe7Id6uXr1rcevtd 4dSrz923PP3omQdPnFjceuvfPv3AmW978PRbz555cPHAQ6cfObN1+tw5rPef+S76y9YDD5556OHz D54h2uffCYSytOS33vrqe16z+H9Xe92f swift-im-2.0+dev6/Swift/resources/icons/warn.png0000644000175000017500000000101712227051774021560 0ustar kismithkismithPNG  IHDRa pHYs  IDAT8RM/Q=oL;:Ӱi#XXِHH-Fb;H!ZBǔf7{ιwF8[svMĬ]v1#t_t8i860w먵q(F;^>i@dJ1StboxH9RK⺨aT w>FAYu%At"OhÛ {,I~1)R&LJ._̖Ju n]{5]4^|rŁ_N p7 FE (UxLIbW[򜥻aB@c@3 V=xd(}!Ƹ0p\{^w$JN 4ШzvR!4pr^ܾ9ͱ,x% ! +q fYIENDB`swift-im-2.0+dev6/Swift/resources/icons/actions.png0000644000175000017500000000133112227051774022250 0ustar kismithkismithPNG  IHDRw=sBIT|d pHYs  NntEXtSoftwarewww.inkscape.org<VIDATH=hSQ{ׂPZK&* ..q*&Mg(L"R! ҂] B:tbr_xIzs8Wj5`l,X*׃؅yRg|t` "Qj[x캅F,뺅5@4,!72p/mb+1 Pu+[Rj,kGwp\r(Eqr*(Rƛ|z=H6-e5Ç@E(ͬCsf;8.6'&e`. DML&_셙ҳ&SǐREÚA)e}ߝ}6$"y0pScc,WM&9əry5 )"O;o`chӘjNc;4-ѱ" 4eEV"4*Fw \( 4C}6z'EkDhR^kn/oD뫢u;lWk2I*3LGt@_&+aj;KWCA*ugȗvt'*}IENDB`swift-im-2.0+dev6/Swift/resources/icons/circle.png0000644000175000017500000000033712227051774022056 0ustar kismithkismithPNG  IHDRw= pHYs  tIMEyiTXtCommentCreated with GIMPd.eUIDATHU9 cBWz P5'y`=`<4 ;=t image/svg+xml swift-im-2.0+dev6/Swift/resources/icons/tray-standard.png0000644000175000017500000000175312227051774023375 0ustar kismithkismithPNG  IHDR%%Ş sBIT|d pHYs#ktEXtSoftwarewww.inkscape.org<hIDATX͘Ml aϛnuG*XikwgwPAHrh…+H"Hp$DD"bmS6QUD%>T}lggqЙ2y}!fD4!$fJUշ3SDV !ZyDoYV?=UUSE $jYV!Dtmttf<VIeYDt4̓hqEƔJ S BˆaUUuPiZ33@EƒUUU˚^q$nۄ癦yʼnuJ 0sP-D^WW/*3(̧>qMDP%-w|>w^B MN22y&3$}`fG,{*r:3'5'|DQd2lv3،?e1}!ѳv.T__\.ךY `1,s@`O}}hD[9in`.sbS(zoۅfQ33$1\MD-2h> R˲.Xi`{RTb73O"nfg;D"1z?.3/T#()t"Öl)bJ&)H$[R^&@m$ zq`%ow31:zED4?,kK xL&h*hah4\ס2d!Z 3###nܪ?f;͟>3WKr#+:TSS mǞCiB B/˕'\(3oRs~㆒RyImmmܒbP0cl&O 75u幇U&IENDB`swift-im-2.0+dev6/Swift/resources/icons/away.png0000644000175000017500000000105412227051774021553 0ustar kismithkismithPNG  IHDRasRGBIDAT8˵?ha'ҢiR"A+mU`(n.Eqq nKL".B2X *?Irm/wCijx|ڙCm \uҫtۅ"eq۳OxoA<6|pxMET^_Ώ8zsp}>jM;SR\g0KNu/4(.k0'Wxn׭~GyQ!w|4K-H_2fN[TWAN-cKw33@)FV d'B}/7S+TrSCAB )_5!/?ճdvK@H fK ^Hi-L!N+BQ(Q*b &K̴\b.imo"er}EӅ"gdžحk5F1P']\?)B[IENDB`swift-im-2.0+dev6/Swift/resources/icons/lock.png0000644000175000017500000000326112227051774021544 0ustar kismithkismithPNG  IHDRddpTsBIT|d pHYs+A+A>uNtEXtSoftwarewww.inkscape.org<.IDATx_TUǿ3Q.梦D%AЋ4!hmܽN CP/D,TO]Vi!DmTn *n29F+e8̙}s~~Ͻs! !8"c !8"cTjE0.K'U(`;k4]0<3'WcCCCS5r"lv-X~M0D4uhhw^IAxt=Dx jC8D|߿dM8'Hkk.PLx?[% ŴV%pPt-9!H"hFe2G?:in 5zQB1 ]MOutTxqjCvzqpò 1jM?*}<ϻjM %q9 ja Ng'l.OԴb'WOq7C楸AiocFf…|`Ex<Pb[+GpYYj/-8Φ/QFR Z|F0Dr0Kf`ׁ77==cccp]'Ԧ3F&%OagVǧ~4J Cˊd60h_Yv(%Lx!jb4 GHTVB&''W @L><᳉E/8F"Ua YPf$"cH1_cDBc >H*"3$TXlΔ/$d ݀JF`nN)b&K1#EC(Y\OCrSHq S9DV{J2 !91$8䐺DB!$8p% E Y&ٺDHqrH#@^ą`Nz]YJQDCTNoo(:[M~$0#1J?Dc^J7⭊:3 ׾_F'c(ܗ"c !8"c !8"c 7,S6IENDB`swift-im-2.0+dev6/Swift/resources/icons/offline.png0000644000175000017500000000100412227051774022227 0ustar kismithkismithPNG  IHDRasRGBIDAT8˵Aǟ{|Ot8RD${" {SCSEK[S{4p"8Go w) u]χ4Yé{!DRZ[޿%R+Ez^PVa8r)d2ޣx~0(Fn77Xe8JNscr~{4 'BfsnR Vi ZhZPՌsѹ+R@(*  c,c hwʲ :hat<x/R}H>Zo#!diu Z$1l?\97c,_k rvecߝsO, (`)Ik$Il>|bTb1X,"~R>ʜsNyI/B9fOPe—$^IENDB`swift-im-2.0+dev6/Swift/resources/icons/dnd.png0000644000175000017500000000103412227051774021355 0ustar kismithkismithPNG  IHDRasRGBIDAT8˵SkSQ=}/M&T- F*fB88%(MũCY:Epq Jq y߻?R%?&rι8sIM7e@AA c[Bkk|d7=z=[h=ٚS[xveee0?;]Ɔgl& @5TjFa !ָt"KTi.wD0[4l'qkB i`frX$8S.۠b0@͵# 8Hgy^vD RG$>r2KCmA[ *KٜHD븖3Q&>q~6 AbԅRq>Oy<97nx oPO~P3EnWy Ϡp'@|De`IENDB`swift-im-2.0+dev6/Swift/resources/icons/avatar.svg0000644000175000017500000001033612227051774022106 0ustar kismithkismith image/svg+xml swift-im-2.0+dev6/Swift/resources/icons/cursor.png0000644000175000017500000000057612227051774022137 0ustar kismithkismithPNG  IHDRw=bKGD pHYs  tIME ZփiTXtCommentCreated with GIMPd.eIDATH!KCQgDQ1YdAX.VfVeiMdei b8 2q/^8 {fc&F8Ap;g'|Sp~E0{4qϧx^/ޜÒ҂1~N)>ݪVI<U J^Up OW) r"5Z4g}qpxP^"IENDB`swift-im-2.0+dev6/Swift/resources/icons/avatar.png0000644000175000017500000000406012227051774022070 0ustar kismithkismithPNG  IHDR00WsBIT|d pHYs11(RtEXtSoftwarewww.inkscape.org<IDAThŚKL[?m1 D& 0U긪ԎZ] *;jL+wHDjH9L6&6`l|Nqll9k^:EQЂ`0<:-,xl(kF  >:! ,/EQ+h]`#3@wBjſYp@04(QW!p*Coo/===tetz<$I$ 8zD~%85f`0099(z\.j=SRH$«W888PWKQ?J/ӧOq8u_fbd2 vÁtVtQbYVWW/DQ,OFyVg~zzF#/eٸs:bH4%077 &Jt200լ AR)Dz k(?NLj"2[[[,,,fϐ344͛7A6$/_T*UrB Ʃw:\ضEa}}P(9 L&߿Ͻ{D۷ϫ 6mf``ft:׮]F)geY&DTu@YϕuӂrhPDQ,uŋ Ey5,7|>Pob#` Ӭ4#gZop8lfNzC=51 iUq\M S(jpQ2!I~tp_EE+mmmi"`2ښsdYn{lvvvZp\eP&annNS_E<ƥP޽lB!, of||tLC;ymBTm֭[iX\\dii >| @{;e5'IP(EnӉ lmm1<<|&O<'V Zvrr0L-mJtxL2z'ݎvr0  ,Ly||p@|bD"X,XV<$Il6~˅;KTc|NG$auu|>###-5]|L&S=p &PAnܸ2>}j?"Ж5gfA399I&!N7[ ;Zfݭ}>_K^Wӻ;F/#BmHg@F2lf2OGϪ,Š5#y1ݚeh@'zRT< LMMx.~i.[,Z-gBgU`cc܎˃A F)*1))~F1FQ5kYd2ꪏFjUr9,KǶ^$I{<ZP>_ל fdL7*I F####|vfSTmSHzPf^J6N$>~1>'Oz;J[c|[1JK,cxxaBV;ef3@jjwii6( UHH. Ae"~zEubD"uwpRՅkzi6;ԿKkkkz?EgNvcZ{>t*B.zɂoDQAUZ_f TL췖6I8::j (͠_ Sh_UFK/S Aܭ'< @0&_m˕Mbnj-?f Hr:[>;:ɯWx&b̦:ߛZQ8 `+r%\%Gm3@Cf2%5ne"`7bi]_>F. t.$#K-d6{|4MÉYTØe߰~M͢v? b=cIty u"v|#z%ۚix[Hr  ^/ Axe[lsZ֒>waa0<+YnD] 1^:GIENDB`swift-im-2.0+dev6/Swift/resources/icons/no-avatar.png0000644000175000017500000000660212227051774022506 0ustar kismithkismithPNG  IHDR``w8sRGBbKGD pHYs a aJ%tIME:{aX IDATx]KF5ٝ%\"-V!H9!qA合NJȉ9$Q\"qZEq@Xޝtvš,w]U˞Yv~iU\J(RATpI!/!, \p.n0Ox2:sVJ2g<  | moB`0*^2*R0 n /B2 !S^\;Q 9Bȟ0 n !׹ '.Z~E (+?^zzJAUUT*sD"`\ׅy<m0 ,4|.o+ڹ-:ZKe=q`& \eBMaV":.F!J( @#B;˼v]Z-!% qy*JHO"ԔFye^mNA;j*mLk6,˂eYp'$ i4 Vn AwmByd)K.ZJQ! iUEQ:ڟ犒`<wڷ!`^xu0gQ69[j5Y'@hS Bt:hZA, a4Mllyce>޽ qX}M}V|ra>9 prrl&)FiZid&M~ ;IWV8N1NE\5j!I!s4F#:syjHafwou%n1l6 EQ,0FFQ,2@Vkzg/MT*48<w۬VՔ8ka*!Z\m6ʈS&A)U*OMi|>!djK7Y7m+R%QAZ^TD%Q_դdkX ٶ}eu1͠iZ<,K3\!/LZ-> <|>6̤rZ-lfC7u,de`&7(0UvXUtBO6ٶ-B`sXnɚ(t z"]TTД6#G+_y>WMbuEY-6` !caV -Y! DR T@kq,߱0|Xq.~pyp]mdol 漕q<]l# 2^CH%}/  noqft I K"O56Fón̅-lA=@8Xnj4P `\rog<tXߣYޗO DZܓ*x @ mϡxFX-'KCx/IENDB`swift-im-2.0+dev6/Swift/resources/icons/check.png0000644000175000017500000000075212227051774021673 0ustar kismithkismithPNG  IHDRabKGDtIME 963@IDAT8K[a罷! 4B]UB!\ť.EXlic?]JAɠ7*Jn>N%n+ , MrrZߧ!7nOm \իG$i XX2IENDB`swift-im-2.0+dev6/Swift/resources/icons/connecting.mng0000644000175000017500000003272312227051774022745 0ustar kismithkismithMNG  MHDR W% TERMZY pHYsooCBACKQ2L"bKGDC IHDRa vpAg\ƭIDAT8˥kAǿsά9I.Nˑ "lDL,BZk+ `{b%7&d[S @2|7( 攆] !DsyB#29;@#9׼0P;(ҍ%NXNc2s Б `cj)^Ya9^n}z84yh4XRrH_:#=͞/ϗ0 2t}33 0w0$H_XXcn*nGW "v\S1j5 "crʑS@FclC1p΁ʾύ'Wac=GN9(IJ BU%y{T+Nwkk?%tEXtdate:create2010-10-26T20:10:22+02:00X%tEXtdate:modify2010-10-26T20:10:22+02:00tEXtSoftwarewww.inkscape.org<IENDB` IHDRa vpAg\ƭIDAT8˥kAl&$]MM$z4)xSAgOT zPP/^S!ns   5R#+W y;ʔ- "A)r 5jJjӻvG&39Db8EZk~x{3-aXßKJ`!bfׯ'\^tPJu]ׁrǝoch28t0A`>l:̡)U/8;킍$N3/_m<,%eLf_Wkqݯ%tEXtdate:create2010-10-26T20:10:22+02:00X%tEXtdate:modify2010-10-26T20:10:22+02:00tEXtSoftwarewww.inkscape.org<IENDB` IHDRa vpAg\ƭIDAT8˥kaǿ{?"6$wh:!15 :;9t_PǦ ^\r;{sw)Ŧ<χyR |^1Ɩ:Kڕ1f7g˝P;,Ma7m*vwv)_SJKsvӾ8N!z/zoϵ`۵Vn P.R%ĥ)~# CdyƝjZ^ PR=]-nw9OpP(6ɿW^1G4 2`YvD(Zڰ?_\@ER y# %0Lc*\(OIygÔ%ϙt];~rRDS % P;f.1 ,s\A L:8|I%{:njsbbeIM>9}\M++>?U7-to%tEXtdate:create2010-10-26T20:10:23+02:00aS%tEXtdate:modify2010-10-26T20:10:23+02:00tEXtSoftwarewww.inkscape.org<IENDB` IHDRa vpAg\ƭIDAT8˥;Q;sg2M 6;V .X[XHnfKrm, m,1es3L}$\<blH!}n!4n47ԘusjYms{wV(di5 R@mv˦=+X @)E'?hlگ6o`*…ZZ.,d3,d6:\ZFQ$N DʪQu&(\- R𘃏888Ck;;9'<ݡsnW\s! @&2L`2~)e4h9V:9|TL&(JJDy#Ci!\ׅ !-d\)VT4M܅E]%nV.U@ȩSzaf3${QJ-9A 0KxmEagxOp %%tEXtdate:create2010-10-26T20:10:23+02:00aS%tEXtdate:modify2010-10-26T20:10:23+02:00tEXtSoftwarewww.inkscape.org<IENDB` IHDRa vpAg\ƭIDAT8˥?hQL/M1D ZM:898ъMKQgĥBѡS;Ew~Q4 Ӄ{{$"2&m7|rW_E[^y+^mlފTDZ/m,>x"0<{L4.A';<_[#I/и؀P`FID-wDmMWMC)( dY~*?\U*;0 bXňk7צJv3_e\͔(D\FЩ fF}ܖ{6h1@D5#5) d\ X%SJytkg[c#؋{qW,jEjhD=hc"+^U=_3);H9E Ɋx&Gg &09c6r#%wA;3\M&Б\ބNS?Ne籃%tEXtdate:create2010-10-26T20:10:23+02:00aS%tEXtdate:modify2010-10-26T20:10:23+02:00tEXtSoftwarewww.inkscape.org<IENDB` IHDRa vpAg\ƭIDAT8˥=hAL۽KvA A-B@$ES X:Vb*&hgj h05Xď7!Lo9tАBF/@!ûۄG|Gm0~`SlbZna]*3ZuQK_媼: rCHD,iESVث%?{%%%tEXtdate:create2010-10-26T20:10:23+02:00aS%tEXtdate:modify2010-10-26T20:10:23+02:00tEXtSoftwarewww.inkscape.org<IENDB` IHDRa vpAg\ƭIDAT8˥?hAƿo/an-6$ K![,6V"B@<@KNeaaKI+ޅΎF \ j{05&cYWډ1&\quEt,\Bx=vv| `} wlݤ`IC_}IˌޣѹqHb%ߓq?~Piq|(J%zs+@bD0@$# 9T)mOTk> dkfQwt!X @D BlLB)۱A#:+RJR " k8@F!TH`eV*U@cd?4;^RAbgRnݱgӭε! ɡD yrl0)sD1* 喼Y 8yO2͚Jo/~dh%tEXtdate:create2010-10-26T20:10:23+02:00aS%tEXtdate:modify2010-10-26T20:10:23+02:00tEXtSoftwarewww.inkscape.org<IENDB` IHDRa vpAg\ƭIDAT8˥?hSQ7woL&R mT,t!LID(H :;9ءQ܊ϋ}Pbb@z3s~9q^fTS) sJ)efE__j9=tv]֏|W*^Td톴[bݶ2TmJf~qn5988xmFGZPz60)DOY^]U]g e=cafa&vhDQD$^ebbGQ %ϲ#ޠiEJk/bo[~@L IP:[BL9_p@,1Qcb3w x7 ðvC4EDșRTcuq/6M&UCuHGgow:6gX&JRm s&PGg[4 'n: %tEXtdate:create2010-10-26T20:10:23+02:00aS%tEXtdate:modify2010-10-26T20:10:23+02:00tEXtSoftwarewww.inkscape.org<IENDB` IHDRa vpAg\ƭIDAT8˥?hAov6[sy1 !A@ -"VV*KJ&W.'6]fvX$ 7jM "l"zI4Z:[]qymQ'>a}9U \~ffgYaueUYl+puq/6M&UCuHGgow:6gX&JRm s&PGg[4 'n: %tEXtdate:create2010-10-26T20:10:23+02:00aS%tEXtdate:modify2010-10-26T20:10:23+02:00tEXtSoftwarewww.inkscape.org<IENDB` IHDRa vpAg\ƭIDAT8˥?hSQ7woL&R mT,t!LID(H :;9ءQ܊ϋ}Pbb@z3s~9q^fTS) sJ)efE__j9=tv]֏|W*^Td톴[bݶ2TmJf~qn5988xmFGZPz60)DOY^]U]g e=cafa&vhDQD$^ebbGQ %ϲ#ޠiEJk/bo[~@L IP:[BL9_p@,1Qcb3w x7 ðvC4EDșRTc dkfQwt!X @D BlLB)۱A#:+RJR " k8@F!TH`eV*U@cd?4;^RAbgRnݱgӭε! ɡD yrl0)sD1* 喼Y 8yO2͚Jo/~dh%tEXtdate:create2010-10-26T20:10:23+02:00aS%tEXtdate:modify2010-10-26T20:10:23+02:00tEXtSoftwarewww.inkscape.org<IENDB` IHDRa vpAg\ƭIDAT8˥=hAL۽KvA A-B@$ES X:Vb*&hgj h05Xď7!Lo9tАBF/@!ûۄG|Gm0~`SlbZna]*3ZuQK_媼: rCHD,iESVث%?{%%%tEXtdate:create2010-10-26T20:10:23+02:00aS%tEXtdate:modify2010-10-26T20:10:23+02:00tEXtSoftwarewww.inkscape.org<IENDB` IHDRa vpAg\ƭIDAT8˥?hQL/M1D ZM:898ъMKQgĥBѡS;Ew~Q4 Ӄ{{$"2&m7|rW_E[^y+^mlފTDZ/m,>x"0<{L4.A';<_[#I/и؀P`FID-wDmMWMC)( dY~*?\U*;0 bXňk7צJv3_e\͔(D\FЩ fF}ܖ{6h1@D5#5) d\ X%SJytkg[c#؋{qW,jEjhD=hc"+^U=_3);H9E Ɋx&Gg &09c6r#%wA;3\M&Б\ބNS?Ne籃%tEXtdate:create2010-10-26T20:10:23+02:00aS%tEXtdate:modify2010-10-26T20:10:23+02:00tEXtSoftwarewww.inkscape.org<IENDB` IHDRa vpAg\ƭIDAT8˥;Q;sg2M 6;V .X[XHnfKrm, m,1es3L}$\<blH!}n!4n47ԘusjYms{wV(di5 R@mv˦=+X @)E'?hlگ6o`*…ZZ.,d3,d6:\ZFQ$N DʪQu&(\- R𘃏888Ck;;9'<ݡsnW\s! @&2L`2~)e4h9V:9|TL&(JJDy#Ci!\ׅ !-d\)VT4M܅E]%nV.U@ȩSzaf3${QJ-9A 0KxmEagxOp %%tEXtdate:create2010-10-26T20:10:23+02:00aS%tEXtdate:modify2010-10-26T20:10:23+02:00tEXtSoftwarewww.inkscape.org<IENDB` IHDRa vpAg\ƭIDAT8˥kaǿ{?"6$wh:!15 :;9t_PǦ ^\r;{sw)Ŧ<χyR |^1Ɩ:Kڕ1f7g˝P;,Ma7m*vwv)_SJKsvӾ8N!z/zoϵ`۵Vn P.R%ĥ)~# CdyƝjZ^ PR=]-nw9OpP(6ɿW^1G4 2`YvD(Zڰ?_\@ER y# %0Lc*\(OIygÔ%ϙt];~rRDS % P;f.1 ,s\A L:8|I%{:njsbbeIM>9}\M++>?U7-to%tEXtdate:create2010-10-26T20:10:23+02:00aS%tEXtdate:modify2010-10-26T20:10:23+02:00tEXtSoftwarewww.inkscape.org<IENDB` IHDRa vpAg\ƭIDAT8˥kAl&$]MM$z4)xSAgOT zPP/^S!ns   5R#+W y;ʔ- "A)r 5jJjӻvG&39Db8EZk~x{3-aXßKJ`!bfׯ'\^tPJu]ׁrǝoch28t0A`>l:̡)U/8;킍$N3/_m<,%eLf_Wkqݯ%tEXtdate:create2010-10-26T20:10:22+02:00X%tEXtdate:modify2010-10-26T20:10:22+02:00tEXtSoftwarewww.inkscape.org<IENDB` IHDRa vpAg\ƭIDAT8˥kAǿsά9I.Nˑ "lDL,BZk+ `{b%7&d[S @2|7( 攆] !DsyB#29;@#9׼0P;(ҍ%NXNc2s Б `cj)^Ya9^n}z84yh4XRrH_:#=͞/ϗ0 2t}33 0w0$H_XXcn*nGW "v\S1j5 "crʑS@FclC1p΁ʾύ'Wac=GN9(IJ BU%y{T+Nwkk?%tEXtdate:create2010-10-26T20:10:22+02:00X%tEXtdate:modify2010-10-26T20:10:22+02:00tEXtSoftwarewww.inkscape.org<IENDB` IHDR7 vpAg\ƭIDAT(}/q?]t$鵜A#$F?d0I ,#b4 b3Qk%z?Ý'  (~r01Hޖl886qCS7usaWg G-fe,sUK!dB/ZO/M@捥F ̻YM&+n0F iI;VFz9ָkw.]BOc#Waۘ%uX#0B+R_x%vESـ_ ten%tEXtdate:create2010-10-26T20:10:22+02:00X%tEXtdate:modify2010-10-26T20:10:22+02:00tEXtSoftwarewww.inkscape.org<IENDB`MEND! swift-im-2.0+dev6/Swift/resources/icons/license_info.txt0000644000175000017500000000027112227051774023302 0ustar kismithkismithResources: Filename Source License warn.png Swift/resources/themes/Default/images/warn.png BSD check.png http://en.wikipedia.org/wiki/File:Green_check.svg Public Domainswift-im-2.0+dev6/Swift/resources/icons/line.png0000644000175000017500000000025212227051774021540 0ustar kismithkismithPNG  IHDRw= pHYs  tIME5^y%PiTXtCommentCreated with GIMPd.e IDATHc` 5|QG 5|X> l 7IENDB`swift-im-2.0+dev6/Swift/resources/icons/handline.png0000644000175000017500000000061212227051774022373 0ustar kismithkismithPNG  IHDRw=bKGD pHYs  tIME 8(V>-iTXtCommentCreated with GIMPd.eIDATHկJDQ@ "h6QFA6M>"&$b`rwe~pʙ9w̜(H}+fc m|ւῈM?U^]pVfxp)0Nh K@7!jG!%f 1;d}tJX{|\0| U |g 7)E%/α|a KxzU˘gts~X"7duIENDB`swift-im-2.0+dev6/Swift/resources/icons/rect.png0000644000175000017500000000027312227051774021551 0ustar kismithkismithPNG  IHDRw= pHYs  tIME7 xNiTXtCommentCreated with GIMPd.e1IDATHc` TC/hԂQ F-`0XBu 2 IENDB`swift-im-2.0+dev6/Swift/resources/icons/text.png0000644000175000017500000000034512227051774021600 0ustar kismithkismithPNG  IHDRw=bKGD pHYs  tIME !GpniTXtCommentCreated with GIMPd.eIIDATHc`#300'cLx, Hrȱ`@XM4Z0H |%+?(`tgVIENDB`swift-im-2.0+dev6/Swift/resources/icons/new-chat.png0000644000175000017500000000423712227051774022326 0ustar kismithkismithPNG  IHDR@@iqtEXtSoftwareAdobe ImageReadyqe<AIDATx[]lgz?x!!&vJJmHEmU$$QJ UF}!"TRPҴMq4qpwmgwgd]8T;wwι3wéxlV@ r|GPcC$]i߼rJ E[1^{)TEa#sn13Ʈ]Rz5")Ûk> {dn,d&R)j_j Kgi_h~?G 맾= [ />}ĂB}u ofEwcx<:&ŭctw@ KkF*s?zo+ӗ=܆`??s!zI$9e@9s 32vSEU&³1#?"p`DޏֺĂO3ٳ;HP(Ѐ*|^,YL[mۉd3D{ϭ{hY@'ȑvc)9F$ \l gw,ߜ˟}]q$?`[}Tsd%*zXдTfh}1? ýuuud2Vc\o4fn9 )ą ̳@3_'^~s`C;'ARQ(Eg\17AUH)@,@c @_~!#z.#"J}o(<~u_k3nK*υ^Yb@jk޷ˈ]pcq(fR4pUr䨒B-{ Xor#9m%wV%z>yH9 6FYR)LUۂ>7ڎ@4 Q4tP9C AU8-IJpwC;.sqwz5wKYoQS=}!y{YP; /WҘfqWc[a "@,Gkԗ)$06i֦n06R+si(ui'{!bRdL澧)$ܕSGtbmSo#gvBWB;p뙂[B€RC˽F[r2nr`g!PG (n88?eė6Mk,q(OCw*)$LmFR$[TRt\Fx{"٩ ^{1K`씞;ɸf:.Ϲ @4؋BUTF8+`iiI5;v"L/,,  v"Tī`)"SIOW^eFQBFeYNX_()22h~{e4['O|'J9/v0mnn֌a@{ʊu4 ɛ]]]'Pf+l`}ٗ,fzV]]7n0yߔ#)+hpWΗ%I:<>>̌v̨/E+Wb`ݱutt+W|Տ&ׯ=L^JU$066v9H{#$ !}8~Bf0+k+lm a g^B׼ey_ _}*f)4"ճa86= T)Mq$E|~gI:ڍYnj*A!;Y !i[H: 8V#ˤJv3Ve{b,ˤR)9Ƴ]---]'fXNbY&IdE.=\g1iS|y֡}D QimĚsi$$zQ]xū7b i$'Xd\L0KF4hM@XՔHd]9O)Au$hVA0`:Nbn75O9n\ nV@ rmtHIENDB`swift-im-2.0+dev6/Swift/resources/icons/certificate.png0000644000175000017500000001061612227051774023100 0ustar kismithkismithPNG  IHDRw=gAMA|Q DiCCPICC ProfilexwTl/]"e齷.H& KYe7D"V$(bh(+X "J F;'Nw>}w(!a@P"f'0D6p(h@_63u_ -Z[3C+K;?r!YLD)c#c1 ʪ2N|bO h{yIHD.VV>RV:|{ [RF ”"MF1L1[Te'Jx%C%_%RJ#4GcӸu:(G73%Ie%e{SC add1T4UT*TTTUzUUUoScemUkS{Q7UPWߣ~A}b}9Հ5L5"5iјi<9Ъ:5MvhWh~Tfz1U.椎NTgNΌ|ݵͺHz,T NI}mPw ,tӆF -5j4oL50^l\k|g24mr6u0M713fͱBZA EEŰ%2res+}VV(٬Ԗk[c{Îjgʮ=~mCNNb&q'}d]N,:+Uʺuv^|o]5˟[7wM׍mȝ}CǃQSϓY9eu빷ػ{^>*}7l6 8`k`f 7!p2)hEPW0%8*:Qi8# z<ἶ0-AQ#p5#m"GvGѢG.7xt~g|LbLCtOlyPU܊|BLB}&:$%Zh`EꋲJO$O&&N~ rRSvLrgIsKۖ6^>!` /22fLge̜͊j&d'g* 3]9Z99"3Qhh'\(wanLHyy5yoc( z.ٴdloaqu.Yf WB+SVv[UjtCkHk2zmWbuj.Y￾HH\4uލ6W|ĺ})76T}39usocٞ---zl=TX|d[ fEqūI/WWA!1TRվS疝ӫox4صin={j-n`[k k+x\S-ۆzEjpjh8qn6Ik:8w7ޜw[nn?uݼ3V/~ڟM~nr:53(ѽȳ_ry?ZrL{퓓~מ.x:LlfW_w=7~oLM˃_uNO=|zfڛCoYož_CgggI) cHRMz%z%z%z%z%r2` pHYs  IDATH T[h溳VŊuibb9c' 1[S($ נbh>BM!ih\Jp؎]dvLlR[?we.-oB_k7mK{چA՝.^81sà=U "JYk?U~z=zzzGLvnK?yC<zvg̼=|nnCb%DKPuj X#IDDт~ON e?~ p eP*,y4je$QD!qPWjY^FCt$/e12X4A;@a$_gf#B` l/[h+ݬͤpѶqƝ6MfRq91swb(A_W8tZ{8x`bYp]PyT Wn4Q7b0p9[ja{&z.E٬XuE,\BH H&`|yzXjMg{p;@&!ьO 9ڻw܎)X9z-X6 @ @j涨 cgE("AbuoIrO+r]_xFtV&`E @lJXF^D#Pe>?T'SNq7ͪ'^ҌxLZVMTK.VUq*Dzy4JZtI=/P 1Ĉr2LkuI2*Ԑh7X EГ - 8G5XN<̙hW nQ};+|ӌ4TA<.ܙaWĭ[φ1,5J\z d]MYmO B1hu^{BCDKLH I VX(Mg٨EdmF(zދc^:Pɐѓ`7 -mPmmkuM~~3'ƣ2² 务7Mȿ1 $hh2䨌vl, ^\xfrllSOx}p08mcqjΘ8l꒶u,A"oAbF8y& '`Msq8Nw]XLk.BLYeD𰻝qVsfmGVF38q6r)`@6r}!vk;H*[kL;oUM]G$o`02އh*u4kւR#]BRCs7tVpBQUrNe6`Y)0|-411fGt C 8ªIp[xyˍq١\͏0ę͞")X 8SGjBqFDkl̯iR96` ;_:ڷq^ܸJVnz|NsR_- S?&}G]/WihJߚCijL/wXS|V>rq@ Vx/ qz o` E^Bs DIENDB`swift-im-2.0+dev6/Swift/resources/sounds/0000755000175000017500000000000012227051774020304 5ustar kismithkismithswift-im-2.0+dev6/Swift/resources/sounds/message-received.wav0000644000175000017500000002137212227051774024240 0ustar kismithkismithRIFF"WAVEfmt ++data"xvzs}zip{~fvwv|x~}wuj}}ou{|vywwzxh||}xpn}|ut|~y~it|uwt||s{s~nqv{|xyqvxn}}vsjxx|su~vpwtoq|swutqw~v{isv~xqz|}upvsostz{{pxswyyy{poq{q}v}~|rvwusivz}xs{yntu}vsjwyvxwrtoztkvv||{v~vtg}vkoy{q~rop|kt|w|v}}~skm{yy~pm|~|vvwxmkwvo|~uz|x}jjvx|z~kz~uxyv{rgrxtv~|||wzzrdryr~xsts{yxxxhnxwzzz~~{r|qkjvww{|{|ntvyx{klrz}{~|v{~wrvkllxu~rmqvzxukpw~{}vtzrqpklutzvljpxz|pot}~~xqwtponnpyx|pgis{~}wrt|}{|wqp}{plooqx~x~sidmx~|zxv{zvvqnusmlsrxy}||}~vkdft|{{{{{zupqlq~znmpwv{u|z~wofclzzz|tmklkx~umpv{|xrsy|{pkcgt{zz{liijqztox|{oorxsoggpy|yyrfgemxzrx~zojlrxqlgnt~yzw|wgdcht|xz|xnghjyvrlpty~|vwx{~mfbdpv|xnfgfr|wpsvv}zyrtyzskdalr{yodgek|vwyuzzvwppxxzqkbipt}sefdft||y~xyzsvpksvxsggmoz|}yhfecnv|}{zqsphor{}zmhljtxvnfeajpw||pqqhlmtskkgqvr|}thf_gko~spqhkimznnclvpxy|}nh^cjiyzqqijidv|upifsotxuvm_^ggp~wtjgj`kxtvnfopsvpx~yvgRWYf}~nRP[D=LZvʲ~lZH=AFV_[+(;@TtǷ{m\WCImȬ_TQ7C^o|rqptzpPD1Njlx~xUAJZCQa{qZcbZfqqvuZkd|w}{hg}nin|fcwxv|`qxyt_^QRtssyreRa^`U}xneWIdbavl]^Ykmjvx`c[UP}tnYbk_d~t|bof{~ty~~tjnbXekfn|\`LYcjendcgpws\\xzza\l]qwx~~ogfnmhm~u}qkk|zg~}egy`WltxsWMT\cko~pelvgiiTct~^`krsvrjq}cgmgjkl~~xzsyytq~s]j~g`n~sNKY``ur~rituZ\__f}xh[qyoah\x|dnodkjrp{z}{~vwx~ywkcr~ukx||whQTc_lu|nrrgNS_qij`ww_XYd~vunm`kixzvn{px~y~zvfpky|py|z}pof__cdp|otmnXQIous{pgsp\RUlv|sdc`olmywt|q~w{wpghqws~xvniipcdgrppj`XMTs{{~zvpiiZR\ktna]bk_do|~~wsw}kc_jvsw~{{vumgvtifl|~rl_YRP_szv~~oec\Y_frl[\b`X[ktxvf[\lqtxwqqptw}}pnwyplqvdYRQT^nwrrc`__Z`nl][]WQSg~~}|yyqbT[dnsniknnmsvuuy|xs^POQW_txqu~rccge\etp_ZTNHSi~}vz~zpl]VWep|xgchgcckt}|rVKMSXf|zxwspwy|sifmlgh|ybSLCCNe}|tsvwlgbWWdt~hab^WXaqyv}rWHNU]mwquspqsssmjqxsscNFAAOf{yurx~qccc\\mk^[UMQ_p|spyylWLS`i{tovslmmnttq{{~`HDDFWpw{wv{{yxlaagffwiWTOJSbv|ypozxol\R_nwtntohjkpw|||~y_HIKQcyv}{yxurpmbanqn~eQPMNWi~zvpqypjnc]l{tmnkgipr{{|||t`OPV`n~y~vtrnmpcfu{xx`OMTQ`tuutsy}wy{jkmnjzx}ughkhlwvvxzswcZUhjz{qtpnksepvxyocOSXXem{rwszhxrsftw{|vsgdrfvs|zyyo}yts\cpv|qttmmnso~rnhUY\^nszxtppkpvsgxyw~qeirmsxvwpt}x{tdnv{y{stungtzv~qrl\Z]fqw{|qjplnvqm}~}zyphmsnm~qnqs}|zups||sz}uqukgx|~vvp`Uajq|~~lgqnlsrtz{vvmjqijkluu~~~u{xu~xq|y~woqjn{zy{uq^Uels~zjhopimx}wvwlhigmzjpzy{}xw~xzrs{|zz{sijquz}|{}ytiZ\iqqz|sihnncp{wvvhaajsupuys|v|vsut|~zvwock|||wu}|nd_`kslwsrkfkkes~}ysqdY`pv|xzvn|~|ppyw|xpribqsuxfeedmojz~mpkdghnz}wnj\Vgt{~}zooz{wxmu}}|{pjiejz~|s~ndjigjlqrknjbcpxysibW\my{wqoox~vu}spy~wvmdcjr}|zylhmmgfpy~qjlkadu~||~{rd\Z`mzy|omposxsx}wuxxspi_cqx|{qmmpofhzvoikhaj{~{|{wl^[`fqyyvimqooquzxw}{uolc\gw}~y~snpqsohqxsnhgfgp~|{ysg\_fjpxpglqnhnw}~x}{vrlg_^k{{yyz~xoptvupq{|trngchov~wlb_fmnq{xmilpieo{~{}vrmf`_frwswwvtqpuyywx}trqlccnw|{||shcgosqttnjkjfgs~~~{upjb`fmw}pnstolovz~||xrppibgs{wywohipwxuyysokgfgnxzvtnf`ent{ukkppiks{soolggp}|zwvvrppuy}{z|urogdgnw}yuuskdenwy{zmhhljio||vronkknxyssvtsqvz~~~vssogemv~ustsmhlv}z{}ukgghimvzvstnklqv}|qmnttst~urtojir|}yttussot{~yzwpjgfflt~~urtvolqw}~sjjotuw||utspmpzzwvwwuvxwz||y{{qnlgfiq|~xsqwwppv~|wpklquy}~}~|xttssv~{vtw{{wz~~{yzy}yoonihnv~xwsuwxtrx~}tpkhmqv}||{|~{tuvw{}utuz~|}ywyz~woqojmr|}ywxxwwywu|~}~yplllpsy}{{z||twz|~~||xtty}wvx|~xrqpnpv~zywz}xwzzwz|~wnloopv|}z|~~{}|vw|~}zxwvw}~wvy}|ztprqsz}}{zy}~xwz|xx|~wnmqpry}|~}{~~|||~|wx~~~wwxw{xv{|}|srttx|}}|{|}}zwz}xw}~woprqu{|~~}~~}~}z|~y{~~wwyy|~~~xvz|~}|tsvwy{~~|}|{}{wy|xw|~}xqprtv|~~~~}||x{~{{}~xwxzz~~}~zxz~|}{wtwyy|~~|}{z~|xz{yx|~{xrpsvx~~~{z}{x{~|z~~wvyy{~|~|y~{{|xvzzy}~~|||z{}|xx{{x}|yxsrvwx}~}|yy{|yz~|{~~xvxy{||{z~zwyyz|~~|zz{{|}yx||z~{xxutvwxz~~{zywz}zz}||~~}|wuwy{}~~}~~z|~{xx{{|||zx{|z{|yz}~}zxxwvvww{|xxww{|yz|~}~~|zvtxyy|}}~}{}}|||{wz||~~yzxx{|y{}||~zxzxuuvw|~~zwwxx{|zy}~zyvtwxwy}~}}|y{~{{zx{}}|wwwx{|z{zyzyttvx|}|xuvyzz|{z}xwuuvwvy~~~|{zzy{}zz{{}~xuuxxy|{~}}{zzwtuy|{yvuw{zz{|~|vvuutvw{|||yxxy{~|zz}~}{vsuxxy|~~~|||{ywvw|~~ywuux|{{}zuvvtsvx~{{zwvx{|}|{|zvsrvxyz~}}}~|z|}{yxx{~{vtvxz||}|xvwvttx}|zzxuvz||||}|vsstwyz||{|||{z{~~{yz|~~|xttx{|~~}yxwwvtv{~|yxvtw{~||}~ytrsuwy|~zz|{yy{}~|{}~|yvsvz}}~{yyyywvy~}{xvuvy}~}}~{ustwwy{zxzzxwy}}|~}|yvtvz~~~~|z{{{ywy}}|zvuwz|~}~|wttwxxy}|yxyywx{}~~|zxvvx|}|~~}{|}}|zy{~~}|yvuy|~}}~~{wuwyzyz~{yxxwwy}~}}{xwwy|~}z{~~}{|~{{|~~}}|xvy|~~|}~|yxxz|zy||{yxwwy|~~|||zwwz|~~~zz{}}||}|}~}|{yy|~}|~~}{zz{{|zz~}{{zxww{~~|{||zwx|~~~~|zz{|||}~|~~~}|{z{}~}|}}~}z|}||{{||||{xwy|~~}}||||zy{~~}|{zz{{{}~}~}~||}~~~|~|}~|{~}{{|~}}}{wx{}~}}}|}||{{{|~~|}~{{{{{z|~}}~~{|~~}||~|}~|}~z{~}|zxz|}}}~~|}~~|{}}}~}|~}{||{zz|~~~~}|~~||}~}|~~~}~}z|~~}~}{zz{|||~|}~~{|~}||}|{||zy{~~~~~}|~}{|~~}~~~~}~}||~}|~|z{{{||}~}~~~|}|{|~~~|||yy|~~~~~~}~~|{}}|~~~}|~~}|~||{z|||{{}}~~~~~}~{z|~~|{zy{}~~~~}~}}|}~|}}}||~}~~||}|~~z{||{{|~~~~~~~~}zz}~~~{zzz|}~~swift-im-2.0+dev6/Swift/resources/MacOSX/0000755000175000017500000000000012227051774020063 5ustar kismithkismithswift-im-2.0+dev6/Swift/resources/MacOSX/Swift.icns0000644000175000017500000026643512227051774022055 0ustar kismithkismithicnsmis32{ ԙ\Bfzmn qsru |@_r[/q`H)_\ 3/I!k8:>{r.֍H$C#:fH~Pf<$<[il32 %O#Skӆ<<^N7Dnf7{iD8g<>h0[\:`':]{{x' z4mipvwE d-YX^dixMt8BFLRVi-C..5;@ID L#)/4?D'{2 N;4  16/ߗH hψ;b$ b?" &VS?%ߩC"ZVE8G4Ĺa o J 3Lk  '@ B N] J@ ; %O#Skӆ<<^N7Dnf7{iD8g<>h0[\:`':]{{x' z4mipvwE d-YX^dixMt8BFLRVi-C..5;@ID L#)/4?D'{2 N;4  16/ߗH hψ;b$ b?" &VS?%ߩC"ZVE8G4Ĺa o J 3Lk  '@ B N] J@ ; %O#Skӆ<<^N7Dnf7{iD8g<>h0[\:`':]{{x' z4mipvwE d-YX^dixMt8BFLRVi-C..5;@ID L#)/4?D'{2 N;4  16/ߗH hψ;b$ b?" &VS?%ߩC"ZVE8G4Ĺa o J 3Lk  '@ B N] J@ ;l8mkUjS/h꽓g;%p‚;'܆"( yaP<vu b^&(m^IJОT2<HԚZ!F5گa$*2?@ 2We)R \-Ait326? <݇ (#L`x  &=`x w<  ?`ÇJ 3\ÇJ18,$Gz݂ t,  'T||`C. 'XחM  +dz_B& Jx L 9ukM+Z׀ L  b^9 ,t 3TnC&u ` S]0 't x\xE; "tx?x+Cs8 o+lwY o*;_n#I v$9)Do(4IRZ(4Z~!4(4 q}|~A4(.}wy{|~[4'_xtvxy{|~cN'N-vorsuvxy{|~Y( fmmoprsuvxy{|~<!("Mmhjlmoprsuvxy{|~(W9jcfgijlmoprsuvxy{|~X*(+e_acdfgijlmoprsuvxy{|~('`Z]^`acdfgijlmoprsuvxy{|~3('[VYZ[]^`acdfgijlmoprsuvxy{||L`(_/WQTVWXZ[]^`acdfgijlmoprsuvxwVa)+9QMPQSTUWXZ[]^`acdfgijlmoprss}Ua)CIJKMNPQSTUWXZ[]^`acdfgijlmonyH`)aFCEGHJKMNPQSTUWXZ[]^`acdfgijju3x* -A?ABDEGHJKMNPQSTUWXZ[]^`acdfgk*b 99;<>?ABDEGHJKMNPQRTUWXZ[]^``gQ*$64689;<>?ABDEGHJKMNPQRTUWXZ[[e)T+//0235689;<>?ABDEGHJKMNPQRTUV[M+N$**,-/0235689;<>?ABDEGHJKMNOQQY5/k&$&')*,-/0235689;<>?ABDEGHJKKS5-  !#$&')*,-/0235689;<>?ABDEFID ?mՀ' !#$&')*,-/0235689;<>?AAG6,t, !#$&')*,-/0235689;<>?@ 1=x  !#$&')*,-/0235689:=B88;/# o@e  !#$&')*,-/0235743YO6DFE>/ oBZ  !#$&')*,-/1170==@EF=(  o18  !#$&')*/ u@5::;<@EB. 6  !#$&)c425679:;=CC/(k2  !#% T%001345689;BC. Q90  !"*)+-/146787435- z  "%%() &"  9   `    (#?ABDEGHJKMNPQRTUWXZ[]^``gQ*$64689;<>?ABDEGHJKMNPQRTUWXZ[[e)T+//0235689;<>?ABDEGHJKMNPQRTUV[M+N$**,-/0235689;<>?ABDEGHJKMNOQQY5/k&$&')*,-/0235689;<>?ABDEGHJKKS5-  !#$&')*,-/0235689;<>?ABDEFID ?mՀ' !#$&')*,-/0235689;<>?AAG6,t, !#$&')*,-/0235689;<>?@ 1=x  !#$&')*,-/0235689:=B88;/# o@e  !#$&')*,-/0235743YO6DFE>/ oBZ  !#$&')*,-/1170==@EF=(  o18  !#$&')*/ u@5::;<@EB. 6  !#$&)c425679:;=CC/(k2  !#% T%001345689;BC. Q90  !"*)+-/146787435- z  "%%() &"  9   `    (#?ABDEGHJKMNPQRTUWXZ[]^``gQ*$64689;<>?ABDEGHJKMNPQRTUWXZ[[e)T+//0235689;<>?ABDEGHJKMNPQRTUV[M+N$**,-/0235689;<>?ABDEGHJKMNOQQY5/k&$&')*,-/0235689;<>?ABDEGHJKKS5-  !#$&')*,-/0235689;<>?ABDEFID ?mՀ' !#$&')*,-/0235689;<>?AAG6,t, !#$&')*,-/0235689;<>?@ 1=x  !#$&')*,-/0235689:=B88;/# o@e  !#$&')*,-/0235743YO6DFE>/ oBZ  !#$&')*,-/1170==@EF=(  o18  !#$&')*/ u@5::;<@EB. 6  !#$&)c425679:;=CC/(k2  !#% T%001345689;BC. Q90  !"*)+-/146787435- z  "%%() &"  9   `    (#1|ά_;>ݴS+Kx?'Y5 g++u <<Tz(m  >Y8}Y8}CN˫מ^%&5DSgWȰ|bI3" \ۢb)=jftT='uަf,S!+j09\Fo3 _fs6 ^#w: >{>pxAdEc,H4P-fO5GUmW '&o?V:df.eW>F1irhB+Bd=P XVyl4cjYQEHm?A)JERg[dn&t23?*Mic08N jP ftypjp2 jp2 Ojp2hihdrcolr"cdefjp2cOQ2R \ PXX`XX`XX`XXXPPXdKakadu-v5.2.1 M(XCor)kWѰ8fM 72AœG3i1q< epI5c/{u ]JfH&8ˬ}AhUu% uV] 2/;]Xۥ'NV=6L~h^rPnaajRlA^9_P@N}&U*6ߪy9W^Iу Hv KH)gѶEJm7-dl*&!2F^;\IRGˉ@۔#IM"s*p^NȩM!3R耀~g].kKJFޓ.)™@8~c~ ;E4!ѤGXkD ,9r,xԾҤ@>yryS>$F.pWaOu-S7x}}^: mՑ{qQBObuAlo8;0+ SPδΩ!A3JidWtQV׏Tm pOP#֌$'x Tȕrv9YkCvDgd 6;@V.[ XBH ospUĪ(^؁[m؞͐-"\~CX+n@Ӥ/5eJ)5ON䄔'0ZWs]Vɑ=ܙ%=n9@\lH؎(=WP?}Kv}\CsEs{zS1Ue^K|J}Јkw:\fG)?^~Of4 $ȨQYضohR#$? U_wLd|6s!Y6;v6ӽѓwRǟ 4$L: I[{O"g$ oܡ|gV8Tmtf j\%X3+YQ\L<*4\yHǙ7ܘёڈP1ΔLfKZ0}ń#A4D)a> ?l%Fj}*-B@ ؁p,w0 ^?x}~{{ܶ YZ^kNTaR+ޥ}hH(;\Ts7`b(t Gm> mO5$/y>=St^ ?"Wyeϛ&\?Okt&81aT9WBF}.m zO$ Ve]2fo5h^.|{6࿹ebr?fq@l ϩ²^ZAm`\]I69KV̓ isW@Bwk9(N쀍0 Zǧ-?ʽG@% :OOW;.-IH FiBDDሿ7X-Aď;-6̕)W@ZvVJ Xۚ\Y#?)zv]$t~F`DyXU9食{F<ω_Ѷ]GdP+Dq4)ǦI= ziG-Xh$TǒqR'LaUj mxa,2 2jNv٥"8`@CKLzI[]9FJ#$jQ9◎Nm뽣uMWɴH,"@`4I܈7y-kZ% f?c"6" Tv-?^ ct )xn+˿ڸXTV\+ȯMvMh\Q }Oh17M> a"Lr΅Zw}Ls #{ZXʖwFFNȗ)lYJ]ѩo6 'BL .eCDq],691Gd®G/oy! PfTyB%Cj]WZpnARgD90@.WxYiC{'v{ ?R\+9F#2*BN=Gv~4RbBPU3]z:ֹ6J\NY(^V+"k,QﮱŨήql6eIz*Z{X%]M͈&JͥAeBl+鴔ZGAuJ~"x.iR3r_N[9I@Mk, A2;hX?ܣ\뀪00Fy݊2bPū:X )Ygu(4YL?_5d`n6˹W?SWIܜ۲9zwVJChh2;C4&hRTS>d^fYl0 ->>ozۨR~GW]@]\A?_QM.)o͂L%aB)(cyc'4OXMku&$,x78˴4'OqMDǝUzޮ}Y|R1TmAD(Z]buI}=8?'Һe‘G+LKg} s|0i$M=WSٌ-I瑆8TluPՊ"@2's%\~h0ʑpkĵ.Slb_"w~T'%,/eٔclQ;nrY+葎[ǜ;7lfs3#W<Hy;Z۩ v`TQnFpCLihuiwfoBX]͏ùpH˘Ku2hu.e[Xw;独uRUOl2)9=r$ᾓV}eyL.o+;C3y(W|DtE $cAN7>?tn4 欯Ot{'>%r'_NeTv,6X+\77Iu)`υw-%š~>&=s+2B %!$0SV3/eќ߃wjC ۆz|,dt F[-KPQ*0˂Uen|$;Q|~:`C Xbjx}4ޖHm=9^+f86t-ċZ~ LLќ\C'd^yL>wdr<ń1 4_@)0iW p!¼41 1ATW++}}$hRlu?hQƛm}HK|iQPBaI)~r RzĬRwhrT3a6**=|*M!w1_5*] \qo*13x򟒲C_ }m{ٍne pwWc^.w9xB[Sf)0C3G s1=1=e.wL l9@DZpHg:/!AUٶlix!+pNo8Q÷-3Mk F7MLĪ ՁCb>~2`_yC6>\6KEҍjNֻPz}n(Vj+(|*.d++([9W*>Ȼ?[/OttӐbs[˃FEǃˊ3YF=I<'b\kHNǰ p -ӞRKqQ)ThgzNY8OOYSw-BHi+hd @wR0OapxʐC[o& {U$<+2kYx$G7r b'7\}ߧ44޿0b;: YUt0G棫ݸIpgV/.U)Y1k )\dvY(XI4M? گKE83E/ nf=Y&[ZBؼ[|Z}.:]ГLAt n ͿE$ԓuU6YJ?[c^8jIs9;m I59=5y@O )Glc>ѴsriI+VcxW#A;!~̒t,i}utwZ%qQl{cR~Eb?C("|f1b"‰$%=zj:҅x fPКm~ ?#Ffdx:IZc"OGSN^&nTujAJ8\j}]AZuJQ(0=eo'L @ xbqQ -eJ(2Ԫ(&jZ^m|Y^2bz _n3DH~Nt S!i˝*C[c_SKEmh"0 %qboO*I$䊻^Ib9G YT{UVsC&׸ܢ%a\C#Rd >&vר|BC7o.d]jBjUJd.G,OUhy;> Ry< ]tw:@,d~otZ G)<45ͫ0%<6/<]AhLv!OY 9WK3]^7*i-GWwێccLvË# hqUךr.szY ֦ iwC'&PM@F? uVoaw7mJ~;֠ᵾčk+H@Ht\ b'h'o&@]7bFC}AWyJ9nqxJMk"3c'T'![ )6D'BV-${&fXa V:&ryZj*TkK ;)m@oxS#?ʽ}D3O@L"e5ՀD~׸ᴘØB*4Lt b/q;Kj$y&(I/ɹЧ퐵j&a& O2\_Tzo#"2ΐȴ[fe)A).)BaHt! h=(X/K,)x| tbuT_Y{f e.bw|OZiAfJtI+ʫ>yXRg5YeVK>nnFI1]l>?e 6/͉ ;[5a7>ɣf:`t3vxn{TUHظً?as]=7Z2sfhgNO^W҉ٙ4P+$L:T9y!ܰ1eJI>qo+zjY(tۣW#*tƍrKyZ J\g<07e ((;m%7.>Lx‰ 2%!,i,2҈7& KN* h mV%@֬~&>g.) ȉ\迷0Љſس @fq,@JԜ3dhxC].IƝRI> m1ha@@}#CG/z"++ɍ$=qiK~4 1Q+, {w\NM N\ݯ`>9U]Gh Q'b4,e0p [4duͮ?”j'&v!`[mܱ̳" AyJ-f00/5V 5w)p`fD$r U~gvh8ԇwYM GK#ߠIY=L!W$t'NN$@zG~b) *U޲2!h VYn>惆Ք8^UT:?͘#O\Jwuvv/tn_ioj1}O}_nӀeo,4̸p<)DBq\'?,^c<f [ߕОrF=-a /289VIBonJ ޏH `v<~gzՎ5,9sMDѲ1Kӳ_ʦiohܙqDǵi'J^A{[Eކ́bUpeQk 6Y[h4=ܲxt[msnO-a{r) "j!%QvT7i!9]XySiJX0' _ׯ|A~#c'!kRZIЍNU M:4zf-j*?Keɖ +oߎԕaF4\r&jIhJc`9og~Tx#%]:B?.U/1?+ :*ÁŸD;X#?OփЅ=R4FFV;bFS;Y$ V._4(ro~il\xfSW겦hM 8uP^|ngiEnd$/NSo R],Br24ʆ&;$ܥiN,,9Op3Jc{ g ': \eT ZVj}noh Zxlgqp]mLmeM}:m].kT6@ гB@y*@8h=sg]y&O.'4ϼ&Bѩ=lܛl203r99PIRGǁҚpO,6||51;`ʸ [>.폡eydӟU=Zmodp\؎5ZaDOPN^dNtTOIұ1Tym0s9il7Kâ,ZzL)W:8g0Fȥ~ww㈼鎉cĵ+f=},7F^ɫ˪ S\(OY@ a?8~F)Jt̊ ʧ_=%z)ճgi_#g?(yB=P%K<b`{0Xɕvٯ ]IE.?T? E52FO!y-Ak1)nշހ %dw2O*ؤ?7"ۏjYfЧֶ18s*x$`@b`<_`J췘4E*Z14!TQӉL zE `<"B'(پdkU8gITYzl6I~^H!3QB;oHq&TǏɦIpY,[' * ԁ#E&2k.)Ff pDZFZ$#η9w&ysVvbz<8q>CZ!4L0d7uo"9Y-)8ܥ8Vp fnECAeLF 49u.Qsu6pSonR"q[_> g0>,xv 7HMtoVWs~b_LrpEgyСWR; &0'@vЁstb,`#҈}T2#ilom#olH 6ջs#?cs--Ѽn=f1~1=;T9:iikL1Zԉv#i!W\8!IjWlPzx/W=͔2 vm4(^/]0!hm^']+kXhlIf,(A^x4 T GvH⩍nRcCH Ά};G!/C歷%+хjܝN~^,D@lKIB(]elDYſruigA_zb(ڇ#^zM`J*Iu %p" FynL}{m`.Ȃ2ah-C%AP+%tk_vA`{@@2:Aqy1!,ாۜiİcoTڳ!>]NlPc d 7W>Sp9z+"C'2A6yՁʔ"eyak$ rđx`?G,h:=҄3p6BD|^$^H99>ԊHb춅sU\`{b Yؤş3elӷ=kW ;Ƅ3R։\ZCx>ɓg@xRC䙱Rc9K(0#ޟ f4Sޱ(Z,F'B.9|mIX5OK"̜M`x'i X|| {kDϠ; -JJM ŋh .oq1'c @;Y=? }"W1%!${*@uc?u'9Tp7ԬMw Q[,hN6=qou<ՈkYġ+_IU}U<>>=_#nMLfk:x,cg^Oc^m[f~5w1/`BRm.~{.=^e"G ^_ma_pP];[HcyDCzbs'VʧוE|j_ v0cKIml{2ŌCu!œjS&SB'Ck>@<>gj m1U w'~Y a"lLɐ:x/,E#oZIw(z|{fWY,(ul@'PlCO^艳x g$m΋+r]o"NA˗yzfW]J 2ٳ/ּ6f(^LJ^+$O}j`+#cޟAFiRa^Z*+X˶T'ff2\[2D7VUEְD9ă3xo --A8~ʤRL9O:RX5NK#T?۷5pf-n)Q1[bHҟhqt$ovӞ kDqkHv-Ƕ CMY /n"kMevXwKY䛒sPy%B*VYKWd@9V0.UB1&3d/>(()ԇX ~ޔ?3xk' 3_JKa&CKo4. 뼟o`@vHx3e|"$'NJN{BΧ~<\GoqF`*ȝM}u:k3O j6pH#nG!2aجnCCnS"? 񉻇ƃ~D: aUKh-2v߼Wp>(4@I}Wg[^~@ϧJ" 6Ne4XpHf\t `r@46,IwCCnѧRK9-d[Vb`x'{ h9}}7.d pHH!lmL W0W?00?Z^s2's&1>6QB @J4e0Tވ{*? L ` !"PK7hXe+<'wr^6E7yŮK, x9jLZv%5uR3,=[/Gjc^Yu6"`[uKA4/9~,J>ZQw7PqwDӄԡuuOxn>SJ2ŽH] /AP%Y-(V#J{viEXÅ@0n}0z&jU )v%rfxEןU Fv|ݐb `ݽղ^ ZYJ!9' S˶"V l܌ k9Y`DA EM AdϖҰlil;$ \VrQt d8e?1ZM< nbL8HO6ھghTv ,$%nD Yۑ.Fw5ch[ t۟_xm_'mgD9MD0]5ynT £&K`Ec+[FNd7jtqMvm,9?7Dx[y=KY~+鶨\]+o/X~DIX{|T'Ep &e;US[W?'{dtJ^He5?zK)6AJ2e!VQH~/p ڷG䦶'z$2tQri>ϴke=}F-UڏW_@]yƢL,=(Yܭl쵸'=Z1Ng*& 5YU 3[r{q|;\=:O$oG G΋b DOs ~{^v$ە-6w=RSw{7Z>C1YúLu+t묙4(]x\Fm2ȼv ^$ 4 |̛o BC΋j@+ԉ|Q1(ϟDZr4R8 ^,2%z*g7%AK>33(CXPշB}nW-.9~$n}e9<:J]Nqcw^Ki؏-R{k%7^,HXi#=اK ]jKk5D#RUԐt" R~{i"(Ռq2 xKڠ})ػkeDxA}Uh56SxhW/4:v1mCEM *OB{[= trbIE~*]jʫґwIcP>MioOqw"x3u'5}c|Ixe2:1K ,%\N#mph2+Y\'c%&EJ_i0|g(hxf@>jj"w _faDuRXN Ly㷗CfvE3/eowjєhiG}.DTlgGNsB$E\%NLW8 rc5) ٰ(PS3r>|`HH]1r*4R1\"0JTlZfp.ӮRk3{HR.x.&?F`϶}6Ib[]ỹ`U7ayYME.ێ F"'-4sh+u<ﯡ -0IQ@["ތ8 <f_m~7P@L9=Q[FIig<# (-)TܴH?xԛZ<Zճ)7 iQW#=ys}.IAttKMql S3?ء`͈x 5WT.腺վMy(#'f%R$\<.Ph$r:]V߳!'eF>Vm6aUd:gKбҫ;w{G0{[*0V؁}EXϒV/Ѐ 2!@fo ʞe[Vs#{O'BB|I]Ni:<,;1ܞ˪%#K塇@ C~mA$=r9˕{|l&`|/Fb +uӛq'1ya:e5-_SNhn&Q)9Jqɛr`6Oxt,bJHTƨ `E # wƛ5Qb8f;-{?`T}*!D5yXl@f7P,W{aS.zV'Q`@}<-x#͑bbeE]WVc>~JHعQOyo\o 4~^jMp7w?+*?gdūJ&ЈhDVe13PC=`{{dY/lmXxf/q^M%z?'i+:Y]d~4mR͚7 Q8]ml2|/IߖT{%.ZM ؔz#:c2e-Gؐ.sȌ6} WxL<۹Ƙc N1-ޓR  (M2rS(? rUvX %)g2U00aC?NPrb>k}yUSUjҙ ݞUWڢlVE u(\W~ W/XJ_ ,P %P;^P.Պ@$ٺFՆ/h#Vd~~P{ 3q }6S(Ʒ1ǁ\d?DN svȐG j#8CW݊-,61ێ748k_>oxfBq?LSDokJq5Da^۬l.ErE`rTc Lݩɪg/pz +@N v+mʈ Hkݝea'%Gq0?oц̲w 8aGi{=fD;DnåZoSoI$pA#Ym < ~[ 1THG; +8;) J]&$pވ:C~)xM Φ \vZZ ^C D`. 7uH{c#}k?/_;, xU-7@` H5yb)!J/Uo[Rae|n@]TZkju,kp* W+Q.})2:mG˕weJK 6}Hs9_-Y?_lwXXH=iZVXvMQ4^wS֩%|c }e;N1նR1ZQ>){l J{ZjW'̫% ==8`clj9p^s}$&Qy"nby0`2<,%l:Belt`ټ,P# S$h, ҈p ̚3wOIV #ׂjC%m]PB)W8ܿ&(2,ٲ!iBuc&R7t$>C\;Sj!YvՆNqu?4?"Ŝ=\OiPcXLj1 So6|RpEzlb5qk4*9<*qUax/#{.ݐ©7儼HFtc 膨9Z:Y9~yɏ@QN)dK*DUTdX:@tEZ9PUuxCfkǃ <>Q6: 7saWQOγέ!A3F8ao܊ ]6DG_N:/{48L*eX3dt_+4c?`DWݮrFi"c>6iIo !{QYضkX)YYc+BK߱_;lUQc<M檡;j %^DE=NRJ ;ٵRӽѓwRǟ 4$әa[DHf1]k6Qm: `Z7GZ'=}a" KPog"-++Ld|I3Sي"Kdk=}R˓j Uz$2ѡ,"9 M,Hg) )#寲=dͱ(IOI`b1 JB 5j/%B U9nA:Wzjy4WMJY#Y}>&*¹<1 xg;jؑ+i:k1Cp)5YԵyx M.{u>MrEO$VC7wH!F='˩U樥^2)> 餱:Xa{LJ-E١G#Ppy'JEacEhXɸz)_dW<;Ic)/x\>oc2%tᰳ1rs+Y ̟Fv&)]T\٤_aer-p6|=ҍ ,)VP*3mlDg'q誓vE !<9m23 r_֌8,GX.-z's6{% CilX-?9|;>y.Q2QC.\,)Љ:S;q惖b?bFk$#/\J L=M̹K  G𬚂,z7|KU@7tʾZȠ:ɕ`Y H5"W`%CV;~-q ?5ڬ1=«StX`AEeYJˈ#upp%E?7օ ̓ +x)uoX&$be9Uo@Թĝ+{xZ$5bI}8k0anTmt&qɕqV7UЋ?2uKkg8$[uO Ghg] l[ͳ#0,k)/5~Z#JTa4?őμmo\CEBr"x(JfyG+OPnDƜԢ'vԾع<{#?~EZO?;MwmWhzg|a-e*=Ұ8ggNcHybXT0ߠzx6WΫ-xkt_ ]sE=›5ַ+TD^?BLq )1]@rnPꡪ7\ p [ߗCKו#5&_K龢rGf8Hf{Eԃlp"=|FiOhE,DWV}Z^txRo Vq+#EkRT^hpmلE*Mfa썆tW! F%:W*I4Jic]liVy^Г:2H^,=Ma,{'"a$WMG戭vopݘvp, ɓI0WAƅ1Yg΃iyY:ڡibSpŜ 55Ƥ OuP⌳f}IEVu3RL(tހu9ԁ}'ggxʨFűI6j(dQ~dP<|vᬳ17"т7f~Ml͸ BO7}=\ﰵ~Ph\ЍhJFLv'9q)J 2(ё#[jxA ^8%;0P :F m's!4-VC|L"Wi%&'7.k1[$8 SuxxuI-`O3Թ"B4xf0_0oFg# `N7?F?tƄƈuTdCqBq/LSik" M5N*̊"tN̞R ߜH~7@kMm(e_2ɥ{9z9*nFO$n'V>̘B*Y$d岊H&a-) XєO6^d{8}Ic@@˵{/ƎޞZv`ֲW4S4řkzo ACOps 4۔݊1˅x<S"/h^D,|OAh%Yɩ;;nIw !Mg@;LiƤ,,[G77ZkZ5j~8\U\4yO,tn\GJ $ĩ!:9^A=HL}5VܼQF>4{alr _ '7E@5RtR!‡t@+A o5țb9m?T`RYD8"=@w!W@qjޅ lfp`7 &:=*T,/n^ce|(^9h)rmyL9 Q+_9U+ES^YT8NhF#.Sis<fj^m/gYZ@%Ϙm@URVK1$X!#"P,sl~o/%YPLՓx,%@RX"m.Bi 誴z#1#,%0| ڕ[](dUNb^s|0ND!"x '/&Tډ`Z. Tk[*Sz6KK s-͡K'Lr.?^4 8|= 6s+wj]a?GPZ3EtZҒ2 PH!êW|; !@ j.:N>L`Ru zUXjF9s $p?/M;&SzkxbG\o`zh 'E(LW;1$;tDܽE 7;sGʵ5%'Z?5p؆>KPnL@zk<_L|N&òP9QAŒ\Gq[EQO&M6'g\qv+jk-'olGIj-}sâ_mqE'@cT7 їْL#2lxZ*FIã.ӡ ?K%_aЀ—a `d 1j.5 ~*f6FGk۩DKwMTзz-xukqz 2Lfhգ8*~?|lp5PgpjW)OHy(X8JdsY+"dh(ƈyGH&-#NeI ]gX;SlA*<5"[y48t܆e#YUGm'XOͥϷޖf~H%t8.NOPn?Ic_wuԛ :'ATX3|~= Ti?F}U<;1 Lj۬ |B-Lt7 \1Uĭ;FwyW ɪcCUXO ʮuҸ3xGOt5yA?.-"w.n~a+C8J39?{u`P[GzIm5kσaeus]~Cm(ZCv2JYc p/6ZBX%vxiץtӣVS^::q"3j"LL7t%ؒN:){4$yOj?$^}ސDiZ-,X6Ŗ⒳>Rzjwz$Ս҈ ll43 q&Gl%o~|FydPH_>nkN#P3׳ s\sZYqۚ8n{!f>xqs 51 )_a$p-nd{EsFR9>ƁQw$0(hspuU%]h+gC96"݇%Tdb#eb=뼏8ax\_ircތQ!s8h[Rh3;=.vE`3*o=◬J"NhHgZ3 X 2 vͥ9"ܒ959sp͏{9=Q.Gz87-^PG]D;+&nci4^3&؎n]62>o'Ǎk@ϔJj1[,yO<?Fi7S*+<7ϝW-Rl&'9Q|PM\[ԧP-~e/Iu2>BRLͳ~}@ӗWPGOO*̲5D >uӤգT?̹CUR O6*ޜLf{=nU׍0@e | 41L A&DkF7'Q/ e)b>;OG@3 8I}ܮl(m]mZLqЊ`N\FכS;hRC /{/"cÂ~ұL.mM8ҵj[B)!o՞_m:/ "RV-BO j$hr̜jH!^}f߂%W/r66-"ΡX[ N M g3Ʌ--LWy؉|at7xXU4|Z [FkTr' {;leOW-'c\%|ڧ.0b5o'WKQ1A.R TP /M&hB65R]_q5s~ fe`R"̎ĎlG?܊b휆p.PqnQgc=]~QXDaAfk}qɌ`IzQO8JBn\/āZ(68h_ Q'qlqđ^GDC?cfU;7{ L5" C>qJ<{&"%fZ zy,ZJ_j.D\V} w4viS(HαZh۴%kSo T%w{Yg"uOwv5'o4JZhQ.6>gsjMi)pB-rdA4 8} Vt+VngilF]ߠ߽ik8e*H5JV*bf^&R=[F;Z к%~Sx)!>P-㋂FHFJ|L@fU̢m܈t6*[C&O5sҫICZj6N`m-C] 1]LL0`#7 DM2bCV(2>z gL_z ]W&8yc3bf-d([껮dLlQ"_l:SաO\T͐)T2Ȥ39)c3i{pnXn&5RqZ>.|4 ׀Ī9v0+5LҨW6acppgq?h!a$z9]+nO1/SuI3m qn [xK存G8EK%T-7W]tȀU?5(UʂBOiIH6t@Chg}z4EDh?[FCn}h:YMu o''EorȌV+ݼe` -mYu+ 䎀In's_ \Lf,B\b:"Vp~El{V4i:}jX|1:kWIu&0=EBLk+}+%Ws$H83ٽZ,F%v^ O.-%fW"])KN|"&ʫU {ob2' e|EA04:^|F 4[ɠUʶѐH )rXhө,[}Bz:X-ע{y d*/"]%׊b+4RhYm? ߂6OdM˸ʼe.X_gjw+48!壋Ս5ȹ= Kj(WF7c[R^Sa0/ ]2>095-5UTp'i]8oޯb|~{0օx~ [t~*Udh muӟrw|EPL}>l-~_CF3 C< ͢+۠ eC<4 9yj>Ţleu͗ դ"|>xl@{'iS+2eC!WMׇ닩4W7+&.&N1Gb"EIw%lk'hST~~{Ctӄ]}uyMƕCY(Z WE'榮quVBc^_&ê1WoU_tUTݫ9ޢ!vݨ訢+FL~5}HӪ<;1-CKu FeYin|}l>.[1}`% Na.тՊG&;=­f9\. &i`ZK}r ݳW8mʩ( Xwgyf}Jȴ?քoE#|s+'jT2Lg] 0Q8k>;Sw}lhKó8|!C y{fcƋwAkyU5pe}&"G#0P} aUV\[ven7&OvCo f\@;@NHMPH#d3ZhB(SxF@r4]2,\'&h t4NԟڦVdĂZ{F̞dUu{HK5)/E8n}i!XgxdL ef@\d؆y)6N$-F=|F@ Mku8نd&h8G2h5 :%B,4'P?$KVY>6z㞀ct.0+i_/ n#߀So%%)vV'.ҍ%eAcdQ1lRYO.O$X%Dy=O<⠵\- X-y(', SL0libNɰ K%R(TS "]ػk[ܻ7"p8$jJn8&DYFrوkT2@O/C'f4WMd,*Ϧ16;*HS].svVwJz^=/MB^3H5xwmخc^jjdsuƠ++Nj|>f`eרƥǒX#\+R4K =.yq@8eH bJTO"ȋҿ}#hA0'N@Xgqޙ]kl=),.eh TkG/.kcFؤL)˥ɎQfNeʟ i!HciҟY dxeoUMuw ;:WcDx: Ҽ{ s%ƜmMn%Í!0Ԇ3_V1|Mmt}GWfy5 ^'>fh^6= p>g'`~%S jOixdlU31хJ' `SAq){:`R0g3;FM6*pvGp>T^XY[)*!6y[]u: v /1hyPI(0rn<(ė>(e+ -@D$1fDM;iΡ*S/crEL!}!Mw0_Mlb~!% GOw=y^ 2̬;ISR+so!IChȮV/!w\nsgw)ԟ tqva|*ݥ@T[qu8-֑A'vމȿU|*q iuosbj-WdZ]]7˒4]i!q=o;rîM:T 6e@>߲L:yr0aR/aN@- KNϬ9LWP!n:COe`nDC%uĬ ~]M Ņ[J0Qghڣx3}i/d*YJ] V&< \)= ܳ="1b( eII/@P"TUӎ}V1Yu}W(m*d0s,MR1N}Q.*$ R W ki7G<"2RQYb Y)Q V\T$̸]Z0hYWIRQ (mjӻCˉ?dRɗtzuwL"zaHeMbHX묮"Ұ&x$#^y,>3 r5~>w,5s$&LZy˳LI:BoK hxirots[l7fS9#mv2BD3hS{뻖zȄ+TLE?WBװ':^$D/?P׵1U&شZ5 px/׆xF a ܬF*V}-@y᮰#]hwY5ۙSf4Ϗ^XO!Ý /CzsLs#J FЭ=34~@ v-ADJZbLj M-h9+M\Y6n^ }WhLzX0(|BY9|&e{L413vaPyO69WYr4F /eoWtR Nk*hG+Q\NtO[HrIP:~~ q=]6 75|1@%0 }G9 |eMg0n:!KwFi: 霕ϊ7'or07q03x 2JJB*uY$h2U|"'Ca-/,w &"-ՙQ&c:zD,A%Zf>0 lw_Җ?RCNүJHĖDYn*~Lt@͐ @OQk굈Ae< RhIGQ9x;%J{'LPH'Hx +ToLe.+DтqN<toٓx<^_bpΧ<訉i8{W6oA4)l[(OYH@#x}Я ZԼ)~>iQlL %PDOBoU֟l(!tqQ_o8/}2j59BD΂wiyM<30}g֞(ϟE;7_(S6ҠZ0_?d\kyrޯ KdN}ysAӉLeLdJ#fפKt:pA7t_^1m3x@~QtvuJF{ra?Kt86%DϠ:3?2Ζ*E +ѿqQ!(8e 1ALEzgޜTS<Bw)؉64ʨZX6cM6l48' 3KŽ8(({ n.#U~ 7SJq_L#8E:VHaqR/"]&m>$1A0<~^>{м.L|qq'&:kV!s2nEwF"$p%_s%?bQ8`RMDi_n573m? 6[-h:!N:ќ'E]pXNrIX2yvYKKnF1 1SFrPr>Q)sQeΔ6$M#EaShpk;$~lpY4JkJJOCAo+S'{5zcMgc>0dV.C8/GL_Z$]3T(4bR2rP­Iڞb%1S ָ:-۫.,`"ZZ^A)#b8HNm=c΢S=gj=t̎XVRMQ)̓?&%\{ȺW^%@~2Zh֫r %'2da`.TX#R$`XSpSI&*ߌf@;V$X~{l\%_ڿ &:Ɩߡ|N}F)!<ʧRBaA J!1^=po 5+ Ĩ.Ǜlo"L唸>V/̕t&;&ٙ3ܼ-})HsDP!d馵Vah~$x\+l3QF)8^]\\=`/EDf#$`N{-2ps|@J3e~'\seW$6ħz;jO`^ #7>ޢnm Jƛ"@Δz.WW4qWL}wpQt Tnff9I>Ɖ4T\0 [UlutNf2(͞Cy@ۉ2Rpei`?g(&k;d+%t  |&6|{8_?VB5Z[Pާ.v;OCky USgkdus+AL÷gtuN(J"kC>S1¼vQȕC?##;^AҕI0_W&!1 r5ͥx^ il @ i@Ea=E@ JR%_*xyP:4.j|MQ* S<Ǖ乳o\̊nbW]"DHr,43cp0iLɽkbeaeO~&GC?,9/Ӹoeqt G?;ӣQu<3T-1uhGmF= 2ŪT:#B+ǁAἢAsq]J2yhFB"=:{$1W_iVrJ2GK`QckMo:lDlGߺTw Ԅp? e*$ay&jŅCk(r.gNL4.(VU8̸Y]廆K'WQ=gIr81^[p]Flx')/yz*!DIJ >=LpbkP{CJe!Kd6]I[Z#kҎdNJL)3:FNv1<9Do-(s!* \Z@<ۓrv'&: DzK9{--C /HRxhxYvf{ ~Tx{Jz @GbykGYPcd3_C]"jH13ד@)ٌMM_d @OTel2"?0!)_C|앣zQe.C 2VUAC"E%Ð lް,uo"t~&f^5e:V4`AĜ$n Ik6f:ᙁF\I2^Eޔ=,Bp[;#rc"*Pn3S%Xc +4_t\]1E)gϳ}-ycxQZ:.Gq~VVr̵)&&6K1LZINȯ溘20m@c+0IʃLX_5xՏDTn#<=?3ӭz9i9B1O¾sÐ-I4d>C@b|;2M/0%a> 4lUG8֦ KY˫ϟ,R&Q<{z8 *$@R,b:~ ({9f9ikIϡBS|;TG`O1D]6ϙxEkqRj+1| Kz| ;}xT[>"I1[g2?-üM̈UNA5|Bqҽ~u+>&P™pyD8u=VnO-Z䡍=AN UND\mN?Nu07΂IO}0[d_Wjr.XQ`i6YQbyxň 0ms61A2p3S'#K}袒~{.6s `떥| H(L|APA/M !~ˤlطuqm _ \r/Im8֮BkI!gui.Aza>'~g> ޸jl0Z@f~@XbҿV۵m©2+92Jl9#~Mv,St"5D=p-8:K? H!\!{AUHn^Cm~(#ؔmMESS%4YDDߩ u2ED뫏Ȼڡ| 0؅Mi2+Yepse_{]}\<>,C/̬'.*V>Vq~MA`E%H=eW Mo%LԒ3SLf彨̶3j9eM<'4SN)` 40٬s^ н>tnkDhA#W" Ѓl,4?|TT7\/<@Qt2cb;ᵳ+K4r}Fp7,$^]lx>(F\"rS9_h.fR.S=y5"o}uVg,i6V4da@G?À"hvS攁σ\puY;,c?X xMfLq(|[ZcN HW], ,{{:4%)T-UW'@N}[!]ۘ/W@PZ[b`N?-T4Ij$PwZ?a8xx .#7.YAԩ'J+m u[[e&&\ƚxS&?я g< nzﮕkDwB96LKHX ş؎dܱD8xErbՅ@|im a4d'X4<6{٩0BOUt-OA.o#^RY0-| ~jIl7ާuJ*L1'וfI=b)LxRHOlSäT!e3^C=T$ *cK}i'-4 ^ |net=[ѭSljH6Az p½ ?1_v0;O/4 *rH{.WZKF8 o@qL&m`~xUpY-]9ہD~Rqy4A7g j1JJ4n^YPh)iv[[<.tl… oS?𸯵ia//5)g{}|t+)NpqB!?i7WPa| BwO/ol[4Z @?[*%_)PCH'ufҔ  ] 1/܃Z?嵍:8O3isVOD]b*'A9 dOnHn;><ş^X}Y?ځAh "i;Iƾ- w]09罔 ,u Aֺ{YxPLPaYPuRGCc$$zc%h+oB iEzɃ7zr_{ʽZ ܴR̐pĞ=_Us=LoLߌؼ*A3-W': ֥v"_'8:ϵm>8pq'8䯡cɈŹ GTww.ERY zdj׋HoД \\{22eϚ_3D{xi=B@"][@ۗ)UI&Y.c Xo M i Zul2Ɏ7oGWU)Df ~ޜtnE7q|?xdPڔ|LH2Ewl``xhG6iwn%I,b:Nޤg .^yv^)Vqlۋs)(mDԫXQ xAIϤ0KWhk9[7t߯\gc v}yI¨*L-bR=UY,TuW:Y1 Od55WS/@0W./ ;Qm^"{rU; O zZ!Hr,%܉4?Kas1Jjh"侰-y߿,A4c7OLBI3^ǟr.%JNy״`0/ :Uk1IZ{8zw0nJ ;P<b6 s+vV0됰hMnbخ#YɟAe.&uJw«+WPۍeT\/~S"I*׭{*˙ZS!n!#ި/PEbD;e>:B'ղ/xKS8g*Du4 lIѻG,kڔXDGuL;>%}۔*k )&BbTDGQ+]ĪL^ [MaP\g}P~"JiCH9@-Wg#ej?=D $^{:?h\c'ӡq<䨻کn>1he0 |B}c~9{*64EK,jg%c )o9{<,FWB9R*Kq.7RMC1wq x,HAД۟n{+HF\{'p9\3@nSf@xz\eީBfLݨ1|zr?hW$pS~ޤQrNR[_Z[fߣ)M4k>χ̓tS\~XUFUKO {سkAq&Aod-[Aes g=>مߣ/Hhb yB- .Oh!`6lɆ3% gKS} S"[UV^j/>睽 UnHb)û*h5&$9u(eYgQ}Rێ+wF!#^vHԡ9g崎1 - 7J`3P]&ͱONB;yc+:풃fYa N*:ZpZbJo|<\1ɰ{Y7uJiS4 phvٴO/GCq稽Ck?(73oj S8&:/kѥ H#N_TMҏηo9侸C8p]G`'s}<5ɬjyGe${Gda(0fB31$qT0&[xc7DG_UJӥ<.[r nbi6?CP`xsp̻c&Gu2Q$@VC >?DZ@!.&>;X%Nqec8]>F ֬17)5 0K98Ea~`(w(S\]%$".QB-W1`kdp勺˲铮qaӎ7X$SW|>o~ rЕ ƤwiXx iBO.I(eW+)v{,~+Q ͌zbdE._ańG% (Ha;&Šg/TrN TAu7WZ33?lO8ԯ %b+cxL3"XaWG]_ u{|> ݵR0á>n4HѐHao2s/2c1 !@le(bC{w'8sUma?A&D!&-ԑXBn;&e`Scw_َKNgr2\'o$_Hg]Fo -6"O' }wN(@PK3H ,d&zA$W`3DdYuVr3QԋaЇ/ʇmՄ՗! $RSrqlG)x)8RSr:H ״3}%g.WVTsYDBBِ7 |;f4K=,|aX烗o$-FnQDs#*zB ]j{X_x $(^3׍䇡z^7 bZ@Eaj'oy,k :*. шxV+100i(~~`SɷVjNX]CH &J9൐d.`铻d,ybl\S F<(WQ3d*|ܲU*X<C;P@b7\;,=obn5u{H/F$l7䁙{5~A N*}&;~}jJB|!UٴBע8>c?^,xݴx 7u''yl$^Omܔq; ҈P;łA$)LW7'x-ӍdA$ܙήoN[Ij@o;7xś#442C7!K$zrF67F(g2\MH/J?Nb azZ@1k5Cfӏ.-l=*Ρ(SǍ\ۮ3M8v7#BRpv>aECr xfq9OGA Qeƽ *#V]Cd OȰKF*+fhL[E(ZFDzQ:V$M:HvOug}P>W$/)4v\oƻK>?]*M NՑrX0R9L4 "B #6N ml ]ҡVW'ketמ`NqbdZ#fJqN6j]Fxbߡ>foSk/5ۢN.ul}+[@__|uCtz[D,:Tמ_~b!NRc-Vy `5egę- ѤKܥڵI5\z}XYj+m(d(1ϔ~VJ8I('@4  aDF5SqTC.lp^sԗZ9}⌎E ]Xu)0n$:N= KEʼn2fE-e(HKʺL} M"4&9s xҥ2z[oevXIQ* 9DwuKF` U8 HHiH0FU^zۃ&i{vNiUSنQ>"EdӞ+-,Q2jFҔ{uʏߙW3!E lF•o"ۖ,ѧR#Y&7DVuJpn4F҉qB=d9p%vh}"LPP€DEaZ6\ p@E\G P@"$VNm*6Z)yjO MοOw_TsEqSe">+|yRp t4K%2N@Ta) J98% @X1u=a7nj?&p[hLe:Ft,VZg<`Ѿ& wb˩!^^hf]ijD)ѬAԄW1pi&)YVx[I2ʷyO!ٌFE)̡m7 I nNjqиIgc֯@]Jǥ+Ӈ-p3}M+Ӈ-p>4~g[U\^i6;N (XI^kj2%ֺ_/C'0KSk1%UЉ?}g3T7A]k(_|a d,G^zZN wȏS!5P$Wͣ8=z{"SFON?[Of|?7~a~ѶKeUcl>䌍 {Oߵ>=㿬s$kmSWeÙӄʆẆ}<g&:E<NW}c'uڬޚo]& m~ӱRˌRb}"xJK:knw6'nYZ u.)Mt>i+$qhlbu6dY[;GSꍚ֍J\G<:m O`wLTVar)E1&:=1>7fU,"(i4[÷!D }&Q'3"^Y(J zPg4@`nT]/C-'NHιdu\߼oO#ITV5W?/[8sP5C9HJ)kjݔ:+gbJSƨg3hI1.ջ)u y!Qv8bx lR-a=YaRqUwf#7j/B[:JL*7.˂*xgt=)فUT-M&"e?#aZ:z hu?#{|Fn{<c2p]=[댕ipj;TωbxEOX6hCj+DDM D&I__ ]QW!:t3+ӁtU v e/nÎ/TCj/*^*Q`B,4/{ Z¦,V[dt7z5ɴ:7ӻkArBqaqF ,S/,E_v˙pmTIErU,QEG}G<0i2h`lBKD=o(Y ˑͭv|IAahN Ai2=狅r[˗c5-e.VSmVUVL.x-HGR5$AD$xHC t^p>QYE PB\$X;Z;y=SJ5-3gّMtt º_ AlcU0.QLyAle.cq[Ruv`訉"' b[H$hH:L _b_opr#agg8ыV:L _b_opr#agg5gZt@.HĿF{insOߐSf*>YF[1AiX T^߼hZ&~ns$ w$m"yOfCH,QSɐh\@&10oJ:#ǹO4bFXRp'zBS;*T2G@ꗠb! 1`7p48_`m/'pшZi!N@?o=m[yuJU<%ǖ։\GSGnJz(ON` T%X!T9iZ^a|,9}<sʆ=XnH1nvB( 0E[dt9`nxC8Ν7ch h |[v%m*gXN6U-w?<'Q(+KPg4'TGM b@l>%~.Ldo›HF߄ժ$eG(( ě)vRV쨞#pGtŻQ-yK?DW3OИݏljP B.CV-Ġoi9̓k- *Ael NW$hu}B 7Df]W$Pʥ޹p.6@?Wr#~,;MT:58+DI&)iG >^rNkQW ]k {!N> oy1Vĉjy4~2_^KaS5h/I\7h&-=eG;;kq4{zLf(~ xxZK oank &xũِ+j\!<}J(W'F;% B?SYHES!sB] FH0AѲD&id&RyRPx@!ap\~L: h~ddUAo};t'b\؀}Sn15|^aA߂*]?؃껚7hɅ):VϦL.>a|oL/M]`}~y$`6DВv ;J5i54Ius՚d_׫t]obBRq94T AtH{@㬅,H\FC+ !Rd./oyy~5͏ G$ǴQtcղ=熅_QCgnV ʥe#I(s4ekH8\~īN!8](OThi^t? \.oGf!sL ͬbI{[)e&"HZ!͊ߜY/<̓0r)v(g\SZB*(y.4'QLU3So|Q9P k Yսiiwg\tX}Y@5E䰂Z@Щ~~#Y8eu.!Y}UXuy.;󊞹"y.4GDvbKv+Ok6n^7e3 TSoO\X)J&ON3ݻo]O`Hi*KNO R)NSL4IMF{㹟ޢ4?Gk k .mQjr6 ?2!<=Av.~m6vsAcI--^/k d(ruy)0vIȅ82]gID(;ӂ zghm%=OA#URixY#W%w+]V77A.P@9;W Ann'Pg\^%ɹқ0%q>.k9(}Cuzr&X:P|W \ zX7 `p _CV+8j-heI :aYHjx}P`GVN0t>.Zo^~E@ɠ "{z0(~b"`P28b- DLqJm,,ZIE495{|7j|FRiD1 Q6\wTFvsfBE۵͛wQ]\ټtgk7xp`s(X*QAmh͑OgQPG=Tc<cbKln3DJ y4đM1$~_YM1$~bHaY̮~gharacPZ͛O%e۲ #+Qp%dB8V3q184+:2?@utCʅI|v=mqq=V-&40Ɂh4emB-MathO|@$dC/lT!DMן,վ&^=J}[k_Ң_Vdz9x@F!۟,վ&^=\'"rzΐ堨)ߏm}s[`j: +ꀀ isk>J${PenT߶[ԉ~oڥk&^o߶)o۩kcOQ~ԟۣ]6пKi蝱>v~|gѿ2,U\}̟$?9Qz?έ sXl$G-B9s~3zL,mjc֮dv@&Cq}^*7i{ @WPnqQJVjݰ`fWU@_-j!A0AʋrwN92Bv>7FU9$d;=Oc]#(Ԗе-~,g3alRӯVo6ʎͯ(Oc{\*G)ٳ6D>Z٠cmVtV x@ ˩8QnHP"oL6m#ʷfuٶrWWAS$?ŚRfU3HNcJڞE9p`yٓ%Q³03Ÿ vԖXu@iVyV?9lHp̸)V~mK{haTPPJ)Wu@<dӒDh)J4?_wf3'`r #Of7%|CTsl)l w;kϽ9ehqD nQg$k.U1z\t kPBh-X ."( p4g8AX(U*F#}N P84 c A\$ !7:/(%Gz,=E4qg7R?U)B+2$hkαX]˜([M @-QzrJBKnɭvgω/`)[B=Iҧ#!I<͒<]T)#NNB};/F0럎VFlS0ɫ%U"L5|쎤(kG},Q31m[pt|@[u$?2]M[OTZHͻ++b (V ̸虌O8JPK+]3kZb_<'uRї7?r#V$n4VtEjo WfJLx}oyz9Dh%6@$D~l4T@vb{Gz=>9*`hzn҆.U>^wk]*^|=`A8 iNE[mL # aQ#P3Q:C.{!p"H Q؝6[$ii0] e'(_wƅUUA*A`+'b" `y#Ѭniޘ'sk{C fS_+G?x G6 'Zu<}>uL ?#P31E&1Ծ3?\nفX}`Gqח%+Y5`~.}ލchF'WVWS]Q+=B2Ua bG5g1S\!*To g@;3saΪ<*LYDž]Boz^u+ <@j NJo=Rڦ4 +A.Fn5DW+9e ʄ>ANwd6Lt{Nwrl 9PYl܉ƙ;EPzioUbpefnW簞|ZnH"ߪ,i+'gkax̴*` NKů_ .į=^;Jg ~-sE.E/}pfuiNv-| ר^:cNDo'O;4 d=![8dhaB m߅ÿ څ8) T@rΜ}/$sRX&gm0y}s9"8؝hyFu7hfc'Lsm"QSKעw_>O"HuXxS͊V8 mLEunR\iz-״YYHY7,A9,BI3g ٩5 x\Ȝ f9<5ۻGwzlWw$ 6{@@\m4**]9(N0N?/Ie%xgrj˾eMwv%; & )hWODJ-w͜Ѭ\EO{(sB Fd/O{&0s)^@1_t~[|Pa\g)#NmH>1QbLFS"B}g'sT{ܵ*' r)"yӬ@8sON9B`yQϰ} I:jd[܆?pԊG.+%TCl9ihIZp}"jȾQhUYu.ggۤP=❦ >NσG5~m^@\/{0%܈X(j{Ni "b(zOz6Ӧ:s3i&QKrȿi8B'T! F >%E#}&R:%l@zJmRH?PiG]yMkdjϼĤro oLDkɻt8rY$9Bk(TYYѠ) 1ִwf8Q7j)/?yUv]qQBȞb?u%롵j^'lfz;{Y $X&>d O<8Oz4(,;,nEahq@8zkThx6GVGsYfʓּ `7tA\nV5 5.یtW EK",v b7J첨˯Rf0ϕǶ؉B6o8T672<$MJ<6o,<+ ?& LJ˫Laӫe{p{c$'r*3VSWNsɳ?Q'|VX(V5nsnNG[_gΦ͠]{o MB=3JUe.fMTPt~]DlZLRE[Y~g&cc׺\)ҖwC2 .sy8t1[E LAz{9㥦cX_ʡs,cf2"x2,Z& |]fWV?  |d)}z<wa߯#mEIv/L9wFkP.kDź<6XV^}SuS*"tӺL(S5/= .232dGƆ'Թ<\ yUw'7 'sË'TV|!-}Nh簏N˄) ?DOsn8:[#nr؟Wx<љ *r3[U3Ą)B$6N;anH#߰ `Ey)R&̞g(__yF=]J%ZAhy'>$`Egtۍj6)tL0ThvXИ-1p $H7k9cy_ kfDUOI({3zBK@ J p(zaAa_1‚ 6lVdioO8nh^$(4;vcMn=I:l oޗbӷ/)`0?-4fJ!):U %"nY %sm2%dž$cuU-ψdSME~ &Z ĸ3YV9 MZp?E\ޏ F\r!Gv' E(œV8U 1 OhcVg1,4^YiKz-]iq$xJ,}&KSSb%;np_ @&BJUgyN@Ȍ~"Wl._>/=.Z}ύ:a!6Ga|%E\u 3)[_<ΨH` p~uX?BSYTrP)bq2:JtY35WP#Z&Oةf/ ~\/\Bxxfe\b&%W&^FiYr/V[h x_ljr i;vWuQ35vCQFz yN l& : O8A?,5k;QJrC}qzzz~LMMZ/Y~UU[KuBLӤRs@{$qXuu5i>'E[[[+ G >;,˒---䪙\ Lա8lnn~ ک011F(>'a@ #+~DfNaX5a+.."d: ug, ䷋!mXeYLOOc6m _l ꥔Iy<@ PlRb;W@wj )gÞLiTTTDb$ 8H)[+`7p8mEQd"|Rv!{΄myX+%y쯤#\wòXŊm/ =;=x |JBTeHr`$0KEA aDzCU`BB*!.,P/vb%"Ǹ:xJ. Q>ȃiyO&/x&yă|qy}R~@X&/4yQgIENDB`swift-im-2.0+dev6/Swift/resources/Windows/0000755000175000017500000000000012227051774020423 5ustar kismithkismithswift-im-2.0+dev6/Swift/resources/Windows/Swift.ico0000644000175000017500000010174612227051774022224 0ustar kismithkismith 00h ( 00 hn" X!'00 %.I  n h~(0`ppppppwppppppppppwppppppppppwppwpppwwppwwwwwppppwwwpwwwwwppwwwwwwwwwppwwxxppwppwwwwwp???>??( @pppppppppwppppppppwpppwppppppppwwwwwwwpwwwppwwxxwwwpppppp????( ppppwppwpwwpwpwp?(0`  !!!%%%)))---111555999===AAAEEEIIIMMMQQQUUUYYY\\\aaaeeeiiimmmqqquuuxxx}}}                  "   )"          "$  """$& "$$$&&""$&&&( $$&(($ $&(($  $&"     ???>??( @ !!!%%%)))---000444:::===BBBFFFJJJMMMQQQUUUYYY]]]bbbdddiiinnnqqquuuyyy~~~                ! !$  !%%% $%&  $      ????(  """...666???DDDHHHOOOQQQUUUZZZ]]]cccdddiiiooorrruuuxxx|||      ?PNG  IHDR\rf IDATx{t\W}?3#i$Y%K۲v`b;8/ܰ(Bx)!^zREiI MM -ДvX~%-YlG-Kh?4H83g~935ُna`9 ք׉VHICLN?"h\% L$=q>_4(E{X ) p%#.$@j3V2%,>e,҆A!ahz K"7 ] (3 Bb:c\X?h5.0$.dCQR&`$ ZVfD$ AHł|jԮJKKr؏ށcR hH`=e(&L`ѢE̙3 8{,9s .P초. .b)E ww 9Y8fɒ%XKRXXHoo/gΜc=B .lKHoNNF }#އw"LOTRRYr% ,  x"F-74 «hWdG p CP&Oʕ+Yr%B(h4ʙ3g8~XCex!Q8gQk edX555\UVQ^^>$>0A8v9ڵ?V׈#8\݈B E(cڵ\s5 <1Hwwwcǎq)Y}xߢ]OӁ͈#YiӦf͚VfVh4ӧ9vMMM466r zA\yr%{Tg pp?⮜  l2֭[G]]]FƟO|=( 466r[nzӈxhJedsa-[68A  q)ә3-C" J˨xH}H!e***X~=Vwtt :đ#GKxh Q=ǀ1x8a֮]˺u8qm-H޽{9~uPO Cx9GTTTTĺuذa&G'?ٱ . }K!HO?E¯)6BZ$VAqq1ׯ[oVO廱XAAhjjf! A#)ق @q ~8/an馔:wؿ?{a޽?>:#3 ^!CTr'?DeҤIl޼naiѣl߾m۶exl'WTr2A3TPWWǖ-[v%ٶm۷otyt/,wHWAI>|"$Anfn's'b8pm۶c:;;3Wo c i TG @x.//g˖-,]p%DQ϶mعsg&^Go"݃b\elN;H⬧={N]]$bqN_Oʕ+;w.hQ` Lo}72 _#aӧO{njjj R[[իY~=&L5VA p2GB氨=7knvEoo/h"N͛˗S]0qAJ^F 8<%1B777sQj b>P]]M7իɓ8MD;އ/ &*wϟg߾}̟? &x%8q"˗/n'O;jCrd cϤ\E ?E03gt%B ,`ƍ<^ִx^y$rY{ /¦& r%χa/_βe8qc%Fܫ`x69BEE`"'OfݺuTVVrA"K0ށgKUd!Cudhlln0P̚55k23QN~sd(VĐ<ຑ9|0/`a<_\\̪U:u*kp>G[㥛B@ԑDzzzhiiaɒ%C_1Od֬Yx444pE,E+ U!NC3Gx"'N*gRV^M{{;'NX{Z T\@@I(KdςQkN8%K @n`0)++7u|Spw@@INd1Y(… 3gs̡jQ209>idD֑ژ3g@\5x P__o588#d .;-zzz%0uTZvm/ ؑ젗QP% Ej [J5\C8&MbܹӪ;p3 x+Ad F;~fϞ횁)4SLݻwBɡD*JAqZAn`\ۚQP22>l+h4ʌ3(++Ǚ7o V[ S*J6#cƹرcٿb  |3pWbR0&]%|_d gXü{zz,"XZMP8Ny~B%[2"`,c@ @qq1oIlTiTly;z3f̠td~^Kyx3dij(RFUUc~j \tǏzh+4doFQ]eԵ/^֭IoI2F@ɖBqYkjj(++sT|fGPPRѨ?>}:YhzU*JT%{3 vLNWUU%Ũ(>do&kK`ʔ)V35t%[F), Z###'NF]D@ɆblRRR78rA U(e=P2͉'ks PPa@EEŘ3~:t %SBL:5t2p!?@su:*JlFvFYY)dc|֍Vp,Zu%S>iӦed.ONVjeTL;@@b4e\'1Ic CTL2  2 ŖcҭXQP&]TUU3~-G-=}#5 $yO8GsU 9s&ȉ)H@IO a֓Ɵι29oiUWI7*Jɓ)..eIܙ'pN5k%ULf 04zA ocNt+*J*|ؐi(((zϤg"W\СCV)׬a4(2k3cv;|bE[UUX֩?ϟ @wUlŊ)logΝVui- PdZ:gϞłMVddBW]uѐN{kwC.%y,~kt^Z`72x8G)((ȫx]]]?H /KAncJ?c1_ZZJIIINӝ8www7Ne x/0eT$VQ1PP6?EEQQN̙;wZEEEKy?(Mp}@@yOX.(( bTVV͒ 0108Qt0 (B%%%VdyL KOO===twwIGGY # 'ohR pxL8ytD蠣v.^g#7]d,T"o@ @aaaZ?]?@__mmm[Y o~y,t ( ^qB? ;λ5&PTTD,6``0HYY̞=I&ŸrʨXPE!~=|q<?T#s[OL7eW>i&~fϞM$:؈555Bss3==mB6ow`A[fdG%n^4uOc7ϲ Σ>JeeeRXXHggxbBZ {~L2YT .#qZgϞ+V0h4g?Yv S[[KAA.] Yu"Iw/U]­~m۶Huu5'OW^y~zW|&Mh4Jgg'0x(Y'@邤#V v^+ ɭꢩ)Uw o30m-ܥ<$0pY&ک'h,#nG#W\z{{SY\t oMOo7];q2.000D#Qݽ*E48E0H7,Fqcv];E[0e*Ǒe b'Xz7~S]oS{x7V1ħ{&!]%9qs3Y:pvأ(htĐ gF[G72.YN<썿gb5˂ld]n_%MGlEK7̣ O q~F7<T% G t҇y}Bo)Bwpc7ϣ`ӑHnb%/dzm56g7e܂>o<b2E(p?>MI}RlelJǔ&o HH[+U vaG)R2՛S,*N c@邤B yuU c@!9Ʀ4lq9L3vt shV1^g.vd#c?Z'ppUl |׏3S;.\+q}1.1NY}L5\~d9,P'!Yeȟ!"_Ӂ&(Qt'IeiXT#-|J"+6twt=Ju0,o(>Yl`чۿ0ɛ9D4AfpLӊ'(sEPޅl9 d%}+3*WT?:-yH2 n'b 4үvދx)晏Ĺ3m8~I^GA5ܫÈoQɂGMMP[NSuւ7 W@.2˴rNڵ+l$$<ϝR&| u(4}+*;V)d/.ݓbM@ *72l;X4Pt9$C_GE dd1l < |.Q6daiʕtOf՜+3,EZ~ 1X~V 8+G+\'<&!Ur1o\^OW0hS?IR"J֠q)x.k/@aR|W3ZE m:΢^%'XyCb:::x]Ӆ `TiKx2]Eqor{)]u/qTtO0oN;b+% 7R/н3Œ[1oN}4eL~yCu" 5~E?ic;=XOK~yc;5~Ekgz5~EI0ov''J&:ϯ()֎|Q_kG>fs()axM6׍y6nbF3m٤׀+FQ0o٤gRkEQaހ3M_|SVr&2!DQ'0oș#`dr&WTCQj-s:) o4ZdMˑP1:(+x)Gi4TF5#!goWQ8w7bUQ!#& a(2 89W׹ xsƿTex& ^_Q0f  ׅSłBp{\\?EQ,OX)bMw#xꜾxo?*ܹ-EQkc{]'EQR8oGх;)Yÿ5P(-~FQOAdk,''ֽ[Q%DON)gЇ9IDAT| SO)?fyϟa ;P%#} +1bw_-('{Ï"~_QL O;peW% !;ߊTx /pV%kʀߐ>(cLVEz<꺫(9tuR7H䟩& ('~/Pk*7~$CeT!V2?,7V:EQ8hbXLd`(zIENDB`(0` %H~ @i8F=#F{W=z 3lA Y)x#UW`  \xX jw\A+jS;p_CF#oU_D6rJ#9    """WWW---111555999(((=] """&&&***000999<<<,,,i?"""&&&***...222555999...$$$n 7"""&&&***...222666:::===AAAEEE z5U'''...222666:::>>>BBBEEEIIIMMMQQQ<<<:::>>>BBBFFFJJJMMMQQQUUUYYY]]]aaaR1DDDJJJNNNQQQUUUYYY]]]aaaeeeiiimmm222EEEUUUYYY]]]aaaeeeiiimmmqqquuuyyy:::NNNaaaeeeiiimmmqqquuuyyy}}}^'fffmmmqqquuuyyy}}}aaa 222uuuyyy}}}qqqHvvvTTTUvvvnnn-c111\\\` iiii```+++O@$$$hhhddd???dQ777YYY\\\BBB%%%|@LR#aB"?|( @ }k: >1| njI ? mE \MuA&7OuF$pZ>L\E9* LLL;;;+++)))  """(((---aaa///0 """(((...444:::???"""+(((444:::@@@FFFKKKQQQLLL3FFFLLLQQQWWW]]]ccciiiWWW]]]ccciiiooouuu{{{###iiiooouuu{{{xxxVVV{{{ EEETTTUbbbGGG UxQQQeee>>>1; ***>>>''' \qL%???(  @ ''3JlV0s2Whd%qpo ,"""...DDD$$$x >///FFFQQQZZZk<<zJ^66J:xYu6GYV+r*G]Fgfff:N鴞ft]\(22k"^$ӑH)%JLܞm?H|NH)(˙wm_(~V*BXĪP( zN!D`|| c #080808", ", c #060606", "' c #777777", ") c #969696", "! c #9E9E9E", "~ c #AAAAAA", "{ c #848484", "] c #393939", "^ c #4C4C4C", "/ c #8B8B8B", "( c #8F8F8F", "_ c #949494", ": c #9D9D9D", "< c #A3A3A3", "[ c #474747", "} c #5B5B5B", "| c #7C7C7C", "1 c #838383", "2 c #898989", "3 c #090909", "4 c #1C1C1C", "5 c #6D6D6D", "6 c #717171", "7 c #7D7D7D", "8 c #878787", "9 c #767676", "0 c #101010", "a c #5F5F5F", "b c #656565", "c c #6B6B6B", "d c #707070", "e c #808080", "f c #1F1F1F", "g c #1B1B1B", "h c #4D4D4D", "i c #535353", "j c #595959", "k c #6E6E6E", "l c #141414", "m c #2E2E2E", "n c #3B3B3B", "o c #414141", "p c #525252", "q c #494949", "r c #0F0F0F", "s c #1D1D1D", "t c #232323", "u c #292929", "v c #2F2F2F", "w c #212121", "x c #050505", "y c #0A0A0A", "z c #111111", "A c #171717", "B c #2D2D2D", "C c #2C2C2C", "D c #040404", "E c #373737", "F c #070707", "G c #020202", " .+... ", " ..@#$%.. ", " .&*=-*;>. ", " ,')!~{]. ", " .^/(_:<[. ", " .}|12()23 ", " 456'7189. ", " 0}abcd9ef ", " g[hijabkl ", " .m#no[hpq. ", " .rstuv;now. ", " .+xyzAstuB8aC&. ", " .....+DyzA4qEvB%..", " ..........+DFxG.. ", " ................. ", " ....................... ", " .......................... ", "... ....... ............. ", " .... .......... ", " .. ......... ", " ......... ", " ........ ", " ........ ", " ....... ", " ....... ", " ..... ", " ..... ", " ..... ", " .... ", " ... ", " ... ", " .. "}; swift-im-2.0+dev6/Swift/resources/logo/logo-icon-512.png0000644000175000017500000004664712227051774022653 0ustar kismithkismithPNG  IHDRxsBIT|d pHYs88CtEXtSoftwarewww.inkscape.org< IDATxyU$d%! a'"a KP #hDQqVgF8Ό#( d#tV';^Kn[u_}[uӱSUݑt0iT`4z/mݽ>H""Yfv<0Y VDDN l prjk0Z_o{SkR~\NZ8(w=)mFHulH(Lt ߯rPʼnT+0`@:q:V@@ *uFzu3Ta""Yfv1PJ3m}KDDJ %/#v/w0PRj> \N5BP0rRH3h) cVToƀh`}DDrkBNX{C&"R(1yt)EIk :Jh5X0)d\:KpXТ'R_oTH 9dfo#\L,hQқw~ASfvQ h AyBlAsЪD$7 ! Z_~AB5,rUH&)fv0p1`":emºUH)H7f6x Qx pB؊ֿ;0vͶmhn,9?DDEG F 'OOgܸq455e6mĺuذa6l`ުM"]CBBN []mӀa+8934iFjlǎ_=ߴi---z3=hU"Rv R23;((jac9I&1i$?u\ƍAUwf%H$QfvWVG)SKÖ$R$]#dL<#GɅ޶ظqc{h 7oNJ5tXUseHH zfvQ8ǏgԩL4wb@v V\S߹-3;x'Q UСC9:u*zh+755vYBXNk~kD 㫉8:u*~z@g>06ō7lROT#$ #i f6k[ Q9 =zt8w uuu] -xفYd#䎙 $ P&LhIeh{{n/_βeXt)˗/Y00-(B\$̬9# 8ۯh[~8a5kְtR.]ʒ%Kظ1S m5TֵAF/ 43&Nȥ^|mm%eY/1;7G$8Jf6( Ltƍcqׯs]}OKطo+Vh˖-cǎSa78Q / [H Rl<^0PGʹi80`@NYdI{(Ņ+}AbD*E@3; x70R2Sr%0|Z:~׮],_=,[TϾ/# KB#RN "=0ۉfBk 2ydOرcS-&XreYM6uwHDa'^)E pRdĉL>?{?{I~^:߼y3-b,\]R3O}]bD M>J {L>I&9! b ϟY|yڮ!h vB@D:+Y #dƌz꩙{mv… Y`g֭H(Q_wXDJdf1+0{\uUL0εt{0`f\  /x4C4zhdHBl'?;.J  K ֯_OJ" \HD.&_ξN>df̘GwO`mo_|{S1~Aq}i RFfvIceO?+?;}@ss3/f,X:R`3p/p 0a-me쇳>+QFe4@aغuk .dΝ6 x@%PD*Ȣ>V~ό34hP[ߝ{q>OJwgٲe̙3ٳgyf 'p/ YT@2 0\} :ɓ'SSSӹn0',^ٳg3ϰ}{;&P H)ffۉ?PG7뮻ۅ0̞=9s\pw-T ")affC>>W^y%>@9 4773|fϞͳ>F-D,?71I?dJ;nb@cc#?|87p&LHd[qƶmxꩧ={6˖-=w_Ɏ%D26 .`ƌ 8^a q0Pݟd] "ef_ޑdcƌ=ysL[?]^a vZyJV RJ6)d| 8+6kjj>}:ӧO_ iy駙5kKVd?~ R%{Drj.2pHR}Ѽ=a̘1mtI+W2k,x C?ݗ3> `ߟ3fpgvNcػw/O<fͪĵMD,wg "9dfg&' ]i-[ƬY={vjhFrv$ S23F'Q;vl[]>}a`ǎw2Nt'ɷ`R93 #18-0p@nFN:\ imϯkxtGىH03g$]vӦMgiNKpwy~_b prv" RElE3$0psuѿi /2> ,(ײ{_q@E@ T࿀quG>8pZ~W'2XqI*efÈԶ>=w{:DxOSO=Uvh\Srfv ѓJS_~\{L4}t{a`ڵ<̝;A`_I7,)=r%;N>`Ϋ:x͛G /G3wKhs9'fZ|9< .$a[[ޤ0D 386ꪫ-fZd wK,!a?7&ݰTtcf?.( f̘0hRzlB6%٨TȢ5>TJ;W^y%SLikއ4Wcc#> =Ph%$P>m׀~Ŷqsfjc_7nG?= \klTO@D.~(3fp9tn4ykѢE7k&vxR J)HAl< 0kjj馛xӛ^綻yG~݉|ݿDcR~ "R03;hsРAr-9._{{A3}mݺ}{,ZjPC@Db1D!b3f 3gd4c=ƽ޽{IK5,Ƥ<D$6#[MNrսi&/^L$)HQl(`j1_x\r%ܗ3k,~Ko'p.L') ~ \\\s rJ|}R(H"(9`l}MSƍַŚ5%/wZHD$1fv0 ~g̙1"Wfjll??3g%[wRHiD$Qfa{q7nn LI[^JRI]y啜r)%KpwsN\+)$jY%q4h3gdY4׆ ׾Ɩ-[(R=0ۀO@DF/GO+NY4W-[pwa8WۀG@D̮"z`,3fL[>MbsNozuc$>)+3{.>&Lq OfڳgַxW)ңDWl\>555̜9aÆѹ^+ |W^H?[}+>gu]tQ>˃fV*&.-ZDZKbvxD,%  ̙38p`[]>fjhh_:+W{}1;KjB "ёGBilldѢE{WKKK%*C9_7_/fGG@D*_~g\f~ذaz 4_[bvTMb-$饗2a„ܟ;k_/;Oy87Q w <gŋy#jOo,p;( ]Tuhll=~]:\}իPP=\ofsG(nիSuT]wo}h_1&=6LD݁ogʕȲW>Ыv3$ "!<@KAVZL}ױ[b~_XN78[lٲ%5*~F/#+s]w'8e S_ү_/`^(QPbkצj S_us ;It< ]m ស[[V~i 特N3"ݻۗNQm qaȐ!L:5fvrܝg "R;;wL@U^_|1tPߙ =SVxWq4.( qw )VhtV_>~ԩ<ٳ:lP3 5F4 d;0`vszH@t @Dq=D}-H}}Ν簯N;-HfD$lgϞ.3WMobȐ!ڵ:qfv?Wҝ ێ{,@V]w&N4E@ D$nXSS8e5XSO=5n8=ҝVp0n3iWq7.iHW "ڀB7)tL}QGŒ%K 8jݽ+ dy:PvX08x+ S,c'P4 B7%*C-WͩqwcfC ~A3O9jkkij* n()HH'R2C-zIƌ8B7D$lW, *Agq6D$ΪaS_+V %P tAx%<}5,t "![oGI,!/Tafo\CMbH㠙*G@DB2$9`FZ?iJ "̈ȑ#Xi4 m8KW "Rq ]\.,$[_۷o/Vl,])HӉq1c( 䯯m۶f8KW "q6=zt&9E_>t!ZZZuXR2w؞C9$ ore ])Hٙq-q9b#e3ID_E;HW "R _&555;6ֽa@+j lkUU8ҝ \gQF|ڦW@ZVZEKKK@G Pr 1]~Z龒 I[J_/rAu)3gaÆ1dȐ2U<[nݺm<gw>[:j(Yww'N@D8;9ǣ,Nb*6 T߮e˖}/L@Dgfs<Ȓg,)V0d|rWP=g$j7zh ֿ hi+maW^)Nuqw)H>g_WoaR?ٺ5X~gw "3;۸;~e"<9 ͛7z?w'$M@{\MZͼU0rJ6m붽no "ΉG>duЬd_!Cp44rصkuwD IDAT .u>-fG)~ wOV4d[Is=GsssAuѝ%RF "R3!|A}kjj*M*e˖e˖gi S_}~z^yX+? /i Rt7_SSðaèS0/*z,YŽ;Ș??pH3iDӠËٿ!CPSSہ,Km׳qF޽{ijw١)*ef7 (rL~|gq S_w^ %z= J@ ٧فe #x_---׳cǎ׮]H:_H׀( ~䟥E}UjjjbΝر۷}3ۀ{oڐB@Jn<}z=@*޽{ټy36mb47 BP *`fÁo->kV_---lݺM6qFC߅ "9gfwF)ࢾ׮]شi6mb֭^௲DrN~+~|%kmU{_MMM[[RAv+i "9ef=mT)|޾̌?۷{\{aݺu[-zwRJ@$*'ࢾט1cx{I'hfEDD2>|IJ<H{_vbp n[ ;T[[XvmҧF=zcf w*DR r(wH-um.\XHukkk?~<s uuu[1 ڠnI13;hʿ'eQ?=/$R__'Nȏcjk;|˿˲]ý{RWW;/tbQI)3hk - CֺgΜN\k׮~7|ձaÆ^,nsHʘY?g/H૯뷿mONG>‹/ڟYbE )w5N ")bfc$p)?˸g?ѣo[կvYd'?W)oݺe˖{w 5w A@$%l*Ѫ~%6l7pz*'tG}4wfɒ%+<#̝;7uu/5kְrr!{ DRn'ZOw$ mW DCCCsycc#ׯ ZMpAo(dfc]K^R_=߱c˗/gΝ$w߲ "5]kiWްa+V`߾}$l3ÆHBRͲ0 nK}%yss3MMM466o߾W j3@@m#C"]eypQ_jii.{SSSz\ogZ]p$Rf6еH\_c޽444t1?74 RfCHr_~DyOIiE2pP)R9f$,T[m +ǺiuH9DINRa ζ꫸ܽ5H̬?6G@:jnnre} _[F@$)fv*_T{]@^N477O۷Mݗa)j1OȢ}`@r$Erq[|ϼ 0U3"%0D ]O38@Nϛmޤ R 08p9 6MMM-ؚzx90 8h{"%SwV$YSyρKpu7wzk3<bfDA`xa@m[%/޵^?hpx؊$ϲv&׼-Fz;6{TXl5a+jYN}۫JׯK@Gi ١DGӁa+jW0goiij;_,@ݷ,, R8wz%zjwoh@#%D3q4_ *f6h85p9"%Uh@BZ"=ql؊DSlp<_Ϳb(H R*`t؊Dʯ0/x,vA3  [H8  mCBZ@2̎"7bhE`]^*T`JDG YDh#]y"lIC@Rm")::5aKn :Z~W$~d!hEҍɅЅȁ"@ oE^_zzTmA+.t1ҝj{ wq ϚHIDw!Z~wT؊D$v_{BSh-v`p؊D$CVu.Z)H,Z~WD$p?j ikI?j(H7Z~WD  wo]L)f6x Z~WD{ u #*wE$cf]H(T3{3h]ɢYDB 9efg Z~WD`Ѕ@N@UfwE$~G4g @ l,p%р0$lE""h ]HV)dL #s [HPl]H(LcjuTBD p~0=dGD@nBB Z=3ԾH1~ ]H)bf⹊B#V$"j̀)TPtۦV$"[Ѕ@NCa+*Ѕ@lp)рpX؊DD}zpW 0c8 zH<w.$-`f5d:?-lE""R'+}GB@@f6hDW [9m%t!)DHV Y?\:O [R&t!T}0ۉ+Qa+ # +BBU3;u!Vi$t!V̆-{1a+YLkrt/""1.&t:ou/""q ]Dd:(_DDETBED̜).Rdt/""ݟ ]H9.(_DDRuw_rIEQs/t!$(_DD2"V,_Nt|ɂF w%t!I+[QSs J4t:ʿED$>$t/""Ub'p]HRb勈H_0O6=勈Hz8tI1(_DDG7ᩁ]]  UHA RcfÀo ]Hm~Z-fv08 UDwo]H) ^N\H9O1K?dↆd-"R1׻(#Oꁡb{t Ϻ{SؒzffDuװ`H_Y(M2t!) lDwK| 83@ժx7-)9f6MY;] e>+t2`%qk_#՛k{A}kGonޜxղ}:p5n1̬( k0"`i"Qwb BR-tmJr#?#=TM<;lIgf5N)$w.t0c EoG_*yhfGELˡx w|h1888u: RBQ #y=G0 gB:?%U '`0(R-Y%mہ/Rٹw[}szZ&!4 (tݟ]D\+ \:j/]A4!YBǀ=s0hD|?_ ؀0@ڋ:z=jrl}]z$A+^DJߊfǤ."؏#3 >M ijH١uttTEQ \N뀿"p=^E [H|ݿ82YMh6-6/ [dA멂Dart!,w,tqTMh=-~rn3]Ö#YgfC+$88K>(QIm_;9$X#DhMɓE*&M=o' $6fh5ҷ.G$ vBQ\ۺ Zrd]\W|q}|Ƞđ3;?LO1mc+jf6(ptrD?Be: ';4l5A51? aK) F(Hv<.P ^F˩1?{–$ a x%VCQۈO4O\N5ǀ'}WؒD*ymA 8(+X>8[> ZL aK I |=X)3K6DW_ \N9"]5lI"ef'wSC"E"UMr{p[>|8$pI"m2s3;3G.\u_\01ׁE8.AKt;]553 ry(D@Bt~rE;/5t}Dn]rDOE?&Di(tXh(T鴼^-GDZ;}q" Q 6u"զq497K꒙e2`fw %Ael#r_#rE̦?EJBQD$Z5\ہ?1/#rE'oеHU8<+wy%D+t N$ZZ YYQDQ i&Z )t!(9Y cTT>gu'Erkg&dt`3ɔi:9$ י[.ɧWBGwHz}:[#r(5Hic#"kDɕ8-* Blۀ`"]JUrB>"w{*?5ݶ,R*2@\IwىK$×- +"ҍ $;еHvcBGI3~|&uzDDp3x] ̳\)`Wļ_Dh> 0t-isCW"+ CN뀿DDffF4 Ku9#g'Zu "f6ɤ D4@,2B!}wp" ]H|h8 ]@1DDZB!x$tP_B hVW_^]dF&A@D'? ]d.X ""݋.CSOGC!B@Dg: }Y 2tPٯC wo]D)H/̬86t:}kBJ=I ""}y>tJHdZ<)"@>uH*985 "ҷ- \^P9 Tx$)M@ZC4 ]#w)M3mEH7}M"A@Do{B l%t "ҷC ]'t "7M )')Try(7= @Df 8:tR1/t!iz4DDzefCA됊/ ]D(H"t-'i4 HW?O^ߝHWϏЅHW3B p I+Vfvpr:d>7t!i "af$sBv @D0u@YDE}_DD"]7B%: "U ,,t-RgЅdfDDChϪe T53]*p ]Hi@DhϢD " ^#:_,)Zfv\:$:bw_ T ]IJ_4 "U&/m ]H^h@Dj IDAT_?+V]W< T3;XԆEh5ёЅfD} Y_h@D%HV .$4 "ShOK3"R5PsB" \CwjhOY[5WfD*]V7{cBfDZ| iu'pR3C!=.䚙хegEpo V ixj-3, ]]H ٟ?Mv9 HN(Uе/3}eB$D$-pt 䎙 F9imSJ@Dr5ЅHtgvz]n0~ybC"}1ɓS ٠ "`f7BQC")<3 ]KrK xt7n~B$>He6(t!R,3 ,*po ]OH} }4gfD$j"[oC"P1C.7&t-Ub d_)?? ")f`F:>~ ]$OD$3Z ]Kν]fD$̬h/'35dsCsFЅHC9-_^; QT3 kɩЅHeh/_4 "ef#67C pD$D9`hZrDK ]S":$_$_ZIȑuDG.DC3"*fv pS:re@DR&PZr`y?I3;x .DKD$-~$h_IHpfv;udz/) .~HJqqw ?"t- 3w t!=J" ZGq~|7.DIH(. ]DmhRh@D* zkC"٧kDR%CO= P1`dZ2q^] "Ѻ߃h/6ErHHٙY p/0!t- w_/H%|#ZuE"3h ]K|h L@DJffnl-aw]BDzkD$fv#_LH07?m^>O.D@4 "E1!Cn5'i ffѽ>+C"NE 2t!")̾'k,m"R03{ߡh6(t!" ̮#tl!_B3"r@f6j߁>B#$铙]@еTD?r)镙MO5 =OfDGf6xJzЅng_ oBD*ED 3;h hN/F3"̎]K}5UP 7%p -GU64m6L$Xw[a$H::NH|:&,9t leﭪ_"H\VQ't|uIg5%y U^kib<8\:ӞcH 4!I^@wE]E|mFRlHB[Zg{e M@4mڪaf 5tW0Ə } 3H P~eƗԈF61iH*i<LUc%M 4$[7ǀO7WՓ#+il%yp𢑆<|GS H#I 2p |aW҂HsNhOϔ$@o|{#I@⻇HzlI]ڪ:wIZH=OnGUF1HkGw;G %IA@Ҁ\F%%yt+ |N&i FぢIENDB`swift-im-2.0+dev6/Swift/resources/logo/logo-chat-100.png0000644000175000017500000001336312227051774022620 0ustar kismithkismithPNG  IHDRdd] OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- _IDATx՜{tT !P%C@<Z(E8=֞CZKk|x(*(<  H6?ݻw7 =swv~}7sV2rs,eqP CO/)0@sKJ\V s(?H:"qcTLn&1$f>!s짬%?>2t4PM)XJ7FO@- %]~ē!LRJ.SN,2NPHni .I&P@&.f8&խ9RL[?TVQӲ@ܒR&њ8h#Yƌ f% Փ 갷,W3 tE&7 0G <ೖ7x7YtFQL{!w,Ul-Df1e}QUq'm0aBYhM*i'L5dƐ澳e2 iHЊFr\8kk";3I Lui̤ ť~,(Ԑzi(3?2lc{̤DfO&5I)L D&1yt]F_56$xedO}36e5|FmLġűPEiGa~>`"mFz?JiK'H=N7aL ?[Ebi )e $'EXK9qAd3Ky|F=I)DA/*Hퟛ|S.FkГ5TcMH\Q[-Ӧx6jaÎӈfF3I-b"gGZjQ(^ U=3\r^ @3pҨ)]W7O#ZN G4-Ê e V8q{s"Թja(.E9=VD {{ueЙEmZ^r'bJ ΝT9**ӒP;H%9;,A*]٬4Yͣi{tUx1ǀdhcj#6-91(eǰNv |^XWvX>I'^׈nu3=}3+vGF 4!HA;wןf], n~T+Y !ͧM8H2Fc!Ε:6P y H bT"E."50^@d:gb+SXh[p0uH$Rzs7Oe1e,m:F=>KµY`& A %8TNMrLM:-$` ) ^ 9C)v$H3ya\b[َidFgeR۔}B) frtAh46a͋#H$1 0c±*y!KРQI. #d:қ aX/T˪ L6O_ZOXroR`y\j/2tLj 2^_#evdRLJ|p"g-W)v)]?A('(N_T-j-SeI9Y:BkDIkZ\a;]QORCpZtg70-mH?~I6ݹ)V攑l55t@I9"M62)Y>Xk xШ=D!)$/`<4:p+ٌ Ck)3J*1nge*XҡvQ$2MQWI1q/K琑X6w$WqcFBċVa Y&0诎H! ^iG&qtd<)q5 ZVI6UуA P*mVRҔLR0 74ܢIï<-5FZƝ:6W?gD 2WM)b';(&(:;[Z5FcV^7ʖB6G3Zߜ Vr) ӽw12ϊfpge={3^ XAfkZdK"gp`B{Q2dSf(%5(`һi,%X#R}]]SѷH.%ȅ0~) ~i Pml[s NegZȗu 9,M=NF%XDb B,/z$*%=W3Wx&CidTg|6~n\K;M\GÕ) , 2C݇"u:yjNrv+z׸RXǤ ?hlqc"ȡ !0ʹoP ?lL#Ơ9?hW9!A"(fbN(C͒ۂˑ1neAucj&Lj@#9I̗fqCs0^y䦠f%ԳrIs(H1Ԩ^-R4~N2㑶]Kxv"mW]¡'KR}s}u8UzO,O¢~uUҠM,"Enުc[rw; ^LKIA"{ޡY|#'wǀjRq #)f̴"DhM&E3f$~^C[rbK:Ua$|Qؐة#N_jfK_As BJFcX&AϞ"pm~su6W,jտyOf~Z,ѼP}H\G2WaZV_J(fי,PߚBԱ cu^EJ&=[}]Oסs>=.뺙MJmo7ǀd*X7([~)<5: ܀q>K@WھKH߆)ԺV}&d,@U@/ @]C-`/1xkܶA$瀯!cјgm ;l@ }(.[C 5D` iQe`۞͔H qy V稱Ju^ =QMt+@[܁e-u}=m!- 'uj뒋ЪS Ep6ߗ F"Xlb$rc%~#`{_ ոVVG܄Ǚ ^kD~$`-%!$xiIhj4}=& -/!$p !әzu=CJO 5@ K-VA\G$xvG.ҼZp}q4ee$!xoNׇRꕮod:>!v6ʙ1ݪseR+9Pki|랲ݯZ+XrA-2h%]}?Gv6Ubh%iRӵ=6ԢK o%4Ӹgb @F6p -׭DPX ҵdf[k@+c95s%7WB/"T<%m xeM%7"VRȹ7!3]D^LkXekO#>̏`#5-6,ҼV[r-^t"m4KdPr z_ eF[dP4|) ޅtSV@%@[Z[[?jH@7;z H<ɠ[P TgO_ܗ$6C#,AV8cQJ9"k Ql{[.r$b5ࣈH`$%K$z_MD5@ x@ja@*Q=EsdAuڪK@&8͆FH+%h hpb1k ހMf O%߳j,>(4I`4dD`A.8K-Ժۈ%p7|V"_C9:}_>G3ix `Ͳ欁RPD!Xt뷅I;N5gUX׏So0R2#r1v:5w%JVmy\Їla@ jk@KzarW#Íyb00qMYKY)ȽBN<;L])tj_S$bԚ8c/6 `<-5J%Z}]- s ܘKe DmY[{WoB,;e'֍2 !ZDJr/;*e x3Z$Ӗ5Zrri2H`P s `CDl\G "RA&@oy.\C-"XD5ۙ&=X `İfv'@_[4% `랬Ab x=krʲ{*c `%6븭:FtD:4 lދ,G&>J ZߏR4 u N98Z* -kE.9­Y#[,T1Рe$ $[" %<hH@`,/wMD܄vm2KH>^:U3I_FkӶxAΑt zFG5`$} $=}᭱\ҞP ZA}o@fQOdZ j_NeIMl g紺WT[ȟ)##q"9+@o1s\X k!+ArFFؿRg>>$uދ;dd$}&V@)@˕Iba@5րG Qļ+i2l@wɑ@ Y[* bKHP2JyОXCk}Lu<HOj_#|>Ӟ+S enH[j/ZNDr =$f9{ܪLa ȕ -["Zѳ0J) b H.[ `=_sQae,B`u##3)45~}L\j,-PpHCrwm!z2#Jih3[IlL." L/z<|)nBHD\>^3ٌoo*Yl }=D^C˵{7.BqbБ~z|X8紮+lW^z 'C `å/܌V~̳{ Nj -H,5?7[Nߘeot2.#l 3+oT`n$;&ߒSL}hVH(Z"vka}ڿLr,|!H& ?ϴȢ =P$7ݙU|xP_.m5"v]uIMǷyC~ҩsDz!%h&- J*#k)-@ߧr $e{aȚL淬qa!H͓P-ry9VH KmlZ<\!BZg2ww<3]M0}". K6j݀:5><8+^PeOɯxۀ;Ƀ^f-* \Eٛ@)ukNFR2H`_\[ y@V+2WKwJKu?ڶ^~39WZ|+p]ŵrVsa|l\t \[j[ROD1gYi_-cڙ|~/P#Zs{y񸻑8@;A $ϩm] | f\8#r~eo:J_1\}Ǎ:yPP 9rE|v^k\Aا>ƤF}c:;,JD[}~ms}z]#ؠVݽH| !zȒiJ~MϾf:7Fu=kӿjA=(YܺkGɃJ^ٜfխX{uV@&\J逻q$Vz+/||Z hcn8ejLi/9-jӌE_DOOsHk\ݵ` `͠U*qx5p>l([\ZyD_Y"`> :KIK0'CЁ",}yi˫K}{}9S=_L OzLG%wOM `O%mHzm5ejk恿FJZ[1% g"`?,:/ }7)gu ZKXӼVٟkN;3 K/mZ߶n&Y$b5yK01cЧ鸭5n|轫xu}CWVhG?tQS"`_-?g[6O4W1y&(5i[Kg~Lsdu󘓍#%EӼOӵo!"_wטVyO{d9v<Oۻ*<Ug П@LC^ݼ.=Ml[MtBL0~DK-}5~x8d/M:d.yc==߻9 U]һAf9KMj}g5р hyL cz@KbQyz6"Pc^o)G|F`S۩(A jyL+tз_ d,?\GUo>( QZ @k8%2Y"x(lYs AV^D0W3KϻfַY  Y<]cAo@C,lפ{́I `ZK%}=ܢkAoi=xAsyC>`OH_b_~52Zݾ ۼ/?]紾W.o,m_9%8W)7j.歌6 i@oB4u5= 7Mri@ :`[3;Uא%UK5y 6zrylB}:/}- q>L["6+mmK7y`5Ƚ+Ayu1N6o%KX~Eǡe66`Gi[V1K{y-~_8p~2sI+mԚ%7un܁\ O ͯmoYBͺ hm:~D/ϵ Z@,+++^q9Wvjٝ=[էV-_ 4}lsd?G-0ג`.]ߒF5s [cM&{ݹ>< _7s-?1r\]Os޳}-` 2 jcr d\w^%-S-~BL^QCds[T!rC#m pNfv'ⶾU3Gv˪j~)} E{]p!̳KBdrEk=:d/IO]']ǁ/\5 K[u2-L 609 _uZK_*9kwFXu _bmR-fX^Y|ǁo{Pvi OӋj|bO^-ڶs`|ܵd0`$5]fϙJJi}߄XR}:Gld]kVm iKWg/^oZZ˴[XϽy̓;cU7/;~,^o{@5]L#W8WP7,~o5'URKz)8w Xb^ˀG*"M+Xk7 vVƇٗ3QlXǟKs~~{f~V]u@zE9E3N} y^vRx`/ IDAT{QrL= ;J@$/]NJC z~.jH 8o3ZVtne }MtƜZ!z'Y˓E[ֽôDZ\Ryzr_%r}W־+5&me6oUK`-9w@+,u@ }}ʓdf/"'2_^'!XuO܈huLX_BiEYK1o y_W#8=JݳK"XF\,k20-Z  !)rn>F&)SbG;]LE8TG"{i C-o}+e`I "!T=V%DЗ vDs1WSX`ZZriou ̴Xk5; L~k@KNz$,з6Zx!Ȼ7/oUR{NS+ T x(`Hͤ}_0Կy}oyPK:ja )_,W0%+{b]$>sρ4?$kT&nefEtB& K-?g{>a̋3Y*G5$hvU:uŲ癶cD7R-}ȑ&J}0w^3w]]6vӳ-$9 `x^EQ+R9ffsGVCԢ#Zio)hR4f78djn׍^:󲞛>ΏgArb}9MA8y ؑa `=T,m=Ǽ^ꍩO?hNT.!\܅A |]'MZ4^Ǧ#UxC &=Z?=~ ٭4Ӈ}҅8j!zIV#>#K{Dj92=̺fC%n,>{J2G3zIM2ۭ)uR.h̻̼Y<[4jj2&tM`YȻq D }޷I!nEQe4kS@@W#Q,o!u%>[ߞ=s@ZFv9 ݤSk"d5]L݂$4l+@cR5u9t0_Dn*,j>f[}A͢lYӞYL ﭏ=[yq&_lc?G$i> "y3sp-[׊6 nJI[;.ݗGֈIѵ̴MY$X:i ^v#Ik]Ki:{4 Fե2լ- 3P]YJjZeԖMHzf#q9hh֟V+v;z}O%u`߲"pA^K$ Qv#KcG,XmoI tk?2Բ]@ ߉ >si^BqȬuڪ%gry"+筣veIGױ^ 6Nwiz|,ҤbHzۈ%2u 1G3Y9^[ϝG_Z[ˉUrZ = 6Ŵv4瓚LjX%~gOX&zMZ[\?Z+>&=moECvǩ%e5]#]봵*["\R"ؖ}Z?o Zz#hi ˥Z%~ADך;9kYesuu~,H54h?znj, "x)iݼwYy<$ HN_u(X=Ɂm-9X\^j嫦z.^R YӈkD3[xod~5ԁ@PϤ%"H`˓[?W2 EȠdqyk\D#/g5_dIVBNKVs糤Ft K@I[\hVRY`uW/+,%`Ǥs |}1#cJ;޲rZ뼾f_ H!GU<阒>aqq<4 U[D_Z9)% Xi^_˒>B11]D̨,퐜W[ީ" ܻh!HR jM_rRY;G_c.{kݭ(ְ^dנO@Vm>=eրݳDsuf^HZS<+ xt*ҹkWYܹ9ԒG9E7ֵRk g 1QUHio],|u:V\^-Wa֯-b]7C 92H߳ta}޴?ܚWsJu? ֋uܾ\3 ˵&XjU3ށ$^ !e'zZ6Iʧna{G4EA_k\n🭬oL f}RkX 5/*Kֻ jaܧ-/Ⓔ۲EgܑL}3-_z?2!+ܓ[{A pG7J'Mh! 嬑O0ҟb jFj4zMD[{/i !X+H Ei4_nzKDZ{k"Yv.+ rRuN!zYX2SW/[ޓ ԐՄ@rKisY+G]Ԟvo9TZ־E}OJ  }ڴ/i@ˬnǢRc]怬}}LyF@T2TJ%YzQ zj JcǪ$UzȚsu*KÒ bEJ&eje)G t jIEC)Q^sugKf,$ˤҧOǎ_4Z#s2Li*m5`chU|s.@/#@z([`C!x,u+#/!ZČ/-;|nE&<7l?Oüuo^|ȟ~ xOa%5s[[Z[\̚V*ր(Cȟ\?Bd SBf1ɱoı$Ef !lt]w?l"2$@ur*g^2?ȁ#_28oߴ)wNyY"v?2YޏLgmPu6n~f&(Yр@ueg  wO$e^J@Y; Ndv?"gbJ{"\,{%*2^%2Рee2t]wL~2C3ǀ70O5TӾ1 ~uݫ{#B/M.uݵ=I ʕǭqRBH%Ƶ[!>t wMһ_BSElɿD]Ie^|̤~{H4Km]\-1s | q-k~zy.$C7/ LA!?gjWg_5xs_V) @_9 ??SU+sjo,p _u\h|TI&&|j?BlMdTzIu}+6 !| Ohzu-3 ^w!ɘ%j?}}-5, C^l|`v}";;񏗺v]N'+wIC]YF<5nkd"u]A_#$ͻiO>tx5__V24~N˧ktR,z[E& $ruʓ !FO]m!Oawgr;?2;8ʷȌGLO)\G9̟wsA}܋w_Frq]Ȥ+Gd u[Deo1|k?{_RV笅H1Aɞk.#6 xs^!p" !xL'P[I Ѧ^߄k<|/?Bކz=5[O"6is.=Cf,/&d]X8~G&W {^7.@_=p[H.Z``Q_De`1҃` B?4 ȟqeٟRWRri!DCe,Yj|O[f#P1) ];, .A?-4 Bx>?"^a-};`iC {cYPa%JŔ_.XCV/+6=Mꁟ2g'.REQBĒ7q[8]/׍}n?o[,!W[cQngv_䷀^- *qK`t)E!r组U^ w!/|Ryb|0W,~h`vt /!MDhKCBU Eu]):Ft]FDIk[&x->POᾹy->׳/w`7_;`bY>XYC09 gyqwjo|8Pwy=$k/z ޝc;j@bJ#c3aAq 5 N~n6 d?e}o&j e/ f?moy? nvm ?gd%QI족h7Cfz`'&u&߆+[&IwoiuEZH6W|q _`1oߟ@u'=dp 9j>z}>!]7?ZG*C| _2cZb:kݍ%H_E:Ԃߒ?Vu ی#??±8&]w|7 T4pɨZ,͉7'&G DrMC:=d[d ;1_!&r|{wwH%LqnHK4k#%?櫘kiHM|Gw6([~ڻ z ֌'Ц=Ȍ^L[L}mX܏KXHk7Kx^TNy- V/!M-B^eov#1]9mOD_GMҙ/#A2cʁ? K'\K8&>HqB>*r/ s~dT^ |2P;I*{lsj&>?$2kLiKH ߑE" Ga~x ́oW/!Lܱ%}~O[]X"- v~ ٟtF=bvȏ-zIuof !lSY|m~>@O\&um&Ǽq]j_2 ~__>n#GB@_<ߟKҟFf #e~Zx2n,EP`j <͟JD|ב0x.i~x;잖u]nM6|f֠9|.c/0sHE|A9Iywe_tºis,QFҼA?#MG}01ؗ-$0M6.׺5?懁o IDAT!~[%5['I/_YbE[XL~4~?[~M5>￁Yk2Zy!%o2?ƎA? r}2?`-]ם@r&J<5)+P3p<<_X4Y3!W{28qOԨ1$*Wgv8D߯;ug_{=Z@gxIU4~ߟyaB3;?~5C^X 7I:S{AVw뺿p1x;]|`z }~+!VYd?b22a%# P}%i>./!Cv?4ȗ&d$Im ~V/냌y3|tw8B'գX2jF `@~+ߊ{ְw'y5M~tWa0i-+ T^S_ HCߢG2$g\r$#d#l_F?4ys4?ƶU/3obozK۾N_><"n`OFVӁ> ?}b9dhx[_TFD `yaTW z\8p=F?  `ix@M?ZG `^~ZBe=0 Ysü߂%dOcwE!O02Hciyֲ, A52j˗^hiOk|[&1s~(#Y ڿd[@o>  SHc5}KOQHF[ؠV{dA[wE@?jHk%k})c-D_??@Fy$# i/ݿz"#zfQ\Fo#B9A~1]|.gL3F0l|ȓ@N a qd#I__2Qy.n$=$5=oE~5?e5ؚd>^  -zJcͼfl/q;5 q|]ٌ6W<$@[o?L `3{败vIۏ> =:b >.We$^^ɡdd$aQ^G] s@ ` BX .@iVÏwQRU]4IENDB`swift-im-2.0+dev6/Swift/resources/logo/logo-chat-16.png0000644000175000017500000000573412227051774022551 0ustar kismithkismithPNG  IHDR7 OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3-HIDAT(uO(q?ӚY$%aH)vv!ɑbV.ʲBG+6akk}x{~?yWd H\CkRJ1lL#M-;o ldMl2C Ӌ;oSAudL#pATuHAG)# ])êTur oPRS)yhEfݓ|9"Z3Ru`\aKE]xt$$!7bqD,@[tV\)lgM{ -mu[~ 1^s39)IENDB`swift-im-2.0+dev6/Swift/resources/logo/coming-soon.svg0000644000175000017500000001463412227051774022712 0ustar kismithkismith image/svg+xml Swift Coming soon ... swift-im-2.0+dev6/Swift/resources/logo/logo-icon.svg0000644000175000017500000001063412227051774022344 0ustar kismithkismith image/svg+xml swift-im-2.0+dev6/Swift/resources/logo/logo-icon-128.png0000644000175000017500000001037712227051774022645 0ustar kismithkismithPNG  IHDR>asBIT|d pHYs00kqtEXtSoftwarewww.inkscape.org<|IDATx}tT?Atp8AɂX".KeQ˺{]ֶn˺[Vk_<`-TqIłQ$ "I&oxfdܙwf2s?sg2>y>/wEUq>lSW3I~|gZ$mn3WIW:y. P T]#gp]dٮv2@_axL.,2y~`0+CoBFLap 05qL2ɓ'3~xhhhBK1*7U5nu g2Fш'ۀ}g֬Y1k,TfillN>r~ƈx[U{+U摱""1=ۀ c)++!"tww+N:ř3gt+xG?,ǀcNJYYcĈHAD]]]sa9r݃],#.a'+"2 #{/**b|͌5*`09x =ztg P<2ZVDdXLVPP… )//xPX`G رc $h6Ϩa7$Y#"<"ӛ%??nr.2["#m^A3>u""s+"~?˖-<"}رcG KZawJ 'Y-"r-OKKKꪫAb׮];w.V6U1%AN |7nF/_NQQQB"AUٿ?UUU[ A7T%uMN ,2A5f֬YɓU mmm޽Jx\U/}IH;_nr=0}tGD`Cmm-ܹ3Vh4c 9+"90 //+W2gEhiia۷/:+kU9ueYt) ,pE^=4cDj "/b^뮻9sk"|('0"RB2rH 7A8`?,*`$ţcǎum(EGUQ~ *{ @Dc/.."©SZml$K7.. @Dʁ_99rkFGO󴷷s`E9@A\\, SXX2+顽V9>GSDV` P[>"6mgϞ=455ik7?ԁYS=U," "1 ="(//gA .PWWg4xl^Dt,Hj@TwvDPQQa7K,zs9s&SNɓ477G VcуI9QȧMoDH.]ʆ #իWs!iNk &FLhY7^A}(>gϞX嘨cS|ݔ.Ƣ."R|t\_UQUBPb|B}<3k)TcV\]O('^C1y6GnL/Qa))nbD TbB5GC:xq H)Kv` 3.F Sk0q~W!Yʫ0/rlX,2y\49;}_-,kҚm/T 7O[1g c#+:G^i ,2a&oTWN%D$^1'\PnMUu$fq~Ŏ%ͳ:U{JR϶]>e+ M|9&G?`a"`S]N$N6D"ѧ%=Tp:ޓzk_1x9U2ÆU}<' OoWֽLlc,\z\\U*I$ifzeɏN hļwP?~:j?{ v`b<8 feըoG4Vy1s%Ƌ "35N_# ,$'x%ӊzfOΰ3n~q{pU¸f6=s|&b 'H9DN\iv~Zi_ ob9\W8Mif;pwp!PL̀C&=$znp"!HդFDslx Y t!iżBm)GMDd:·p67qq`or8 tGyv[<[J/)< B8>{π/fҪ+ 0p(^ P[H>}& ۬L4>x5@< |&4o:j(D I$ t새Twc^xPKj0"r# CyQ,`"ǭO;-Zoll 4440gW\vmkks䈪^%) cPyTDM^CC]nXkC8ٳkkkXkv~yկ$L1fyUlڴHFss8qB=S9y/WUu4g P*;w-[۶m3 .$ B(rZۡGrҥ y`0XyfrAH9pw9#"SJR "1/{@SSk׮M 1c ---22|P(T!U{rM!ʄT5yޞ~{E"HJqFVX1氈,NWn"pBZh4ʖ-[ٳg(1Zk.D"ie?~|C2 "r A"rYV쪪G"7 YfLHc.;FґY6VaҥDQxe+,jܹs`0hyZN:"ҫNWvyX,FwwwVțJ] U{leeeV'㿵߀߫)u("6ܴ͆ڵk puo``|xFU߹IVl}&fϱcؽ{u˗/<ϋdW?v2={L(t9GWWpX>Uh$\Wc+sb1(hF"bb4Eo> <FT`cPU<9G<ڄVի8>YD.3瀏8fp,堪qns ZTҴj T?M(%s랃 -h ,-]@KBX(Vҵ]7T-/% _g0b1Q|׌9>LM1*ɍ46UJK ^#QbcMvUuo "WwIv;[ '{fU=f"2h6g|ҧTIWY@w?Ow)򰟸 >~s݉ϥ\𫴉AUɇ3 h#&8"a.n~ROaS܈r!UDd6;:T7pt:7s)!(Ir2`B8pwer$=58qhExAU1C3T "e8pz=I)]r e/jts؁FOxx(S%P53w*5@D*f6> _PPHgϫMQ}(y)f rĵO%,܊b3L6>ߠفx(5@D᧼Mds(CU @c~> ~kס̌O.r(%2~uMppOI^F& ?4 OPtiCr`cUQQxIENDB`swift-im-2.0+dev6/Swift/resources/logo/dmg.png0000644000175000017500000011476612227051774021225 0ustar kismithkismithPNG  IHDR-e\sBIT|d pHYs88CtEXtSoftwarewww.inkscape.org< IDATxsy?k::Z|-9B&LäMHәNE'3}іraB4I )h|-t%KZEe[+A~>3 _ɒy߹eTg([3-ʖ eKy<@Rl)Pg([3-ʖ eKy<@Rl)Pg([3-ʖ eKy<@Rl)Pg([3-ʖ eKy<@Rl)Pg([3-ʖ eKy<@Rl)Pg([3-ʖ eKy<@Rl)Pg([3-ʖ eKy<@Rl)Pg([3-ʖ eKy<@Rl)Pg([3-ʖ eKy<@Rl)Pg([3-ʖ eKy<@٪,uS&HR111L&fK9J$H$ʨ 3A(< *NRG`SeʨJuOyNcll,FGGctt4&&&J *XM^YY555Q[[L&K@y9d21::z0J\,ll62̜3111<<+Ӝ,(Ӑ’fĉqD"1X[[L&#LFEEũN&H$ UP l6td2Hӧs򤓓djjj! kg7kjj1&eb8l6{$8qD;vlƃDGCCC8- gurpLLLs}uuu466FSSS455EMMMRPNرc144CCC166v{d444DCCCTUU!% JR188###g]H$%IScǎ@ 4:!8EyIcpp0N8quK,hkk"Kq8rH ummmA(;144tsjjj=.]/8rH>|C$9ꊘBy̍GYkkkhll,b2ȟ8x`>|8UUUjDQ)SL&ӾH$bҥrJ)hСCd]S__L&RP)CǏiH$lٲXbEԔ ^*t:}D"1D R,3ed||<=㧽WQQ|򨮮.A:(t:}}}iK4rZg@63W6iJbqȑiohh֨(r2 MyfKRqHRWSS]]]Zd0 Ů]ĉL&pEFyf;~xG6;8HĊ+bʕq0E6ľ}=ʩ%JBPY2L=z4FFFN{9V^,68ؽ{w=zjkkcҥL&K|RYd1111z"իWGGGGtرciShd,]Nyf91Mvڨ/Q2XFGGc֭g<Eb`` Nt6& (Ʈ]K.-A*Jyf8rH OVQQWe˖(,Ng:Ʃ.#H(&]Ӧ0),@t:blll D|$Xn]MJJJ\(,0'3SbݺuL&K O"kFGGǤg|Gyf9yTĤvH$%J孻;VZ54),l6:tZ#mժU]P)VիWONdJsQY >ccc\P:hRT:t(lRq63 #Gĉutt[%Τacccq%lg湁tQM0Z*:::&];qD9rD8y1444ZSSS]DI׆cppD|82L<3O D*::Hڵk'w,lt0UySRT;vlҵ;/J(tmxx8FGGK)G1/_^4@E[[ۤkfK|)ϔpzH$tt|t'Px3%db```ҵΨ+Q"Xcʕ F:.Q>~u2&9zbb"t -.3266lH$%L,TMMM^>?32t1L)LL!zLM(5zzzbŊyfqر׿^zi\y{6/عs9 ظqcC={vyv{'xg`C:)JeΝK/ū9[[[qW\sʲgϞؿDDtuuY,>T*߼ysl߾=?ʿ~:{Hy}n:'|28'*gaK$QQQq`)L&/B<72::7o͛7øK⪫Y}gϞYgao9.L*Gy$^z饂ybƍQWWWнXX)hꫳ۶m8nYJ/~E/μ͛__n Ν;+A2l6cccw~i/D"n馛&gjii[o5.Yg/~Ly`޽=FFFY sR&>^|Ÿ bڵQ__wgy*HDUUU|ouuu'q%9D"O4ؿ9lG?n-{3S;tPqĉkii---555J188{쉭[Ǝ;r.դRx_>hll#G|_}}}p<ÑfO{=ڜW]]7nќlٲ%8˗/^`z3PB###TYn]\{D5Z[[;暘^z)~G___>8͊+r.TVVFGGGޣ>:f6l6l/+@[ E6o}[3.G?Ѹϟ8s&qW__ƍs?d2Y4gϞx饗}z5M]zW͛7z_t3P"?۷o+V9H$ /;6l0 ,o}[*{(m2̅^fd2nY{Y NyJ`߾}ӟtFk.]~{477md2{⮻e˖QWW|;g~ٲeq饗0Q^}عsߟt.Xzu~Gy,wH\wqG,Y YVXW"{hii9纊?< ݝ}>>(g}68pud26n8\>/a᪩O}SQWWwu7xc)ݻ7w+**=/{uuu5(gy&ιnUDӫO|QQDϫz5uuuy߷.>O4w;+hA6~Fk6UXVX]w]c@Qضm9d>pׯ_6l8BDEMy`׮]qȑsknn.mÆ YP4;wdSg`v!rlڴiF.ys\R2}C}Q(l2u}39r 8 L?? $7k֬իWǮ]J n֭3ZW3'UUUŚ5kf~hh([Dx͏*,bcǎh|+DD\N3Z[UUU4sd̞ 믿>udɒݺubʕuСH3ZL& &wlS۽{5668,u(x|+ ǶmJ,(h]CCCޥ^3ӈw /⌧SGh|.ƅ^XP0}}}3^;ߩ? IDAT&lڴ`AS:|pdo,+J kj___۷1`AS&uEUUUc@A x|l :,x3P@3^{СHRL3w3,Fg惉^((@Rd2+VD}}}c@^?~bux#N: ,j3P`]]].DDٹ4v[!Yo~({\2ٶm[|k_+Jx?{̹f3^Աc'?Ilڴ鬿UUUso`` >9g}oxx8طo_SM%ݟ۴iө_EEE-o)qM 9;wyyJ*gOO<8p %/@1g .]vYw{7>OEsss^ f׿u'?RYllڴ)~x@1yK/}<_җy{f)ҥK G"Xn9677_L& +"*K.;ohmmD"1Lm۶{GQWw8%b Accc\tE6o{7nXfMޞ[LVۿݻwǓO>jޞdɒ馛bQSS3{[nL&{'x"6oޜ\D".x;Wʹl{_;.驧'|22Fuuu]:ZZZr__y>;v:5(k&噈'NЇ|g^]L]]]cǎxcpppN|3Q__?gqرcG<1666\---q-ĪU믿>ַ9=O}*3ӟ4{d2 PR3P$]]]ws3LGxb鉻;կF__߬s*Lkƍ}m%d2wqG:5>?|uK,Oӱ|?{bbt-H<':iAS}Q`s= ( 67<駟'? ٣ӟt˿K |uuu\|y^{m<䓳*ZqK.$yHӾH$[nqq&Ƌ/?c`` Q󦹹9!{{xꩧf1hg|fF?­nݺXfM}{b޽ۣguoKKK&gY{W9͹UVVF{{߿+c3~??-̆ M7TcQ_rlڴ`{å^:cg9dʸkguoKKK̉'^WW7xcNʥhP(@X"111k֬ڜ9rHҜݻ4_|q,]`7(뮻E}җG-ڞslٲ]v(TTTڵksgϞ=Jsf}jkk /,===}>@>)@}o߾{W^)ڞsu%䴾噈 . ۷o/P3n룲`gĒdz9OWxL&S}g/i|,ϼkf tGӮ{6 @(h=H$fgzzzj###L}FGGO^LDDuuu jkko/{?q}䑋XjՌ;v, (2S&)D;{<,30OUVV7W_}uޱcG{qѢ}6NN)ףcڵQQQgByD"'r:(:_cϞ=Elz{{sZ_L^{fJ122{=u t*++l)p5ĭZiկ~5^~{&]]]Q[[;;vt:]D<Ǐ7x`ym6m9' 9sL&l)~˿hii)꾩T*zo~S}SQQk֬T*v*`ߙOG7Mhjj*؞Q X@/_wuWtuuuL&w׿uQ|<3[l)Pb<,3,Y$θꪫo6Gy$6mT}u֭[ r$gϞ{i׋}dSDDeee X*++#H|#)ꄏl6^cΥ5g7(HQ夑ضm[Ndق|{fuuutww}sq40 *>FSSSd2CE___*I*l6rK.^yH9WtG6YJg< yw}wmϱxĉEz{{sZ?]$_oVkfcbb"vqRH$J/@g`XdIqwh{ǣ>Zi֭[ c6G64<<{[;vD*:z3 ,~0>ODuuuQ|_.^oV]]3^G{tR>nnN{{{m\<,3\veq]wEGGGQ###ErRf{dI[lS鿾RNQ X:::⮻.{ O >3_=ZbE466xm"mW^y555źufl68=2?9?`SE~wqP=&&& 3ehݻ7o{Owd%\Sy&"?G7MW鉪9?`S2bʕ_=::LG6]z9g^{~zڵ`tm׭+aFA4!@%Kri`3/L 1$&: D \1dlkϮkv^h9=#1q5:Z,be@~3xg;,\[n-ɻ=***>_I_ .,xÇŋ}.ȏxR]]?x<裙|mݺu\TRJn9x`\|y{McYÕg(jllKNg A<@<GwwwQ9,HݻwJwMcnD[g'DuvvO~X`AQ;ѭ,ޞTW7Mi޼y|---Q]];>}}}g\<? $ogy&,YRw~'122RMfʕQSSnbMsּ̉9::ZP駟 y*++-R%566ƏXlYQ7000]?~<Ν;Ww峲iL_ Y4ssss u ͛O?t466}S% oM qm;ёݻw&l3@DD̟??zꩨ_5)4geӘEeZS>d#;uT=ʤ.]*DYhQ,^8{LߑeeӘRn֙X|yHxJ_{.~d6ʡ3)cdd$>\%T닣GfzMcn޽{gWO<dxx8Ξ=[1aÆhll;N8Qiޞ|M;wٺu뢲uժUQ]]w9r$&=722~ϭlN<hBUWWdž 3giFUUU'z'Un)"bΜ9jժgtttuL_v|5UTTd:ydG[WWWӧ8r\477}~߾}1<<ىV66|V7MجX"2}V)x ~+ܼ244D+niLGGG3E|~#ΗC|M4mիcٲe=;88Xi&5 guV6uuu/ŋ=ӥKŋȑ#>F<!{キ熆 W6Z*z̍V7޽;FGG.nL30Ν;/^,~QQQ˗/`ȴwҿ|:;;Z4&03_V_#30>\27o^477g~nddL.K<3::zgo4ӪUbΜ9y .L+ 'ivСrY[[[gJ03rxfMuuuښiLkFGGgG0N<l<1%dr |Ot˘blSM5˗/yeωg`x[nL♈lΝ;'NyV6t~n(q Ԉg`;w.Ο?_12r`L̝;DL.kP2l*dUDĒ%KbѢEy\P߿9 Ԉg n*h~DD466h577G.D=iLgi޽1<<|kjjy@YLtLW[[|9㙪XjU{-3^4&-1_g& |Z[[jJ3N<egϞ-d:uSe UW=ʦsFkkfjmm9s}ꊯq? `3P}}}qrɗWMf%$?fl-/Ғ陞8{l8qbϲ`< \2lEEE%K… >?JT+iMMMMe]6nr@!,ŋ#˕pd U<l̔={Į]}neS/&###f ɩSb#oYntd%0rJ|%[4)Ӎ8'\ S***>;44TIxh>388}}}yzJFeeZtǎ%]4&ϗoFUVsey\L?%gfѥ\?nTVVmTʕ+>s W6us)4̖x&Gy= @ ?r1>,---Q[[[ijvڢl:wΆM%H=fy&"ŋ%gy睸pBǸ{}.$M֘blrqmlgzR5CCCCφg"bsf PΜ9lMMMtuuxln喨/ٚlQs ."OS|)t%r\3yxW^t߾;;;3?xfؼya˖-yG֭%Kxz+d aDץK2/g,xṟM!jjj91S1?>I'O,47vvBSNK/t _غu53[A1c f~;N<7niޞx>GCCC '*樮k=tl{3*'Nd~\L>_688gϞ `rgϞΝ;/}XbEDD޽{7ވSN]s~QQQQa o'i^x!:f͚X~}*9sĪU{ҳӱiLGGG^R{{4LSL!T _гeR{M6E]]]ę3g&<0X3܉'瞋z+FFFJ=ݙ™yc=VyJ!U4ӵi̲ebMkt>;v,Ν;Wi>/mۊY&a7YSYY9knٽ{w?~?K,qM7`2]30:;;>[__>`?'|rZo ?~… c~rELbŊ(ʦ1{]ˣaÆذaCǸ'x#Lxꩧb׮]-;ל|+~4)LF3_wGc׮]S ijkkcqwڵkvrعsgD|~{Lڢ2\45kĚ5kbpp09GsƢEbe]K3xI. ]]]]1<<q8vX;v,b`` ._QYY555btXtiX"V\Ek6;c`` ""2OX~}8 gp8S3N<Z,X,|`SgxVE|5ʲ{ g8{>|@.|>rY p1p8=@3\(b6EYuO> es3B<y}}z] px<=p.tlV ‰gX6\0 %6M3 %VYU1t:%X4xZy^99NG IDATh$jnc<W^5"I3ԮG߯|9mE@Sg8 8veYl6(>: g!˲L&eY;1>: gh4|gZ,>: ge0U;߾}n`g8;yGݮt,˘fQEUG$dYyWs81"pnFweH<*yzz~`шg8kyGU1VY1͢,DB<YkZ1L*`g8{n7a;>pH3\dVGZEQl6,.xj"wv]|-"҉g(^/a;߿j`p3\hn򝇇8 J<E<,t(fQeU=njzө9.vyҝv˗DK" 6Y0Cc]]]`0fSk 6l6(FQt:F4x.xlV 8 Yd2,*9N~3p~ ;v;q;&^^^,.x.xnW 8;~?~;ϱn,.x.~{xvlN0á TeIoM&7c<<<$Z3 ɲ,ɛb~h4|g^kE9PN&wA\]]U."\gEe1~jSef("*܈gx<~7D8g2Ts81"gxw~?N62yE.,L&}`^򝧧 ދ ,b4=jv#˲ZEQ,ׯ3Z-x"˲ uπZZL&ZpNu'24BMb""ghpn򝧧8 u($ZjŊ_FYVu(V+<|gǷo,$qz^ w*".i4Eۭ|!cE@34d2,*("fYeh34VՊ<+n`34U w^__c^'X' 7T1"཈g "&IdYVtD hۑy;&,ރx\fIxkxv򝇇8N oI<eYL&ȲҝVoE<x<|g^kE[quuUsl ?yJ7ʲlEQ$Z$?eYL&wc<<<$X&tb<WZ )go |۷o,R" <ϣժh,˘fQeUOjZy;>,~x~A׋pX|>r`+3FQtwp8$X, T0Lժ(fQeU@V+<|gsE@E^/A;Z,~xt*y||`#3d2,*8N1-xiۑy;6,x~_Kl6"qt:wfYN?#IJ,dYUs:b6%Z vf/// D<oG߯|%mEn+(2fYEh/xCYd2,*9h/xcN'FQ;*`/x *[v <ϣnWQef("*h6 ,"wC<>>&XguF,˘ @g z<==~OK<5Z-*];l6+Kz h>}J+2y;N'iEg-8NndYwwwjy)MvnWM9\.+q}}`3ʲ|eYVnc:&Z; rө򝻻h D<vn|9AE@B)E; nnn,x)2yeYNݎthW3jXt:N`w3nfSu !NS,w&"GgbeYVj.,K `Zp|~n`33}w&I%_PE,wz^&X |EQTj>,K YI:C;v,~x~pjUNyE@AEQ|>|mE@UA"t#˲Vˣ98~ClbW˗z )goX.FSE@* eY|>|t:MHI<aXtt#˲V887~[?ncUsss~?" 5 p oA<,˘Qe;N'iU[Y.q:*ߙNn,ފx~gv|1 ,ޒx`777 oM<Qe(˲ҝv4*୉g "VUwit: v]l6wc8&X v:bXTsuu777 I<@-(˲ҍVwwweYU{X*C;[t&}w&I:gh(bXT6".g>GQnZ, Quwnoo&XI<@cXVyy'XM<@#En @<@#,(ҍ,>Z-=xEQk3h,ˢDۍ^~|˗/ގxp8=Wc<Ge}c,wFQ|)"x,jkPeY|>|t:M87x(MbTFeqwwGiZE%ncUsss~?"gMX. øN8Wj,2y叄t:1NΕx.Ly>(t:%\.ܜNn,Ιmpaa ovϟ?`0H8w0v;~3NX. I@>SY1+Ŧnt:M @yGө{jtg F<?ObTs}}N@Pv;?#>}TtbQU$X\ eYƧOb~(EE$bY"ʲtj]dYhpI3tvuϠû3*C;h0^+ߙL&1,.x#z^&X\2 e>GQnZ,.x^p8Ts{{n7"҉g!VU;yG xWEwn&X|b(*Ȳ,H <)m6|%z^EG"lX.FSEG#,eN4"#pENJ7,h<fݮ򝛛 xr<cZU3:"#p6ʲ|eYVtb:&Z|drTt:v`щg8 6m;?``jw:b\V3 &"):uʲbeYVnc:&Z47Pjt~xZ7c8&X4xvuu777u.xj.,{ p3\~-n3 &"M&u.x ࢴZ,|.mtݺgxyy^ pn=`3,>Z-@g8 nnn4xڵNuH<@en7/t:Nph4px į3~?nnn` DnjZqwwY*& `v#:;sNLݎ///gY?ko΂xnnnb4pEQDYO}[&UxXK<@cgh, aΚʮ yh<`}rsP0$ v۲-ے,+\ =!ZSE(V I77igHK<@Zx3% - igHK<@Zx3% - igHK<@Zx3% - igHK<@Zx3% - igHK<@Zx3% - igHK<@Zx3% - igHK<@Zx3% - igHK<@Zx3% - igHK<@Zx3% - igHK<@Zx3% - igHK<@Zx3% - igHK<@Zx3% - igHK<@Zx3% - igHK<@Zx3% - igHK<@Zx3% - igHK<@Zx3% - igHK<@Zx3% - igHK<@Zx3% - igHK<@ZO/CIDATx3% - igHK<@Zx3% - igHK<@Zx3% - igHK<@Zx3% - igHK<@Zx3% - igHK<@Zx3% - igHK<@Zx3% - igHK<@Zx3% - igHK<@Zx3% - igHK<@Zx3% - igHK<@Zx3% - igHK<@Zx3% - igHK<@Zx3% - igHK<@Zx3% - igHK<@Zx3% - igHK<@Zx3% - igHK<@Zx3% - igHK<@Zx3% - igHK<@Zx3% - igHK<@Zx3%Y, ZϬXf&6MY1o˝WpŊXXgUY3+bor|xfŜ<gV+boJ *gxfŜ<gV+bN3'Y1 .NY=5UEfCX3+&E>h 1ov;zm̚QE!OQ˧zդqeY4C|8+C♊,?i 8>>^ݭh|3iZ?,-Wdfm5^oFK,moo/g*n߬˲7oT7p'޽{fl6ѣGNxx,>}T4   {QEE%V?hTD\]]ׯ~t:&MFxx^,+ r{ͭc;^h*~SٳgN ŋfMe^Åo>xk 0t:h2c6ŋ/b0,73xrS]F=...* 6x<1v<{UMHDz!>1jZ<<h2xuMS^t:M*g6|>`>&I{.:NE^`2VI}>|uLDNlmmU0 p4b:zn_ݭ`2a}6.//o=Vc?v.ʲ8??;;;snҚNݻ8>>~?vww3L& 1Ln=Vӧ?Fժ`:{,Ǐ_lnt: .g:??Ә+"7r*"V1&I|1~嗯:bww7OG3M&899x<~8={=`uFQQS W4$!""b87nc?|QpM&8>>ZVlooG۽/3,L&_҈b??~F㎦_7b0q _}nӉ-H$'᫦i|9...~yEQNlooB84NOOc>nlmmEݾ 3F17jѣGL&qvv0a=z=~h6w0%xm<h4߬hѣvtn~EixʲxWWWquuqvvWWWE^/T& XYqyy Ev{!i4Qף^GVz. eYl6|֯/̗_edzv~?z^j5_I3||WWW7t:]Q/3 Ph477+3\Vj6-u}}]HlFpXѨz6K^^^/"?L&1Nl6xRZf3V܁nbztv |!7s}矹(("j76XVU=2 gDVVVQ$Z% - igHK<@Zx3% - igHK<׮ؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒgؒg =dxIENDB`swift-im-2.0+dev6/Swift/resources/logo/dmg.svg0000644000175000017500000001136112227051774021223 0ustar kismithkismith image/svg+xml Swift swift-im-2.0+dev6/Swift/resources/logo/logo.svg0000644000175000017500000000535212227051774021417 0ustar kismithkismith image/svg+xml swift-im-2.0+dev6/Swift/resources/logo/logo-icon-22.png0000644000175000017500000000132412227051774022546 0ustar kismithkismithPNG  IHDRĴl;sBIT|d pHYsaa0UtEXtSoftwarewww.inkscape.org<QIDAT8?ha{Mry- +%-Тn "889E'@Qn5PISE]lb<M56z<>N=XetZMZm^DT+ ZXP(,ŹBq"Rn]o4 ];L۶++++stjRv +?F~5~i;L޲mYװRghh~5N$ 333E~_GЛ$}{D/9A6 &"< so|~IENDB`swift-im-2.0+dev6/Swift/resources/logo/logo-icon-16.png0000644000175000017500000000072612227051774022556 0ustar kismithkismithPNG  IHDRasRGBbKGD pHYsTtIME.BVIDAT8˝KA{I !"F@jZZ5hqhjAZ\ pUh)+}AG+ oqp}>|d(K)|~ٶBaalJb8B)ul6OhIӵJfn+zja0 ZC)pB`8}RR)$Z뷱 |f xZ-^!RʜeYm߅PXD"A&wn?P})l}[A p_; ͑.pID̑Q@ZTy? E}X*)`~L֊NIENDB`swift-im-2.0+dev6/Swift/resources/logo/logo-shaded-text.svg0000644000175000017500000001347612227051774023635 0ustar kismithkismith image/svg+xml Swift swift-im-2.0+dev6/Swift/resources/logo/logo-original.svg0000644000175000017500000000534712227051774023225 0ustar kismithkismith image/svg+xml swift-im-2.0+dev6/Swift/resources/swift.desktop0000644000175000017500000000262212227051774021522 0ustar kismithkismith[Desktop Entry] # Desktop Entry specification version Version=1.0 Type=Application Name=Swift GenericName=Jabber/XMPP Messenger Comment=Communicate over the Jabber/XMPP network Icon=swift Exec=swift-im Terminal=false Categories=Network;InstantMessaging;Qt; GenericName[ca]=Missatger Jabber/XMPP Comment[ca]=Comunica't per la xarxa Jabber/XMPP GenericName[de]=Jabber/XMPP Messenger Comment[de]=Kommuniziere über das Jabber/XMPP Netzwerk GenericName[es]=Cliente de mensajería Jabber/XMPP Comment[es]=Comunícate por la red Jabber/XMPP GenericName[fr]=Client de messagerie instantanée Jabber/XMPP Comment[fr]=Communiquer sur le réseau Jabber/XMPP GenericName[gl]=Cliente de mensaxería Jabber/XMPP Comment[gl]=Comunícate empregando a rede Jabber/XMPP GenericName[he]=מסנג׳ר Jabber/XMPP Comment[he]=תקשורת ברשת העבודה Jabber/XMPP GenericName[hu]=Jabber/XMPP Azonnali üzenetküldő Comment[hu]=Beszélgetés Jabber/XMPP protokollon keresztül GenericName[nl]=Jabber/XMPP Boodschapper Comment[nl]=Communiceer over het Jabber/XMPP netwerk GenericName[pl]=Komunikator Jabber/XMPP Comment[pl]=Rozmawiaj poprzez sieć Jabber/XMPP GenericName[ru]=Jabber-клиент Comment[ru]=Общение в сети Jabber GenericName[sv]=Jabber/XMPP Messenger Comment[sv]=Kommunicera över Jabber/XMPP nätverket GenericName[sk]=Jabber/XMPP Komunikátor Comment[sk]=Rozprávajte sa cez sieť Jabber/XMPP swift-im-2.0+dev6/Swift/resources/themes/0000755000175000017500000000000012227051774020256 5ustar kismithkismithswift-im-2.0+dev6/Swift/resources/themes/Default/0000755000175000017500000000000012227051774021642 5ustar kismithkismithswift-im-2.0+dev6/Swift/resources/themes/Default/incoming_icon.png0000755000175000017500000005613312227051774025176 0ustar kismithkismithPNG  IHDR>a IDATxy%YvKD53_keKW=3== .2aE%[4$CK%&%C a C([dSfgꮭkɪܷ{#^ -O g!mF"^|/YAC)(|)@Ȋ/c>P@RS@hu騱0rBq9w9 B9gBC2wxWqܵͭ]\_`3?O}5j.8iZy9y! !0eTJ~\@8 *ZEck\< a.=KK+7?৕vK:֝#CwXށ1kmZBqSUժXk0ƐeYCJ! ,wr?vᏅ+bV_7흕BUu!%79BBJBEDI3-Бfpt&+_#c* "aQJpѽ[{7_ى^g6n8"Qc-8j3 u{lݻxͭM crl̹sI}G*ZT5/ѿ ry9)NsNg%S fgg9uz'.ٓϒ$,b:4Uynhw #!kϹP : N pa8jjՄfNf]vw=|=dØksW6M}Ϻ^'A^7$L%sEkxg9:lpcIffX][uNZcvnjo<RXժL(vC,+K?J?^D0_9#1U*1qΡ$2vyW3J sHI}9~ڹ_o~̩'뵉 EgAIs.]:Ͻ+OOr:Ri2J3f;Gz}srB^L4vdYs8[:ujVynZZEqn$N>hLq{j?0tS2'2)B-{̴OLĕK\|,}:?AJSJVh4Z`|:J)>Zao9}#/WN~~jr?(NJ)Rh&X[[ciy-{9wJi~xc8f[GG]|pXB#y/o <u`bA7`8l@H)Ȝ # J)(5@pxxL83bRBaйsX]9-թIͱBkzi^ؓMW,Nȼ : ˳dғ{s<Bw*"1ZB{ 6q-yQHIXν-لfy^Lny0):r,YsC:X31D)EX޷p!OYkw<,-ZQ嗝 $J%fBq2 '9~H)%BY=Hh&8sj׮m/J:TƸ%5ofyd,/aek{?ӭ!)]IH⫅.ĉEfg&iܾNGy PH`sPPQp-z;wʟ_sגB#$iM :8XX?2DWb@H?U4@Te5C4*&3&ObuucFY@H ǑMXs*Iq&1Yr!Ry@ qP8h#DqШW= ܁ĘrAcrn4CW9*Kĕ *x䄯dneĺsLkZ]pՉ/[EY;w,/5,aCF1Fn]CE!y/_:IQecsz-y f<'$w|CGzࣃEHd[p (<^0Шȍ"Tdi`|΁!<<0y!L^P'1*J`"§IXvv|dq'~Y轛uyf5x(kZt%@*(7jcZy~pƢd҅uf>sc6~EFHb@Hp!lK)" C277REf9:QGl4*cW4rǙS+c S@dIS=[e{k_Z/N4gޓ^;^uKMNL/ NE0֊Vk0k 0J5Qf82GYF:JsKn<k%΍/Fٺ˲ `~vx嵫di0Mc)>e4̐Jb T5=pur J'qF#Wzaÿ22'U-?($i\y4Z$gReD nm}7x)TAakqN"e(ͬ)Cofr%3"`,ڷGiqy Cpi~Ô޹IQ٨XCj@HA)&5ťy>h@Ћ+I?>uo9doKW/IRHRMME1u`J 8G]_;4$rYYewͻxEnB 5΢\)$X+L%q`|\8| $R 4/;80qQ2lⳗPZ7owafO,w7Yp'/f{wݝC4e`eiFiwi{}R({4 fc/,̝{ob/3Lkǯ*IJ ɉF Kx.<Ҳ*EVsWZKeYRkC&|/뜏BPJӄhp+cpg:I":k+_6{}a*,Y58fthH`ܩg|_N;~~A|UBdD^ZK6ᯂ3JSԧ|U*<96sQ!<\6w,i"G_1dq ŏ#tXN0e ˴;} O95FFC3M},j@ :78D0"ʕANT릙LN4xϭ;$A6n 1N_.A$*OJJ2c=$yTH%h֘ZIࠍO\9q|cO`O-!S%tܠgx~(Qek޸Ӌ$qD+~ILd#³ݭS[ݍk(=m_UD"YvzQ2BK^ >qů-`E&EATJH=y+%9j8R&:5Jʠ <ʡnL+m!mV|6D:B*AVaqaif'xt{*ZKm滷? '(:;;hN.3y]ʻ *M..qI~Ko " PJ#B*y6ᱏVR%:Eqbկ^%>3Dq̻W? ,XX19Ygn~Ņi֖z%FZ)D!ʅPz8B)0]+ O)֓ASSufpvsW"sRXkCnzic#<9dr/ R̓gO>ū]㵷RCp,$]s,-N{,T@\kHօ/ws4/XY,WrZ`SOeqq0J+tnq /2W.SUjКjrxkw}{+8y'/vׯq6vopkV|kW9n2Rx3| \W_#G T';P"bY(<=]:>Cc(YQ2Z RvƷsV  0;m>ۿUA"%/Rn~ȇyOSxac~͹?BG#ZLN8}bwGLYoxd"Mn R(ln p+%A8秹t~$(ܽ]vW$9^&mF,P9"'KWq!_{dT,LJۀɅ#jEjRG,UPj :D#J*y֫)i!)LM%O?}haq)MܥffHcҗw?u|84u8"/d\gU IDAT*I/@¬opk JkVđfj -&VbRY(6>dNwP_FSE* O#4a:V25ӟy3(TѨK9 ByHu8:σAh _T<h `sϟ⃣Ǧ@krn]y_w =S߄*( 8>^ڍ ~O'0j_Q6'yWIsVaOd;Ԋv}?/07?׫ /s |+|wCϸr:uF)m:L7=MG[x߸O7 BM&k#F}Hyٞ}T%-(~PK YX5, EkK Ө)Ynx|߹;>dނCuـ043 ĸ)^Rr0Ѭ덐*HBd>6#R k]EH1'P[EH!")YN 쥿M>y |%)-:QxMD7dͩ $ȢK_p|ڍ{\GPRROyHOszRSS]Y#O0˔&$VXlXZ$O?qNw߿=2Z[旔^瀉yzk JIlP ш96Ag%u^P^cQ9R(/ qL`"Y[I>~˗Os6 d^5Zܾ#kq Fܼ pal[8@Hk#o} =FiJK_GK/STR_RzOב5tɋϜ^z"z㋯qU*_!5+ OJcoDie PFiN~b3Bxٸ((gH!ogyI!?WIG.0ґoCKZyVVH16;WJ4)@iI%IX+xv9.Yc?fy5E 0t)!hP(JF2?\\j7R<fZ̦!~oq1jr=yfV(RD:bvFRqBRRT~^'`XgV+>[ j [GT!2(lem׿ew[!IʏUH+ /rwxI~䇞ejAGJ?[_̰wnsRH!4+5.U[kEE(!a]^?'/d傡"#ZSMi4^R ZN7Vb(*锡|1ϳRE+/-8~ɅuT* Jq -u ' Q#\VXB9H|)KNo|sm~뷿A[ď@B0J3Hx[lnwDu9[IP!R+wqqF[ D R%AE,Ðʣ!qD%#_g_ZFg~ΠG%HAOnckg&XÄhr{rU)o [lܼ>/b{.bd0H;9<iEE>KEUy*Z[.R "BHE_ц =&"ѥ!QTL]BGȿ //F TkA$ C4(%}4tbjpF!U}L`q RdRiQP/Πmp/*W.h6HIgVxºuz},^OeOˡJ ) {ʇFDZ\aRrhY&L:\}1YFnQѴF`J"O `V)9Y6R#{|*bZѷ"z06([ \N ‰ !gߚVPS\t9QS5nd?,O}[w79 '5٠2٬3JS:'p*{ 5Bb$Q$Hm"ȸ ~ ($N Cf(HkG?G?S;!|j} 6Q/* !*Ά4PQtEj;-YB0= -eJ*R }i)$yFQF8di1NHcq~}̶*%U~$@cVKHM!{q"QyqRX.867?/qY橋'94:|fW_!Akh/)Q|@wٕ8cRE Zcaffd7޹Ef&p.x"g R:Z%js1p.HR#T̆( F)Hp _)-YtUi*Ia걊ǥ.x2.ZGk4aH?BIKwiMԹpUc5>As2G~mVDH9 DY"b ok8ki֫,NtDu;8/]kEƔXT~oquH>BrnQkOl9PR1<vxC`V!`~a|z.c!Fօ$(D= QJ n/R;QNfln=*iYfDFi$F*)g帙'XZ.Aιrk5.?QsG~]A"XQa彌FƩήrw!H ط6*›Wz\~ #UPjLn<@R~$ 3rBF~r=Q()kszVY"3,ln AtTyV6R ]s*ۿEVWfJtU}Vg9<eދ #(TBy%"aL0Q}T8$IDa]E*lbαe&5޿~IBd T!܃P2,MpB(bGOɸE|4R/_ :RXce)y'| ̴|xoc+7'G2Ghd%xZXJ@8T@HB9#KURܸ҇?_區;U[Mw([ 7et;}?crdF^%I"ZMf&=ʹQe*87d$Tz-9,aʃCB&S)WgBZ> ̷3)O28zy Cg7 -I JA^˧iMrbQc8LRnx{7I B{G\̻.+ibe V y5>jJ/(]&Ooq`Dgan#q#A^gzG(զRiM4bNkV)q`nvfB} !ˋtzCro (u:xfuGNwX,ϑ19mR<+7jB-Q\8B+>\'K3޿q͝Cڝ>>lomR Z>v<[os؝rZJO*Y`FPr7b( u^NFvoRY#9=n5?H+q ܸr6w"`t*D^prm@GhHI4##ڝ_ e.`G'(jY>?{ȝ;~4.h|c j$| $qTasȝn탈pxy ո!8@ ?|$yoH~!ƋX|GQ A X滇&ޏ3!>1䉔vwpqIR"ɋ3>s G9iG)phn0g;.VYY$O9(42eL+H,$^_́3)z"g*S,s8BIEY  , r&>YQDQc0!&LGW'/!2 XMbZRp4 1sEp Z[l{wXklej6.8zvF0 ȅcF}K{vځ ᕫ8A B;gTeŰD؋0O3j9qyu('4`(G(:DXX]i,l4T.tsg]gȗw^W}LZ>ERLRۦ}^do(yL H345굄XìRGRѣ"@A$_DZ1Lszvwd 9Y=_3"B V8􇩯̓ZńeI Ϯ4 3 GbpiV\h禘tk{8x>vIoVIb{VpB`>=ʲWP)酝Ys f:ahؠ=$ 3;ڝ!=]uN(mQhWˈۻL4LM4eY"jYi5r$ˋkj`{wOqw`Ww_$u%*kg@AJ)y;GPW_va-;є-HH"!@I ), f03=3]]յf[~{مVETTVeVfV_/_޽CAx5p=VⲞBNyX$-P#Hɡ5/o;6\s`{wϮcuYc{y8sX;;(y)?LOPaعs)}QX"0Sm9˸x;O2̇Ф eScDQ/T^o` E98r(1PBgw,tq挣{u} %!.qůw!1Y(*THw$`bcvDC5dQV&IT#S'9LDڝ'+La ,s\> q}[nSmN":ws!;~Hs!cAVHe` c#h=.]~_◾TZx?ubu8Oee9T0qW?F Μ\ŭ{vsI$ʩno7.q+K:h`94@E-J;oxQGf :e\rʩ]ru{FGOKIx|4 |AHE\tz#hc*8$C>ጥ 1H"G]P`xlii'm/a\Ż=0';{;=k IDATD-e9޽T)߇(N[\q$"hG_è,W@ku obaLH;p{BVK6"|`>/1241ךP`zcHEb' EsY kk@`E* 5%!}xpzB̈ UB2 966W|F߿ԇ4StLR'0Gxp*$20^#* gB\k10ko,27~uXvL~~A3D{%\BXB?(dIݨ}Jx#Dw@D*?3QpU;N]K8Vv&dTV4["H18P:dNCC=-8cy{^<_v$&d]"㗅E(u,eMYq[Q|=/ߐ66x13+ Žq$B,%~!@rj]`4`5 " %T){ TtoR) p24u<|z̓JRJ\0W%:,"DX8Irkx(ƿ;)B%n:S+x $~3LZQ(a:Ŷq!!<( dAJ_g!W4Fl`;{Gu[[][f㵚DQ]O:!Kk qIe?yOGj[o&cf_@ K>Y+KM[ "u Uw$0OHը q>v@61zIB]{F ߧ3y2zRvShM&`s kL)\Ɠ rCđ[>M?Md%"FJ?/-c.GN3d`I 7H.R3*Q-E`(@7k, #tG1r;q}26XBP/L١r `]Waer 1h0Xjձ!`V36 5< l{H  "Nd9 Xk)#kqCӏ= gBHEQ$3 :Dj@Fasߝɲ\ZJ+ R!,H1-WY+,qV`At 1 C8HAi|ZKX[nݬagoLiz>r`sI3'Wq `8%3Xc4Tn_|έZGl0.]@4[kN4 `L nS+Jݬ2S,%S0Ad/EuDD \n}zrU6*k`//H2Yi'X_i#KRb3oF I#Dڵ Wc3 ,0ZJ/ܷ/q~C x_cD=򉵤1֒%wB F%K&ɂcy tCgw3Lٯ<4p;pM6W[EҥyOQK»\ªpq= WQH}lx>\iYx= "r;)"qVfRɨ8疁)ccſ{Of 5w0;'N<.D2q9gQJ /z;o50Tf K`*j8vzz\o  z-Ri\Fb!JuRܹ(2Gݬa}<A0o_AX5\j5N0e+סrvrjcFSg8?ƱwzsksmAq,d$9-ufR'6V C >K] 3"R)I:5Iz F q$0LBb=8)-LpıJ;TXV rI3ĨX"{NINIfIlHf<ϻio~A<02GS4dsyR9֭xxR9vCasc8JdiK-@)"y?ongD:G.0"n}tejN}"\JY 8gdaa=8bđ@i"9[,8c9 AI<>Ppc4 q>!]b\S'V7ɘa1\w)[d"I" :Zk5AdJ>Y=[Z U39;67NSg?'`$ssg@\i.5z=ֻiRn~,7h(s)`S-$L@.fח[ȕ`W"B  UfmqFZkKX3B?ç᠗! 0nZ^]?qs&kŤ.sZ9b#4/0C(CVV!~b.N+k}=܂ 6*U/ AF=F=7@QUw$gR'"lשʵ5(èBƻu9wn""fQ`E|:w<(! 8,$9.0"4"!Ygh?üdPXy!vtOpIf0\CJf=.Ƌeb) v(,gҨ(Ԇ,x27644e֒U 5]yg~7_zSQRGq6 Pe1{7/mlj89kq3!u+K Gu* *WNV~!u (`V8P~iG?c]m,DR|;VUO` F )hCY?0B(&<8ǜd$"PjXB4:~v (H/&_'? 1wջ/qĹ鳏3F18cGQ SƠjQS%.*QdD|sׯ"][npLVQ-h&1jh5jXDE[!>)Z R!={#XAjv2L}"֭Փ7_G_Own<OH#E 0[4۞ U._>9.8g LuMXXGHleVSWӡ[ G#Xry)aLq @Rs8‰ն &u5H!0I/񲅡W lG|z-8ԮĜXq~$.b‹ѯA?G98Jh!S(֊]v}s\=IDes!0nC+Q,ƻRE5JE6n gXy# oHrΑbu>+vmY\9KZ^+vuazCͭλW~kxG߻2}>8*T |\zRغ}}jIOYF "`6S"#$i fDVI=Y ]#X riQ^(s]9 #RjT5:srնۉYFwu_ewK_{7?癅Y\S}cERj^?usſNs.T!5-;Y d!FwcW -sW@$5כxe*D(O1g 3&9\#Lc)L&޽T\11و0ܼ7M&;O}?|̏nc>?U;AR 0tB7o|pW_pcT<^#kkF![-S],Yr]V p }ERw֧vc ZB$L幫mP\̟`-V6Ͳ\gyJq6_'/Woʋ7} Gt ,)0G*ӯwo~ə6Zk""RP r\/3B .^sd&OShe`?Ak(e-JȽ#Un,-h;r5hnܸօg~_}룁RW_e2ñOA VJxʽ7_{V=iERH{YhL++M$Z"c,͗)#' hT6MՊzk&5tXCFkmrkMkhd0{\{^tտx郧ox ׇÁZ)b`l}xJw47NY"R2081f'kܺET- ЀW8p6F3szxu\ZYcQJicmʵ1cOzB{~p;=z cTKؖWM틓rç>p|>_ybEaJH֤9(A!q33T|B)9[Yj`ot9I AId\2c2rUI"⣫W~'x;h;pT)8ʻnz{Ic&,rfa{1Gc3ĵz{yyuW>s_ŠQgy916G. |D_g2m [_m:=K c]9眬[e`6͔%Z)Rƅj 13y|߿7{{-ETB<m"K$ HX2\BJKEǙw,cd%!rgjml#$͍Vk4)EQfdɝ[[ozW;zD4<"S ac41Gu_@]ʨyGWzgV__O.ol&f+8"B9D$5Z+,(L!O1d-gp΍! eJxf !$i&=G{bRojVQ[]ۨ7mQ%<żѨs5w0$Y:ѣ޾3北 iIo|~/8k儏.b1[^˝)fIY߅ȩsM_1&c2Z_?Y8qj-Eq\q\fS0=nVLFf<o߽=lghED*! 4- 170)³f?|WﰱHϳ04g}bv7gCq.H=l~ȝ6241ˬsN,CWn&S=cìܟ1)XtYnԘ&7yW>}4c<,2ٞEYOO'{þ`(C(ƴn2mOsycÎ9yY3'63~Z pqؽ,GQ9A3L0mO _&xq~z/g qB3IENDB`swift-im-2.0+dev6/Swift/resources/themes/Default/LICENSE.txt0000644000175000017500000002530612227051774023473 0ustar kismithkismithThe images, css and html is dual licensed under the BSD and AFL license. The source files for the bubbles can be found at http://www.itorrey.com/adiumx/ The fading javascript is not covered in this license. The code is fadomatic and is covered under its own license as set by its author. BSD LICENSE Copyright (c) 2007, Torrey Rice/Renkoo63 All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Torrey Rice and Renkoo nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Renkoo is a service mark of Renkoo, Inc. http://www.opensource.org/licenses/bsd-license.php # Larry Rosen has ceased to use or recommend any version # of the Academic Free License below version 2.1 The Academic Free License v. 2.1 This Academic Free License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following notice immediately following the copyright notice for the Original Work: Licensed under the Academic Free License version 2.1 1) Grant of Copyright License. Licensor hereby grants You a world-wide, royalty-free, non-exclusive, perpetual, sublicenseable license to do the following: a) to reproduce the Original Work in copies; b) to prepare derivative works ("Derivative Works") based upon the Original Work; c) to distribute copies of the Original Work and Derivative Works to the public; d) to perform the Original Work publicly; and e) to display the Original Work publicly. 2) Grant of Patent License. Licensor hereby grants You a world-wide, royalty-free, non-exclusive, perpetual, sublicenseable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, to make, use, sell and offer for sale the Original Work and Derivative Works. 3) Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor hereby agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work, and by publishing the address of that information repository in a notice immediately following the copyright notice that applies to the Original Work. 4) Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior written permission of the Licensor. Nothing in this License shall be deemed to grant any rights to trademarks, copyrights, patents, trade secrets or any other intellectual property of Licensor except as expressly stated herein. No patent license is granted to make, use, sell or offer to sell embodiments of any patent claims other than the licensed claims defined in Section 2. No right is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under different terms from this License any Original Work that Licensor otherwise would have a right to license. 5) This section intentionally omitted. 6) Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. 7) Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately proceeding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to Original Work is granted hereunder except under this disclaimer. 8) Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to any person for any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to liability for death or personal injury resulting from Licensor's negligence to the extent applicable law prohibits such limitation. Some jurisdictions do not allow the exclusion or limitation of incidental or consequential damages, so this exclusion and limitation may not apply to You. 9) Acceptance and Termination. If You distribute copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. Nothing else but this License (or another written agreement between Licensor and You) grants You permission to create Derivative Works based upon the Original Work or to exercise any of the rights granted in Section 1 herein, and any attempt to do so except under the terms of this License (or another written agreement between Licensor and You) is expressly prohibited by U.S. copyright law, the equivalent laws of other countries, and by international treaty. Therefore, by exercising any of the rights granted to You in Section 1 herein, You indicate Your acceptance of this License and all of its terms and conditions. 10) Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. 11) Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of the U.S. Copyright Act, 17 U.S.C. 030003 101 et seq., the equivalent laws of other countries, and international treaty. This section shall survive the termination of this License. 12) Attorneys Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. 13) Miscellaneous. This License represents the complete agreement concerning the subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. 14) Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. 15) Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. This license is Copyright (C) 2003-2004 Lawrence E. Rosen. All rights reserved. Permission is hereby granted to copy and distribute this license without modification. This license may not be modified without the express written permission of its copyright owner. swift-im-2.0+dev6/Swift/resources/themes/Default/Demo.html0000755000175000017500000000535612227051774023430 0ustar kismithkismith
%sender%
Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
Torrey @ 4:55 am
testing 1
%sender% @ %time%
testing 123
%sender% @ %time%
testing 123testing 123testing 123testing 123 testing 123testing 123
%sender% @ %time%
%sender%
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas aliquam sapien. Aliquam sed erat eu leo bibendum egestas. Praesent mauris. Quisque eget eros et neque scelerisque convallis. Phasellus orci. Pellentesque interdum tellus a erat. venenatis tristique.
Torrey @ 4:55 am
swift-im-2.0+dev6/Swift/resources/themes/Default/outgoing_icon.png0000755000175000017500000005613312227051774025226 0ustar kismithkismithPNG  IHDR>a IDATxy%YvKD53_keKW=3== .2aE%[4$CK%&%C a C([dSfgꮭkɪܷ{#^ -O g!mF"^|/YAC)(|)@Ȋ/c>P@RS@hu騱0rBq9w9 B9gBC2wxWqܵͭ]\_`3?O}5j.8iZy9y! !0eTJ~\@8 *ZEck\< a.=KK+7?৕vK:֝#CwXށ1kmZBqSUժXk0ƐeYCJ! ,wr?vᏅ+bV_7흕BUu!%79BBJBEDI3-Бfpt&+_#c* "aQJpѽ[{7_ى^g6n8"Qc-8j3 u{lݻxͭM crl̹sI}G*ZT5/ѿ ry9)NsNg%S fgg9uz'.ٓϒ$,b:4Uynhw #!kϹP : N pa8jjՄfNf]vw=|=dØksW6M}Ϻ^'A^7$L%sEkxg9:lpcIffX][uNZcvnjo<RXժL(vC,+K?J?^D0_9#1U*1qΡ$2vyW3J sHI}9~ڹ_o~̩'뵉 EgAIs.]:Ͻ+OOr:Ri2J3f;Gz}srB^L4vdYs8[:ujVynZZEqn$N>hLq{j?0tS2'2)B-{̴OLĕK\|,}:?AJSJVh4Z`|:J)>Zao9}#/WN~~jr?(NJ)Rh&X[[ciy-{9wJi~xc8f[GG]|pXB#y/o <u`bA7`8l@H)Ȝ # J)(5@pxxL83bRBaйsX]9-թIͱBkzi^ؓMW,Nȼ : ˳dғ{s<Bw*"1ZB{ 6q-yQHIXν-لfy^Lny0):r,YsC:X31D)EX޷p!OYkw<,-ZQ嗝 $J%fBq2 '9~H)%BY=Hh&8sj׮m/J:TƸ%5ofyd,/aek{?ӭ!)]IH⫅.ĉEfg&iܾNGy PH`sPPQp-z;wʟ_sגB#$iM :8XX?2DWb@H?U4@Te5C4*&3&ObuucFY@H ǑMXs*Iq&1Yr!Ry@ qP8h#DqШW= ܁ĘrAcrn4CW9*Kĕ *x䄯dneĺsLkZ]pՉ/[EY;w,/5,aCF1Fn]CE!y/_:IQecsz-y f<'$w|CGzࣃEHd[p (<^0Шȍ"Tdi`|΁!<<0y!L^P'1*J`"§IXvv|dq'~Y轛uyf5x(kZt%@*(7jcZy~pƢd҅uf>sc6~EFHb@Hp!lK)" C277REf9:QGl4*cW4rǙS+c S@dIS=[e{k_Z/N4gޓ^;^uKMNL/ NE0֊Vk0k 0J5Qf82GYF:JsKn<k%΍/Fٺ˲ `~vx嵫di0Mc)>e4̐Jb T5=pur J'qF#Wzaÿ22'U-?($i\y4Z$gReD nm}7x)TAakqN"e(ͬ)Cofr%3"`,ڷGiqy Cpi~Ô޹IQ٨XCj@HA)&5ťy>h@Ћ+I?>uo9doKW/IRHRMME1u`J 8G]_;4$rYYewͻxEnB 5΢\)$X+L%q`|\8| $R 4/;80qQ2lⳗPZ7owafO,w7Yp'/f{wݝC4e`eiFiwi{}R({4 fc/,̝{ob/3Lkǯ*IJ ɉF Kx.<Ҳ*EVsWZKeYRkC&|/뜏BPJӄhp+cpg:I":k+_6{}a*,Y58fthH`ܩg|_N;~~A|UBdD^ZK6ᯂ3JSԧ|U*<96sQ!<\6w,i"G_1dq ŏ#tXN0e ˴;} O95FFC3M},j@ :78D0"ʕANT릙LN4xϭ;$A6n 1N_.A$*OJJ2c=$yTH%h֘ZIࠍO\9q|cO`O-!S%tܠgx~(Qek޸Ӌ$qD+~ILd#³ݭS[ݍk(=m_UD"YvzQ2BK^ >qů-`E&EATJH=y+%9j8R&:5Jʠ <ʡnL+m!mV|6D:B*AVaqaif'xt{*ZKm滷? '(:;;hN.3y]ʻ *M..qI~Ko " PJ#B*y6ᱏVR%:Eqbկ^%>3Dq̻W? ,XX19Ygn~Ņi֖z%FZ)D!ʅPz8B)0]+ O)֓ASSufpvsW"sRXkCnzic#<9dr/ R̓gO>ū]㵷RCp,$]s,-N{,T@\kHօ/ws4/XY,WrZ`SOeqq0J+tnq /2W.SUjКjrxkw}{+8y'/vׯq6vopkV|kW9n2Rx3| \W_#G T';P"bY(<=]:>Cc(YQ2Z RvƷsV  0;m>ۿUA"%/Rn~ȇyOSxac~͹?BG#ZLN8}bwGLYoxd"Mn R(ln p+%A8秹t~$(ܽ]vW$9^&mF,P9"'KWq!_{dT,LJۀɅ#jEjRG,UPj :D#J*y֫)i!)LM%O?}haq)MܥffHcҗw?u|84u8"/d\gU IDAT*I/@¬opk JkVđfj -&VbRY(6>dNwP_FSE* O#4a:V25ӟy3(TѨK9 ByHu8:σAh _T<h `sϟ⃣Ǧ@krn]y_w =S߄*( 8>^ڍ ~O'0j_Q6'yWIsVaOd;Ԋv}?/07?׫ /s |+|wCϸr:uF)m:L7=MG[x߸O7 BM&k#F}Hyٞ}T%-(~PK YX5, EkK Ө)Ynx|߹;>dނCuـ043 ĸ)^Rr0Ѭ덐*HBd>6#R k]EH1'P[EH!")YN 쥿M>y |%)-:QxMD7dͩ $ȢK_p|ڍ{\GPRROyHOszRSS]Y#O0˔&$VXlXZ$O?qNw߿=2Z[旔^瀉yzk JIlP ш96Ag%u^P^cQ9R(/ qL`"Y[I>~˗Os6 d^5Zܾ#kq Fܼ pal[8@Hk#o} =FiJK_GK/STR_RzOב5tɋϜ^z"z㋯qU*_!5+ OJcoDie PFiN~b3Bxٸ((gH!ogyI!?WIG.0ґoCKZyVVH16;WJ4)@iI%IX+xv9.Yc?fy5E 0t)!hP(JF2?\\j7R<fZ̦!~oq1jr=yfV(RD:bvFRqBRRT~^'`XgV+>[ j [GT!2(lem׿ew[!IʏUH+ /rwxI~䇞ejAGJ?[_̰wnsRH!4+5.U[kEE(!a]^?'/d傡"#ZSMi4^R ZN7Vb(*锡|1ϳRE+/-8~ɅuT* Jq -u ' Q#\VXB9H|)KNo|sm~뷿A[ď@B0J3Hx[lnwDu9[IP!R+wqqF[ D R%AE,Ðʣ!qD%#_g_ZFg~ΠG%HAOnckg&XÄhr{rU)o [lܼ>/b{.bd0H;9<iEE>KEUy*Z[.R "BHE_ц =&"ѥ!QTL]BGȿ //F TkA$ C4(%}4tbjpF!U}L`q RdRiQP/Πmp/*W.h6HIgVxºuz},^OeOˡJ ) {ʇFDZ\aRrhY&L:\}1YFnQѴF`J"O `V)9Y6R#{|*bZѷ"z06([ \N ‰ !gߚVPS\t9QS5nd?,O}[w79 '5٠2٬3JS:'p*{ 5Bb$Q$Hm"ȸ ~ ($N Cf(HkG?G?S;!|j} 6Q/* !*Ά4PQtEj;-YB0= -eJ*R }i)$yFQF8di1NHcq~}̶*%U~$@cVKHM!{q"QyqRX.867?/qY橋'94:|fW_!Akh/)Q|@wٕ8cRE Zcaffd7޹Ef&p.x"g R:Z%js1p.HR#T̆( F)Hp _)-YtUi*Ia걊ǥ.x2.ZGk4aH?BIKwiMԹpUc5>As2G~mVDH9 DY"b ok8ki֫,NtDu;8/]kEƔXT~oquH>BrnQkOl9PR1<vxC`V!`~a|z.c!Fօ$(D= QJ n/R;QNfln=*iYfDFi$F*)g帙'XZ.Aιrk5.?QsG~]A"XQa彌FƩήrw!H ط6*›Wz\~ #UPjLn<@R~$ 3rBF~r=Q()kszVY"3,ln AtTyV6R ]s*ۿEVWfJtU}Vg9<eދ #(TBy%"aL0Q}T8$IDa]E*lbαe&5޿~IBd T!܃P2,MpB(bGOɸE|4R/_ :RXce)y'| ̴|xoc+7'G2Ghd%xZXJ@8T@HB9#KURܸ҇?_區;U[Mw([ 7et;}?crdF^%I"ZMf&=ʹQe*87d$Tz-9,aʃCB&S)WgBZ> ̷3)O28zy Cg7 -I JA^˧iMrbQc8LRnx{7I B{G\̻.+ibe V y5>jJ/(]&Ooq`Dgan#q#A^gzG(զRiM4bNkV)q`nvfB} !ˋtzCro (u:xfuGNwX,ϑ19mR<+7jB-Q\8B+>\'K3޿q͝Cڝ>>lomR Z>v<[os؝rZJO*Y`FPr7b( u^NFvoRY#9=n5?H+q ܸr6w"`t*D^prm@GhHI4##ڝ_ e.`G'(jY>?{ȝ;~4.h|c j$| $qTasȝn탈pxy ո!8@ ?|$yoH~!ƋX|GQ A X滇&ޏ3!>1䉔vwpqIR"ɋ3>s G9iG)phn0g;.VYY$O9(42eL+H,$^_́3)z"g*S,s8BIEY  , r&>YQDQc0!&LGW'/!2 XMbZRp4 1sEp Z[l{wXklej6.8zvF0 ȅcF}K{vځ ᕫ8A B;gTeŰD؋0O3j9qyu('4`(G(:DXX]i,l4T.tsg]gȗw^W}LZ>ERLRۦ}^do(yL H345굄XìRGRѣ"@A$_DZ1Lszvwd 9Y=_3"B V8􇩯̓ZńeI Ϯ4 3 GbpiV\h禘tk{8x>vIoVIb{VpB`>=ʲWP)酝Ys f:ahؠ=$ 3;ڝ!=]uN(mQhWˈۻL4LM4eY"jYi5r$ˋkj`{wOqw`Ww_$u%*kg@AJ)y;GPW_va-;є-HH"!@I ), f03=3]]յf[~{مVETTVeVfV_/_޽CAx5p=VⲞBNyX$-P#Hɡ5/o;6\s`{wϮcuYc{y8sX;;(y)?LOPaعs)}QX"0Sm9˸x;O2̇Ф eScDQ/T^o` E98r(1PBgw,tq挣{u} %!.qůw!1Y(*THw$`bcvDC5dQV&IT#S'9LDڝ'+La ,s\> q}[nSmN":ws!;~Hs!cAVHe` c#h=.]~_◾TZx?ubu8Oee9T0qW?F Μ\ŭ{vsI$ʩno7.q+K:h`94@E-J;oxQGf :e\rʩ]ru{FGOKIx|4 |AHE\tz#hc*8$C>ጥ 1H"G]P`xlii'm/a\Ż=0';{;=k IDATD-e9޽T)߇(N[\q$"hG_è,W@ku obaLH;p{BVK6"|`>/1241ךP`zcHEb' EsY kk@`E* 5%!}xpzB̈ UB2 966W|F߿ԇ4StLR'0Gxp*$20^#* gB\k10ko,27~uXvL~~A3D{%\BXB?(dIݨ}Jx#Dw@D*?3QpU;N]K8Vv&dTV4["H18P:dNCC=-8cy{^<_v$&d]"㗅E(u,eMYq[Q|=/ߐ66x13+ Žq$B,%~!@rj]`4`5 " %T){ TtoR) p24u<|z̓JRJ\0W%:,"DX8Irkx(ƿ;)B%n:S+x $~3LZQ(a:Ŷq!!<( dAJ_g!W4Fl`;{Gu[[][f㵚DQ]O:!Kk qIe?yOGj[o&cf_@ K>Y+KM[ "u Uw$0OHը q>v@61zIB]{F ߧ3y2zRvShM&`s kL)\Ɠ rCđ[>M?Md%"FJ?/-c.GN3d`I 7H.R3*Q-E`(@7k, #tG1r;q}26XBP/L١r `]Waer 1h0Xjձ!`V36 5< l{H  "Nd9 Xk)#kqCӏ= gBHEQ$3 :Dj@Fasߝɲ\ZJ+ R!,H1-WY+,qV`At 1 C8HAi|ZKX[nݬagoLiz>r`sI3'Wq `8%3Xc4Tn_|έZGl0.]@4[kN4 `L nS+Jݬ2S,%S0Ad/EuDD \n}zrU6*k`//H2Yi'X_i#KRb3oF I#Dڵ Wc3 ,0ZJ/ܷ/q~C x_cD=򉵤1֒%wB F%K&ɂcy tCgw3Lٯ<4p;pM6W[EҥyOQK»\ªpq= WQH}lx>\iYx= "r;)"qVfRɨ8疁)ccſ{Of 5w0;'N<.D2q9gQJ /z;o50Tf K`*j8vzz\o  z-Ri\Fb!JuRܹ(2Gݬa}<A0o_AX5\j5N0e+סrvrjcFSg8?ƱwzsksmAq,d$9-ufR'6V C >K] 3"R)I:5Iz F q$0LBb=8)-LpıJ;TXV rI3ĨX"{NINIfIlHf<ϻio~A<02GS4dsyR9֭xxR9vCasc8JdiK-@)"y?ongD:G.0"n}tejN}"\JY 8gdaa=8bđ@i"9[,8c9 AI<>Ppc4 q>!]b\S'V7ɘa1\w)[d"I" :Zk5AdJ>Y=[Z U39;67NSg?'`$ssg@\i.5z=ֻiRn~,7h(s)`S-$L@.fח[ȕ`W"B  UfmqFZkKX3B?ç᠗! 0nZ^]?qs&kŤ.sZ9b#4/0C(CVV!~b.N+k}=܂ 6*U/ AF=F=7@QUw$gR'"lשʵ5(èBƻu9wn""fQ`E|:w<(! 8,$9.0"4"!Ygh?üdPXy!vtOpIf0\CJf=.Ƌeb) v(,gҨ(Ԇ,x27644e֒U 5]yg~7_zSQRGq6 Pe1{7/mlj89kq3!u+K Gu* *WNV~!u (`V8P~iG?c]m,DR|;VUO` F )hCY?0B(&<8ǜd$"PjXB4:~v (H/&_'? 1wջ/qĹ鳏3F18cGQ SƠjQS%.*QdD|sׯ"][npLVQ-h&1jh5jXDE[!>)Z R!={#XAjv2L}"֭Փ7_G_Own<OH#E 0[4۞ U._>9.8g LuMXXGHleVSWӡ[ G#Xry)aLq @Rs8‰ն &u5H!0I/񲅡W lG|z-8ԮĜXq~$.b‹ѯA?G98Jh!S(֊]v}s\=IDes!0nC+Q,ƻRE5JE6n gXy# oHrΑbu>+vmY\9KZ^+vuazCͭλW~kxG߻2}>8*T |\zRغ}}jIOYF "`6S"#$i fDVI=Y ]#X riQ^(s]9 #RjT5:srնۉYFwu_ewK_{7?癅Y\S}cERj^?usſNs.T!5-;Y d!FwcW -sW@$5כxe*D(O1g 3&9\#Lc)L&޽T\11و0ܼ7M&;O}?|̏nc>?U;AR 0tB7o|pW_pcT<^#kkF![-S],Yr]V p }ERw֧vc ZB$L幫mP\̟`-V6Ͳ\gyJq6_'/Woʋ7} Gt ,)0G*ӯwo~ə6Zk""RP r\/3B .^sd&OShe`?Ak(e-JȽ#Un,-h;r5hnܸօg~_}룁RW_e2ñOA VJxʽ7_{V=iERH{YhL++M$Z"c,͗)#' hT6MՊzk&5tXCFkmrkMkhd0{\{^tտx郧ox ׇÁZ)b`l}xJw47NY"R2081f'kܺET- ЀW8p6F3szxu\ZYcQJicmʵ1cOzB{~p;=z cTKؖWM틓rç>p|>_ybEaJH֤9(A!q33T|B)9[Yj`ot9I AId\2c2rUI"⣫W~'x;h;pT)8ʻnz{Ic&,rfa{1Gc3ĵz{yyuW>s_ŠQgy916G. |D_g2m [_m:=K c]9眬[e`6͔%Z)Rƅj 13y|߿7{{-ETB<m"K$ HX2\BJKEǙw,cd%!rgjml#$͍Vk4)EQfdɝ[[ozW;zD4<"S ac41Gu_@]ʨyGWzgV__O.ol&f+8"B9D$5Z+,(L!O1d-gp΍! eJxf !$i&=G{bRojVQ[]ۨ7mQ%<żѨs5w0$Y:ѣ޾3北 iIo|~/8k儏.b1[^˝)fIY߅ȩsM_1&c2Z_?Y8qj-Eq\q\fS0=nVLFf<o߽=lghED*! 4- 170)³f?|WﰱHϳ04g}bv7gCq.H=l~ȝ6241ˬsN,CWn&S=cìܟ1)XtYnԘ&7yW>}4c<,2ٞEYOO'{þ`(C(ƴn2mOsycÎ9yY3'63~Z pqؽ,GQ9A3L0mO _&xq~z/g qB3IENDB`swift-im-2.0+dev6/Swift/resources/themes/Default/noname.css0000644000175000017500000000003112227051774023623 0ustar kismithkismith.name { display:none; } swift-im-2.0+dev6/Swift/resources/themes/Default/Template.html0000755000175000017500000002475312227051774024321 0ustar kismithkismith %@
%@
swift-im-2.0+dev6/Swift/resources/themes/Default/Outgoing/0000755000175000017500000000000012227051774023435 5ustar kismithkismithswift-im-2.0+dev6/Swift/resources/themes/Default/Outgoing/NextContext.html0000755000175000017500000000016412227051774026612 0ustar kismithkismith
%message%
%time%
swift-im-2.0+dev6/Swift/resources/themes/Default/Outgoing/Context.html0000755000175000017500000000132112227051774025747 0ustar kismithkismith
%message%
%sender% @ %time%
swift-im-2.0+dev6/Swift/resources/themes/Default/Outgoing/Content.html0000755000175000017500000000134212227051774025740 0ustar kismithkismith
%message%
%wrapped_sender% @ %time%
swift-im-2.0+dev6/Swift/resources/themes/Default/Outgoing/buddy_icon.png0000644000175000017500000003363312227051774026272 0ustar kismithkismithPNG  IHDR>a pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3-gAMA|Q cHRMz%u0`:o_F,IDATx}yeUuo3ݹ[#cQA&a"1 AEDԨ<>D?/bT>$iD!"@wuWWWt{k?VQT{ν[{-Xٲ^}&xcthuWqU^rgs+х|[M oi+G UF,i XR*z.W^?2433l? U6|w߼c.y#j"`a9k5;F$=u2;u|= _G֭^u<>p}ϼ7p ˴kW~OuxjYc`J]?sZ^Gn:l)ǫrXn 9~G 0Mm{>%}S/yū]q#=/*K 9m>7+L@lb_zOud*;6 A b>=l@,ᛴzzZX( !0 1m_(7z%'ׯ]6;#x=ϼѿAo$O~/kō^ykV۶e蜈߮Vƚ-b[=';ȀOtL4;k{r$n8|.VBJM})0+0X>)}(J(Lޛ3Q no9VG+8C7:zerUՑ>78AOGKi{yo\6Tp~ < #BHH@{*n:zY>"~{Ŀ]0^[ r"@PѾJ%*NN@cL2l˲*sW$oן)嵇gӞOI(-'w_B@iH FIt޺ʳtOq|ay|ub7E 4- IxND:&-%Zst◡V%-icOF'#ހ4fұb03pہ2O u R_#JCDz_n??<} b=38CTRQ(9}> fho\uν|m> 3p]mY9˂-ɚN[;.@CO峢?s{gPͯ'0hG E2׎߈l@ޓOw{T|w x7Ov#cZiI   Mg3bPP1r;[A! f"!TP@>ٟioç~|6F`F,͠Q^Fۅ<-K%`Xݐq)Ŀq;AS~QBE3Yt#kW1]w\װ-$`!z@k3l5Zc>lp?vP*kO~Mvٟnڮt͹b?\_}y $}Zh'ďMD@M8roӈ&#W*^AVkܮ_ĸ"'i"v*G$((I{އ1?3r $ P߷NbۦI⿝u:< =GNEp"`9A.qמ|wlvK`Mr"{Og,J0"5v\Q݄o Asߌ;g"fy:p"R-ѓM/ _v1uq"$&(+("0SBAA W_f[>=h4>|;)M}@n:j:رmFG:~H-W}O:fPLh7h>r}˻P tjr'\D`"(q-Ҙ& ڞ.?وziwv'&@x氃_14vGT2( i9cZE_q^pI _TjiE%0~/U3go ڢ@!C))")- 4i#&<7T:a3,X8p6: 4M4M(^roy[7 h4}/kÙ1D%Q@6e`IڧBW'=4kݡ(M&] dK`i?n)sPk0. _o5=LLL$)#Ҟ>?YS=g"!-Xϧ}= eKݠ[,YZCﷂz"c݀kV+iccd|LiEԶXr& 3ci\Ǭ`M)%c$$@J"J쓩/tOvyYI@d  f ɲo%#*T{q]W 7,9=6,{T#-J?^X{~IAZ$M*"ޏf@Ю{$$m>+?) "w>fh~\8F̘nLcy}@M{3N'l8|ڿΊTV"QL%ii<;h̝QFa %'H޻px`2o`۱:MTmi@~d{1@ID_H)3XEFhM {ڍ0`aQI:ݜU}[cC$N M<$QX qFŤ 8\28z D  mbL'Ddf|_N!2J"(bL'p=F(o+9")` @(:׼+x~]"NF@&\ \ygPbbTV0*+%Hg?x&<303'oʈ|.JADxpx^bO., ؖ`F>YDp]Onk4D5x֯NQ_~FD-Q,E˓~Fr{XqV6#'!Rd66 d|;E5^h^o`ãorD&'-O M܃7yN<$H˩GOއCXѵȮ7" fH?&fY"HLX7Wdm?w Ƌ5$lBwJd!'Ǩ`ƪq\+-alwj.i{OC vcIoB5š~S\~s;l?n1 Q:<"hKo.R8J-`=5l'!K_K5Q1tN,c~U8[`Ɔ;MT]VC~9y% ?+.飯uw灡Zh nu?p vs[n]3F?n.jkWѥ/wP-௟8W/n֭['W8 @SG~OR7SYN v43~moҪ'H֚E{?hN}wnݺ`){Ow{H|g˚vΉK6k5 !%y v[0\^;8À39i̔FT]x+$V [s@0]v镥/,n}ݺnݺ#{( K4}[4,4w:PA ;Y1oݜ,7'W 2.8U8ԀN8sEYy֭9g^q«To)已=Hۆe{]BH !5(;}ϣ_UWX5t?‰%܈7 (BB^9ڮ%+oC-D>Fef2k!? oδ~vEvN+yGBc+ h0nWPB r2+ʪg~Dܼ@$ FyʼnxE=-*P}&*>M!/ ˲Ъᵖe}+N<{Ed{'^dΥUCk% Z;t'F籁%e0CՑq˶me ڃuUze"q:84:¥;\  876,N9 TUD56$FaAB iD4{n!W( lחD%# x_WW zJ8]"} 3m7BDtfKnBE HrJ  %hJZGЦU\iGF1z-A׿dڧ0O'LёlV0qnOF̈́P80:yDԤp 9zdV 0'.%j!!¨-NqT8tGGRZX|w {&,H! a1RR߳زA`4:^uC\|@NwGW^+b_P:Btg;`Ű#/Dof>1+`Y|J %(A>\ "M;ٰF϶0ZE Nc/}x`ñlR~aWs- 0)fA, &'"\ `;?P VWy碟q}D[ְ; 5ᘞyX4;-Hk"I)zX*ڳDO `+yJ[XQ `d>[ lM X}cwv#> `EQ)@K 1_<Ю?c@"{, L,;?AaF%b TQO%0^5`kG_T> / x ^v!Ë&Vg˶jىP  +%n4b3]$c肋W͝qSP`z Fu y/26UfT{܏j}.a~“!W}㶽E}0VIL E6̐oJk kQtiw0 *DRű9+Yp2]Z2RVd>c ҟטF F&dr! ܏$h0rxxiۦ,P?xu0J )R0/Sv6ËTj^n̐ fK^[`\G,!ݒi3Zp /-E2ET\25)&`(x aBgl\LN٨D"kbօ"#j'Nfl FrR % 気JI}Gt 8{d$LHs3<&:sumRPl+`leFMCmqw 8 7}QOS O=ޤ^B/fx[R t?`Rbbe+p3L 0F#$@OE8ލ4sH̓R9TO!I )znX+?U]?"K˶ >yon8p[flT]@-HPESqv^Šf~7#jDaQfh6!̄uO#!XV0nH';?zz`3|ʉUOXG⇽m303%0ض`JG+|9 `-  2F)F LIl"k*jP?P5~Jnld3ۿ0<͙οZ=@ȨYD"* $j-VA˓x۔2A` Jlw APW}h A^޹@7mݘ@=[[u8% 8SHsDId5ۙJ%p]-Ʃ% 5iX/VyA--mH2P`b@]C(YOyߙx.@lZNosg~4QaMGڊYF؈4[X m攅@n]0SVSΈB@kBؒclUj? *>!(zMͺ!LC<^|j~$ ) {@1zeHqV}Q&Q]A(ڌC Za^A%bFHd w[S 1:H|2cq/@;h* -@-7la&1"Ώ/&azc76&ǼӪ/# hޕ !=[Q*@7b7l=\9L'YI h JKȹ,O%UD&u> CSDhA^8+7 qfvKƎN,3ܹެ"#78&#1;z䱥"sG $ZS=Mj=s}L|u =&WS V& 6K7W^GuO(قIhN C#8SOHE+" R+6_>So}`ǢߏG ^@0A^Ku+  :d%-yd7m6ld֎@Z(K,N{{ԣOܿ㙞Ib}7O0 87'H Yh;a/yMn9YCur~%m}6@$$ !XBT<ќ<6; dD7!=F}-|@p `,_<|AclZeyr\ڢ`{֡Kpf$PHY9v& 4v֎s6vÍU=\or~o&Rɘ`D%i2_{#Jkv'ӷl+ȽK}A}i+ "s@x[1E߻HW//), f@`zv 4K/ұ;}W`ݐ;{]2D7?Eo` IENDB`swift-im-2.0+dev6/Swift/resources/themes/Default/Outgoing/NextContent.html0000755000175000017500000000021012227051774026570 0ustar kismithkismith
%message%
%time%
swift-im-2.0+dev6/Swift/resources/themes/Default/main.css0000755000175000017500000001255212227051774023310 0ustar kismithkismith* { word-wrap: break-word; word-break:break-word; } #header1 { position: fixed; top: 0px; left: 0px; right: 0px; margin: 0; padding: 10px; overflow: auto; color: white; text-align: center; font-size: 10px; font-weight: regular; background: rgba(0,0,0,.65); z-index: 999; } #heading { position: fixed; top: 0px; left: 0px; margin: 0; padding: 5px; font-weight: regular; background-color:#fbfbed; z-index: 999; width:100%; height:45px; border-bottom:2px solid #d5d5d5; background:url("images/steelHeading.jpg") repeat-x top left; } #heading .conversationIncomingIcon { position:absolute; left:5px; top:5px; } #heading .conversationIncomingIcon img { width:48px; height:48px; } #heading .conversationWith { position:relative; left:60px; margin:5px 0 0 0; font: bold 16px "DejaVu Sans", "Myriad Pro", Myriad, "Lucida Grande", "Trebuchet MS", Arial, "Droid Sans", sans-serif; overflow:hide; } #heading .conversationTime { position:relative; left:60px; color:#6d6d6d; font: bold 10px "DejaVu Sans", "Myriad Pro", Myriad, "Lucida Grande", "Trebuchet MS", Arial, "Droid Sans", sans-serif; } body { margin-top: 65px; background-color: white; color: black; } .status_container { font: bold 10px "DejaVu Sans", "Myriad Pro", Myriad, "Lucida Grande", "Trebuchet MS", Arial, "Droid Sans", sans-serif; } body { /* font-family: "DejaVu Sans", "Myriad Pro", Myriad, "Lucida Grande", "Trebuchet MS", Arial, "Droid Sans", sans-serif;*/ } .followUp { clear:right; height:1px; font-size:1px; line-height:1px; margin:4px 0 4px 0; } .chatItem { /*Removing opacity, because this causes Qt's WebKit to draw low-quality images */ /*opacity:0.96;*/ } .tableBubble { width:100%; } .tableBubble .tl { height:8px; } .tableBubble .tr { width:8px; height:8px; } .message { padding:0 1em 0 1.25em; word-wrap: break-word; } .tableBubble .message { font-size:11px; } .tableBubble .message img { vertical-align:middle; } .tableBubble .messageRight { width:1px; } .tableBubble .bl { height:10px; } .tableBubble .br { width:8px; height:10px; } .tableBubble .timeStamp { margin:2px; margin-left:7px; text-align:right; float:right; } .myBubble .indicator { position:absolute; top:8px; left:0; width:13px; height:11px; } .myBubble { position:relative; padding-left:10px; margin-left:33px; margin-right:10px; } .chatItem .avatar { max-width:32px; max-height:32px; float:left; } /****** STatus ******/ .statusMessage { opacity:0.8; color:#676767; } .statusMessage .myBubble .indicator { background:url("images/steelIndicator.png") no-repeat top left; } .statusMessage .tableBubble .tl { background:url("images/steelCurves.png") no-repeat top left; } .statusMessage .tableBubble .tr { background:url("images/steelCurves.png") no-repeat top right; } .statusMessage .tableBubble .head { background:url("images/steelCurves.png") no-repeat -10px 0; } .statusMessage .tableBubble .message { background:url("images/steelBackground.png") repeat-y top left; } .statusMessage .tableBubble .messageRight { background:url("images/steelBackground.png") repeat-y top right; } .statusMessage .tableBubble .bl { background:url("images/steelCurves.png") no-repeat bottom left; } .statusMessage .tableBubble .br { background:url("images/steelCurves.png") no-repeat bottom right; } .statusMessage .followUp { background-color:#f4f0a7; border-bottom:1px solid #fff; } .statusMessage .timeStamp { color:#676767; } /*incoming */ .incomingItem .myBubble .indicator { background:url("images/yellowIndicator.png") no-repeat top left; } .incomingItem .tableBubble .tl { background:url("images/yellowCurves.png") no-repeat top left; } .incomingItem .tableBubble .tr { background:url("images/yellowCurves.png") no-repeat top right; } .incomingItem .tableBubble .head { background:url("images/yellowCurves.png") no-repeat -10px 0; } .incomingItem .tableBubble .message { background:url("images/yellowBackground.png") repeat-y top left; } .incomingItem .tableBubble .messageRight { background:url("images/yellowBackground.png") repeat-y top right; } .incomingItem .tableBubble .bl { background:url("images/yellowCurves.png") no-repeat bottom left; } .incomingItem .tableBubble .br { background:url("images/yellowCurves.png") no-repeat bottom right; } .incomingItem .followUp { background-color:#f4f0a7; border-bottom:1px solid #fff; } .incomingItem .timeStamp { color:#bdb410; } /* outgoing */ .outgoingItem .myBubble .indicator { background:url("images/greenIndicator.png") no-repeat top left; } .outgoingItem .tableBubble .tl { background:url("images/greenCurves.png") no-repeat top left; } .outgoingItem .tableBubble .tr { background:url("images/greenCurves.png") no-repeat top right; } .outgoingItem .tableBubble .head { background:url("images/greenCurves.png") no-repeat -10px 0; } .outgoingItem .tableBubble .message { background:url("images/greenBackground.png") repeat-y top left; } .outgoingItem .tableBubble .messageRight { background:url("images/greenBackground.png") repeat-y top right; } .outgoingItem .tableBubble .bl { background:url("images/greenCurves.png") no-repeat bottom left; } .outgoingItem .tableBubble .br { background:url("images/greenCurves.png") no-repeat bottom right; } .outgoingItem .followUp { background-color:#e2efc4; border-bottom:1px solid #fff; } .outgoingItem .timeStamp { color:#9ecf35; } html { height: 101%; } swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/0000755000175000017500000000000012227051774023431 5ustar kismithkismithswift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Yellow on Blue No Names.css0000644000175000017500000000010112227051774030234 0ustar kismithkismith@import url("Yellow on Blue.css"); @import url("../noname.css"); swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Blue on Green No Names Alt.css0000644000175000017500000000011412227051774030526 0ustar kismithkismith@import url("Blue on Green Alternating.css"); @import url("../noname.css"); swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Red on Yellow No Names Alt.css0000644000175000017500000000011412227051774030564 0ustar kismithkismith@import url("Red on Yellow Alternating.css"); @import url("../noname.css"); swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Green on Steel Alternating.css0000644000175000017500000000026412227051774031070 0ustar kismithkismith@import url("Green on Steel.css"); @import url("../alternating.css"); .outgoingItem .myBubble .indicator { background:url("../images/greenIndicator2.png") no-repeat top left; } swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Red on Steel Alternating.css0000644000175000017500000000026012227051774030536 0ustar kismithkismith@import url("Red on Steel.css"); @import url("../alternating.css"); .outgoingItem .myBubble .indicator { background:url("../images/redIndicator2.png") no-repeat top left; } swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Blue on Yellow Alternating.css0000644000175000017500000000026312227051774031115 0ustar kismithkismith@import url("Blue on Yellow.css"); @import url("../alternating.css"); .outgoingItem .myBubble .indicator { background:url("../images/blueIndicator2.png") no-repeat top left; } swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Blue on Red No Names.css0000644000175000017500000000007612227051774027506 0ustar kismithkismith@import url("Blue on Red.css"); @import url("../noname.css"); swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Steel on Green No Names.css0000644000175000017500000000010112227051774030206 0ustar kismithkismith@import url("Steel on Green.css"); @import url("../noname.css"); swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Blue on Steel Alternating.css0000644000175000017500000000026212227051774030715 0ustar kismithkismith@import url("Blue on Steel.css"); @import url("../alternating.css"); .outgoingItem .myBubble .indicator { background:url("../images/blueIndicator2.png") no-repeat top left; } swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Steel on Blue.css0000644000175000017500000000373712227051774026476 0ustar kismithkismith@import url("../main.css"); /* outgoing */ .outgoingItem .timeStamp { color:#a9a9a9; } .outgoingItem .myBubble .indicator { background:url("../images/steelIndicator.png") no-repeat top left; } .outgoingItem .tableBubble .tl { background:url("../images/steelCurves.png") no-repeat top left; } .outgoingItem .tableBubble .tr { background:url("../images/steelCurves.png") no-repeat top right; } .outgoingItem .tableBubble .head { background:url("../images/steelCurves.png") no-repeat -10px 0; } .outgoingItem .tableBubble .message { background:url("../images/steelBackground.png") repeat-y top left; } .outgoingItem .tableBubble .messageRight { background:url("../images/steelBackground.png") repeat-y top right; } .outgoingItem .tableBubble .bl { background:url("../images/steelCurves.png") no-repeat bottom left; } .outgoingItem .tableBubble .br { background:url("../images/steelCurves.png") no-repeat bottom right; } .outgoingItem .followUp { background-color:#ececec; border-bottom:1px solid #fff; } /*incoming */ .incomingItem .timeStamp { color:#7fc5f8; } .incomingItem .myBubble .indicator { background:url("../images/blueIndicator.png") no-repeat top left; } .incomingItem .tableBubble .tl { background:url("../images/blueCurves.png") no-repeat top left; } .incomingItem .tableBubble .tr { background:url("../images/blueCurves.png") no-repeat top right; } .incomingItem .tableBubble .head { background:url("../images/blueCurves.png") no-repeat -10px 0; } .incomingItem .tableBubble .message { background:url("../images/blueBackground.png") repeat-y top left; } .incomingItem .tableBubble .messageRight { background:url("../images/blueBackground.png") repeat-y top right; } .incomingItem .tableBubble .bl { background:url("../images/blueCurves.png") no-repeat bottom left; } .incomingItem .tableBubble .br { background:url("../images/blueCurves.png") no-repeat bottom right; } .incomingItem .followUp { background-color:#ddf0fe; border-bottom:1px solid #fff; } swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Green on Red.css0000644000175000017500000000372512227051774026302 0ustar kismithkismith@import url("../main.css"); /* outgoing */ .outgoingItem .timeStamp { color:#9ecf35; } .outgoingItem .myBubble .indicator { background:url("../images/greenIndicator.png") no-repeat top left; } .outgoingItem .tableBubble .tl { background:url("../images/greenCurves.png") no-repeat top left; } .outgoingItem .tableBubble .tr { background:url("../images/greenCurves.png") no-repeat top right; } .outgoingItem .tableBubble .head { background:url("../images/greenCurves.png") no-repeat -10px 0; } .outgoingItem .tableBubble .message { background:url("../images/greenBackground.png") repeat-y top left; } .outgoingItem .tableBubble .messageRight { background:url("../images/greenBackground.png") repeat-y top right; } .outgoingItem .tableBubble .bl { background:url("../images/greenCurves.png") no-repeat bottom left; } .outgoingItem .tableBubble .br { background:url("../images/greenCurves.png") no-repeat bottom right; } .outgoingItem .followUp { background-color:#e2efc4; border-bottom:1px solid #fff; } /*incoming */ .incomingItem .timeStamp { color:#f88f8f; } .incomingItem .myBubble .indicator { background:url("../images/redIndicator.png") no-repeat top left; } .incomingItem .tableBubble .tl { background:url("../images/redCurves.png") no-repeat top left; } .incomingItem .tableBubble .tr { background:url("../images/redCurves.png") no-repeat top right; } .incomingItem .tableBubble .head { background:url("../images/redCurves.png") no-repeat -10px 0; } .incomingItem .tableBubble .message { background:url("../images/redBackground.png") repeat-y top left; } .incomingItem .tableBubble .messageRight { background:url("../images/redBackground.png") repeat-y top right; } .incomingItem .tableBubble .bl { background:url("../images/redCurves.png") no-repeat bottom left; } .incomingItem .tableBubble .br { background:url("../images/redCurves.png") no-repeat bottom right; } .incomingItem .followUp { background-color:#ffdada; border-bottom:1px solid #fff; } swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Yellow on Blue Alternating.css0000644000175000017500000000026512227051774031117 0ustar kismithkismith@import url("Yellow on Blue.css"); @import url("../alternating.css"); .outgoingItem .myBubble .indicator { background:url("../images/yellowIndicator2.png") no-repeat top left; } swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Green on Yellow No Names Alt.css0000644000175000017500000000011612227051774031114 0ustar kismithkismith@import url("Green on Yellow Alternating.css"); @import url("../noname.css"); swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Green on Yellow Alternating.css0000644000175000017500000000026512227051774031270 0ustar kismithkismith@import url("Green on Yellow.css"); @import url("../alternating.css"); .outgoingItem .myBubble .indicator { background:url("../images/greenIndicator2.png") no-repeat top left; } swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Red on Steel No Names Alt.css0000644000175000017500000000011312227051774030364 0ustar kismithkismith@import url("Red on Steel Alternating.css"); @import url("../noname.css"); swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Yellow on Green No Names.css0000644000175000017500000000010212227051774030406 0ustar kismithkismith@import url("Yellow on Green.css"); @import url("../noname.css"); swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Blue on Yellow No Names.css0000644000175000017500000000010112227051774030234 0ustar kismithkismith@import url("Blue on Yellow.css"); @import url("../noname.css"); swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Red on Blue.css0000644000175000017500000000371512227051774026130 0ustar kismithkismith@import url("../main.css"); /* outgoing */ .outgoingItem .timeStamp { color:#f88f8f; } .outgoingItem .myBubble .indicator { background:url("../images/redIndicator.png") no-repeat top left; } .outgoingItem .tableBubble .tl { background:url("../images/redCurves.png") no-repeat top left; } .outgoingItem .tableBubble .tr { background:url("../images/redCurves.png") no-repeat top right; } .outgoingItem .tableBubble .head { background:url("../images/redCurves.png") no-repeat -10px 0; } .outgoingItem .tableBubble .message { background:url("../images/redBackground.png") repeat-y top left; } .outgoingItem .tableBubble .messageRight { background:url("../images/redBackground.png") repeat-y top right; } .outgoingItem .tableBubble .bl { background:url("../images/redCurves.png") no-repeat bottom left; } .outgoingItem .tableBubble .br { background:url("../images/redCurves.png") no-repeat bottom right; } .outgoingItem .followUp { background-color:#ffdada; border-bottom:1px solid #fff; } /*incoming */ .incomingItem .timeStamp { color:#7fc5f8; } .incomingItem .myBubble .indicator { background:url("../images/blueIndicator.png") no-repeat top left; } .incomingItem .tableBubble .tl { background:url("../images/blueCurves.png") no-repeat top left; } .incomingItem .tableBubble .tr { background:url("../images/blueCurves.png") no-repeat top right; } .incomingItem .tableBubble .head { background:url("../images/blueCurves.png") no-repeat -10px 0; } .incomingItem .tableBubble .message { background:url("../images/blueBackground.png") repeat-y top left; } .incomingItem .tableBubble .messageRight { background:url("../images/blueBackground.png") repeat-y top right; } .incomingItem .tableBubble .bl { background:url("../images/blueCurves.png") no-repeat bottom left; } .incomingItem .tableBubble .br { background:url("../images/blueCurves.png") no-repeat bottom right; } .incomingItem .followUp { background-color:#ddf0fe; border-bottom:1px solid #fff; } swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Yellow on Green Alternating.css0000644000175000017500000000026612227051774031271 0ustar kismithkismith@import url("Yellow on Green.css"); @import url("../alternating.css"); .outgoingItem .myBubble .indicator { background:url("../images/yellowIndicator2.png") no-repeat top left; } swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Yellow on Green No Names Alt.css0000644000175000017500000000011612227051774031114 0ustar kismithkismith@import url("Yellow on Green Alternating.css"); @import url("../noname.css"); swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Blue on Red.css0000644000175000017500000000371412227051774026127 0ustar kismithkismith@import url("../main.css"); /* outgoing */ .outgoingItem .timeStamp { color:#7fc5f8; } .outgoingItem .myBubble .indicator { background:url("../images/blueIndicator.png") no-repeat top left; } .outgoingItem .tableBubble .tl { background:url("../images/blueCurves.png") no-repeat top left; } .outgoingItem .tableBubble .tr { background:url("../images/blueCurves.png") no-repeat top right; } .outgoingItem .tableBubble .head { background:url("../images/blueCurves.png") no-repeat -10px 0; } .outgoingItem .tableBubble .message { background:url("../images/blueBackground.png") repeat-y top left; } .outgoingItem .tableBubble .messageRight { background:url("../images/blueBackground.png") repeat-y top right; } .outgoingItem .tableBubble .bl { background:url("../images/blueCurves.png") no-repeat bottom left; } .outgoingItem .tableBubble .br { background:url("../images/blueCurves.png") no-repeat bottom right; } .outgoingItem .followUp { background-color:#ddf0fe; border-bottom:1px solid #fff; } /*incoming */ .incomingItem .timeStamp { color:#f88f8f; } .incomingItem .myBubble .indicator { background:url("../images/redIndicator.png") no-repeat top left; } .incomingItem .tableBubble .tl { background:url("../images/redCurves.png") no-repeat top left; } .incomingItem .tableBubble .tr { background:url("../images/redCurves.png") no-repeat top right; } .incomingItem .tableBubble .head { background:url("../images/redCurves.png") no-repeat -10px 0; } .incomingItem .tableBubble .message { background:url("../images/redBackground.png") repeat-y top left; } .incomingItem .tableBubble .messageRight { background:url("../images/redBackground.png") repeat-y top right; } .incomingItem .tableBubble .bl { background:url("../images/redCurves.png") no-repeat bottom left; } .incomingItem .tableBubble .br { background:url("../images/redCurves.png") no-repeat bottom right; } .incomingItem .followUp { background-color:#ffdada; border-bottom:1px solid #fff; } swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Blue on Green.css0000644000175000017500000000373512227051774026460 0ustar kismithkismith@import url("../main.css"); /* outgoing */ .outgoingItem .timeStamp { color:#7fc5f8; } .outgoingItem .myBubble .indicator { background:url("../images/blueIndicator.png") no-repeat top left; } .outgoingItem .tableBubble .tl { background:url("../images/blueCurves.png") no-repeat top left; } .outgoingItem .tableBubble .tr { background:url("../images/blueCurves.png") no-repeat top right; } .outgoingItem .tableBubble .head { background:url("../images/blueCurves.png") no-repeat -10px 0; } .outgoingItem .tableBubble .message { background:url("../images/blueBackground.png") repeat-y top left; } .outgoingItem .tableBubble .messageRight { background:url("../images/blueBackground.png") repeat-y top right; } .outgoingItem .tableBubble .bl { background:url("../images/blueCurves.png") no-repeat bottom left; } .outgoingItem .tableBubble .br { background:url("../images/blueCurves.png") no-repeat bottom right; } .outgoingItem .followUp { background-color:#ddf0fe; border-bottom:1px solid #fff; } /*incoming */ .incomingItem .myBubble .indicator { background:url("../images/greenIndicator.png") no-repeat top left; } .incomingItem .timeStamp { color:#9ecf35; } .incomingItem .tableBubble .tl { background:url("../images/greenCurves.png") no-repeat top left; } .incomingItem .tableBubble .tr { background:url("../images/greenCurves.png") no-repeat top right; } .incomingItem .tableBubble .head { background:url("../images/greenCurves.png") no-repeat -10px 0; } .incomingItem .tableBubble .message { background:url("../images/greenBackground.png") repeat-y top left; } .incomingItem .tableBubble .messageRight { background:url("../images/greenBackground.png") repeat-y top right; } .incomingItem .tableBubble .bl { background:url("../images/greenCurves.png") no-repeat bottom left; } .incomingItem .tableBubble .br { background:url("../images/greenCurves.png") no-repeat bottom right; } .incomingItem .followUp { background-color:#e2efc4; border-bottom:1px solid #fff; } swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Steel on Yellow Alternating.css0000644000175000017500000000026512227051774031304 0ustar kismithkismith@import url("Steel on Yellow.css"); @import url("../alternating.css"); .outgoingItem .myBubble .indicator { background:url("../images/steelIndicator2.png") no-repeat top left; } swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Steel on Red.css0000644000175000017500000000372412227051774026315 0ustar kismithkismith@import url("../main.css"); /* outgoing */ .outgoingItem .timeStamp { color:#a9a9a9; } .outgoingItem .myBubble .indicator { background:url("../images/steelIndicator.png") no-repeat top left; } .outgoingItem .tableBubble .tl { background:url("../images/steelCurves.png") no-repeat top left; } .outgoingItem .tableBubble .tr { background:url("../images/steelCurves.png") no-repeat top right; } .outgoingItem .tableBubble .head { background:url("../images/steelCurves.png") no-repeat -10px 0; } .outgoingItem .tableBubble .message { background:url("../images/steelBackground.png") repeat-y top left; } .outgoingItem .tableBubble .messageRight { background:url("../images/steelBackground.png") repeat-y top right; } .outgoingItem .tableBubble .bl { background:url("../images/steelCurves.png") no-repeat bottom left; } .outgoingItem .tableBubble .br { background:url("../images/steelCurves.png") no-repeat bottom right; } .outgoingItem .followUp { background-color:#ececec; border-bottom:1px solid #fff; } /*incoming */ .incomingItem .timeStamp { color:#f88f8f; } .incomingItem .myBubble .indicator { background:url("../images/redIndicator.png") no-repeat top left; } .incomingItem .tableBubble .tl { background:url("../images/redCurves.png") no-repeat top left; } .incomingItem .tableBubble .tr { background:url("../images/redCurves.png") no-repeat top right; } .incomingItem .tableBubble .head { background:url("../images/redCurves.png") no-repeat -10px 0; } .incomingItem .tableBubble .message { background:url("../images/redBackground.png") repeat-y top left; } .incomingItem .tableBubble .messageRight { background:url("../images/redBackground.png") repeat-y top right; } .incomingItem .tableBubble .bl { background:url("../images/redCurves.png") no-repeat bottom left; } .incomingItem .tableBubble .br { background:url("../images/redCurves.png") no-repeat bottom right; } .incomingItem .followUp { background-color:#ffdada; border-bottom:1px solid #fff; } swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Steel on Green Alternating.css0000644000175000017500000000026412227051774031070 0ustar kismithkismith@import url("Steel on Green.css"); @import url("../alternating.css"); .outgoingItem .myBubble .indicator { background:url("../images/steelIndicator2.png") no-repeat top left; } swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Green on Blue No Names.css0000644000175000017500000000010012227051774030020 0ustar kismithkismith@import url("Green on Blue.css"); @import url("../noname.css"); swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Yellow on Red No Names.css0000644000175000017500000000010012227051774030056 0ustar kismithkismith@import url("Yellow on Red.css"); @import url("../noname.css"); swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Steel on Blue Alternating.css0000644000175000017500000000026312227051774030716 0ustar kismithkismith@import url("Steel on Blue.css"); @import url("../alternating.css"); .outgoingItem .myBubble .indicator { background:url("../images/steelIndicator2.png") no-repeat top left; } swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Blue on Steel No Names Alt.css0000644000175000017500000000011412227051774030542 0ustar kismithkismith@import url("Blue on Steel Alternating.css"); @import url("../noname.css"); swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Yellow on Blue No Names Alt.css0000644000175000017500000000011512227051774030742 0ustar kismithkismith@import url("Yellow on Blue Alternating.css"); @import url("../noname.css"); swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Yellow on Blue.css0000644000175000017500000000374612227051774026675 0ustar kismithkismith@import url("../main.css"); /* outgoing */ .outgoingItem .timeStamp { color:#bdb410; } .outgoingItem .myBubble .indicator { background:url("../images/yellowIndicator.png") no-repeat top left; } .outgoingItem .tableBubble .tl { background:url("../images/yellowCurves.png") no-repeat top left; } .outgoingItem .tableBubble .tr { background:url("../images/yellowCurves.png") no-repeat top right; } .outgoingItem .tableBubble .head { background:url("../images/yellowCurves.png") no-repeat -10px 0; } .outgoingItem .tableBubble .message { background:url("../images/yellowBackground.png") repeat-y top left; } .outgoingItem .tableBubble .messageRight { background:url("../images/yellowBackground.png") repeat-y top right; } .outgoingItem .tableBubble .bl { background:url("../images/yellowCurves.png") no-repeat bottom left; } .outgoingItem .tableBubble .br { background:url("../images/yellowCurves.png") no-repeat bottom right; } .outgoingItem .followUp { background-color:#f4f0a7; border-bottom:1px solid #fff; } /*incoming */ .incomingItem .timeStamp { color:#7fc5f8; } .incomingItem .myBubble .indicator { background:url("../images/blueIndicator.png") no-repeat top left; } .incomingItem .tableBubble .tl { background:url("../images/blueCurves.png") no-repeat top left; } .incomingItem .tableBubble .tr { background:url("../images/blueCurves.png") no-repeat top right; } .incomingItem .tableBubble .head { background:url("../images/blueCurves.png") no-repeat -10px 0; } .incomingItem .tableBubble .message { background:url("../images/blueBackground.png") repeat-y top left; } .incomingItem .tableBubble .messageRight { background:url("../images/blueBackground.png") repeat-y top right; } .incomingItem .tableBubble .bl { background:url("../images/blueCurves.png") no-repeat bottom left; } .incomingItem .tableBubble .br { background:url("../images/blueCurves.png") no-repeat bottom right; } .incomingItem .followUp { background-color:#ddf0fe; border-bottom:1px solid #fff; } swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Steel on Yellow.css0000644000175000017500000000375712227051774027064 0ustar kismithkismith@import url("../main.css"); /* outgoing */ .outgoingItem .timeStamp { color:#a9a9a9; } .outgoingItem .myBubble .indicator { background:url("../images/steelIndicator.png") no-repeat top left; } .outgoingItem .tableBubble .tl { background:url("../images/steelCurves.png") no-repeat top left; } .outgoingItem .tableBubble .tr { background:url("../images/steelCurves.png") no-repeat top right; } .outgoingItem .tableBubble .head { background:url("../images/steelCurves.png") no-repeat -10px 0; } .outgoingItem .tableBubble .message { background:url("../images/steelBackground.png") repeat-y top left; } .outgoingItem .tableBubble .messageRight { background:url("../images/steelBackground.png") repeat-y top right; } .outgoingItem .tableBubble .bl { background:url("../images/steelCurves.png") no-repeat bottom left; } .outgoingItem .tableBubble .br { background:url("../images/steelCurves.png") no-repeat bottom right; } .outgoingItem .followUp { background-color:#ececec; border-bottom:1px solid #fff; } /*incoming */ .incomingItem .timeStamp { color:#bdb410; } .incomingItem .myBubble .indicator { background:url("../images/yellowIndicator.png") no-repeat top left; } .incomingItem .tableBubble .tl { background:url("../images/yellowCurves.png") no-repeat top left; } .incomingItem .tableBubble .tr { background:url("../images/yellowCurves.png") no-repeat top right; } .incomingItem .tableBubble .head { background:url("../images/yellowCurves.png") no-repeat -10px 0; } .incomingItem .tableBubble .message { background:url("../images/yellowBackground.png") repeat-y top left; } .incomingItem .tableBubble .messageRight { background:url("../images/yellowBackground.png") repeat-y top right; } .incomingItem .tableBubble .bl { background:url("../images/yellowCurves.png") no-repeat bottom left; } .incomingItem .tableBubble .br { background:url("../images/yellowCurves.png") no-repeat bottom right; } .incomingItem .followUp { background-color:#f4f0a7; border-bottom:1px solid #fff; } swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Blue on Green No Names.css0000644000175000017500000000010012227051774030020 0ustar kismithkismith@import url("Blue on Green.css"); @import url("../noname.css"); swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Yellow on Green.css0000644000175000017500000000403512227051774027036 0ustar kismithkismith@import url("../main.css"); /* outgoing */ .outgoingItem .timeStamp { color:#bdb410; } .outgoingItem .myBubble .indicator { background:url("../images/yellowIndicator.png") no-repeat top left; } .outgoingItem .tableBubble .tl { background:url("../images/yellowCurves.png") no-repeat top left; } .outgoingItem .tableBubble .tr { background:url("../images/yellowCurves.png") no-repeat top right; } .outgoingItem .tableBubble .head { background:url("../images/yellowCurves.png") no-repeat -10px 0; } .outgoingItem .tableBubble .message { background:url("../images/yellowBackground.png") repeat-y top left; } .outgoingItem .tableBubble .messageRight { background:url("../images/yellowBackground.png") repeat-y top right; } .outgoingItem .tableBubble .bl { background:url("../images/yellowCurves.png") no-repeat bottom left; } .outgoingItem .tableBubble .br { background:url("../images/yellowCurves.png") no-repeat bottom right; } .outgoingItem .followUp { background-color:#f4f0a7; border-bottom:1px solid #fff; } /*incoming */ .incomingItem .timeStamp { color:#9ecf35; } .incomingItem .myBubble .indicator { background:url("../images/greenIndicator.png") no-repeat top left; } .incomingItem .timeStamp { color:#9ecf35; } .incomingItem .tableBubble .tl { background:url("../images/greenCurves.png") no-repeat top left; } .incomingItem .tableBubble .tr { background:url("../images/greenCurves.png") no-repeat top right; } .incomingItem .tableBubble .head { background:url("../images/greenCurves.png") no-repeat -10px 0; } .incomingItem .tableBubble .message { background:url("../images/greenBackground.png") repeat-y top left; } .incomingItem .tableBubble .messageRight { background:url("../images/greenBackground.png") repeat-y top right; } .incomingItem .tableBubble .bl { background:url("../images/greenCurves.png") no-repeat bottom left; } .incomingItem .tableBubble .br { background:url("../images/greenCurves.png") no-repeat bottom right; } .incomingItem .followUp { background-color:#e2efc4; border-bottom:1px solid #fff; } swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Green on Red Alternating.css0000644000175000017500000000026212227051774030524 0ustar kismithkismith@import url("Green on Red.css"); @import url("../alternating.css"); .outgoingItem .myBubble .indicator { background:url("../images/greenIndicator2.png") no-repeat top left; } swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Yellow on Steel.css0000644000175000017500000000375612227051774027063 0ustar kismithkismith@import url("../main.css"); /* outgoing */ .outgoingItem .timeStamp { color:#bdb410; } .outgoingItem .myBubble .indicator { background:url("../images/yellowIndicator.png") no-repeat top left; } .outgoingItem .tableBubble .tl { background:url("../images/yellowCurves.png") no-repeat top left; } .outgoingItem .tableBubble .tr { background:url("../images/yellowCurves.png") no-repeat top right; } .outgoingItem .tableBubble .head { background:url("../images/yellowCurves.png") no-repeat -10px 0; } .outgoingItem .tableBubble .message { background:url("../images/yellowBackground.png") repeat-y top left; } .outgoingItem .tableBubble .messageRight { background:url("../images/yellowBackground.png") repeat-y top right; } .outgoingItem .tableBubble .bl { background:url("../images/yellowCurves.png") no-repeat bottom left; } .outgoingItem .tableBubble .br { background:url("../images/yellowCurves.png") no-repeat bottom right; } .outgoingItem .followUp { background-color:#f4f0a7; border-bottom:1px solid #fff; } /*incoming */ .incomingItem .timeStamp { color:#a9a9a9; } .incomingItem .myBubble .indicator { background:url("../images/steelIndicator.png") no-repeat top left; } .incomingItem .tableBubble .tl { background:url("../images/steelCurves.png") no-repeat top left; } .incomingItem .tableBubble .tr { background:url("../images/steelCurves.png") no-repeat top right; } .incomingItem .tableBubble .head { background:url("../images/steelCurves.png") no-repeat -10px 0; } .incomingItem .tableBubble .message { background:url("../images/steelBackground.png") repeat-y top left; } .incomingItem .tableBubble .messageRight { background:url("../images/steelBackground.png") repeat-y top right; } .incomingItem .tableBubble .bl { background:url("../images/steelCurves.png") no-repeat bottom left; } .incomingItem .tableBubble .br { background:url("../images/steelCurves.png") no-repeat bottom right; } .incomingItem .followUp { background-color:#ececec; border-bottom:1px solid #fff; } swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Yellow on Steel No Names Alt.css0000644000175000017500000000011612227051774031130 0ustar kismithkismith@import url("Yellow on Steel Alternating.css"); @import url("../noname.css"); swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Blue on Red No Names Alt.css0000644000175000017500000000011212227051774030176 0ustar kismithkismith@import url("Blue on Red Alternating.css"); @import url("../noname.css"); swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Green on Yellow No Names.css0000644000175000017500000000010212227051774030406 0ustar kismithkismith@import url("Green on Yellow.css"); @import url("../noname.css"); swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Green on Steel No Names Alt.css0000644000175000017500000000011512227051774030714 0ustar kismithkismith@import url("Green on Steel Alternating.css"); @import url("../noname.css"); swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Steel on Blue No Names.css0000644000175000017500000000010012227051774030034 0ustar kismithkismith@import url("Steel on Blue.css"); @import url("../noname.css"); swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Blue on Yellow No Names Alt.css0000644000175000017500000000011512227051774030742 0ustar kismithkismith@import url("Blue on Yellow Alternating.css"); @import url("../noname.css"); swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Yellow on Steel No Names.css0000644000175000017500000000010212227051774030422 0ustar kismithkismith@import url("Yellow on Steel.css"); @import url("../noname.css"); swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Steel on Green.css0000644000175000017500000000402612227051774026637 0ustar kismithkismith@import url("../main.css"); /* outgoing */ .outgoingItem .timeStamp { color:#a9a9a9; } .outgoingItem .myBubble .indicator { background:url("../images/steelIndicator.png") no-repeat top left; } .outgoingItem .tableBubble .tl { background:url("../images/steelCurves.png") no-repeat top left; } .outgoingItem .tableBubble .tr { background:url("../images/steelCurves.png") no-repeat top right; } .outgoingItem .tableBubble .head { background:url("../images/steelCurves.png") no-repeat -10px 0; } .outgoingItem .tableBubble .message { background:url("../images/steelBackground.png") repeat-y top left; } .outgoingItem .tableBubble .messageRight { background:url("../images/steelBackground.png") repeat-y top right; } .outgoingItem .tableBubble .bl { background:url("../images/steelCurves.png") no-repeat bottom left; } .outgoingItem .tableBubble .br { background:url("../images/steelCurves.png") no-repeat bottom right; } .outgoingItem .followUp { background-color:#ececec; border-bottom:1px solid #fff; } /*incoming */ .incomingItem .timeStamp { color:#9ecf35; } .incomingItem .myBubble .indicator { background:url("../images/greenIndicator.png") no-repeat top left; } .incomingItem .timeStamp { color:#9ecf35; } .incomingItem .tableBubble .tl { background:url("../images/greenCurves.png") no-repeat top left; } .incomingItem .tableBubble .tr { background:url("../images/greenCurves.png") no-repeat top right; } .incomingItem .tableBubble .head { background:url("../images/greenCurves.png") no-repeat -10px 0; } .incomingItem .tableBubble .message { background:url("../images/greenBackground.png") repeat-y top left; } .incomingItem .tableBubble .messageRight { background:url("../images/greenBackground.png") repeat-y top right; } .incomingItem .tableBubble .bl { background:url("../images/greenCurves.png") no-repeat bottom left; } .incomingItem .tableBubble .br { background:url("../images/greenCurves.png") no-repeat bottom right; } .incomingItem .followUp { background-color:#e2efc4; border-bottom:1px solid #fff; } swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Green on Steel.css0000644000175000017500000000374512227051774026646 0ustar kismithkismith@import url("../main.css"); /* outgoing */ .outgoingItem .timeStamp { color:#9ecf35; } .outgoingItem .myBubble .indicator { background:url("../images/greenIndicator.png") no-repeat top left; } .outgoingItem .tableBubble .tl { background:url("../images/greenCurves.png") no-repeat top left; } .outgoingItem .tableBubble .tr { background:url("../images/greenCurves.png") no-repeat top right; } .outgoingItem .tableBubble .head { background:url("../images/greenCurves.png") no-repeat -10px 0; } .outgoingItem .tableBubble .message { background:url("../images/greenBackground.png") repeat-y top left; } .outgoingItem .tableBubble .messageRight { background:url("../images/greenBackground.png") repeat-y top right; } .outgoingItem .tableBubble .bl { background:url("../images/greenCurves.png") no-repeat bottom left; } .outgoingItem .tableBubble .br { background:url("../images/greenCurves.png") no-repeat bottom right; } .outgoingItem .followUp { background-color:#e2efc4; border-bottom:1px solid #fff; } /*incoming */ .incomingItem .timeStamp { color:#a9a9a9; } .incomingItem .myBubble .indicator { background:url("../images/steelIndicator.png") no-repeat top left; } .incomingItem .tableBubble .tl { background:url("../images/steelCurves.png") no-repeat top left; } .incomingItem .tableBubble .tr { background:url("../images/steelCurves.png") no-repeat top right; } .incomingItem .tableBubble .head { background:url("../images/steelCurves.png") no-repeat -10px 0; } .incomingItem .tableBubble .message { background:url("../images/steelBackground.png") repeat-y top left; } .incomingItem .tableBubble .messageRight { background:url("../images/steelBackground.png") repeat-y top right; } .incomingItem .tableBubble .bl { background:url("../images/steelCurves.png") no-repeat bottom left; } .incomingItem .tableBubble .br { background:url("../images/steelCurves.png") no-repeat bottom right; } .incomingItem .followUp { background-color:#ececec; border-bottom:1px solid #fff; } swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Red on Steel.css0000644000175000017500000000373012227051774026312 0ustar kismithkismith@import url("../main.css"); /* outgoing */ .outgoingItem .timeStamp { color:#f88f8f; } .outgoingItem .myBubble .indicator { background:url("../images/redIndicator.png") no-repeat top left; } .outgoingItem .tableBubble .tl { background:url("../images/redCurves.png") no-repeat top left; } .outgoingItem .tableBubble .tr { background:url("../images/redCurves.png") no-repeat top right; } .outgoingItem .tableBubble .head { background:url("../images/redCurves.png") no-repeat -10px 0; } .outgoingItem .tableBubble .message { background:url("../images/redBackground.png") repeat-y top left; } .outgoingItem .tableBubble .messageRight { background:url("../images/redBackground.png") repeat-y top right; } .outgoingItem .tableBubble .bl { background:url("../images/redCurves.png") no-repeat bottom left; } .outgoingItem .tableBubble .br { background:url("../images/redCurves.png") no-repeat bottom right; } .outgoingItem .followUp { background-color:#ffdada; border-bottom:1px solid #fff; } /*incoming */ .incomingItem .timeStamp { color:#a9a9a9; } .incomingItem .myBubble .indicator { background:url("../images/steelIndicator.png") no-repeat top left; } .incomingItem .tableBubble .tl { background:url("../images/steelCurves.png") no-repeat top left; } .incomingItem .tableBubble .tr { background:url("../images/steelCurves.png") no-repeat top right; } .incomingItem .tableBubble .head { background:url("../images/steelCurves.png") no-repeat -10px 0; } .incomingItem .tableBubble .message { background:url("../images/steelBackground.png") repeat-y top left; } .incomingItem .tableBubble .messageRight { background:url("../images/steelBackground.png") repeat-y top right; } .incomingItem .tableBubble .bl { background:url("../images/steelCurves.png") no-repeat bottom left; } .incomingItem .tableBubble .br { background:url("../images/steelCurves.png") no-repeat bottom right; } .incomingItem .followUp { background-color:#ececec; border-bottom:1px solid #fff; } swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Steel on Green No Names Alt.css0000644000175000017500000000011512227051774030714 0ustar kismithkismith@import url("Steel on Green Alternating.css"); @import url("../noname.css"); swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Green on Red No Names.css0000644000175000017500000000007712227051774027660 0ustar kismithkismith@import url("Green on Red.css"); @import url("../noname.css"); swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Red on Green No Names Alt.css0000644000175000017500000000011312227051774030350 0ustar kismithkismith@import url("Red on Green Alternating.css"); @import url("../noname.css"); swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Steel on Yellow No Names Alt.css0000644000175000017500000000011612227051774031130 0ustar kismithkismith@import url("Steel on Yellow Alternating.css"); @import url("../noname.css"); swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Green on Yellow.css0000644000175000017500000000375512227051774027046 0ustar kismithkismith@import url("../main.css"); /* outgoing */ .outgoingItem .timeStamp { color:#9ecf35; } .outgoingItem .myBubble .indicator { background:url("../images/greenIndicator.png") no-repeat top left; } .outgoingItem .tableBubble .tl { background:url("../images/greenCurves.png") no-repeat top left; } .outgoingItem .tableBubble .tr { background:url("../images/greenCurves.png") no-repeat top right; } .outgoingItem .tableBubble .head { background:url("../images/greenCurves.png") no-repeat -10px 0; } .outgoingItem .tableBubble .message { background:url("../images/greenBackground.png") repeat-y top left; } .outgoingItem .tableBubble .messageRight { background:url("../images/greenBackground.png") repeat-y top right; } .outgoingItem .tableBubble .bl { background:url("../images/greenCurves.png") no-repeat bottom left; } .outgoingItem .tableBubble .br { background:url("../images/greenCurves.png") no-repeat bottom right; } .outgoingItem .followUp { background-color:#e2efc4; border-bottom:1px solid #fff; } /*incoming */ .incomingItem .timeStamp { color:#bdb410; } .incomingItem .myBubble .indicator { background:url("../images/yellowIndicator.png") no-repeat top left; } .incomingItem .tableBubble .tl { background:url("../images/yellowCurves.png") no-repeat top left; } .incomingItem .tableBubble .tr { background:url("../images/yellowCurves.png") no-repeat top right; } .incomingItem .tableBubble .head { background:url("../images/yellowCurves.png") no-repeat -10px 0; } .incomingItem .tableBubble .message { background:url("../images/yellowBackground.png") repeat-y top left; } .incomingItem .tableBubble .messageRight { background:url("../images/yellowBackground.png") repeat-y top right; } .incomingItem .tableBubble .bl { background:url("../images/yellowCurves.png") no-repeat bottom left; } .incomingItem .tableBubble .br { background:url("../images/yellowCurves.png") no-repeat bottom right; } .incomingItem .followUp { background-color:#f4f0a7; border-bottom:1px solid #fff; } swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Red on Blue No Names.css0000644000175000017500000000007612227051774027506 0ustar kismithkismith@import url("Red on Blue.css"); @import url("../noname.css"); swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Yellow on Red No Names Alt.css0000644000175000017500000000011412227051774030564 0ustar kismithkismith@import url("Yellow on Red Alternating.css"); @import url("../noname.css"); swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Steel on Red No Names Alt.css0000644000175000017500000000011312227051774030364 0ustar kismithkismith@import url("Steel on Red Alternating.css"); @import url("../noname.css"); swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Blue on Yellow.css0000644000175000017500000000374612227051774026675 0ustar kismithkismith@import url("../main.css"); /* outgoing */ .outgoingItem .timeStamp { color:#7fc5f8; } .outgoingItem .myBubble .indicator { background:url("../images/blueIndicator.png") no-repeat top left; } .outgoingItem .tableBubble .tl { background:url("../images/blueCurves.png") no-repeat top left; } .outgoingItem .tableBubble .tr { background:url("../images/blueCurves.png") no-repeat top right; } .outgoingItem .tableBubble .head { background:url("../images/blueCurves.png") no-repeat -10px 0; } .outgoingItem .tableBubble .message { background:url("../images/blueBackground.png") repeat-y top left; } .outgoingItem .tableBubble .messageRight { background:url("../images/blueBackground.png") repeat-y top right; } .outgoingItem .tableBubble .bl { background:url("../images/blueCurves.png") no-repeat bottom left; } .outgoingItem .tableBubble .br { background:url("../images/blueCurves.png") no-repeat bottom right; } .outgoingItem .followUp { background-color:#ddf0fe; border-bottom:1px solid #fff; } /*incoming */ .incomingItem .timeStamp { color:#bdb410; } .incomingItem .myBubble .indicator { background:url("../images/yellowIndicator.png") no-repeat top left; } .incomingItem .tableBubble .tl { background:url("../images/yellowCurves.png") no-repeat top left; } .incomingItem .tableBubble .tr { background:url("../images/yellowCurves.png") no-repeat top right; } .incomingItem .tableBubble .head { background:url("../images/yellowCurves.png") no-repeat -10px 0; } .incomingItem .tableBubble .message { background:url("../images/yellowBackground.png") repeat-y top left; } .incomingItem .tableBubble .messageRight { background:url("../images/yellowBackground.png") repeat-y top right; } .incomingItem .tableBubble .bl { background:url("../images/yellowCurves.png") no-repeat bottom left; } .incomingItem .tableBubble .br { background:url("../images/yellowCurves.png") no-repeat bottom right; } .incomingItem .followUp { background-color:#f4f0a7; border-bottom:1px solid #fff; } swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Blue on Steel No Names.css0000644000175000017500000000010012227051774030034 0ustar kismithkismith@import url("Blue on Steel.css"); @import url("../noname.css"); swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Yellow on Red Alternating.css0000644000175000017500000000026412227051774030741 0ustar kismithkismith@import url("Yellow on Red.css"); @import url("../alternating.css"); .outgoingItem .myBubble .indicator { background:url("../images/yellowIndicator2.png") no-repeat top left; } swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Green on Red No Names Alt.css0000644000175000017500000000011312227051774030350 0ustar kismithkismith@import url("Green on Red Alternating.css"); @import url("../noname.css"); swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Yellow on Red.css0000644000175000017500000000373612227051774026517 0ustar kismithkismith@import url("../main.css"); /* outgoing */ .outgoingItem .timeStamp { color:#bdb410; } .outgoingItem .myBubble .indicator { background:url("../images/yellowIndicator.png") no-repeat top left; } .outgoingItem .tableBubble .tl { background:url("../images/yellowCurves.png") no-repeat top left; } .outgoingItem .tableBubble .tr { background:url("../images/yellowCurves.png") no-repeat top right; } .outgoingItem .tableBubble .head { background:url("../images/yellowCurves.png") no-repeat -10px 0; } .outgoingItem .tableBubble .message { background:url("../images/yellowBackground.png") repeat-y top left; } .outgoingItem .tableBubble .messageRight { background:url("../images/yellowBackground.png") repeat-y top right; } .outgoingItem .tableBubble .bl { background:url("../images/yellowCurves.png") no-repeat bottom left; } .outgoingItem .tableBubble .br { background:url("../images/yellowCurves.png") no-repeat bottom right; } .outgoingItem .followUp { background-color:#f4f0a7; border-bottom:1px solid #fff; } /*incoming */ .incomingItem .timeStamp { color:#f88f8f; } .incomingItem .myBubble .indicator { background:url("../images/redIndicator.png") no-repeat top left; } .incomingItem .tableBubble .tl { background:url("../images/redCurves.png") no-repeat top left; } .incomingItem .tableBubble .tr { background:url("../images/redCurves.png") no-repeat top right; } .incomingItem .tableBubble .head { background:url("../images/redCurves.png") no-repeat -10px 0; } .incomingItem .tableBubble .message { background:url("../images/redBackground.png") repeat-y top left; } .incomingItem .tableBubble .messageRight { background:url("../images/redBackground.png") repeat-y top right; } .incomingItem .tableBubble .bl { background:url("../images/redCurves.png") no-repeat bottom left; } .incomingItem .tableBubble .br { background:url("../images/redCurves.png") no-repeat bottom right; } .incomingItem .followUp { background-color:#ffdada; border-bottom:1px solid #fff; } swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Red on Green.css0000644000175000017500000000400412227051774026271 0ustar kismithkismith@import url("../main.css"); /* outgoing */ .outgoingItem .timeStamp { color:#f88f8f; } .outgoingItem .myBubble .indicator { background:url("../images/redIndicator.png") no-repeat top left; } .outgoingItem .tableBubble .tl { background:url("../images/redCurves.png") no-repeat top left; } .outgoingItem .tableBubble .tr { background:url("../images/redCurves.png") no-repeat top right; } .outgoingItem .tableBubble .head { background:url("../images/redCurves.png") no-repeat -10px 0; } .outgoingItem .tableBubble .message { background:url("../images/redBackground.png") repeat-y top left; } .outgoingItem .tableBubble .messageRight { background:url("../images/redBackground.png") repeat-y top right; } .outgoingItem .tableBubble .bl { background:url("../images/redCurves.png") no-repeat bottom left; } .outgoingItem .tableBubble .br { background:url("../images/redCurves.png") no-repeat bottom right; } .outgoingItem .followUp { background-color:#ffdada; border-bottom:1px solid #fff; } /*incoming */ .incomingItem .timeStamp { color:#9ecf35; } .incomingItem .myBubble .indicator { background:url("../images/greenIndicator.png") no-repeat top left; } .incomingItem .timeStamp { color:#9ecf35; } .incomingItem .tableBubble .tl { background:url("../images/greenCurves.png") no-repeat top left; } .incomingItem .tableBubble .tr { background:url("../images/greenCurves.png") no-repeat top right; } .incomingItem .tableBubble .head { background:url("../images/greenCurves.png") no-repeat -10px 0; } .incomingItem .tableBubble .message { background:url("../images/greenBackground.png") repeat-y top left; } .incomingItem .tableBubble .messageRight { background:url("../images/greenBackground.png") repeat-y top right; } .incomingItem .tableBubble .bl { background:url("../images/greenCurves.png") no-repeat bottom left; } .incomingItem .tableBubble .br { background:url("../images/greenCurves.png") no-repeat bottom right; } .incomingItem .followUp { background-color:#e2efc4; border-bottom:1px solid #fff; } swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Green on Steel No Names.css0000644000175000017500000000010112227051774030206 0ustar kismithkismith@import url("Green on Steel.css"); @import url("../noname.css"); swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Red on Steel No Names.css0000644000175000017500000000007712227051774027674 0ustar kismithkismith@import url("Red on Steel.css"); @import url("../noname.css"); swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Steel on Red No Names.css0000644000175000017500000000007712227051774027674 0ustar kismithkismith@import url("Steel on Red.css"); @import url("../noname.css"); swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Red on Green Alternating.css0000644000175000017500000000026012227051774030522 0ustar kismithkismith@import url("Red on Green.css"); @import url("../alternating.css"); .outgoingItem .myBubble .indicator { background:url("../images/redIndicator2.png") no-repeat top left; } swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Red on Yellow.css0000644000175000017500000000373612227051774026517 0ustar kismithkismith@import url("../main.css"); /* outgoing */ .outgoingItem .timeStamp { color:#f88f8f; } .outgoingItem .myBubble .indicator { background:url("../images/redIndicator.png") no-repeat top left; } .outgoingItem .tableBubble .tl { background:url("../images/redCurves.png") no-repeat top left; } .outgoingItem .tableBubble .tr { background:url("../images/redCurves.png") no-repeat top right; } .outgoingItem .tableBubble .head { background:url("../images/redCurves.png") no-repeat -10px 0; } .outgoingItem .tableBubble .message { background:url("../images/redBackground.png") repeat-y top left; } .outgoingItem .tableBubble .messageRight { background:url("../images/redBackground.png") repeat-y top right; } .outgoingItem .tableBubble .bl { background:url("../images/redCurves.png") no-repeat bottom left; } .outgoingItem .tableBubble .br { background:url("../images/redCurves.png") no-repeat bottom right; } .outgoingItem .followUp { background-color:#ffdada; border-bottom:1px solid #fff; } /*incoming */ .incomingItem .timeStamp { color:#bdb410; } .incomingItem .myBubble .indicator { background:url("../images/yellowIndicator.png") no-repeat top left; } .incomingItem .tableBubble .tl { background:url("../images/yellowCurves.png") no-repeat top left; } .incomingItem .tableBubble .tr { background:url("../images/yellowCurves.png") no-repeat top right; } .incomingItem .tableBubble .head { background:url("../images/yellowCurves.png") no-repeat -10px 0; } .incomingItem .tableBubble .message { background:url("../images/yellowBackground.png") repeat-y top left; } .incomingItem .tableBubble .messageRight { background:url("../images/yellowBackground.png") repeat-y top right; } .incomingItem .tableBubble .bl { background:url("../images/yellowCurves.png") no-repeat bottom left; } .incomingItem .tableBubble .br { background:url("../images/yellowCurves.png") no-repeat bottom right; } .incomingItem .followUp { background-color:#f4f0a7; border-bottom:1px solid #fff; } swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Red on Blue Alternating.css0000644000175000017500000000025712227051774030357 0ustar kismithkismith@import url("Red on Blue.css"); @import url("../alternating.css"); .outgoingItem .myBubble .indicator { background:url("../images/redIndicator2.png") no-repeat top left; } swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Blue on Green Alternating.css0000644000175000017500000000026212227051774030701 0ustar kismithkismith@import url("Blue on Green.css"); @import url("../alternating.css"); .outgoingItem .myBubble .indicator { background:url("../images/blueIndicator2.png") no-repeat top left; } swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Red on Green No Names.css0000644000175000017500000000007712227051774027660 0ustar kismithkismith@import url("Red on Green.css"); @import url("../noname.css"); swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Green on Blue.css0000644000175000017500000000373512227051774026460 0ustar kismithkismith@import url("../main.css"); /* outgoing */ .outgoingItem .timeStamp { color:#9ecf35; } .outgoingItem .myBubble .indicator { background:url("../images/greenIndicator.png") no-repeat top left; } .outgoingItem .tableBubble .tl { background:url("../images/greenCurves.png") no-repeat top left; } .outgoingItem .tableBubble .tr { background:url("../images/greenCurves.png") no-repeat top right; } .outgoingItem .tableBubble .head { background:url("../images/greenCurves.png") no-repeat -10px 0; } .outgoingItem .tableBubble .message { background:url("../images/greenBackground.png") repeat-y top left; } .outgoingItem .tableBubble .messageRight { background:url("../images/greenBackground.png") repeat-y top right; } .outgoingItem .tableBubble .bl { background:url("../images/greenCurves.png") no-repeat bottom left; } .outgoingItem .tableBubble .br { background:url("../images/greenCurves.png") no-repeat bottom right; } .outgoingItem .followUp { background-color:#e2efc4; border-bottom:1px solid #fff; } /*incoming */ .incomingItem .timeStamp { color:#7fc5f8; } .incomingItem .myBubble .indicator { background:url("../images/blueIndicator.png") no-repeat top left; } .incomingItem .tableBubble .tl { background:url("../images/blueCurves.png") no-repeat top left; } .incomingItem .tableBubble .tr { background:url("../images/blueCurves.png") no-repeat top right; } .incomingItem .tableBubble .head { background:url("../images/blueCurves.png") no-repeat -10px 0; } .incomingItem .tableBubble .message { background:url("../images/blueBackground.png") repeat-y top left; } .incomingItem .tableBubble .messageRight { background:url("../images/blueBackground.png") repeat-y top right; } .incomingItem .tableBubble .bl { background:url("../images/blueCurves.png") no-repeat bottom left; } .incomingItem .tableBubble .br { background:url("../images/blueCurves.png") no-repeat bottom right; } .incomingItem .followUp { background-color:#ddf0fe; border-bottom:1px solid #fff; } swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Red on Yellow No Names.css0000644000175000017500000000010012227051774030056 0ustar kismithkismith@import url("Red on Yellow.css"); @import url("../noname.css"); swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Blue on Red Alternating.css0000644000175000017500000000026012227051774030351 0ustar kismithkismith@import url("Blue on Red.css"); @import url("../alternating.css"); .outgoingItem .myBubble .indicator { background:url("../images/blueIndicator2.png") no-repeat top left; } swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Steel on Blue No Names Alt.css0000644000175000017500000000011412227051774030542 0ustar kismithkismith@import url("Steel on Blue Alternating.css"); @import url("../noname.css"); swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Yellow on Steel Alternating.css0000644000175000017500000000026612227051774031305 0ustar kismithkismith@import url("Yellow on Steel.css"); @import url("../alternating.css"); .outgoingItem .myBubble .indicator { background:url("../images/yellowIndicator2.png") no-repeat top left; } swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Blue on Steel.css0000644000175000017500000000373612227051774026475 0ustar kismithkismith@import url("../main.css"); /* outgoing */ .outgoingItem .timeStamp { color:#7fc5f8; } .outgoingItem .myBubble .indicator { background:url("../images/blueIndicator.png") no-repeat top left; } .outgoingItem .tableBubble .tl { background:url("../images/blueCurves.png") no-repeat top left; } .outgoingItem .tableBubble .tr { background:url("../images/blueCurves.png") no-repeat top right; } .outgoingItem .tableBubble .head { background:url("../images/blueCurves.png") no-repeat -10px 0; } .outgoingItem .tableBubble .message { background:url("../images/blueBackground.png") repeat-y top left; } .outgoingItem .tableBubble .messageRight { background:url("../images/blueBackground.png") repeat-y top right; } .outgoingItem .tableBubble .bl { background:url("../images/blueCurves.png") no-repeat bottom left; } .outgoingItem .tableBubble .br { background:url("../images/blueCurves.png") no-repeat bottom right; } .outgoingItem .followUp { background-color:#ddf0fe; border-bottom:1px solid #fff; } /*incoming */ .incomingItem .timeStamp { color:#a9a9a9; } .incomingItem .myBubble .indicator { background:url("../images/steelIndicator.png") no-repeat top left; } .incomingItem .tableBubble .tl { background:url("../images/steelCurves.png") no-repeat top left; } .incomingItem .tableBubble .tr { background:url("../images/steelCurves.png") no-repeat top right; } .incomingItem .tableBubble .head { background:url("../images/steelCurves.png") no-repeat -10px 0; } .incomingItem .tableBubble .message { background:url("../images/steelBackground.png") repeat-y top left; } .incomingItem .tableBubble .messageRight { background:url("../images/steelBackground.png") repeat-y top right; } .incomingItem .tableBubble .bl { background:url("../images/steelCurves.png") no-repeat bottom left; } .incomingItem .tableBubble .br { background:url("../images/steelCurves.png") no-repeat bottom right; } .incomingItem .followUp { background-color:#ececec; border-bottom:1px solid #fff; } swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Green on Blue No Names Alt.css0000644000175000017500000000011412227051774030526 0ustar kismithkismith@import url("Green on Blue Alternating.css"); @import url("../noname.css"); swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Green on Blue Alternating.css0000644000175000017500000000026312227051774030702 0ustar kismithkismith@import url("Green on Blue.css"); @import url("../alternating.css"); .outgoingItem .myBubble .indicator { background:url("../images/greenIndicator2.png") no-repeat top left; } swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Red on Yellow Alternating.css0000644000175000017500000000026112227051774030736 0ustar kismithkismith@import url("Red on Yellow.css"); @import url("../alternating.css"); .outgoingItem .myBubble .indicator { background:url("../images/redIndicator2.png") no-repeat top left; } swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Steel on Yellow No Names.css0000644000175000017500000000010212227051774030422 0ustar kismithkismith@import url("Steel on Yellow.css"); @import url("../noname.css"); swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Red on Blue No Names Alt.css0000644000175000017500000000011212227051774030176 0ustar kismithkismith@import url("Red on Blue Alternating.css"); @import url("../noname.css"); swift-im-2.0+dev6/Swift/resources/themes/Default/Variants/Steel on Red Alternating.css0000644000175000017500000000026212227051774030540 0ustar kismithkismith@import url("Steel on Red.css"); @import url("../alternating.css"); .outgoingItem .myBubble .indicator { background:url("../images/steelIndicator2.png") no-repeat top left; } swift-im-2.0+dev6/Swift/resources/themes/Default/Status.html0000755000175000017500000000106012227051774024013 0ustar kismithkismith
%message%
%time%
swift-im-2.0+dev6/Swift/resources/themes/Default/Incoming/0000755000175000017500000000000012227051774023405 5ustar kismithkismithswift-im-2.0+dev6/Swift/resources/themes/Default/Incoming/NextContext.html0000755000175000017500000000021012227051774026552 0ustar kismithkismith
%message%
%time%
swift-im-2.0+dev6/Swift/resources/themes/Default/Incoming/Context.html0000755000175000017500000000131212227051774025717 0ustar kismithkismith
%message%
%sender% @ %time%
swift-im-2.0+dev6/Swift/resources/themes/Default/Incoming/Content.html0000755000175000017500000000134212227051774025710 0ustar kismithkismith
%message%
%wrapped_sender% @ %time%
swift-im-2.0+dev6/Swift/resources/themes/Default/Incoming/buddy_icon.png0000644000175000017500000003363312227051774026242 0ustar kismithkismithPNG  IHDR>a pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3-gAMA|Q cHRMz%u0`:o_F,IDATx}yeUuo3ݹ[#cQA&a"1 AEDԨ<>D?/bT>$iD!"@wuWWWt{k?VQT{ν[{-Xٲ^}&xcthuWqU^rgs+х|[M oi+G UF,i XR*z.W^?2433l? U6|w߼c.y#j"`a9k5;F$=u2;u|= _G֭^u<>p}ϼ7p ˴kW~OuxjYc`J]?sZ^Gn:l)ǫrXn 9~G 0Mm{>%}S/yū]q#=/*K 9m>7+L@lb_zOud*;6 A b>=l@,ᛴzzZX( !0 1m_(7z%'ׯ]6;#x=ϼѿAo$O~/kō^ykV۶e蜈߮Vƚ-b[=';ȀOtL4;k{r$n8|.VBJM})0+0X>)}(J(Lޛ3Q no9VG+8C7:zerUՑ>78AOGKi{yo\6Tp~ < #BHH@{*n:zY>"~{Ŀ]0^[ r"@PѾJ%*NN@cL2l˲*sW$oן)嵇gӞOI(-'w_B@iH FIt޺ʳtOq|ay|ub7E 4- IxND:&-%Zst◡V%-icOF'#ހ4fұb03pہ2O u R_#JCDz_n??<} b=38CTRQ(9}> fho\uν|m> 3p]mY9˂-ɚN[;.@CO峢?s{gPͯ'0hG E2׎߈l@ޓOw{T|w x7Ov#cZiI   Mg3bPP1r;[A! f"!TP@>ٟioç~|6F`F,͠Q^Fۅ<-K%`Xݐq)Ŀq;AS~QBE3Yt#kW1]w\װ-$`!z@k3l5Zc>lp?vP*kO~Mvٟnڮt͹b?\_}y $}Zh'ďMD@M8roӈ&#W*^AVkܮ_ĸ"'i"v*G$((I{އ1?3r $ P߷NbۦI⿝u:< =GNEp"`9A.qמ|wlvK`Mr"{Og,J0"5v\Q݄o Asߌ;g"fy:p"R-ѓM/ _v1uq"$&(+("0SBAA W_f[>=h4>|;)M}@n:j:رmFG:~H-W}O:fPLh7h>r}˻P tjr'\D`"(q-Ҙ& ڞ.?وziwv'&@x氃_14vGT2( i9cZE_q^pI _TjiE%0~/U3go ڢ@!C))")- 4i#&<7T:a3,X8p6: 4M4M(^roy[7 h4}/kÙ1D%Q@6e`IڧBW'=4kݡ(M&] dK`i?n)sPk0. _o5=LLL$)#Ҟ>?YS=g"!-Xϧ}= eKݠ[,YZCﷂz"c݀kV+iccd|LiEԶXr& 3ci\Ǭ`M)%c$$@J"J쓩/tOvyYI@d  f ɲo%#*T{q]W 7,9=6,{T#-J?^X{~IAZ$M*"ޏf@Ю{$$m>+?) "w>fh~\8F̘nLcy}@M{3N'l8|ڿΊTV"QL%ii<;h̝QFa %'H޻px`2o`۱:MTmi@~d{1@ID_H)3XEFhM {ڍ0`aQI:ݜU}[cC$N M<$QX qFŤ 8\28z D  mbL'Ddf|_N!2J"(bL'p=F(o+9")` @(:׼+x~]"NF@&\ \ygPbbTV0*+%Hg?x&<303'oʈ|.JADxpx^bO., ؖ`F>YDp]Onk4D5x֯NQ_~FD-Q,E˓~Fr{XqV6#'!Rd66 d|;E5^h^o`ãorD&'-O M܃7yN<$H˩GOއCXѵȮ7" fH?&fY"HLX7Wdm?w Ƌ5$lBwJd!'Ǩ`ƪq\+-alwj.i{OC vcIoB5š~S\~s;l?n1 Q:<"hKo.R8J-`=5l'!K_K5Q1tN,c~U8[`Ɔ;MT]VC~9y% ?+.飯uw灡Zh nu?p vs[n]3F?n.jkWѥ/wP-௟8W/n֭['W8 @SG~OR7SYN v43~moҪ'H֚E{?hN}wnݺ`){Ow{H|g˚vΉK6k5 !%y v[0\^;8À39i̔FT]x+$V [s@0]v镥/,n}ݺnݺ#{( K4}[4,4w:PA ;Y1oݜ,7'W 2.8U8ԀN8sEYy֭9g^q«To)已=Hۆe{]BH !5(;}ϣ_UWX5t?‰%܈7 (BB^9ڮ%+oC-D>Fef2k!? oδ~vEvN+yGBc+ h0nWPB r2+ʪg~Dܼ@$ FyʼnxE=-*P}&*>M!/ ˲Ъᵖe}+N<{Ed{'^dΥUCk% Z;t'F籁%e0CՑq˶me ڃuUze"q:84:¥;\  876,N9 TUD56$FaAB iD4{n!W( lחD%# x_WW zJ8]"} 3m7BDtfKnBE HrJ  %hJZGЦU\iGF1z-A׿dڧ0O'LёlV0qnOF̈́P80:yDԤp 9zdV 0'.%j!!¨-NqT8tGGRZX|w {&,H! a1RR߳زA`4:^uC\|@NwGW^+b_P:Btg;`Ű#/Dof>1+`Y|J %(A>\ "M;ٰF϶0ZE Nc/}x`ñlR~aWs- 0)fA, &'"\ `;?P VWy碟q}D[ְ; 5ᘞyX4;-Hk"I)zX*ڳDO `+yJ[XQ `d>[ lM X}cwv#> `EQ)@K 1_<Ю?c@"{, L,;?AaF%b TQO%0^5`kG_T> / x ^v!Ë&Vg˶jىP  +%n4b3]$c肋W͝qSP`z Fu y/26UfT{܏j}.a~“!W}㶽E}0VIL E6̐oJk kQtiw0 *DRű9+Yp2]Z2RVd>c ҟטF F&dr! ܏$h0rxxiۦ,P?xu0J )R0/Sv6ËTj^n̐ fK^[`\G,!ݒi3Zp /-E2ET\25)&`(x aBgl\LN٨D"kbօ"#j'Nfl FrR % 気JI}Gt 8{d$LHs3<&:sumRPl+`leFMCmqw 8 7}QOS O=ޤ^B/fx[R t?`Rbbe+p3L 0F#$@OE8ލ4sH̓R9TO!I )znX+?U]?"K˶ >yon8p[flT]@-HPESqv^Šf~7#jDaQfh6!̄uO#!XV0nH';?zz`3|ʉUOXG⇽m303%0ض`JG+|9 `-  2F)F LIl"k*jP?P5~Jnld3ۿ0<͙οZ=@ȨYD"* $j-VA˓x۔2A` Jlw APW}h A^޹@7mݘ@=[[u8% 8SHsDId5ۙJ%p]-Ʃ% 5iX/VyA--mH2P`b@]C(YOyߙx.@lZNosg~4QaMGڊYF؈4[X m攅@n]0SVSΈB@kBؒclUj? *>!(zMͺ!LC<^|j~$ ) {@1zeHqV}Q&Q]A(ڌC Za^A%bFHd w[S 1:H|2cq/@;h* -@-7la&1"Ώ/&azc76&ǼӪ/# hޕ !=[Q*@7b7l=\9L'YI h JKȹ,O%UD&u> CSDhA^8+7 qfvKƎN,3ܹެ"#78&#1;z䱥"sG $ZS=Mj=s}L|u =&WS V& 6K7W^GuO(قIhN C#8SOHE+" R+6_>So}`ǢߏG ^@0A^Ku+  :d%-yd7m6ld֎@Z(K,N{{ԣOܿ㙞Ib}7O0 87'H Yh;a/yMn9YCur~%m}6@$$ !XBT<ќ<6; dD7!=F}-|@p `,_<|AclZeyr\ڢ`{֡Kpf$PHY9v& 4v֎s6vÍU=\or~o&Rɘ`D%i2_{#Jkv'ӷl+ȽK}A}i+ "s@x[1E߻HW//), f@`zv 4K/ұ;}W`ݐ;{]2D7?Eo` IENDB`swift-im-2.0+dev6/Swift/resources/themes/Default/Incoming/NextContent.html0000755000175000017500000000021312227051774026543 0ustar kismithkismith
%message%
%time%
swift-im-2.0+dev6/Swift/resources/themes/Default/images/0000755000175000017500000000000012227051774023107 5ustar kismithkismithswift-im-2.0+dev6/Swift/resources/themes/Default/images/yellowBackground.gif0000755000175000017500000000016512227051774027116 0ustar kismithkismithGIF89a !, :$0I8ͻ`(dihlp,tmx|pH,Ȥrl:h0H;swift-im-2.0+dev6/Swift/resources/themes/Default/images/steelBackground.gif0000755000175000017500000000017212227051774026715 0ustar kismithkismithGIF89a !, ?S0I8ͻ`(dihlp,tmx|pH,Ȥrl:ШtJZW ;swift-im-2.0+dev6/Swift/resources/themes/Default/images/steelIndicator.gif0000755000175000017500000000051012227051774026546 0ustar kismithkismithGIF89a !, e@p(x 01H4b3( i Bs8@I!|*A 00D1(}s"'m !b #) % & $A;swift-im-2.0+dev6/Swift/resources/themes/Default/images/blueBackground.png0000644000175000017500000000024512227051774026545 0ustar kismithkismithPNG  IHDR  GtEXtSoftwareAdobe ImageReadyqe<GIDATh1 0W h@LtLL ݙ`K."bJ6CUUUUUUUUUUUr#| I+IENDB`swift-im-2.0+dev6/Swift/resources/themes/Default/images/yellowHeading.jpg0000755000175000017500000000264712227051774026420 0ustar kismithkismithJFIFddDuckyPAdobed      7T  ?[Z @P( @P( @P( @P( @P( @P( @P( @P( @P( @P( @P( @P( fZP(V+J ҅iBZP(V+J ҅iBZP(V+J ҅iBZP(V+J ҅iBZP(V+J ҅iBZP(V+J ҅iBZP(V+J ҅iBZP(V+J ҅iBZP(V+J ҅iBZP(V+J ҅iBZP(V+J ҅iBZP(V+J ҅iBZP(1T(E (QBP(E (QBP(E (QBP(E (QBP(E (QBP(E (QBP(E (QBP(E (QBP(E (QBP(E (QBP(E (QBP(E (QBPN ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) )sV+J ҅iBZP(V+J ҅iBZP(V+J ҅iBZP(V+J ҅iBZP(V+J ҅iBZP(V+J ҅iBZP(V+J ҅iBZP(V+J ҅iBZP(V+J ҅iBZP(V+J ҅iBZP(V+J ҅iBZP(V+J ҅iBZP(V+J%LU(E (QBP(E (QBP(E (QBP(E (QBP(E (QBP(E (QBP(E (QBP(E (QBP(E (QBP(E (QBP(E (QBP(E (QBP(Ea@swift-im-2.0+dev6/Swift/resources/themes/Default/images/blueIndicator2.png0000644000175000017500000000057412227051774026471 0ustar kismithkismithPNG  IHDR atEXtSoftwareAdobe ImageReadyqe<IDAT(ύJa}F5luQ4Rl#ID ҍ3O u~~puÞM?o doqI@R]5"3P)4<(NNrЯ}3@Ɯ&֔p6@y& V8a%ȹR6[t7H0bЈ_¤E7#NKN.XSd:m NZ& /ƣaqkmrViq}W~y|\< ;j隥U".O 2.H&###7--M߿y{n'~t{Iڐ'XT蔂g977pv~{hVV B_[[sKKK )[J f{~~wsss+++F!/4{*"T{Q޽La.(R lqqѵ"o~{}ä})NWRRD8 m={cO8Z@`:tȝüe799O:f4??Ϝ`,Qۀ_&;Xp!iNMaۢ9'|=zT)B})K|;6'g3DPxT"ߘ70Gɓ'wAp;b^*]da6wCCCꐴXwB0!e\EG?|V{{myA ѥ! JmIe $Ey7["u{uŪd3nh֭[Ç*ﰀDhX)?Y&*mc4 $mo>f/qh'͉'ɐ$!JJY@ ;VpaHd=7A|. ,7xҲ,4Xl) @"*|N_ʏ 5Tg{VqdN)Q&)Uʰ dpD5 ̣Zi`T]WWRIJ-'c:+ؾ0ϲ%-6!RUR^[m!_a 0(lV5j]rE2L@ZmccCz+(a1655{L[#GhyÏ$ VJ[ >k:!g d\TF===<P;'kez}؁â±>?}MLL(Ooo P[[`1ձ1-&ɺMQA" 7X8pMMM`rQYQ3l?yIٖhrAFO7t>sDb.ItLVS;`bƼa1Nj90zOvSGFە(=NɈw=UY"]wgUhFUNVv ̙33.IR*ҩ(5qi9& ߂K(꺴v¿:Mx@PHMX$h&}ܢ}ATUx۲9[HȈuiY!l߾}[}1cDV Jx;8 uwUlQ/V8|JuE%ZaNlim׻|]=_ ŭʮIENDB`swift-im-2.0+dev6/Swift/resources/themes/Default/images/greenIndicator2.png0000644000175000017500000000053112227051774026633 0ustar kismithkismithPNG  IHDR atEXtSoftwareAdobe ImageReadyqe<IDAT(czWH~<~7j ׋N=jK?v@'"^M=ß~>ﶁ4d 6'?|gKa-PC@>Q!gl~8³`ϖ_~(?cn$:t!XcE&H 5D{@P/P N>PB$cLsIENDB`swift-im-2.0+dev6/Swift/resources/themes/Default/images/yellowTL.png0000644000175000017500000000050612227051774025371 0ustar kismithkismithPNG  IHDRFgAMAOX2tEXtSoftwareAdobe ImageReadyqe<IDATxbdP̄ %0ˉuNb|1,L,:@1ft,Hجη p%sqpp20 cu & fqGXD> AMAaxJTL5_h 1XWLL-4FGYi23101snAx7 kf?#%_ly30g `8 )gIENDB`swift-im-2.0+dev6/Swift/resources/themes/Default/images/yellowIndicator.png0000644000175000017500000000057412227051774026773 0ustar kismithkismithPNG  IHDR atEXtSoftwareAdobe ImageReadyqe<IDAT(cd 01[SEŬNO-7D #64Trr5 101qVOh 'p3\_%#$ģɥČo_Rc`dbd`ffY..ĆWgaf`eg`#.(^lS`bee`g ca`cd`a:8byx9aIDATx}}?̾Y0m8m) d M uqJ RZiIP %Ĺ$;}o;;7;:}G;77<=/2e,cX2O u@| t֒O q1E3/;'4l#.r"L9sLu 'Pk ?5i8>V$1 PI*NZ`4k7mHsc?Ek1zyPM )ESϰx;!>, ]Bܻ) ;:_R qK8XCsuA L77CJN,UtiC@bp+r2mjRc#F8y)鈮B@<8<#x7ӨJ{6jJU$F-zʎd;hR %bW eIqВ2_T2 k {vOwjn#@Q~m`&qMeHrCE_kmYd7u-}eAWPdc>A $6-M'ASY MInBbM8Jtѿ"*W1@NݿKrNpjRf}K@7\V3I "$G)[R1nF`JLV<+ԾSch ]:NEc04h4:N_LHPzXDNZ$د_/AT#L_k<1t؏͂ao7hN&8$J>֗ eZS!d@(;.砤zU7_H )o)@ Z@ دyI]y2ptHS%`X_ = @.Nu h!.3p%871 D),i%Zo4vt[ tIw#'n:74dQ8V10=iJ$0KȺ>~#^|x}rP ^M9-3m#;X:0p)Npof$n!U&)Z (:SٽlPPhfj%\f8bxۦA'8ў8;/G%5mr Pe' 2Jҿ jl(AXUuԯ["LqHA5HU"&]/1 hCp Pn!Re`~\9BAd7ak_UkraAA$@o^+&7 k -D'ؗa*),00y̌ĜoR2 !'F'ѥqXk;/T-4NEٓLy*NdiJ5"f !q$IE=V'`8kh`|k=1d5g^ɵL Pʝi%I&dPXߣR-EPP|fURb$BBl$/9zԡp 9s -@AQh*CU!j30af *ɠf$-.rrXfsu@=A +Us-scyܐfj \w:g}աL}^H )8-7Uoœ13^jv2$z-'Z;PpR#M0-G'# nׯJo\]'IaK\b'~\#aN_1¯LDɇm;p2-*#~^G$ۯL}&`aQ/4asTp>Z @<0&uԂɱo\ ㄓwzi6fZD'II3.`EJ'ξcG-A PXphvDmf`2 N^Q5O6 4N 8<鷾·~\<ўm5Èd= !JbΗSf5^p;l"!nnF^qs2ƭM~!\8-ϙhQ EV?xh^|U^G Xf~jֲ_(NT]$Z&`p@?CZ"pf#Iv80}cF@5U*E$3IW E6?}qȱޒ򼄂S3 |Cf! 1+b >N1 C,8-#Si϶4&@II_gjXZ/4!XBg/gʶ(mH({kĹ*Wc] ("rK&ў*[IromvUsWMkIyp뷨k hred^|V[Ե%`52Zׯu/e5;ov ?ͶO3g'\ mZf|ŚXMis~r7_ُw[m?/S9, 寴h (~ 2rZJD*έ?jh&JOZ=uz_4 " ?uzih ZBjnD鍸 ML"HGc ME?J_\g$@7M85dLRaJ6wC폵 '{w :3Q2ZD5S5W? q(ׯ6,uS\sQJfV$4xiT8Rbu/7&%hx-1,h7ۆfhG9j5/m F@&T@q.};Stln#;OC(3twX\ݸnw Vv!)z<Ï}PKr>I%)E @o] i0 8j[S~j8&kƶ 9$dC(mfvc4 îW z9C> ,ZֳՐ ,.03t3?|qzJъl۰A Eg_Ip?R @GiwH k0m]Zn0<)Jbdy`X{xO=UBN?9dO>]u\Ht[6չxbn< Ț^>OJ*}~'uFC֋n#'Ac@Ɏ>_{DP JU@l5S"'0} Wվ-M&`YRjS_K4X/BRS_sn[uy֦j鑛-m]'j}A*rju+@M&`ق_@`+!F{8;ftȠ:!1B`PlNŘ .nhk8wXH*<_yP{6l< /8CΜ6,L]J7B%g޴@5Ka{0|>-n)E89AZ x$y( v)MI=<?=|ژ(&V9cǫkJ(gUk9vb XVR| h{[^)IX 7oe#U' gS4Ě۔ 'nN8qA޵4=?c~/Ix+|zmeij`c$A" =+g޻޸ [y4KoɠA9Ftc6n r8ѼDFC%[}ñD9LXV۲9s Iγb"Y5~ S9wo=.ܮTFC~f>@U p"Ns9#v"c%Mt]rX\U<,L1sqi4 |~& pzJz\N^_ß|;3ԣWg6~3h&pM[8Oa ?|U8\QV[3J+Wp^V"0"Nqv:O4`mv[ )XbE_>)T&)~9^ʨta[7}\Jk߂wMȔc&vy}lK G H@J3a~8.+`#4T#yH!ŀ\e-[6auJ!.B[oB:\:sy#Ø =1' p4j…DD!20aԛɺ vhTV$y~cUŎevO !Ի6J0o2-Z0Wad[/Q2f.W452u: LkUF%e4^z)\QkU#[7`\nࢦ^_IENDB`swift-im-2.0+dev6/Swift/resources/themes/Default/images/redIndicatorAlt.gif0000644000175000017500000000101712227051774026645 0ustar kismithkismithGIF89a 칹ּ\鵵!, l3AA@@#6:7;@91(089>*/! :8??,5 =?>< =><=$" <='2)= %6-&+.46:7;swift-im-2.0+dev6/Swift/resources/themes/Default/images/yellowIndicator2.png0000644000175000017500000000062012227051774027045 0ustar kismithkismithPNG  IHDR atEXtSoftwareAdobe ImageReadyqe<2IDAT(ύJQ辖`' +EDP$bgf71;wM3pas8CSчU J2bjljxWFƄڬixWK,m(@3uUZ_s1 c~_h+%'-k}][Ju 'MrJ1vƸjs-[O/Ӟ9 wg8yFlHB9OiX@?&AکzkQvU=ج4Gn(E lޝIENDB`swift-im-2.0+dev6/Swift/resources/themes/Default/images/redCurves.gif0000755000175000017500000000473712227051774025556 0ustar kismithkismithGIF89a 载俿!,  [[  #?-H] H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲK.`0  ,!ę@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ 7 Ah.!ɕ2#KL˘3k̹ϠCMӨS^3#U 倘ָsͻ Nȓ+_Μ 10سkνËO~ 𠀘˟OϿ(h& 6F(Vhfv ($h(,0(3&{Ѩ<@)DiH&L6PF)TV8~\v`)dihlp)tix|矀*蠄j衈&袌6裐F*餔Vj饘f馜vꩧ 姤jꩨꪬ꫰*무j뭸뮼믵&@6Ʊ&6F+Vkfv+k覫+k,l' 2,Wlgw ,$l(,<4l8<@-DmH'L7PG-TWmXg\w`-dmhlp-tmx}0sހ.n'7G.WngA!褗n騧ꬷ.n/o'7G/Wogw/o/o H28'H Z̠7z GH(L W0 gH8̡w@ H"HL&:P 4pX̢.z` H2hL6p*" x̣> IBL"F:򑐌$'IJZ̤&7Nz (GIRL*WV򕰌,gIZ̥.wK^R @zIbL2f:Ќ4IjZ̦6nz0LpL:viq @JЂMBІ:D'JъZͨF7юz HGJҒ(MJWҖ0LgJӚ8ͩNwӞ@ PJTR,@QԦ:PTJժZXͪVծz` XJֲE BRֶp\J׺xͫ^׾ `KMb:d'KZͬf7z hGKҚMjWֺ}Dh /hBlw pKMr:ЍtKZ7$@A %8HpMz|K|q`7]K;'L [ΰ7{ GLX7p 0fπMBЈNh?w@ M 10\@6N{ӠGMRԨN%VհgMZָεw^MbN(&/ $I4 DH#%D9D83=+1*A7.@ 6< AF B;swift-im-2.0+dev6/Swift/resources/themes/Default/images/greenCurves.gif0000755000175000017500000000627312227051774026101 0ustar kismithkismithGIF89a ~榿܃ľՏ۫Ծہޮ؎މϫۀߩӜѡ˿܂܍؃ߕޓ}!, KNet H*\ȰÇ#JHŋ3jȱǏ CZ0K &D(@*A6$I͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻl}l" 1*@rĀÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+/> *!سkνËOӫ_Ͼ˟OϿ(h& 6F(Vhf`pIƆ$h(,0(4h8<5vD 0 ǑH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袋 !餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vk*iv+k覫+k,l' 7G,Wlgw \ a(,0,4l8<@-DmH'L7PG-TWmXg\w`-dmh%<ip-tmx|߀.n'7G.Wngw砇.褗n騧ꬷ.ӞEơ/o'7G/Wogw/o觯/o"@L:'H Z̠7z GH(L W0 gH8̡w@ H"HDb 8&:PH*ZX̢.z` H2hL6pH:x̣> IBL"F:򑐌d$5X̤&7Nz (GIRL*WV򕰌,gIZ̥.w^ 0IbL2f:Ќ4IjZ̦6Y 8IrL:v~ @JЂMBІ:D'JъZͨF7юz HG:R$P (MJWҖ0LgJӚ8ͩNwӞ@ PJԢHMRԦ:PTJժZXͪVծz` XJֲhMZVj@p\J׺xͫ^׾ `KMb:d'KZͬf7z hGKҚMjWֺlgKnw pKMr:ЍtKZͮvz xKMz|Kͯ~׿'LN;'L [ΰ7{ GL(NW0gL8αw@L"G0&;PL*[Xβ.{`L2hN6pL:xγ>πMBЈNF;ѐ ȆJ[Ҙδ7N{ӠGMRԨNWVհgMZָεw^MbNf;ЎMj[vs@"MrNvMzη~NO;'N[ϸ7{ G>rRHW0gN8Ϲw@ЇNHOҗ;PԧN[XϺַ{`NhO80!xϻOO;񐏼'O[ϼ7{GOқOWֻgOϽw{܏@2B`+4A?;ЏO[Ͼ{OOOϿ8Xx ؀DO+Q!b3t""8$X&x(*,؂.0284X6x8:<@P4pp@,RNPR8TXVxXZ\؅^`b8dXfxhjl؆npr8tXvxxz|؇~8Xx؈H,@@ &@Xx؊8Xx؋8XxȘʸ،8Xxؘڸ؍8Xxȍ@& (;swift-im-2.0+dev6/Swift/resources/themes/Default/images/silverBackground.gif0000755000175000017500000000016512227051774027107 0ustar kismithkismithGIF89a !, :B0I8ͻ`(dihlp,tmx|pH,Ȥrl:h0H;swift-im-2.0+dev6/Swift/resources/themes/Default/images/steelBackground.png0000644000175000017500000000023412227051774026730 0ustar kismithkismithPNG  IHDR  GtEXtSoftwareAdobe ImageReadyqe<>IDATh1@Rvd2(5>"̳}UUUUUUUUUUU/g橪\"IENDB`swift-im-2.0+dev6/Swift/resources/themes/Default/images/steelCurves.gif0000755000175000017500000000417112227051774026110 0ustar kismithkismithGIF89a !, @y rl:ШtJZجvzxL.zn|N~CBF $D  H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳ@ JQ( 4;JիXjʵׯ`ÊKٳhӪ]˶۷p( z20߿ LÈ+^̸ǐ#KN*̹ϠCMӨS^ͺ<Ȱ۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼ˟OϿ( \P[& 6F(Vhfv ($hbwV'0(4h8<@)&JЀHL6PF)TViXf\vH.@ɘdihlp)tix|矀I j衈&袌6裐F*餔Vj饘f馜J*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkfv+k覫Bpd+k,l' 7ɺfWlgw ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w'dmhlp-tmx|߀s n'7G.WngNx砇.褗n騧ꬷ.`/o'7G/Wogw/o觯/o`HL:'H Z̠7z G8(L W0 gH8̡w@ H"HL&:PH*ZX̢.z` H2hL6p#޵8x̣> IBL"F:򑐌$'II'ba&7Nz (GIRL*WVԤ ̥.w^ 0IbL2f:Ќ4IjZ̦6nz 8IrL:v~ (@'P|/hІ:D'JъZͨF7юz HGJҒ(MJ 4 * ,t8ͩNwӞ@ PJԢHMRԦ:UT @%PA J ` XJֲhMZֶp\J׺xͫ^׾  4p8d'KZͬf7z hGKҚMjWֺlgKͭnw pKMr:ЍtKZeP@v0@& $Mz|Kͯ~LN;'L [ΰ7{ GL(NW0q-t`3B(pN@L"HN&;P>%r̀ ;swift-im-2.0+dev6/Swift/resources/themes/Default/images/greenBackground.gif0000755000175000017500000000016512227051774026703 0ustar kismithkismithGIF89a !, :H0I8ͻ`(dihlp,tmx|pH,Ȥrl:h0H;swift-im-2.0+dev6/Swift/resources/themes/Default/images/blueIndicator.png0000644000175000017500000000056212227051774026404 0ustar kismithkismithPNG  IHDR atEXtSoftwareAdobe ImageReadyqe<IDAT(cd 0nkK#:]Ԭ-@&M~Vrj0113ĸC)R>|5!㺙t-mX=8$x$gz迿}B]lg^&t6e+!|gm޶u ;N&ڱ)yUn+?_O]g#S""hJ>Wg;^;]NcՉt#S"f=8iŸUVRD1=_lu{QFDG'w<ȋW<)lOV7מ?GsxMOO"EiM:GuJ=wnw^D+"ZQ쿹8kF"RKTµ^pR=ǹ \㈨RDt"b,"&#brytcybb-ueeUGe[7.\zǿ##}{'(i'o[# ۱s"b8ELGV0~#+|58s u s9? oIENDB`swift-im-2.0+dev6/Swift/resources/themes/Default/images/greenCurves.png0000644000175000017500000000232212227051774026104 0ustar kismithkismithPNG  IHDR ^H tEXtSoftwareAdobe ImageReadyqe<tIDATxMVe9yuPs"0 6ʝjBZFD ZԢE ((2" K>L3Mmf|<-Q[s󜳺?*^ KMRO)hT/8c%#Kٶ|zW2~~{~O=qGMl69=k3ޟLe<K̟9=u4GN~S^ڹeIJM;?oM7_}WVכbwcs VIFxǞaNޗjN#W?k4[v\7vn\S@'4[uS6m>Pdwܼr䪌4))&@'Խ&I3;hUD@TҶi3Ӄ$)5:RJ$ڶTkC:fYn~o(m~{ůw%%$@ 3沷 m~/IJZs3{Rb;]rI~.i莒6Ifo~/wczp&4IfؠJ6U*`̞s?gG?K_y cֱ5ƔX6mryzIzIO?o]՟uzp:M=ӫ\SOЉs_}%TIF'v'ڵy5،U L%5=584unfoO2a>}4B$c_v1 d.x>J)mdԙzx*s` mE{)a{?6BRJ){ b"pjIENDB`swift-im-2.0+dev6/Swift/resources/themes/Default/images/blueIndicatorAlt.gif0000644000175000017500000000101612227051774027021 0ustar kismithkismithGIF89a !, k;$A%<2":A:& +48>>?#*:8) (1=@?-=?9 3=75 99. =0, !/='6;;swift-im-2.0+dev6/Swift/resources/themes/Default/images/steelCurves.png0000644000175000017500000000223312227051774026121 0ustar kismithkismithPNG  IHDR ^H tEXtSoftwareAdobe ImageReadyqe<=IDATxAkuwfv6Wm&%9䲠@bQPzOkC?^zHJ=y)Sx*m&1Yu{Ȯ4#~̞ÓſeˏDD]J)>t)?v[n e^Y{81u]4/^eVLKe,;w>^\\hyysVz۽^o!,ph4ٳg?|q;"RE޽g/^<-k4dkk/7666(#p8bmmJYdY9sfim`0… fY VEwiip8\DDy~͢(SJ拢8GDneXhi"<hi<"wZ)%:h;֬n{=dV@)z^CJI"Ƭnֳ@kY~ ED@k?DDwPU8gM&ߟ?{EDmooųyiӧOܾqED?x`g0"Ϻ˲y^(TU˗/?~p{{k׮},"1/,,u.]~QE8IhpѣGݼyǴ>[ƫ"|luuɓJ4IDTqTxOORJM>+ ߛ?>R:^luhU>_^]tnIENDB`swift-im-2.0+dev6/Swift/resources/themes/Default/images/blueCurves.gif0000755000175000017500000000637012227051774025726 0ustar kismithkismithGIF89a !, 4*C=a1 H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲH<_`#@;IV4 ϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻx˷߿ LÈ RD ,P#G˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+_μУKNسkνѥ& 6DXϾ˟OϿ(h& 6F(Vhfv ($h(,0(4(/xAT1@)DiH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&eܰF*餔Vj饘f馜v駠*ꨤjꩨꪬgYp뮼+k&6F+Vkfv+k覫+kt $ 7G,Wlgw ,$l(,0,4l8<@-DmH'L7PG-SA\w`-dmhlp-tmx|߀.n'7G.Wngw砇.褗^XϠꬷ.n/o'7G/Wogw/o觯/_WcB HL:'H Z̠7z GH(L W0 gH8̡w@ H"ш&&:PH*ZX̢.z` H2hLGDpH:x̣> IBL"F:򑐌$'IJZ̤&7Nz (GIRL*WV򕰌,gIZ̥.uF% 0IbL2f:Ќ4IjZ̦6nz 8IrL:v~ @JЂMhBI?D'JъZͨF7юz HGJҒ(MJWҖ0LgJӚ8ͩNwӞ@ PJԢHMRԦ:PTJժZXjV5j` XJֲhMZֶp\J׺xͫ^׾ڔKMb:d'KZͬf7z hGKҚMjWֺlgKͭnw pKMr6w ]tKZͮvz xKMz|Kͯ~LN;'L [ΰ7aGL(NW0gL8αw@L"HN&;PL*[Xβ.{`L2hN׼wpL:xγ>πMBЈNF;ѐ'MJ[Ҙδ7N{ӠGMRԨNWVհgMZָεu MbNf;ЎMj[ζn{MrNvMzη~NOx0*'N[ϸ7{ GN(OW򖟛 ~j8Ϲw@ЇNHOҗ;PԧN[XϺַ{`NhOpNxϻ^JA;񐏼'O[ϼ7{GOқO֙0'$WC V0LϽwOO;ЏO[Ͼ{OOOϿg}PHP.PhG`|Xx؁ "8$X&x(*,؂.02<kP)PO0` PFxHJL؄NPR8TXVxXZ\؅^`b8dXfxhjl؆npr8tXvxxz|؇~8Xx؈(F?@)4,(%@8Xx؊8Xx؋8XxȘʸ،8Xxؘڸ؍8UXx蘎%7p3!@ ؏9Yy ِ9Y 0;swift-im-2.0+dev6/Swift/resources/themes/Default/images/yellowIndicator.gif0000755000175000017500000000102412227051774026746 0ustar kismithkismithGIF89a ؟̮ѳ՜ݶǵܢڻ!, qJJH0/JBEHD9=B?6 " >,?H$-(.5:+CCF!I 4F%@AG;183F@AAD&7 @EHD#' )<* 2;swift-im-2.0+dev6/Swift/resources/themes/Default/images/yellowBackground.png0000644000175000017500000000024212227051774027126 0ustar kismithkismithPNG  IHDR  GtEXtSoftwareAdobe ImageReadyqe<DIDAThñ d"[nFbKkX%5/u1nUUUUUUUUUUUͷ~|P.IENDB`swift-im-2.0+dev6/Swift/resources/themes/Default/images/typing-left.png0000755000175000017500000000324612227051774026067 0ustar kismithkismithPNG  IHDR/w]gAMAOX2tEXtSoftwareAdobe ImageReadyqe<8IDATxĘOUW7P@CR Ac!Vm CL:klg& :Q;1qA:-H1 J"&@/^y(O~g{)mws9gﵿ;ݻw.˗Qz{5)ĉ4;-""7%c@wkP(?x ]?t~q[[[+{DgI/ W iGu~oyFBT*2'_$I`nllLiGqQr=`d---h(ЛP999qܜqyyyYOUEZ[[W[ݩ .|EA_00>tO>U5uꦧվ/t s1+֦,,,2?!m+`h}}}j.;;[glᰚ5c222833w||\e"Cԃe;vLAg (~C~ sWH}o4u @iii &2(oAA\ ΀|,~xXggz9~h.Q^ iŤ***ӧ'x~v'n +0e H#Ȉ* )9ea?vvD&I ` 3ƴ8XT0>44Μ9N: $t& D7Qx̦ x,%vK"B+(4xGd60UjdZׯ_ޜB{")05AQ)FfgeF6[ 3RܲZ!---njj jWUR}fik8)x qv҉ (E\NT$aHi(4; Ys@4KXjZ?P"PybOEqTRSm=9/JBȶӐ'ӠܥK4s*"G*a|ʊ5Ep))MMMAeGGWi'Oteee, vG]xQ$,>#*Av!_xL3$`Ν" {AozΑѣG*wuuujvH0bn$ϪPssHĎ0KVSS?b%AZ_sQd``544G1: ѱ}׮]S3Cc9ñd6Eh8cORM; ]WWUY-cQmFn̢̈́xoL$4Zv°D7SY 0e Xw,2纙W0+ȉUom!O%yjsX,x<[Ӑo4EWPۺi7ES̻r߻=K_m6w+`El}E̶+:cT,`wk_" zG;ʰ8oۉȘ/dh E]_Ϳt 7FvW/njIENDB`swift-im-2.0+dev6/Swift/resources/themes/Default/images/steelIndicatorAlt.gif0000644000175000017500000000050612227051774027211 0ustar kismithkismithGIF89a !, c@*d:A*,P*<΀PvS)R%K Eat'("' n("' # d  )$!A;swift-im-2.0+dev6/Swift/resources/themes/Default/images/redIndicator2.png0000644000175000017500000000062212227051774026306 0ustar kismithkismithPNG  IHDR atEXtSoftwareAdobe ImageReadyqe<4IDAT(ύ=OPs 8/\ as"cJB/%D#*'y5I:}@CbH$y&s;b,Th??m`ZR.9~0͛la]uiqux9f&h<_C[뮽 (Dշmߡ\aG,z|!m׾ [ 1YY3B  P@Dp)QUʫ `]Z펷 m8ԲVt$/*zũ3IENDB`swift-im-2.0+dev6/Swift/resources/themes/Default/images/redBackground.gif0000755000175000017500000000016512227051774026355 0ustar kismithkismithGIF89a !, :(0I8ͻ`(dihlp,tmx|pH,Ȥrl:h@H;swift-im-2.0+dev6/Swift/resources/themes/Default/images/silverCurves.gif0000755000175000017500000000533212227051774026300 0ustar kismithkismithGIF89a ι巷羾㽽»ڿʸ!, ??>8=6:6=58>:<0,9 H*\ȰÇ#JHŋ3jȱǏ CZ|P" |q#ˆI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻl;È+^̸ǐ#KL˘3k̹ϠCOֱ(Vװc˞M۸sͻ Nȓ+_μУKNسkνËOӫ_Ͼz` Ͽ(h& 6F(`A Æv ($h(,0(4h8<@)DiH&L6PF)TViXf\v_^hlp)tix|矀*hb8$袌6裐F*餔Vj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6L05Tkfv+k覫+k,l' 7G,Wlg0m ,$l(,0,4l8<@-DmH'L7PG-TWmXg\w`-dͱlp-tmx|߀.ڠ7G.Wngw砇.褗n騧ꬷ.n/o'7Wogw/o觯~t<;o HL:'H Z̠7z GH(L W0 gH8a1@ H"HL&:PH*ZX̢` H2hL6pH:x̣> IBL"F:򑐌$'IJZ̤&7Nz (G9.@L*WV򕰌,gIZ̥.w^ 0IbL2f:Ќ4IjZ̦6nz 8IrL:Nv&@~~ @JЂMBІ:D'JъZͨF7юz HGJҒ(MJWҖ0LgJӚԦMwӞ@ PJԢHMRԦ:PTJժZ9nծz` XJֲhMZֶp\J׺xͫ^׾ `KMb:d'KZͬf7zmV%@ҚMjWֺlgKͭnw pKMr:ЍtKZͮvz xKMozH|Kͯ~LN;'L [ΰ7{ GL(NW0gLco`j@L"HN&;PL*[MvdP L2hN6pL:۹ l@p*@@ BЈNF;ѐ'MJ[Ҙδ7N{ӠGMRԨNWVհgMZָεw^MbN6I`+4 ,9XHn{MrNvMz[&h`p>@O;'N[ϸ7{ GN(OW0gN8Ϲw@ЇNHOҗtw@ p@n3Px`NhOpNxϻO7O;񐏼'O[ϼ7{-_3J~;swift-im-2.0+dev6/Swift/resources/themes/Default/images/steelHeading.jpg0000755000175000017500000000050112227051774026204 0ustar kismithkismithJFIFddDuckyPAdobed      7P Qa ?0ր]H swift-im-2.0+dev6/Swift/resources/themes/Default/images/blueBackground.gif0000755000175000017500000000030612227051774026527 0ustar kismithkismithGIF89a !, (C0I8ͻ`(dihlp,tmx|pH,Ȥrl:ШtJZWCAAxxL.zn|N~ ;swift-im-2.0+dev6/Swift/resources/themes/Default/images/redIndicator.png0000644000175000017500000000063212227051774026225 0ustar kismithkismithPNG  IHDR agAMAOX2tEXtSoftwareAdobe ImageReadyqe<,IDATxb` 01ˇ.f!V E%"l`jcd`ddbP~L85' ,Pp*1RQQEӄߍAOOAIIn"sL. l B@KiY6tg.$ 'JL0Gzo=|ϟo ̌@yH3OOPCC I>>A=EE)iiiAAq`1"kB ?5dX[kIHKk `D =d0X44dtu- %45IENDB`swift-im-2.0+dev6/Swift/resources/themes/Default/images/yellowCurves.png0000644000175000017500000000226012227051774026320 0ustar kismithkismithPNG  IHDR ^H tEXtSoftwareAdobe ImageReadyqe<RIDATxϋUeϹ\GI &( *E@* "*(\/#ZTbiAaD䏄IsyZ̽8-9|[5:ؚ$e|J)e^SgcnX|lmIbjrr4g|EUCމco왚|qrrzɻSS{b<m03x,́w})I2ѽN2on=L[kj,RO3{6޹*ɲ$ˏ}}}kn~<:&*M?|~X_'|` ZhRUI)βL؜-\|N{nέt+"|-ݕv;$`n8QJ64t.ꤤ@4M$UiJ5yӾ&="{X@kh{I6 ;-vE@\'~O"̽F@\o&~/E@{$wZi.e0$#~-Rǟ|A7{^ݻRU]S`ɔ2̑qkO:Q7I7IС_=-7<^LKf8ܕ2;eΝ{5ɠJ,DIVNO]ONNNlkN)S`Q]K), ۫8p#4—RJKSIENDB`swift-im-2.0+dev6/Swift/resources/themes/Default/images/redBackground.png0000644000175000017500000000024012227051774026363 0ustar kismithkismithPNG  IHDR  GtEXtSoftwareAdobe ImageReadyqe<BIDAThA S%< X@*$lhFD'ǘyN5_<*mIENDB`swift-im-2.0+dev6/Swift/resources/themes/Default/images/steelIndicator.png0000644000175000017500000000056712227051774026576 0ustar kismithkismithPNG  IHDR atEXtSoftwareAdobe ImageReadyqe<IDAT(ύJPǻ]5rGSPhm_čOJDzߩ#8S1x෹ >8B3E}߿RJ?0|D3*B(YcUUAX~ʲ<3 YoEyvQ{Ƕm]-pR$ڡ0 ߐkFٙʦi56#@@>=?@ >0=?!&'2>ɝ71(*3=8; 7+ -;swift-im-2.0+dev6/Swift/resources/themes/Default/images/yellowCurves.gif0000755000175000017500000000620212227051774026304 0ustar kismithkismithGIF89a ҳߞب۞֬!,  H`;o H*\ȰÇ#JHŋ3jȱǏ CZ԰I(@( a R I͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۷pʝKݻlxaJ7&LÈ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺװc˞M۸sͻ Nȓ+/G&Ĩ `سkνËOӫ_Ͼ㗏*Ͽ(h& 6F(Vhfv ($h(,0(4h8<>`V ƑH&L6PF)TViXf\v`)dihlp)tix|矀*蠄j衈&袋QO(pƤVj饘f馜v駠*ꨤjꩨꪬ꫰*무j뭸뮼+k&6F+Vkת (PƷ+k覫+k,l' 7G,Wlgw ,#@ ,0,4l8<@-DmH'L7PG-TWmXg\w`-dmhl8rtmx|߀.n'7G.Wngw砇.褗n騧ꬷ.n7c/o'7G/Wogw/o觯/o `w׭L:'H Z̠7z GH(L W0 gH8̡w@ H"HLu PH*ZX̢.z` H2hL6pH:x̣> IBL"F:򑐌$'IIJ6elȤ&7Nz (GIRL*WV򕰌,gIZ̥.w^ 0IbL2f:Ќ4IjZ̦6nv8IrL:v~ @JЂMBІ:D'JъZͨF7юz HGJҒԤpҖ0LgJӚ8ͩNwӞ@ PJԢHMRԦ:PTJժZXͪVծz` XJֲhMZֶpkJQfxͫ^׾ `KMb:d'KZͬf7z hGKҚMjWֺlgKmnR5 pKMr:ЍtKZͮvz xKMz|Kͯ~N;'L [ΰ7{ GL(NW0gL8αw@L"HN PL*[Xβ.{`L2hN6pL:xγ>πMBЈNF;ѐ'MiJ;[ȴ7N{ӠGMRԨNWVհgMZָεw^MbNf;ЎMj[ζnw{'VMrNvMzη~NO;'N[ϸ7{ GN'gAP0gN8Ϲw@ЇNH4 t \8N[XϺַ{`NhOpNxϻOO;񐏼'O[A` ]r OWֻgOϽwOO;ЏO[Ͼ{OOO%4X@Ё% 8xD8Xx ؀8XxY`*Qp%pF  *,؂.0284X6x8߃:<؃>@B8DXFxHJL؄NPR8TXVxXZ\؅^`b8dXfxhjl؆_p%P(Q0&8Xx؈8Xx؉8Xx؊8Xx؋8&0 ;swift-im-2.0+dev6/Swift/resources/themes/Default/images/redCurves.png0000644000175000017500000000225212227051774025560 0ustar kismithkismithPNG  IHDR ^H tEXtSoftwareAdobe ImageReadyqe<LIDATxܽk]e9&1iKKl} -""nYEЩ8gtqqTC "Ii+iL{{mDc9g_UN ;mRFm?ۦؿnshi ?WW?j)ٽV~l7?4;Ïȑ33sg4VWoڵKW~8NR%d+>or9u5vE/^|{raN$}OI2~{=ٽt[`|+? g~IIR|ONN޼oee=覮;g~/s,-}tϙ3$UIIƓL%:0===ľ;;6ugW-}k] nnǒ4C\e2^[ o ϽRʠJ2:[aQNeQ}|ox/=^aARJ)e9uIENDB`swift-im-2.0+dev6/Swift/resources/themes/Default/images/greenIndicator.gif0000755000175000017500000000102212227051774026531 0ustar kismithkismithGIF89a ߨֳݛՔݱ܂݅݇ά٬ɧ~Ԟ݈ܹύݩҐ܄!, oJJG-0:F',)GBFA5GC;? !CF2"H$ /&(>EED 4I9D%#H38DA*1+=B FA <6 @.7;swift-im-2.0+dev6/Swift/resources/themes/Default/images/blueIndicator.gif0000755000175000017500000000103012227051774026357 0ustar kismithkismithGIF89a !, uA## - 86  &C ) 9K"7DN.+(:;swift-im-2.0+dev6/Swift/resources/themes/Default/images/yellowTR.png0000644000175000017500000000055412227051774025402 0ustar kismithkismithPNG  IHDRFgAMAOX2tEXtSoftwareAdobe ImageReadyqe<IDATxbd```c C?$60><:%@ a4C= O\7m`Bo &&Pnb d!^331s2031DGYiXYjxA6g M3?~f2XH GX<ͤ? F}pIENDB`swift-im-2.0+dev6/Swift/resources/themes/Default/images/yellowIndicatorAlt.gif0000644000175000017500000000164312227051774027413 0ustar kismithkismithGIF89a ؟̮ѳ՜ݶǵܢڻ!, _x"B*Q A~@Ā Zh@>r 5\\@͚!,L0rg @J@#A 02LJ;*(5č&ɨ" N BI"2(0Z,;swift-im-2.0+dev6/Swift/resources/themes/Default/images/greenIndicator.png0000644000175000017500000000051612227051774026554 0ustar kismithkismithPNG  IHDR atEXtSoftwareAdobe ImageReadyqe<IDAT(cd lRէ"n_H7j ؅u%ҴVm) Gd0'~M@Ŋ@*M4_M̝,ΈE12boVf@I>} >=FF&F"! @S؄8XE1\Ӂ;-9eV&c(IyU0.n~~>.y!28 21;szJI z YlR0(\|d}(2 `p  'l >)HIENDB`swift-im-2.0+dev6/Swift/resources/themes/Default/images/DummyContact.png0000755000175000017500000000136212227051774026231 0ustar kismithkismithPNG  IHDR gAMAOX2tEXtSoftwareAdobe ImageReadyqe<IDATxVaa]31QR(e ,([m B6JYZR,,<}ݟ5sYͳ{;Oz04 N3 `8xN&t:0C;z=DKR2XJpt:,\.g5l<(ՊlFh4N7a2DLb8R"{'c8"jZ8JUTb;h4r 3fXߏp7x$ UPv@vUk4@p(z!hqxk3rLB L&nF\.WVW""T) χ%JR*w J&f16Eg1!($W(Ek{ەfE\.fv; d\ET Ï#FgdNvZLjHh[\_?= "g5E |1\JH_YJ򉀹Jr!yb^r.&;6(6WG Y\IENDB`swift-im-2.0+dev6/Swift/resources/themes/Default/Header.html0000755000175000017500000000033412227051774023723 0ustar kismithkismith
%chatName%
Conversation began %timeOpened%
swift-im-2.0+dev6/Swift/resources/themes/Default/alternating.css0000644000175000017500000000024312227051774024663 0ustar kismithkismith .outgoingItem .avatar { float:right; } .outgoingItem .indicator { float:right; position:relative; left:11px; top:8px; } .myBubble { margin-right:40px; } swift-im-2.0+dev6/Swift/Packaging/0000755000175000017500000000000012227051774016643 5ustar kismithkismithswift-im-2.0+dev6/Swift/Packaging/MacOSX/0000755000175000017500000000000012227051774017735 5ustar kismithkismithswift-im-2.0+dev6/Swift/Packaging/MacOSX/package.sh0000755000175000017500000000116212227051774021667 0ustar kismithkismith#!/bin/sh APP=$1 TEMPLATE=$2 TARGET=$3 QTDIR=$4 if [[ ! -f "$TEMPLATE" || ! -d "$APP" || ! -d "$QTDIR" || -z "$TARGET" ]]; then echo "Error" exit -1 fi set -e -x WC_DIR=`dirname $TEMPLATE`/tmp WC_DMG=`dirname $TEMPLATE`/tmp.dmg gunzip -c $TEMPLATE > $WC_DMG rm -rf $WC_DIR mkdir -p $WC_DIR hdiutil attach "$WC_DMG" -noautoopen -quiet -mountpoint "$WC_DIR" ditto -rsrc "$APP" "$WC_DIR"/`basename $APP` $QTDIR/bin/macdeployqt "$WC_DIR"/`basename $APP` -no-strip hdiutil detach "$WC_DIR" -quiet -force rm -f $TARGET hdiutil convert "$WC_DMG" -quiet -format UDZO -imagekey zlib-level=9 -o "$TARGET" rm -rf $WC_DMG $WC_DIR swift-im-2.0+dev6/Swift/Packaging/MacOSX/Swift.dmg.gz0000644000175000017500000123175312227051774022155 0ustar kismithkismith|2gPSwift.dmg]U ^U%ʟ$4* LX#ݲaq! !!&w]iZ68~`mۅB2][kvndf{_[߫U>}sy{yvo ᜰ}*=70xO`yLBJSbziJ c*~ݿ.R,4(ecWbx׺./|):دg?bzwy/f|xip~ Ҭp߽wub3@mɏ.I-t?lKf۾׶ ;gW:‰$ozX'=cwY_|>/oʚlULBj_hs;uw/Z]+ߜjQ3\<e:TCXM߹S-#;f#_XtsXpǏN?#Ñ?~_~rW`SVյͱm!%G#|pA\wuok{Evn޿t'+aI~?>n WwC@5>j:B6>ki5<y<;{nañp4#E[sQtM_gpa2-K^uA\,tk\pCZkp{s+`Zb$~9gx;cQ9>Ǵv`f(yy桭HVqql Þŵu_[ldҳ=aq\WWi-ak\=q̬dd7epwtu< t7i&Ck-C )Gus4틏fGp=JKC϶f5̥[kbzs0K%\S>~x>v\P~= ַ2[ߢpi%ۢ7;2[+{Vޓ՞Ǐ>v&_b˻#a.mZ^bT?Yp"S&~Ұޜ7OTuWZ6q]+qa͕^GPǪKKs4{TsR%?3ymkzQˆճ+Dxw?^>'%]^;piY<#c}+O(3g4Rؕ\KW> hߛ/uu)~M{M w{>U1Iϭ H^X[c_esua[L;q-l,{ߖ=N-muуT6b73q|vm<ӊ0sS?sǧ7e=lRNi+0)9s Fgg)>Ř|ӊϓ0S?sgk~aM1&.{,|ʎ/L!i ԭl5S Ű]q:OO^űzlΝ)fm1Lw gNzuFQJs+3AʱdʱFq^94TZݒrk;ߵ&oɘm\8|JP5gŧ9"pKEQ[Cs(`ۖ]-^?,zx-| x,~۞jr]wVT7UU3pW2,9دgeZWyɓ v1x̝Զ==7A_[]+5B`Txٿc?UO=vݿ5׳L?N;W Mm6_Tp?usCalpژߦ0V7'E4O7~WOn#?0)8̝iCn2?\W$?A?NSẔs?[L.ou|s]w3Ԡ'{rO䙳ݞNG`Rpڙ;Ӥd_$?A?NSa9I2?յͱqdXVzx?+q% >qvG{f ֟d-S;8?֫)ROJWڣuUFgEi<9WJ6V+l7Z\j)&h ;R| _Nib)9R|6 \)|bsϜ9bL>VNib)95l Jc@-=ɧϜ NZLc_8P S|43g͖[?F>Pcr'7Ϟן=N)v?+Oo)>g >S S|:^Y>C Kdw(ՏsUS{椚P(JiuL97#rlh2G#8;qʱ#5R}z5|K|nS-ŵϧ4wm*F:>-FQJso m16a)IF睝u׋o[]¢Gwvu3T[ •YJzd8tj*I^!]dnW_veRx)XǑ~\;i?Q5G8mLǬQ3 qy֚k㎘PL.ou|sGd2j>p;?qRͮF_?|:qIJ-Wt34q#'Xsxu}30xHKy:ts(=1xj)Z|EWesz8 \_r'/Kw3v_r_M_SO_juBfgܶd?ho-|Ă _~q{ymku}7Zn9V}G^x^[߹ɾQIwƩu7{t*Z)^9J/Kw>S<랿㿺,=~Boy:/}=6ۿwQl??rYWf ̘Xƽ 7W)<|?3gJWظrerWlh2G_1 ַ?h?m-]Q[R%>b=2]IW1|7hn` Rxw?;OpweJ+oi5zz~ZH ~mγD+EV 5t]z{>L,kx- m{o|*Ҙ-j nj[8\<*K?[Dgv?TeM`CamG*y"|ak\=qP;~S/ GJ{GW\dz)Mnc\1Ɛ4Y sg}j^5txY>v\P~= ַ2LV .'=;|`-M0HKۡVnU?Yp<'V10tk5Wwę5Wz r4~I̿v`^ԲzgJG8cܑ+e?[Xz jh]3.7gNh͙;@;YfW{#C~:Uq*Җm7_]l?vsq=|??y'f{8.LK?O#/kpysGzݩ ?7\)R#-+}XweI(3=Ҝ+K%.])F/NCGb\_ӡ5]uo/FbpH1L6R;EwYvaK)Nc=RNLז1]esua[L;q-l7mҶ_70xr^ɿ;|73q|vmr'O+>wM!i޸ⳡ~<哧JIg.'kc1{}7bs;sج-)6N!I5ԹV6R{]iuVʱQrlh2G_f^ku(ۖ]_uX]_j/h[_U[rw˱[*P+\5Ssjwd][WB5{+CW]xt^J /'+xWt~zΞV{ѧ3 )y~ZH ~mγD+EV 5t]p>&s~a3*g 7)Ҽk~nn-Fp,luVLo*S?tcgn;LH%oZ-zꮛ%W=Q 7epwtu< t7i&2 -d.Hax\N_cƱ}Gi[cHRQ֬tV oQONo37lY=N'+-gʽl?~Pk^uپ~lW3\]7DSs5Er]npjvVyxKhqlWF۽1aW_6ծ.0o=^ygOQW2v?SWbx׺./|;NV慰/ ?7lv>^o7>Sĉ?o+='+W?.9kſljt_\ªuJHnsB+ӽ''vw1t7±܍43[p{۷=_owq-1;2.+]>/ R+/f2|)s&'+ٽm| E}"mV/_Fvg{oꩲH/VT6nK?ogrήgWʾv#g{Ca83_>w\.Yƾy}x6ПXߑoL4:2 -d.H=6z5g' mjKgd{+y 5SQ9,X;~.A([&nT+{V|zpHc{ni..G\}tXp?|vUvkP|?껮|?] p~ Er?\cnp?5?nH9'qP 77釿{#?/V߯Wp?fԔ-+sG͵?η8x[gtmǿĿ{Cn\>Koyꉡ=:wf.x5?Y C>5zn)Olʻw:Wekj`Ǻ$ͳ4-Ӝ+K%qW6O[xS̛r-.^UoFd>y&z]Ww7┪{MKoχS*:<&5ѭkKs}ʺl3liG8?a_\Soǩmn`T6oEb73q|vmr'O+>wM!i޸ⳡ~]TӊWaS?s@R|΋1X9哧'a%k18갵a5;ߡ8V͹uA26kaͽS?sR ~n^Wqro2G^˥̑)yMȏxۖ]_uXp߂޾϶kn_6ŶZrW=MeLx(J#b=4]IW1|7^~1AKCWtmc?kS<5=`~=7~Ǽ li_q_˛[? Փ|Adr}槼Kys琴tϓZ/c &ͳ4wZHs,l{te7ż)VDݍ9{͗{&O#7cGpRVZ\$= n9ZŽ9l)ׯOJ94͔qmcg]6WWŴ#X[ƿ-o{[?XNK00݉)9#o+g}S>yZ|n gNƽ qϮS>yZJ1L gNYyS}tyV%s)>\Q3>#c؜;]"6kaͽS?sR ~n4R{]iuzʱy-rlh2G~ܧ5#?R^:ߪO$l|vmK.ynӋ/‡:N,o-[pkϻ_UXruOuSuY=< w+f*{R펬kJWw{E}(ݐ{b /ΗK8'pQF%ΏW_m?y{Mگ #09ْKq[_$"mn-Fp-lQuV,w*bZ; RѡmQD}rUwOT3CMq;R;:Migo:w4a[2sPհ|T<[/1GؾhNKQZ:ҟT6x5a.q5[%LOF`=3X۵ϵ|ϫgU '?'G{c8׫^?˶ַwt>wmw_ۋqk>3;ֻcpΌ;3N̝'Όkas0\PϦ)|ʎ/L!̙ڝ+OelܹrݹrSS9j3f?v\۱~ ]y/=喻ttwqyf?8tdg?#0&8?f-O; cj+؟Vc*ZXz jh]3.7gY=ŀ̝=`֝T=7C~=f `s\1^T}KEl?ssq_JK}3`?xtc~ǼFC?R7ggǢTSchyǢյͩJxMOw=țLwy)tyR{yVN iΕul7Z\?êzG@>yf{󥻮HX܊KoχS*:<&toZ$GMcQ6GK-ZyofʸT.48 ~auշa}S?sgGV#)<] S|>73q|z^Sφ+OV|R S|B8>btvs^)<?3Q|:pA)>Jwz`t-9 S?s;ڰdPݙNQl{wsC]QJs7~K97#H964#S̑)ǦrԎ#@? ?ym|vmK.ynӋ/‡::xoذ-nU/WT7UU3pW2kנ;-+]{E}(ݐ;b /ΗKd%,yJh+d.Yې:0xxt=_j6\kxg{o,m)wNå˷E9gYaΈO|d9t[qTF>|ߩak\=qPk{pS\;zgS{9x=D}X斡K5,{8/>>R5g' mjKg{+y 5OIysBt7X0qvX10سӃG;sKvq1Lg6?v`շ㷸PzEV10ٶlk5Wwę5WB:,ͱ4GG5K̿s~߼gkoVy}D_וcr݇E׷7+9.[tP;[?%&o|^iP0.w_\GV%]Q7tzW- SvP\ģ˯)[U?;LTcߣ7J|W6ׁ:Pׁ:  z6/i7l^֋Z6Tﬞ]ȯ5uAWVW\WokI/;,i~~!}m֎0}@{P,v=_𳧎eHCKyĢt%-uYZ?B'+B¦Gx)rx֫N`l_F'+D81?O/Okܦ/N[xtuO>Ժ;?.rS13AW!RP5N-(s07?ic?cܑ֮Zt\Sڸ58V7l3^UCu1->^r;?qҒ#;;w_/l_dC̓an1L9'M0V7TO^#f*`\<#?N__sgFx/q=x5{08?~*DGVwykc?pY3>5lj zn[Cޟtt_x#UCu1->] ^FsS_d~S:]^;>ӲRyjQuW4ϊ:cKgwa5;ߡ8V͹ghJY[ SlB wFQJs+=/Xd] d^L9k2G^SQ;vRj<͡ a[2s¾8n)4|>O_YQ,#a.&pKmFQ^V[;]rےK[EW.:{ax㑎vcÂ<6.n;WV\ST]Viτ]ʰdҝ1+ qV9CK?Ǜ>R?/WvkCd%,m#*Y믎 O<"eZ myyv^$|C<&Ӳa+M{љ-nn-Fp,l _[:0k*yӢhyciؚn.lO\uD%;3Ԯx)X#+ٔ^@c}G8qO3Jeh!sAj[ Gus4틏fGp=JKC϶f5̥vfuRZlM:~jWEʿXp~yֱOZGbaWPke\ݵg\ +%Gai9=9ymeCٕp"{?O>\1V7'__F^q׈? p6tGv$/VqV9vG/ߺ/㓝?ݷ'6~|}OC=86>{vlF'+Yk4?n)Ojʻw:mӲRyjQǺ*#ͳ4mӜ+K%pW6O[xS̛r-.^Uk CG|鮫lyqJUX&)JZjqܚPv!9ƾzd]6WWŴ#ְ/7l6X70xr*^xmɷ"v1LB8>;R| _Nib)9R|6 \)|bsϜ9bL>VNib)95l JTmܦ}|}a gN\n-gӊ 0KS?s{ڰdwׇwĿCqs+Nil{OFQJs+3AʱdʱFq^94TZݒrFƵOJ]&oɘm\"}*<YQ,#20LI1jQo467öv>%{~8CGFԾЕK7. J`TBhPߊܘZ^"RvXRx);,=>Y_=V GaS Ŀ#8~sk?4(o7G7gB_|p,}Jذ#ߺy|BR]Ns)ŮbxgEs-g waW?+g{v]XώoS>\9N)V4R|vϏOV|ϟVLqJos~ɯ)L|6N)./)>/h.>Oyϊ5!gI8;c P;0xxTb30)9˹1fE)ͽ4ZkGK97cGߖdX,yM8ߙTgcsϣh c}qYp1 Gcbid-M8my|Jq_I>yh_lSH\bޅ7R{khnSm5>O5)I(L"GmdNot~3;/\f/?.^E\t.|?Zoذ`/~zUeS?5qЕ=n WwZ@+ٞSu`皔b *vk F0QY)rz(MV'^|Ll Z/e>[:wz-|{^,҄pwSåڑ9RD:bDFrqLkG|  :3sw"nau¡㣴]z-lZz'ǖӰ5Uw\.ٞJvfVX}XEO8R;:Migo:w4H{LɇsDU Gus4틏fGp=JKC϶f5̥Ox{R פ a'׎~ʯVf[.n?Cq;ַÊ4|d#}칥Wo/ si;j̦1=J47gkgyUM\suJiXsڮ"U9hNZ3W./NuVcyVIs,l+-)MU}؏phߛ/u[nTX&nh Iz.}ϭErt=X[cOλ~Cou&}>f] SvϜ1gO>e>73q|߇00xsӊWaS?s'5bL>VNib)?u[2oѳ 븯㺎\ko%?3ymkzQˆճ+D(f[b0pл+/Z a?_!btvs^)<?3Q|pA)>bL']X>yZ|a gN\n-gsӊ 0KS?sƋl)cEm1&.|rqbW1Lyh};0=;.,gGɷ)T|[a?+OO)>;g >O?+ք}*:;c P ^6L`m1Lw gNz97Q(ו387˱;,dž&s[S̑K965&SKzpc8Ҭ[2s-qxci_ģ˯⾶ <}6Lz_Yb$cpH1LhFQJso mUʱm#?q'#ߟw4rDcS0}Cu ,yx߱蛋ZޅoY_븽m,y֏UlʾgF0J7vTHoVӾ꛼γDf녢$|ѵ:dfk]zy,1y,kkE<|kV{qk1KjG\pVHNʅ1)GUQ>nc\1Ɛ4Y sSs}&F';{T5“ɵnKg[b#P}qb`gu9 _=YyHc{ni..G\ڽ.)%Ũ~2FDL4' cDY^wke\ݵg\ut y:,ͱ4GGuʗwɼڔwy)tJ]<ͳ4Hsedc5]wSمuq]e .XpFs폵ymeCٕp"{:TS \Ftjׯ):]xt^Jwo au}}u`ZKC:/0/?0ÃY^1}X츿}Ϟ:EJ1TVl쪘>X OZ7O{y-!2ksqw?SK??w_8Bz^bZΰ-8}7ma 00xsɕAØoS?s@R|΋1X9哧'a秐4ְ9\.(gSģ'O+>w/L!i ԭl}3vwxz^>yZy|i gx-8eV5|ȱ-On=7?{R*)>V4R|@|~|uQ|g؅1V9哛ϕbK1LxgEs)giŧxgŚ$7wn;ߡ8Vwc$r꼨d`Ë?.^E\t.|?Zoذ`/~zUeSTGc~J8{͗z-7S*}o,^7pRVZ\$="9Ls-1]`]I,O緙oT73q|_LY?0xd9OyM!?0e]X}u\g?X{/i7l^֋Z6Tﬞ]'»ÒcG!?2byׄj_87 xћg=LaU'G3lf<'?oxddUҿ8û¡p i1U.&?Z%RγMa_ p1 G;B_؟7(u)KT.}l Z/e>[:j1Y{kzH?V{qk1K·sY!E#F@8ΘbTm1<;w<< ll׹;\ư:[P\{O\ Q.zO6-=uuZrꮛ%W=QJKvS/ GJ{GW\dz)M>(-sk O*JC<ۚ0mkbzs0K%\'=cwJi<\;~.A([oQ4mћvo=izjG;sKvq1]0CvlqI1Q8Ӄ)ŚyֱOVӛ5과\&k%4{mPǪKKs4{T|im)MզKySeU*Xiy?FҜ+K%ki oySť#kUm*1?%=Kw]=ᖛ)7c|8E)ZB-c[]9֖ƺl3liG8?qO[8 Śr*^xmN.vo gNJY?0xd9哧a禐E.|㾎:.[p3ڟk헴ϛk }moE-wVϮta?+C6-ܑ>?@ `=5}~L_*0 ?{_V)FUpZ_/B*S֍vy^)tre~G[F[xݵ;OV|^()>_B3^|fK-NU (rl1t9gύaCŧ3))>_/h1va)>;bLUNs8R S|6^Y\|zJ9<>?q>yZ)?2^Y1Q|)Y1&*|r3z8|c f >O?+ք%@8n/ׇwĿCq~eS{O.Ԛ=j4fkq,Xdq|[cC9zcz5@gcSS9jS1d9aa̿/.77chP_,͚l%c>)7-O#R ӻpFQJso myʱm#?'#ߟw4&9|~wI虹̩u^yfeK,߱蛋ZޅoY_븽m,y֏UlUWHx+TӞrUL`BnȏW_veRx);jxVAW-\(}:0xOxt5)} ~A0rg~79B\&.z]DD/}>L7fk]zy,1y,P5X nj#qPen.8+tĈ?cZ;,ȭHVqql Þŵu_[ldҳ=Dl/N=rUwOT3ǒݔb,z‘lJc/?ӱ#߸hFcj}O>#yjX>*ǭql_||4;QZ:ҟT6x5a.55c`sJ&?Bi<\;~.A([oQ4mћvo=izjG;sKvq1Lg/?vwM1.)F1 'bz0m%47gkgyUM\suJiXsڮ"U9hNZ;9W./NuVcyVIs,l+-)MU}؏phߛ/u[nTX&nh Iz.Ert=X[cO]ԷX>XN NیoT73q|<ɧ禐4O> 7W)<|?3q|0cr'O+>OO!isݒy]X}u\g?X{/i7l^֋Z6Tﬞ]'Ck|+}~[[%[#G?@ YmsaL(Ƹ[~ݿ.Rڛ*t[V R;S֍vy^)tǫN?Ӆkɏ?rx#=l~fO!~Lˈ5qdf{chW\}e:0x[wxqC_ݽnKrƢ\]ʳf|=Q=n8Yu`_!S,Yeϑ3W3͗@3s.Rk3wq5cj4 5g!QΛ4^75} W 5b}oázZx]&\2Wܷg޽7wJKuhMKuC8MCuտ4w7^ 6](,}uzxMzߝLثD2'|_j1gyñѿЛ}{ϦC7|.[)rXẁ;=/Wa$҉]2r\|FkM(Ҳot zA``3\^Fe2 zCAK~FbA 57?ߨ>d n SSǤ? kq)SR2%/uI)cS2$ SF(rkヺ_}iXO8 X#<~w'.(8 =ɝ`Ax,\khxm|bbP0)bib`M4%%|.׾|,%9| 3LM)280w]ZdX řm'ŧ. D/BbpN͉tA޿yev%ĸ_n#۷0L]ƧSDŽ QN׼`qM%%LvGjwF+SS&yRR0uS#ٷ׼$Os&Gun]wGg00glvpmx}~{ɨnj>q\%>;K9cBqas/{Q_ÜIQh.^N.]wAKv,z%(_1AZg|)ut!*pu{v FNJ?>L~'dG.%YSb)]0(LfM ƺSirj4Y2U ͎SŒ{yq)aps(l֨ ᗥ.ZO͜za䚞45zzEӨGᱦAMQN 3Ej\&.ꢺEM|3T\d++rt@Ϣ=)/LVPiވ&敱&K#Ӧ?|pţQE zi9O#>e|h2UZJTa4ۑTH"n&$SO=ACQψ$- L *ԊXQkǟ[GN }1N$@T nd^cih|`_GK;Cbq(SR~ I .j&+ɱ'&%tO&ְߙw5d4.pIpv,qy/{8<CRdԵ_kEع+al|9*NvK%ǒ߉H]wX)Uc,jq{rLB6J:mmp۔pNzb+p>~пn,g{;z3ax:ίEl/qUhS85M׋ݟ c`0Hr L 2&D]eJ4zļU|v"DJ0srtT:s\T(+&Ffrhzfx2>}1AB,`|zW[gaB bc7 k5~NH %,aSOO;U| tJ^rc{N mZCM7:Țt'|u 2dP}>QoQ1a/Գ+sK}S׆rdj:biмfidXs؜%?ۯ|ۂt*;GYR.tY}|p⥏?6kL \tuR;aԴ&UM[)m}ς?8kÏX DXjX,|4 l:[lQʩϘ5k?ē&t C顛S.)ekX,%688H,tN3f7̝;oᲕ+f( C|y?rnX,VڟŇΚ6[䉧ct͊8x"|Myi8`޼yyC.K8ˠh^-,~?>: )S ?G|~ -, V:C&u?EKN\R¤‚Ѩ7)ct52~=/Ko(;j.e)gFw?yh Df 2I7ՂAIг-{U(?bne?5s޲2վ  Q7r߇'ž@ 0j#OYtR åјuF"/0eGX:%"λa!Nީ GAA;U{S;ΎΝ2%x\>O ~y`to ٯGk2Ax;|z%ckX.6/JVV?;cA:)EIJŊT=GxytX&GP\s;[lEx~rl4^J,UrٕG}26gHJ~+MT4eQlb4:LL~m|̈́ѱ]UAsN G%B8`?nh6M~"-%G?6\z͖i_0Qbc^;ۓGG>WQEaq?٧̘n^GIMDŽo6V0.S„$F^;Sӟ~q$<]كIAs k?޵cY^D'XƇIw9Җ?uA͉X#FIa[NHLx$HMMo39>s[4vfg،:9utJ<6)H[YcѸEZ4-;K'DX%& Fc0GJJڦQ'7A cEGrLRQ }yOAZ$,{ra AtfEuDhPl&^ir4NϴF'9")ea4 E*AXғ WNΌ颖8'C>6f$΋,31V̓ Q&n?}kt|"LvHvf*۠I 4&}8]jyɺ%.;݌_B<KZfG@I=.LL  b+6i8g73il~6 ?&9JvQXA7S?Nq4=BE'EIss+oa4E3詨dQ=MnKK8(Cas'xQ|MDup(:BƞzY'c{6Z/G$mƒy+Ј(d(Ӳ(q7Y_.K'n}fO+MlDJaEQI &G-EU(J0%6Κ -u !B6l!dCȆ !B6l!dCȆ !B6l!dCȆ !B6l!dCȆ !B6o5iᾗU/~߾J]|Nݹઓ_jnq<|7..S~'>8}_s~ .}q,Ү?'$}i-/p6cUqz2%L}HZI))IǏ?1rOfj~%N )'NF辶|OfK⩾_CvK}˨;b87=7i_=-mqr\2mjX]Uϛgt>;,tTCڷ }ӲVj=F x5=[^#[,89;Ye!9pdm3tj!.(vX!U]J 09H+Y|bߴ?UҲ'ϥ%ɩA{gtb3?*c]45+]p!-GUgFNW|O~ 0|(X2Ǘ D307?y_.?} D}3dixT?z_T:@kn=ojw$w> 5AjJWAÍq'@bp?>b? 1hʟ.~24gG>W$iI)x?z}iK?>x/J?.(?9cvO Gw?M'4UN O wkp4O[?Jw`?=c tAYQĠ0w]دwAʩw˺H{ 꿗|ob'~%&q邖GzPny|b_YކOs?[&ß_d_&*n-*?A>^n?{ 8*|r;Q_O=zcRSc{IǏcݹ㔓~)yNi W͟x}NM||O?t[ѵVbN~/T=]Y>/ˋ ߉0~كO_wJ|1_{D?[q~tJ/yyOT`w{%\p$'O-~o4)s~w.m=@S}$?$e5OP%wI=8х?Q߬?8?}/'j$~"Oa':ddžZ))Iwn0*yzj@T~˽;ߪ2pł}}H؇{7R·0%L>ͥ˿d;v9'>}Bo|5w<➴&gJ+Gb뗔onzbݛT-9io&)'+).^7wߖgg=uyӝ<\Z~ŽM I\\|||B}<*t?}/牢9Icoix_ i%'$]ͻ?=y_Nى9ؠpSbI=yAU3Iڿ9bsl]IioִdotiڿTD,]>kp _b⡨bOK/W%;z8+vRA;aꑴ^ճ#fO,Q p.m h@ 68,#;$߽|EOH+=~L4vL\\›O476,p{7b 2~O2/K f78piL/|MIFO򧆟y>g٧7?72ڏzvvDJeXwk?g՟9o?hrt'O?JiO>>'/%}ӝ|TE.~Jx".}rŧ?hdyO W$=U ~y 3a'(~Rj𝶿&f%jx?7ALJRbAӧͻ7]q#ؿL#*i2߉-?cs[f-N''>8_S%yG}ij?*Wػ+ :^{~|rrQŇS%o9SsO_5Ӓ=RM.h[l}QţSJ Z&|7$o?Hɿ.]pԔ_/ Vz??6%ߥ%0dï,iM5Ϥqou<5%}vMჴ|Dkha|qtQُN/O= c:yqyç%?4<]s?ny;~÷/+ ߷G 碻Y(V,[񱷽zuR,mOto}od8ǔs{x~&'CoXF-*Akt qq bťKL NZkw/kѢ]3]n=wTN ѻ|st̴_.cfi͠_>/<]5rUYb6xu/Sߧ+'\_SS׭nr]=*ͷs{0ro\,˭:?ݴys7ZAۮ*<,9STv};OnE >?a[xM:3UoozK\ E;0cs9:ȊIv:)~ rRk`KJ]ި>}ݸf.cl0y.!Ku^uԋ>lZp+g|[㼟6|lGg+xU˙=V+SZ]͉O[[/:&s\ޗ~7rm:uܬE\1mӣ]~WmqO%*5GeV^k_Bʯ տ[z>{d8ݼ۷|WgEEu≗[Ԯw:eUskNիG|ދ.|hIŸ}o15ݕSPɷnºEc{];vk>󮌯DuEQ\g{ۧ>Stdx}ˍ 8ᯛwOx.Mwc{*Y>9G|fooYuϖG~᪏-iX,fk{8~6;inC9]-+ίה|Bofkp||׎n74>\STZ/P6A>~]/vy'^4v] )^lcÂ7Mn5\:q׾^2n͙G>󃖎)k t׌bװܱU7kM*)Es^zU;-tڽ%o ꜙ=+o~jr?xV>躲?H|W-W]ѺӟVYDzn2c7n8r%2Ѳƈmm?H_6 ~gyJ[拽?O-a|^;^x{ԯӄsUh)oɵ\ųt.E5v:5+qׄ-*'f/oy~PiBQ{`fߕ}ϺR97_c Z_ 6_dKzz\ѹ7UԶ.[>ЫFeoܽzr:KۿN܂]b։y~x[g R~gCs/-V]$t~\s:0C-}FBWߚ]2>@K-fJ}t{Y7֭se]Rn_rҵQlktvƺ?ziNur櫦zy/ }jq/j]M=Gyx_rʻ<ͧ4_jUx#{Kvݫ.mظ e 2ngM֎9xSߺf/۶WpCUO=:զnÙ}%^wfn:2fu7tCƏ]Hj/}o3'Lz{b׭vg۸뫕bşl2Ύb7$]96fQmVu_9F[dnrdtU˿)B,*WϭW˚u~عǯkqA?iO/~fb/<{bu>Ks[Z;WhP#V}Ԭhqʒײַ'˼ߌ!%ΪrýgS}ov+̢;Г+ j=G?|qg zܦz\SWi: %Ϫ5f~1-oC~s]/yGol5 )b}>|W6;z-N3guݑz0FG͛U1O\]i[\Wf_P.WחpD5m_lRg4[p۞)zn|s7U6~Sg$,rg^2eQ}`YwE;2e[zfėirCyruc}Svƈs˿ngzpG~MUM+.}SM'S 2{汫m?'MWԸf~nś%W:|3|[>-o䑟y~xpͼeoy룃RrܿeGor=xz5lV OzQ듟|njV>t+Ov/W\wЍgbJ7N[~Gs2Noԯs_7{kMָ[a_?;{,}"YkuxNiٗz{WrnX{Eej6yמ; Wyl컛nkwrcrVࢋ3ܸgR*-}4եyvAkr 0G>v^+]Gx_8W>g~m+6={χ+?Xm2Lip>}f|}Uj`y?W+TjtWZsvOsO?RGnA7lu .i۹W{;YNW“ySvϠcӭhOʖoռyKU`jKvqfRrEƻ ~k\n\yӛ=UPO^yb㜫>>ͷ>x{[Y6k5h\CÚYzK~^3./le>Ҥ%sUzob|椫ݧ]7q qЏmg[o[<=ʷ~~g)鳞 sD^i9yV5MyoZsr>\wT CVv%g~vyGl;E'}p &Me-Μ}ҭٟU?2?)tN[s;Lԟu^+ʅe[]+u[r-֥9'׎gף?Gv)<}h:.sٵ ocrmkgԇn!MHr7\zQˁ/~-unS}<ɰN}sxAv''mgXs3lX>*zcu-^ْ[-Y7j޿Բk5%k7xu«~Z?f-;&ow\{_Iͻ[OլӌBG7zY]1QpL>G&I}&qܻku~Qygw׮"OrhoG^o8哏.ysʼ 1d誯sߎ.qRɇFOMgo7.mѽS?[6`mzdx'rim3׮?_ѬA;ʿtZmzeyN=Pl+%`wXzCW/<m(mV>sߎ\1ts=g]7_zq_wQc{ <&,޿ηo?Ѓ睳䕙w K&Rʪ^|w.ylkC+.ce^3?峎.xފeѢervys설o.9se +ru7]폋uCC]"~ɶ)݇t|.ho7U4}һ~}:}õ[.O,)۽y+ͽUSiuي[-}|S+']/HwjWn͸o#Λ|ᤅ\ YϬ9~᭾tF&]wlو/xcJ~.#ͭ|k'Ni?iD].qֻ3}¼åkNNEfnI}LP',]ν{|RZg=taln?.Z%>{irݭ?*~_?_evN=3 󿴼LvMfv"+eڵⲋ.(}n>xEWULh{י}7$u;jМpon9m3ǞVSo9ik?ws_~6}RW&~]:7 ˏ5̽gQ❙SpĬX">籋{9&q(6߳lk:~^~x 0-̩Tgq}7{nHɶ5λ?1m.[[yYWʌ/կ;e 8]=7x\.'U3t`C_ƗrŜn7{߂%9gw߱P] _Ȋ嗽xYKܳ}o|9+{RbY~}rj^&%?K}%kzWmh3Q;ٽ`is^z[I/UuK= Ԍw.IY}Bl8ѧ>}So\ֹi.8apɹs^k-9b{[mq| s<{3;:A?JЧuȺ쒭e'~V:vkwnO^ڼ}5z-{.kKxy·wcݧ=^gݰ~%NsU휹#ew0䦏HmiL?wFwQT=b; 9ђ]+5aRKߵx7sQ雲t^Mi|>f.2xy' X~eۥ/WfK~8]ʍ[4MUy?WeԺߤNHqB↵9gg~M`4}oy陧j_ wZp#wǗ1S͇?]Jg[/S\G7ʱ$~ݷF5]۾O>mF4}6i̚{(z{Ϟ莋T6޸Wi3?Rlwne K|c~[.A&]Yf|լZK?tk~ux`mXo/*Ǣf>Er~C7G^cz޽./&:WWY&4WJo}S?}E*v9ΝNbO9OwWl'Oz}cMJ>r̗>ƛ]rݱey.Ϯ%y=~*s.xk_!_N|g<3w[Lj'LY{o̾Y9+6>+^*7JǷm;u۽sWu2K#lи]RGe|_)\jا?[c.s6atbZ/;;g[\{~Sk>cϊsw-g{T2%Ӟ=w,komZ⇓:\5aN3?Q'. {~ݍuQ2`{y9x,c}m[}C.s{'*:4u_57}߂ڗKm?2rC_k/qUM;M>|5 '5z=L2n]v{ZTv]2*T|ޙ9RʗW^vjY*O^})ʹfdiZgf^q9kxsGI]9{H7:T~/nh\?Uiִc yA\nӼ55Tvݰ6iG/{ qnY=C;qǯOp*>Tsy>٧6~[Ҡ#W1Uk~<⛾׎9j_9zt =0ܖxn蓲>+\mUT[^?zםWeWGd>wsҏ+}c]ru qy[;_\wxbܔA/쥦 6uKϖe뷺/;dMs>L[Ja|ƚf?Rìcg-\v>|+tMY=*6f²N|vC/~Fl튿ڴm_G\[`V>@:x{9*|No\ g}M96|2gX ?xxM-g]U:&TvY7oZ{t{K7o~O|G.tקY͝}TKGܼlZ~%֗]k-Tfy$v~ WWgM=Ӯ?֬E.xnQaGmp-{V\F>/ԾQϕɻ.֌HEοUj}nwgţW]S80'c۶m۶m6Ƕm۶m9N6s7MӤ~ۤίa1]q( ȸOP;#`|\ut=$6̗#m\Bׁr2Bfj馚_"HA#mZ3FU`Mj0(f"$N Ъ~mQny]c( oP{%㯒X2S#$.rr$^~FTߪL?Q(j3v|ga+z*-\13:R'ץ_n_nkm.}vCmV]=)`vK 7=(4, +<"B!:4z7HM~wXQ֨-JZ1rp{3`p\5,HA^fV}TYҦO;x{_)b,:NdVyRS~A*Ĥ5W\Byyat 0\TCy3"e`3GiJ+,gKbb=jCFbI$0j_t,N~6: ifio{w9 %;Kz>9v!-斢v;&$r?J4BNL9,ad $%Hr44[)Rްc_1Mxh\0:LU nz?v|,`R66_=lpjGumeWmMe}b:mOu*kTg¢7 5xPM !qX:z>E!4DzuG& M-Tv6lgM0107.c;5prƀf-(K+|~/fN?;tˏ7d^f]t|gc6loƢ+07^Pڳ7Ivy^[eպ0Wt)PX'g(Ws"-ŨTkːYRt biǎ$4THu'C^Qf>_)Кx6^R~эq0l!Ak"emdjLPry=X_`lJI BГVК8 h$kcl{v7 Ԥt+a %U> ?=h+jiqJ,jzl9=&9UԨ؀GO-EsJ+ pW[r)zM&` Kr`m{'HhSs2w#zwH͡G4CBд YE,ӵN1ٵAN4y;˦=_5 xӹf#Jy|zR7wTA,h*22|3M[Ah0 }bkJ91`M-1}TMwlsJ3qq30-B]0ocUR:^#/ 'LcVrd%zҙaz޿WezuFd&V蔎z70[ 6&FB t~8<]jk I#›s A,=<ٕsX`Hq?Ê;%c4#0 HnqB"zျҧ!DA9L?L=Ei;< ]e 7xͦH1'̽ Hs\lA}{#B^RTWD0bv2-tMAxL]¤;Oo;Չ! ԍ.QMnA_W;L9\xq? /ȥ;4x[gƭ*M|}r- v>mNyxJ*a©,xHIHT3XMN~­]ҹ?C\F*'[Ծ2 b% mF¾r5t{\DcyU ˴!fFxQX)%MbQ6VcY޻E h٨PL S]-3WWbo]hZSitItw~SZ jB09g]L)%z:Cz?4娘Ŭ3kG{ \ ״^2ksS|6 &|^(D/L{#lϞ+]Dmר?(Y{$+#0N ?4\!#lH@BߕY"X>k(Oz$z|%j[CUuj :60a4׋G򆘟MmF@y!ASG_FC1V#uKƄ=#0&/g<37w8(w`R*X3+[PSw(ƆcirV8s}l%50Y奉2AjbNdѳ <-!5||`[DSi:*d F,`R+ޓr_c2`gq5Zy} sH IP:rp/\ÐG']Pɛ8STe6Napi,0qBFxW\xV"@4 P5gfJo 5VʘYsx</"j'U첺aG3a+ yM}ΛܯzBeagfns,@ nF*%ݚWr›7^D恟MpWpМXo7zΝN>PYF_2boy#H_E 3uX61s7w6ѥ!égaO4J;:]+FW.=)fud=zt>@-\]C.vNPt4 dg| TRTppHJAi:o =yy(U%f&C6o; +D.pgiK ]{vx}]]}8Mtٱv a6N9]yfw%IlZ Q&P!_.9(C0j=eT(s!=o6$Y𗙙+yJZ)=$O_֚Lvoh]BG6+Pyu9 G\{RhY:Fիk?l5'YmFpʚ=o"Z8LJ\2CTו")bh:͢\}d1kӻځL?ӛ"_2Ƭ"Ɲ3{\(R2kӢ:,^ИjU!7+3cf)aGQSFK]gF(' ;,ViR`m#W jfp0[Wd6 (;Y̻inmrv!{?u[<js]hqxk ڧ²+O[ -5uHsǓ|8JĭwVhkD :ʧZV8er!pɩvʀK*6H |mRGGAܵ B嗵|<|aM>^/%mH&|*mϒXB45y՝gk-%TUQ̜cL4I7M.!,Ԉ 'r9}9[=ǣón |ͭ'b8 _'fpG9==#e\" eՓ'Al'Na^g7&,l 6uL_EP 1'…Zyxa*mrP@Tr~{rG' yCV09({+'brX=S{Luȏ{O  h7CH niJCx~[6T&9R[)M{6i]+F3w0RS|LīMy\.n`HFkٰ= &V>9*B_ʼnGS/ŧʮ<z ^/\z3*?m' (n0Mh7 ӛpwko D͘Ȃq%d4(N*]O2!B`ʘ 7-,sq=C5ex LGrFeaYNql6`]R(̵(#(Rb187=!~AXnqâgx{Î/SY 7ygd|0VBRETË́Ԉ[FPh]OS@%C/ÅWȼ2eYZŭ*N|ZQT"T+|RASĝ\ONJ^֖a<.g092Te6v4A¡okdFCVdwh B,dc\jbޞ}PdֿstIԆ4njD0_4 ńYA:S~FY88yP* wys[L/`(R;#͟:P}ηÇBwVc mh<}os۾?s*a=ݠ}O|Wi`cjmdġIvGTEV ,*Ng/pOR^霱Mͤ??pꠋ ]KMgm?@ɴɓQ o~QԦMSYUemu~az1Ԝ3w=1|SV螾8 w8Lfy⻳e&5‘۔IE`o?o6Vr/?&!5K6prS_18uxݏ?҄]^ ar^-#"8*aƞo8VMUe؋=dmoǙml/A^.O@{qxf謱M٨4|#XG gX?2ƊMNja7|$<6V^%6wov`漆i>?F͡NYj  ] *1[&7 =YQc7wx)uPBaQ]kVaQˑ3`>s(D^ڴbSc1Y9h?I.ͳ+I_)ޅ14R V}i9I|M?79yBu!pݑ,ShCBnHOK@њh~$0;42-sOx˞V;`u&q ASy~-TLV@.M. -3>]T|?{J5X(#^[ín.D5Vt'5^;M" i/fa=nF;.ᑋ_ +:B$A^6jc!5|wsOkT?lr(cl&4U8XV`Oi^/o6Jڬܠ7o6y a+{wzw1]~YDZTז]PN -㋉(fW_.`=7})ֻMEp<q}!0Tv&,Y?J=->{XmEs""da 0F?K!y ]n"F$EGfju'%10@sd#,B=\Y n}ۍS:C|$[/IDuHd;9 띎X(a!6[N+B)sj|x!r|t^pEްę"2b3}:n>pUMF7h<-x4_+–%)?An O{Yh'wBZð[HjwCF)JIٹ 0TKz!P 2UE@>aY&{-Veo\N%bު`gfr,_{2yW>h2[<%Ř9ڵv}LxHfѶ.=3eÏ7k*bU0l6rR뱸3zjʊmžngCvв84?&?YPN|eV3tJ* Aio]!FsADկb<۶%#(O \ʀq(bh_I3i hT9>n.HXHN֚A׊_iڟf;V|P'FDq绿G')2}ߖ,-{h_ xp_^\ l04NɚpG׻x£3C%.V4nmQ%> K8ĚRIMn am.é}ڵФ((ir~yv]b} #!4+fu_=*/' lOhϨ:/[BgXP }nS!r\[;lhH5V7H54r<0wxH/ BJJӽH 8}x4V4ތd]m!7Yy;]u {$*_Jo'E61:2B@ޚפ59^a79_l-6!ZR Wֲ|bwwCާv:z j/\b*Ȕk&}|,6d)6 1{aw\c[/jF0̈́;vOCe5:!zdx/Wjym?9;2::90Ac(G|(2b+jޜE1d y K2VP¶l ׭eV$8[0F xˆ!\E~d~EBNY`YSBDeznR{.CNd{6OG!y(4U5;P*p qB ډ8K+ r<ܙ_e7`Gq*!@ (Y4fK2eiԤnDžJ*1cCbu,|;@U xafךH"0YK\ZDcA[Cg|6PȸsxR.nG}L2b,^vܶox/U<箚TyD.Ѭ=dzid,>8zlo]zMۋ#'Q/RGTIGXԦ\Ǩ~uD3>1+g6vz`{'iN&?j$z=_;ɒz[Rsi#*dC(q0t_I쥪xǝid>'{EB7q3՘fҮ_e68GF/MEޙ @z7dÎB©ZDT7ZEI%c,+NN=T _V2,DEO ʨfmW$o!$8d&٭j;\f+(]Ķ\lb &A-]fѫu#J-u^8a6F1h,!Tc<7THĥt;<7`*^ $ g|[gȋ7'!ݷbO.ظa'=Z\럚e~mq+rm1&gUhTY"*&\S.袷Z) p¯Uf!c<|Al=\7EcMq5ωpF.aW6~EEMEo}aDi;Fx{*fjz4MԸ?+?QH, ?̷V΂sA#!|@cOWZ`[rc)冝Eks <ڴ-/{ FqDd__Slf^XPUu O}rAIգ}`2#VG9=Y]%-ǹQ|7&.Y,"_;N]b9De|(yi3lH10gYe >gB9nB|/$]pm:h3!{ZYm5A'R +$ýRt8A8{Igb+|oϫ] = µ w[.ap['AƸ 4Ktx$y8V)r%-_L~N~i_ff+R!vNԟHɂDʟc5:۩,AA{9o!ty3Q(-:u̷2vOigs?ii~Y~^_A$z8Ve?(۠C *$nKA Zy87zLLk'>k5A?i~D60b~ʅJH`±*G*`4= )5~e^R;6%+I㋈{GF20Q sHa}WcڒGAz:ʷ} K/nM?撿LU']1PA^7p%j{wTmn);xgKnZ4idN{QTpglH"Pt{u727=mAL1'v_S32F I\Q|?ixcDûECGmqe#bN}^&J_VOiL>O-ޫAB;A\M#` bA[ywIȴ,E1IFz3S̽xRio$fMCL# '͛#88Q : Y*^x_D=#*z;낻1۝5:by% ?M^aH:/G;tQWwY w67XgpT&xV :i)}0A|ЅO&Y4 uFJ˕@ \џiY}YYrpN$u`Vr o,Ǐ5ß;\yϗ YD{j(M|vL/u$Ɍ+s|kk8ˆ[.vTL1ͭu[.(eG6}V$mբ!K%Hlm攽Nh!zP^b Դƙ+ |hrrz61W۝lGjWu[հ>s(`jJL:p9c9$#?]\';X˂J߉IH<XD8󋽴s <bV2lTB)= 5oE"gt(Ky<u5-j^կ4{(2ǞĢJeO*z+h!GYuT!?f#1aoh.ꄰb\T,eR]Ѻm6!9ݖjzI\LWz4U4D@)$ꗲ' Y#⭧5qn;O%w}nWA40[I [꟡S匚#?yA\ao5F+ o## 7gG#5 MO.{}$E!O] !wd0wn(o7]v]9}C=bS'&?F$+"ٰҫS)LL&ѐxjRO}ғ܀넫nzZ_~Yfc':^b_E\3i+n? )`=L7tW߲pYm3a!0#+FY刂M!4EтӕMYxqkU Uǁ[E͠ɕ&c;CLכF&#sFkHEF~E`ج{FQ 1_*@[n$̼A*ŒaШʐa.e~L iP>1 '8?p/72rd*?*4sHq/^ fi]I)xzX̊߅*5E5%I=~q:Wi?*qh/[bi ӏ]Z Z? uě?;jT ~R~dn\o:FL_ !9D;%LB<[/H>ܝ 7ǓoղT BK)F_hmnVA%jcz@rKH׼=d,d*KLRVr?ƩPte" x-AƃFǻ$͉Gn`8$kHIAXBYs'ni˨b*qvv)f JVt+?P?LuwOyKU;0q)X4fBl lΓwA1 _VhEm>Cqm1}'v3i%ßrlPeFA֦8,mNیA/`M̄KS^~¹guVG6Hflj#`0`Md\OT*) hOM}(8S,@)3nmo=e8G 9TEJ4U8K,zϿ >,qypòsGnnl̓5HO+:lsgTۯcԤqI4"h&vGomI572dWzF=hH5^l jf-#A+CybLܘ HO!@TTseL(pACvt}Ado:\ix4羥_ޤ-mL~j@H>{4xLS퀏rb;cLyjTNTq֞HE@= ":<?}]1*g5RNFs#dpl.NLvnGs7c3QqiSH|`7SA^u!(L*T?|B;2CE< `TF6# 2&`TpYGkS){IJ7@f8 H:M-_|s\!}n47eB9S4[Ff7'+}jF1Teh-&0(H#T& &J㚐kx19?WrJ%Rհ OXt$uৗN$UoJ|86ұ1&=iUf &AXrA%c玸u.*la%%׺guYk•FUٴ;)EɗiH~#jUET\wU }Mg#Go7qAwFmZý>%/ϲ铴Eu*x}rLǫ]$xy5.Wa\H> HD!F`Ъ*[*y&G7(L3i@60b <Ík參AcnL+l2K`{*=Gު:}vUo pmv[e|)6x_)xoq`=W討4w:Rڗo}zh2wӄ&2YjXHh'D.h u2Яɾ-эo1nzBzc*iu6UK7~E-(tw57 3Q s e Ga-.?Mzwe|Lm uΦxάR"D;6'5VF}a$cp3E TMJ8z'Րy )kM=4'uGA'%`/=`\:UI.:3[b͏ VRZœύ)s=`UM#X8L+SrTvx> Èz?\-Y:v9Qi,:p!@0Y{z!>/=zp7Y,I@KڜL7mZfXC/\!YŸi𫠝# KҖz.y\pBRu-ԭ)?#8lsZhBQnk%c.KEjʓwz\\_]D5x́VD22.oqE)SrG\Yؑ,$V[o?$z"Z_Q4lE#7M| J^V^$x+1m_)i66ň,go/ d6U_ǤōO~U>b& )xm|vo?r kMS˰8m(:Zip@[i vބOf|Eΐ0hu]$e=v&ޢb=J|o$ E(ߤy'œlDY;*&F7ȦFWq+%(u8:x1XNI]x5wA `QWJ̥e17+ܑh+tnGT5(<Le7 &!}7y#ryK\Cx/7İ[ BT֮f:K@J)\'«ny _Yo$t!.3Qh rL^Cu+1!>mDQp^CiԺvP`ΡŭruBƎ[ʤM$ 30mVVp Xfn\(w5ֳd詠~bSLVn艩$fZ43@ W!ܛˊn lx&RkWmUC cAsS, <#CH_2Zq9LBp(Zqa7cy;0dœW7CbthM4qsVN^%1Z"(67M \zA4Q\r ļ@ VQci>a&ȥ=ISIhEqa$΢}i9 %u_pCUK YDJkE54w4 υԱ`W3i-Fy3 2[9{~ꅗDֹL@ ,:/^3{oj6 z vyu*'}WDhȼ B9Z>ChjO' oy/ɯͼ6 S$-/3Rz+#@|\Q=E65!f~?]-??#}HM'uOzf Hp-NHNDNlKVNg6NEq`mPvZV񤴦`?lFb#KN&X;-]Ԇ6^>{Wkdg+FqaP0ϫly8~K/Kej4tvuZ5W2rNE~$Y^.D Nz 2&>!Ldd߻;T?ߋc4u/qazyF֐)8ץ╢bߋT_,%\oz?pY3o^յp X[K00@d"ebs䓊{F|HIU)YM۵96 ocAZ[2 tֲ-J ca轇艌ZgdКld8[֬RZkb( PWrGm~s@Ւi> bI,琭$f | Hw+R٪lji~_ 3|ԕM _Ȁ-{&Gx;k1ݲW%܆u(O{D! h13mcޅBnpc[i:oK3i!zwlM=Ǡ@mg{E&JhR:yH7|C4|p d-.\Ҹ=EhSe)҂nVq"D5GڇHP"0։',G`TZ:n_r\D.1(Htx]F/tBvo!:[xe60WgZoA<;[l#Ͽ&;3сN\!c:@?wECAݤ i")S±ծR|tܘ?qW25"XCy[05,q"8 G\Q>alCJYw8P",dlT2Z q8A^l\ML&=M(+2G+oqdN؛싨;tlnC} ϓFoEJ*" lLdME?ϝ {rBBbj!seWk"hغN>||m`D%fٽ DAաqtR[^ \P>K ;IrWo):d}Tأ!CF=_W%^8@ͿIE߰(iE?۩=T*f-=]M8BQS!~##wmqز==! 'ٻ:QмU~=ō<}rɞh\jXqB1+JޒY DbxRͅ[-ɮ<²FL-|/xk4J" MJm LL鿘?1n-+?~<+B0@Ԯ|>T0 ٣ u}DX3Bۖ.64*8nmQ9Wy ֛*IaV]69GۖdZa]H=9brD΃?&#D5 [Fتzs}D\/_]nBxϞ犘EUBg8p%x\a m3~/*w߳ 2ˮZTTo@-"]"NX[:xJ5bf7U JcfKv ; tc2V%G-]:赀JN1j֪G2m3Ch$^W@!vaʮu̕+W@Ўxvڨͽi'5--(ҤN!\c-l]Qdt?DNu /Xd\KJܳ%_ X|7.y։[mcj ?ZpjʺBۓ_g~o;GQtG}dآu!%idRC44Hؕm;\ ɘuc4 M|q7'u- : xL^Zr"Yit )^3CFT~b 9urK5Bo6? ku|0aՍa>{Hx ,\z>=Cwb7,xkM 0O%NK 34ھB^\SkmHP&T_1DQkBY,4moRMeOZ;b&M25#̭rݩZY8֦OVb'Y/k;xaभ(V{3񍵕+. 蟃!Bu@8XioXU/US,u9kfwpJ} ~@VC=Ҿa\zTL,P/sm_w\SW}Edjރ}lhQ7Ęy`g ϩ!MyX[`8=Z@[F mL3\7f`W:.0k}r]n Kΐªn*U黨TqnE픟uS)c]}F(^?f]r?C0b<\5 ] !3CXZ0%N X6XcvCCf d%>Ӥ>^[fUQSa/S2-  !;pQ)qK\m|l#^1`#5'xY$ >fj&1ڈ3z I5Kӷ1КZ{dpobS jM).OѴ]A.[840 hL,?z m{Q c7Ċ[`c"r!0 JTG54U8q^l67]WndGcyEh-Rv +Ϛ.+Y&)c߈:ɾ(1QxdA6GAj8X6+o- ۊ㇄2/6 `ƺ#Цہ+K4H]J]%a!-,އÓX|)cc%f1W |~`pic^^  W n.)"^-}|fO r,'D쑄+&tɂ[ECL6e1Dr"PװYf245/6{b-) HI/1i>hӹ@Xga'3izmV/>frt晏U#l{"C_ݥG?XiC~夀,1\%eC)=3֯_眤^Upߵ))p6阨Y2󧧘+\Yx z$uf|%=h52]ãwg&&|HفwVmKZ轾0Xs|F9x| NC۰ѕ8ڥ ٚҥIa_d`@nn ZH#2{FǠ)B A2/5ool"@c3梯W|Ϲ" ճAg{5 Ez‡GJV(>(ٛT(1m2gu*ְ|c@\hU!c37-sv^ـRrzSBm u^$EJƸd*Srť'ׯFr*x1{j4@9@!4yX&'`2uV:$ ymܠvJvV;P𮫲r?ꊁ moL>?AÝN݈/#m 1T0V3!ާB,VJ:ϕq$g#`,b_ FA 8`oK5:0M$w(Q78zZ{kȁ*Xʎl\CLi#"ǺAI@S8})E]d歏 %.m4*JBK-MѽM nʫc%Bw>SvT@X"l+=,㮿tX~Ĺεf44/:c,*և/Ҭ$~|d,Q#kXzL2eJ63Q #ִB" i[NE2$ohR+I:t57dk2!3`G}ph.xmt^ =93BUKSk>Hd=2,5kxn }l8%MN,bSXWM. ;cJrT8Au'-x;ԗ:r.qڈ-N ES\A~U~%[Gȼ5!lPM@>G5 >ѲQ8 /ȣv3\[b\)sg y\Nw"a2D*Yy]v03:)XV]ܒkUWA`g%[ x.%[-vLEj/g»y} = ow5reQUiF:Yx^rNMy`{]#UDi~hj v6S"Xēgh$GANel\SlCݪKߖZiUtDaN~:uq4V+p0B-F& 2pM[͞Ƚr)oMqeVn1!iP )ٽ !R$6`nKȐs"Rr [sLͦ7PnQm!hk셋'l3e`U~)~l7 :\\1 te"u? g'I 8[Sn^S?kkiPy&O<e Fd1=W =c`ngjj ]CD:qN?$XY]fR!%io2|įOƠYsY%#}XP, 7::x$}X7+:dq-\L]W'X*%1$yhWA_|w'"s6+ h}(hʳ~6|`p$)xM*M.q|P'rD.]YLrR2n"cZLGR#r; 4/z26b ׆ op3[wă}^iv1o,XBYi AnԲ\f+ #KY?. }eѶF6c=r!OHn%`rAڷ]qM&g_bw4d' E x4 Eܹ8(ߗT=J7a] ǢaR64=-Mq@xxF|nhѕՕBne &ni(/KA lAE=&)%r+ˮU|L|'E1J×+Yr*!@<_ v_8C8gh&tBz46$+,nڬnE\GXXxoA˄a"\csXU1HuрB*vjx}r5?DvO:YT $b7ģ`WEV{>A*F{[ "z8t'I\I1{oj0{;a1F~|CãwVA w6^8-SΒLh8eB|UwCbPY0mN&<Nj7=>kgӷkFG*ٰLCDgZGIY4|p `T$;9 ֔-,%" ,9. 5}P>C}i^}xB̡y&$v2&P#3nb7ОLA,#+`ZƤgA_|/XS\W8.)_n0,5c cAwQ~v[C`_TBcM)V1 8^2##E|Z:QCic֭C+Ry^BBlDWwlҒٍR&y.?z)嬪 } ܑs37 a܉O[z"&01/'R;V~mi%]m1?FP$Yi &GzjDY0q)>BfytaB|J qxlx@%t ]KnOCOT* Xly;?А_za%c Ts.+倿>i6KzQ"ôOo_Ftn62Ow4uzdd[0l3*[ }$Z<9.3tW`7GY6eE7AV8\.!,Lq( uF("2ZĮaTc=UnJ-m=!R  ϚS^I|H:`GGr.4‹\c;QOi"NR}jR~#\Es%٤ywK͹2!oovw^غ_oCdmZn"«+09ƅlQO4'I &LO*͎8^ii;{ƤLe1@8P'>EJzEd(bQC2 \Ӟ;nJ4IM `3kƶp,ïykaR,eoV/cDX Ls $\5:Ta{dAz }-Ya= v:#%g򌘊\fFev,ڨ*!<g--`<5%&;۽8~2}NuE_L?Ll&"*uG& 0C:_]Ժː[(b%D eeɏ2ڟʽh =G@db5 R@&L on >CYlԽߊ]bJBK|ɲ %׺زCAtH),BJ=?{1jآ`rU>{`@كOygD2n~槹=ں'߸# |qglpB0H0CDcq?qjbzLJXӺ@::;"f'$rp7ޝLE(p>pa=雿R}̦~y:Φ/%$lke^ܱV[z0H|ﻅ[v8GRYDfsbNԷX?G:(^@⭣؅옙qbHF6DL2 nh"fʙ1Q {N UnE2) :D&Z~e(V /a)Si+\,1lbk&rR2m\!bxrn<@%ۜ")%+C΄#Tg:Z)7e?eM> Q U2Ll Έ') s|S*La:BTWш&EIϷQoyW#:x !_\_(̭"pF9KH+lW}DB{֝p7Z*F9Gj1I4 V 0^w?8H@([~P5bAI3W5O(*h*-@"5p˥#ÑfR<f +l$kf*9X/d`ېp7TàjB)cEt<ErϹ}giI MC]IW"2+ mtwY>|@OY$Zlgݑj[:+O"{y8[CE1" r}wlvch\\~FXswJLX{& ڳ?{ˆIs$ʬV̸FVv2u$@/Dv+;Bxoh0.ߧCW옕Ցkuv,oQd"ՠ@td/\bc$0~#~BRb#75!޾b{'z)jsa:=9LPI0bcǵh}Kȍ}"v11 |ןQץ2HNWhUvvdxT c dΉ쩔׽c2/ yqbXr 1:gJ/45 t֬cp/=cjH c()#m7he?P\- ͝ՌٚˣAVO SIj|"YP6븄4;E+XlJOR^YFT0`эc;EvP*<f,nI'(HP#ڔd0gmꖝ@VgyO9 s\9K{CǏhy:0m8 Tn 9 ^b^ LՍjmҫ * <ti5jgɅӪKYN*hryD]zi /WKu%RU>Bjk4ݎfƭ@y(/ĭl˜Ao}zMզX r&IKhBl7?$ p=o{g^: [ڂOBܐke{~lT3# wc w뭝+j"}^N1YaK|ef.햍Kuu ܽSe|D1oHv-Sf4x^)H ˃("2< PK|`Z.OB@c o#v5n5j/,* cYJ zAb9 @ /15%3q?&ҳ@SB8 n_KyөhI}q@ :'umdBKȍXRu,- ,\;9Urlda3i>$H`Lb)<TiLڱtFE9§+t }oS;XokA?3\ߏe4B-ߘ{wIO _sp$ O{/.?iy٘ǭ␿=a>tV;E7"8A5yoB櫜;H|o l^%;u}/AbH0[n5Xmc3f BrL}1a!*XC~bwQ >A!bG3v㈞fz"Ha x,7 D^*nDH3S\iP25ATk4H799Tұ$iH/Y(H+M9;qt'ga+"#ťsӬ?O0pq^n榴4L8kC$0@vS8ѥ F# f,SfP_zri`m4}@sQ-VVAIj_pg ;Ft'wĮk>g'kI1P)Jٿ$bp"% YnrY ژf.Sk$>Gkbx'p,Dy(JC0!lǓBiXgl>-#gǂ}CeqzK!i>\z߰/n!+)A<o{Jr!j`Rv,k叠n|I8~ݨ>J/A]3kۣf뒚ӯ!V׼وj\P*[܎Cs))$qJN6?$hեS|f~=zwuXWDzT6z򅶫a >osx ddo]5WlXdABO$!yH&r1(N| $edD[dN$QWֺmvf31 6|y1/{ t~T5}ˈtx${L NiQbXv"6~' " 9?# kŬK]bV5&8]t3 LM7dTU>F&)J.,2ȬB`\oHJK|NeXd9DoB&?TzU5X($ N`{ nͺQ?7xT(,9㕹8pbF[Yk.Y6b&ax{Brp60yjTiuŦ. rF&gZdf{5r_˭t!<׍6͢!gڬ'CWC\3l}i #cL͋xUjEmqO,diTE"da3B3 ׀=4wCL/ޅF) ෎.TMHs3ی=T^ OFQ<=ð.M7YHxo;W=&S9'j{|ۦ?t $| I=өk;5 ߾njNZ Dz~N39(%2r8 iʽGt '9h<@IXK'd /i ;~jUmDORԏT'Wcl0w3ʩ-x*"6z=γ)Z!\gۑ#bp+У4у]vr=|g2bDa,fb ;aD3 2q#m7Hِ"(߸?`= YAMݚv>42O&ig`׸DC]e ^`U9%%-%7p&TfQV(w-B~xYv6(/)򓊙bdE1G@T*L$[EO'&V#zDeo?\G`%ٷ>cN$dO+%& Lk LPrDnؗJ0\dbP(~<$ >ըJCpAqRd j4}ε/9'e9Pd%rCZlڝ7frx ڲ9\ATJ.6l{wVL== LI%K+Q䔳 c[{a_,)i5<20Č4.zt /bʇ=]-Eފ/wͳ倭=%p<l27<.ԕ3TH>p0(ggՅjNeWZ\_ROzs;Vk,m%L"T2+_)T&|̛XU8ѱtzlPGķvC^KyiMQ!aW$VO+ڜ&U+Ў7G( ((2|%{ʟjj1T5)8)ލ_<)Z?hL;owH "޽WV1O;ϙ1›)ob?x/8wZDK,2zFHkMCb nVKBsWbSuȾSX*Y⽏-UڝfFV}{UwF779Fx15L|yW)byqZyE=uR` i+78UBv/zpui\ g|sМy7)uf +0IZ|)|`>nNQP$7&KkQZ6wĘ9$TnQtub==*$Io3z6nYi4R/GtO u{lݹ{*;M"x{MM7BѸJ|47hxHs"_+Bϻ},8m|EN1GSLՎ(֦ۢaᕪ`=/3Fz2HnJ( Ɛ{I6FCdUUW:+66j%.cM( ϣc@Tkjxc*'XͷG g'⾹4k{_A3LPeb,W<1e3vVz q]p+39, C#mOE0ھՖ#_%ecDfhn99QSnU* {p}sWo|»nxzr9FiRE{PeQ˥ CʣFGEʃVV8e_1sQI1xjMh&|\.;m,i΂|tdpqN f0{i0n@4Yiߕy6#%2TnLVBW|\aA\N9JAAEF- W%hrʇ=o  <j NylƚpC)+ޚ&~ڑ3> ;#,H/va=8{Hm\E".B;,,mX1xGTɿ(li4N26a R(Q7(WoQn/?[/IzM:>n5;w"?6%RvPԚ/jú<*@66Y[d W-Ru9,(rRBM%j;yqx"rZ;y{ G7;'}%wdohb ERxQ1PDguw0sN F&HZVy ? hI#Rl/D_V&I&4g?u۰$ MՉBVPZLpΑ)><()x &:eGN&{Z`-~6C=11QB^A?q3Y ̅UM@#\~~wnDG-VWe1=fvc8x{P i] ~"|])knC{AoϲҾg1&9D$>uk9AM]q3&HW߳y;~k|AtE[YA#ZeZj*q 4 k~Mތ Ngx4n :7SzZjuBT56r|YUVĖ36z"S).kY͹7KdK"ds c_M=ݚZՓ~~d7KP$辪w8P<7٭wIφo$o[Ec1ݒQ0K.vɸubOYO0cuU'to"8UT.ymN e_k'hӕO7XNl m+(IS~Ic']SӚ, `3c h8C ʩpko{&lHR#|Fj5߬B&WmUC,nLnVW <7C(n-阱TA5##g^FΎJ\I/zi:c‘?zzw]g z{ >ĭK=NKia6A@DmyЪ0*Ĵ%Z 6l0dTlFcWa}ޠ\ZC,19Å?"qA˾JL E~n874,ۋ9pɯ(_Au"֜]l͑-=W *{wO[vD}wQS_' uW?r;s6![i뮈ޠۮ1<:#!}$Z6*WdYhvvТ42a,V vߖʡ<޿eFh26vҩopl^Bߛt#>zͿU~P[MNB޵r;2)LW(L9SgbԆ YsqN f@FKm;#&9!'1PNbc.`{6fhfXm$s/MI~by&5dB":--d!>$et\/3At5߽%ql:|}z.{AuJH~ mĄ֥^@v8_CkCVWXqT =ܼ"Y>8oX'K?y)v% l455O}h<̆]4u/XQ~@V7h*z_)o x_~kZ83%7R@t6w5~Y06 %臟Ϛ9T08=t AyKNoEߜ|r"|G^kŸ粱/cJ ZVQa xgU覎3Y"ۘe H7`qʪqȕ㞯h?-ý7`֥4ڦUaZ$ޱ oXإRz|XQ$k۟xI.D2 !wO_cE,RBw014+F)D~(^j|f[.*,N8 (Y8k"f3XqgJ\^ utI}R~vd7W3X 1 NDJY68>H:A!0 YjU " dC,aS4t*ҹD]W,Fe^csQ(9ſZ`#޺Hq 1RRjk_M9|}h"ι~oA6|YD\+|{#ٿ@#4Z ow}!*u<Ŏ;0}9'zr>.cAwH_B{PXۛ2ѹLCd}(La WQåt ֤j"<ܙ7^a` ylqk>-@ޘ~fMܔCB5h#EcոA/ot*h% 4KPxDZK{ j.j0U%o?PGK91+$HeAs־ێo#iF2 w}+\jrRW_R&_F.xHW3SeE _{'#3(>Y*t1\JP;W!3kr@>Xe"O?6Ulil`3{x_':[jL-*E7TY3<`}i x`^%a#X)[3hI6+pDcLRw)DxsC@q2CNj2D,-ќCI@fTxrj"Ͽ$|7N@[Y>a@h:̞j%~+IPтk3„V0-T>EZAfc,)\Z˨I7ʕ$B^4t(;4teNBĤ x!%Ln$M%UFO*CĂ"Y𝽚n]TLOk8V y-["0D6{F "~WGmt'=5#r;zi.-bnD$-k3HZ p.qjSV*qMTy4mDuW}dPlSMN6,7lA-3QhBBP7]6Yإ:-H.Z6i"Xrb-5~}kokn:Q*.zBu3mqvD gBX8Hv=b| b8 67ڔ#vbRV9N%.jx֌/Cj$$K8 Rce:E茺*(H՘6ot?lHGјH[;26|3'U(!o|AYyB(shuj^NJ)6ڣ&e2jVAc5V7YS# HmGzjr暷/t)^'"g%r~PKAnuVN4bY՗+=!5Sf=h5Vr9{$L7:0*{(2^Ǖz68i0Bp~5Ww.)r[N6U \ 5 ߏmʓcVbӠy,ѫ APWT߶>L_vsj}$H7"_ ,G/V̭ =m5HʝƾGe< ] Қ[ugBG9QQ2A΍!vD;۸U\t>J ֤D;wF8`s>&77,E$j燤 @D(#7GQrCx'|  HjFW1vH,_=KğJIk7Sϟa4a*NOG7㙭Lqȝ4(K̟wOo!m=W}.ҝWbI9#q,<]9,̺W_~:'ڶ**Gˤ,_OoDߐJvhx(ߏ' %Lq"C˶la2?t-45q%&hnWn>qwqїFJ&7/ag.zɼLi83e?\ipJQw "-(?6x?ct-X€f E3%%tZd2au4YJ/8Y"{LB{mfgwI]hB }ӤD|T BJU~vsx_?$/s0TW.3=Z:NCùpWc2GDFһE6K.)z.mHʑO =w<[%&9\%?l( fB;^]Q=}G1"V"#:qLD. +xQ3lSWrFѥr]}',d׍L9Q/u\5M4) 3ՓlkൃՄ8~Jz_MdCm:þ9=!$ oe|zcţ'3u=f;_h  MÂW^]|f|Vâj\̻ﱩ!^*򳹚E yL[BA,}SJ[9Ql.Ϥz 1CclmC ouv*WM}ޜ,n$+o_&5P&H̘LRڒp>tr04\eX3e( )1o1ײD~ ZwfR0] 1 ^p7 ؝: g,< ^P`m9oI4ڡBcY=iLhUK0XƷ1~SOK˕L_Q#SN`?h@ʔdڣj4蛻{+E O#,_hJ?- cǽS:#bHhX&Lޔ ˃#4`zς_z]ʢ!}B tn!^!qi fl$!{[|(`ʊ+feD $5aR;ߪx6VӂP%;Аz,sF_}l_4QM⎬2~[ iBq `\PYY B}·lw@A }%}طӵoQ^zonKo;U"Hb}|?|-϶Cj6@T ۛyew'aFEPa.Xiq8BA.WW׳"ngVˀ!ʄȥ2HNqS -8T_I` 7:QaAP)2ŎWȲ}))ނmb2mA$,qPoIF{~K޶\5) aIvL"7>!:k} {P0&.9"5> Z&^T. <՗Hx3N0U+c&aϙ+02Ǘ}E?ٻF%?Oǭڞw>lͼx55(MOb_y9_!6ب hc^:zi]hR s{XDRPӏŰ먋ذL+*1_ g8Xx ɿ!?KOmV|AO;Am)>\JwTΔFxu6&rePSVsOPPk Vcs F H_>7/J]F&E*ݠo yl _.α uwDm:jdfhښ~Aw7% m:}48`",ČΞ.oqA_[`eޅVGg[Fsv ^'5PzCoF2A&X~dz zJ$F1SoP; 1M΍\rG,Fi7 (ߨ.'ûx K|u `v/ۛ;.ą&P(dhK143YOK2&C۫t‡ ĘN䠋Dbfe0tgWS5 =ʹ?*'7͇dqn_"<-&Ff xy% L@byU;$RnBDl2;u$޼p64uVM?Zh3ǿDnK!n\cyV%*EP6 8]$ ЏkE=X~X0"dIu@n Vl\ X9Asd*C_5hi\ƨQGf!#s75RI?#YVzfeш&bAb 7Dž ?n#Q{7Ķ#c[Ihw3k'5m 3k b8\t<Y[c3vg,4 w*]#s%-Kb/NvZ P+aj;ϋ3Ro)b~)ZĿ[X5PjuxYGAv> i͔(SeQ$lM\s^/a!0Vfꑭ~N[R>!DґCsr rO6;ryI>J|aiUBsO$m9H$"ImQv'@}Bڇ' |2I{׹s|Hk 9;d[/4Ep{EHvņVTpR~3pbvizi&de?PWx%Jl_,%4cMakh苭5C59F/>3yܥPi<'',@, qj gj UӶ'9tGdY 閭xz:q{ x+EQCnHQ(UD!<2FT5r'׵`JQ3+בּ H#%*ybN0t3Q8^ d:`%Հ@`+p$;c˶v$%qAaIM |÷&70Rs^GBBB#fHRF"9gfpXP-rHv]MMNupuu#z(JSf.(S񶿿;;c|(6l>4|TLLyqq!Fx/SrrG3&`Œv+v % rYZ Iٵ1o`*S=I[&HO"@yqnme%pkI*K8ErTC +p΀kJ>6q\LM<.QxrEW (ɛWp{ R6ɬo8׫v|0R$).If{g؅=asVG+./ Kub=/xv[Dv.UU>[oG>ZG*7O Ȝس9iFe8S&aC+Kq2c(X/^g ..N/UyۺVϛh"ϖeԫTSf́rWۘw" o,^O{6 F=NIYE HUfHbna@"F:Jh=W^ۅBs2|"xpPmܟjp;n9eRN~'N|/`)kY`P1%>Rm.RG9~7R_'pikv.qw8F8667[4ʜpqҩWiI V)HH1`Y("\R ($ƶ eyj3kLKPyDB^ӇN=98@PoezyԿM+@k;YC?d{ Zj+WQQQYɉ~JAAk- 8舡)kVYkk `G[}*4Q2(%\atI=٥S!W@@z?8}aqqq2oG]ƅjU4ZZYS՗3:n.E1ky"lBHx=%*ON1uzopLZYdOXWΟҬ4Y/T'xg讐wxY6捗.ehR{:fh:ՁZ_"-H]V_ч()qJ5S,?tt'r{v7W~W,fWYY<|588| ߖhy=:o." 1ULF}3T  ̾hƲnvnbyĬ~^h|mʿQ|wH +|lj2LS7++wx(GGS@|3!PrވRP~bek<}[&CvxrG)wp2jji8wYܮҥ*@Ư],&-//7e.7$y-܍USѿ???F?\mu7#)D> UEb-ٗOB)WI?nG0?^DoyUc9~'Q>K 6T@flx-yYY +כMz KZ]y*qRT@;3wtJgt>d '889Kb럍@Gە/m " { kNŎ@~'څz:kݪ^6]&@P GCu \o&?ڧxIt9/͏ 0iBB'9oǪZ{yq^(asިML3[C*),wD81 sU6y~~F}@_1x8Â]aa6i&pz `&4gt|r}-d)MzNkl􎛺B_̑n&ޗ@/680Y:ӈ;R9?d?sYD9~\^Z*5&Pَ&MVwW/of/LwmDO762@Mac*Q֎F l-W^ΖN`Wlesjd{sKV!}sxŒM̡gq CkEw_ZvǻC u׻GuT6i a%fԻJ~|Iwo.?_#a%ŘϊQq[J}Ai\rrBFW'U v-!N=!ŨZA$B͔ }g>c?QʵW֨?+wrqKDJujk?G΄ˬW@N{&o@I hbS,;9T:ir6`0S V@WfemL9T5Uz=۬  ӻXou.vp?}TX%oŅ:KZCn]imJQ<0\ty6]yx8 W708 xo/%PW_~@aVx0;׃qw,S2]`8"H|*[@sX٩HU_Ćr6ϯ3A`<ޕ_v2:ސl\Ѓoll$ZLZK++Ǹջ1D,}eguZz8iV/z34x8] xade1? w-SF ޵šBvvr9 N t9tYr2uK %& j+"&hT`d} P H.̟ IbS1 ǽe)y.dF5dE`rGEfjZLi`׭Qw1“O[ IM&W9HzA)xn NYFMWWa\Zmld J &1rM-0% PuwoG*c& }>Kc_v8oĞSzjjɃ@- Lm|I/g^vZJU$j@񏡾b7tn?E0z_f~#gVQ?p\1W$WăJSpy~wÐr}Hk ][CF_N dD q V3Aսaɘ$g) C ޛ{.KYPZZ 8[ &<LOrP Hq a/]zL $6)`5ݼ,߫8ܿn0jLٹM%I*+=.[Ĺ ОU7"ZK/&dtHf4`a}nݍsMLdHd_cc#;>Y`}c{7+,)"( 9ަF'Yè]M4P /0٘M[+yFbי wJ|'8(s'nn#;WlzĻrT [0 |uؑƅU `#X3<,*Xb#V}jtX`nUdE`DIE7ߡWݷu,IZ 8ʂ K?q5`%QSX~wkfBIvH !I*MvMoc窤~ pm53d +S_w^J A֫vF0WmVX_g51 aۆ3w}<-%Vq3vY2x|ڠmR][e7 =@+׭L/2!{ف}דˢCEF!@ ?#J$ϦcZh(+=X:yhFW8b!~9߁!][[s\vXM8'>9 ]yYT60QS9J9sC,,r)> PZu.ѭ+$$enzzzE8BUQֽp՚5q@(j%q| `)*gW#*2I>?vUOY<Í?'wN@}JZȿu=<4,ofaQB_v66 [9"PXQ{tP-wNI`t$KyM hsf 5 P|3wB4j_ +p 5r&r)𮉃&S2C6gWVI='M_>~+5fhgĄ\ н ۺ`d Tg %ɍSCKQ ̸+ῷ~34Yq`)co@ PjR 7k?t^0KN\2P^Sd_Gvt&F @!ɿ )19*aD}'|UYٹ313q0s2p2s|'|'|'|'|'|'|'|'|'|'|'|;D;1)OG\bU$Qk3Q#4 ozaGNϠ#6=vdanG~-Me%fm1KHm~X]Du*"Tݓ0E&O>O>O>O?0A S>q D}ciX"5M4:r??\ӐYL8k[;Xg>AOvߤHr3O>O>O>5c( STr?/b&_eJzű0jrIƉqpv{o5Q-! ~׻!*.MukGY7Gγ)N7×ȆGkɾ܂w@DsO>O>O>O??!?GnƃUe{GQq;4hQwX?}TZ/;Wg}|'|'|'|:|g}.]=kWCƔ\ \Q#=@q⥭*LVE}'vH$?og7B7 #xUW\5 ^wnЛ`ζy1@JTHbp gG<N% =l;rpImN]l#mT]J۪c,n b6yF #{-ly>S'ފnc\&qG߿s&Cȥ>{1?.͖ԫX'p 8m]k:/+0 I]jqJKNG˧;cyG/o,Xͬ -%^f~_oX81uLb܏-𻴩UZRz>XLcv%5:sZ.+MU܍:mgK[DF?^Gϗi<ҋ.~:g>b*i.uv4lkh8B5@.c2+w.4u7k^m'=u´[;GX`'rU.UF"q׬&? i  ^6ؘO-hHwj'lH:ӗ1qK}ݨ'v7.F.d%nz>*`3 :h ٲdEja瞤dZAқzWR%׏YrP v]iwy{0԰r>݀/դ됫:3ᘼo :-kR;SŌS-\U#ê]^䡵>tg*n[90?%i3qMV2qCBrXsC DrсJt!7Io;Q];kF1$ (I(p =Bv^/?Jc\2bN,+Ku|]2bb< cJ ܋*"=oq:<~ ڛY e64nZpr.O&zc&CyC^qw峲v8EsCoQR"{w26Е"ӭ{Wv0o7GBn 晻Lx&x[v Ęz@ظiHߧS~(I! 楳ߓ=X+e"{y~Qm /yc&0hTnܿ^$u%R)WF#2᦯um*= rٴh]Jzt2(Mm3k,SBA$70" D8zo.4X 個 ]?#u!n.?%fnc2ń $ tPp.2Pr H2Ь]'p'r 4Bށ.'fS+V\h\U1y;!V@{zp}@7Jz\HDt3$;h cxt)8_4U]hkt":2O[3>/w*/d> < :l D?}|ݹhIlLAuON+b4ӭ^N/_U$խ NFG=gbJ?.RrUqrXr{L,BeQƘM8cXy_jϚ \t}_۾-٪Yh.-@PinO-Q^JKޔBfgviJeg_y(+PzD:cȓsr5mp+۸I^ݸ ֙?YmЇQ4"13tpN %EW7E EeXA!]QUf)ܢw}P$~1rdWq:bVd 75Ailb8,9 DqG(dvg`@0Kg QB MdR*óBZY&LL*ѭ7>ொjۖ ȅq^+q !H|i9+ɸXzNHI6+Rˁ|\G]xBpƷjLtxf!j/mL)! GWC09~HO &Z+cZԜJ)NδFM!: w-#v{l*w}|T͞]U4!J*l/c JRX{0 =],c&W=-""9& 6hk*&PYmQ!7/۴Mi.y- {jz?ȱ1އ &h+G's[K٫dE>&]PfpFg5 kO*Y}I?1 R_8Rm'`[A3@+`׊Q6m2k]?qz ])נͲ=(SK 8d*|́mJ5r,h1ʺ0]R ]ֹY&-ywsہ7cnM r>&-7~zeAxH]/]Go ATkvA9mt (sbq-q@O_eNWL cDW'u1]?`N4g V"Uj,bLw0;0횽aX (oJީ {U6#K*tP[##xJY+[r09?‚>/ܲ-ya, >pMfEo /"'Y*7i'nQmѝPF'nM_܍LSjy"]z1KSuodkJ27rg4dh7$4_Vrfď,7@,YҟNKcj4 |gxLx ~ a/ZSm8gh\ڔh^)2_s 6De[/yuRw6 cTe5.O I[US IizWxWE!;̤u\0R̓~pY:fطw{ƿ,lʍzcKe ܇Ė1cGQ7[t"DFڸCa0>TeyAmtȉן&% YV&'F4R2V#E[&.Rcg QF@쒣y`Ӹ֓+^`=?.5F SbZ '.[B*Y3Jqio(56%G"c:wJ4;9pa.''f.CDY#v&=bDDN0V$Τ,d'iN~zb#֦tv 3?db흲W/ $4v%WP?31ozADCO*F`T6ZDG_yC6KN V$dB s ~U[eCN ܼQ#Gʨ$njQQUE:%Lv %r|xO hꪽg ߿Q$v%: `t*xNCF蟩_{rê>1=ɈxrRB|Nܑl$Lādc菋VgqiL0Y.ƢM 1 tÕ"\혦1 0EFl<5z4r;8C'4O,q&Szҥ"D Ԝ+O|3不)#Q|?NH @9[ D%#|W hi!Lͮ `{&_j~QfZ9=IJ5T M}h{^`RQPGF lu:X BBkd$f;:ӟ0 Y>6B@ziNNus>W#Ng'j")$an&>+kDc8N+oTLdzAEȕ%r>S`$;KakNtDǛ +ȷxnnpR">i.@B%wonzIusveoGV="c nN&K{ߊ8 rϖ5,b-cZ Wue/[g$t9% \q`0U5^9= ^wqn+L3&QCa%ұ![qr ^Q 1iڪΊSbcx%Y L'\a֣Ltk )БXm6Kc_sLo޴WfT2[JNryGⓇi>t .8>2G+]:c\jC=;r|{_1`-b$ը["yt.K,GsoBDk Jؑ1݅zy"HNÝM" tђ*&w \rŐP~vN-7+\ձAHOJ s%W P*i"[%Z2C3MM&|_Ye>u[xH狭+ &\'HYuNUgթ_̪;m\RGXūDXi1e?֌os-Of9&Rd@QsrZo{s}szo@rM=g d|m'RA ާH&E.za{fI'm7A Tw 6E|Қ(?;LD/%D޺qάFWs2cÔG qމVu eĠ+ UW67F_Ũ ʺ9b&^/q3Ȏ@0-LXtO]q`\E.S[zik\SZASUI-D=F!r9E0C09a?|5I1DT:Cev_"dJCxߵNPŸbUu ON4N7[0qYY7sp޽f}'rZ_M/5#=?g3e#\9Ħk+λ|FKuD> fkް :H}O pdGXj6$tesTPny{fؓ`XgLR=M8*߱bRI74l<`BYɷ{%Sh[Уd;cp̄k͟lp.u= qrl9قҺZg J1Yok| y 4 B6%)^|osk6-{K׶[Lj`2Cr,R9{=|3g5vNwx=aeh&h l_WQ#9Q|@ r\66X}?`c5TγGky,ҎO18RPO}O%ABO@74T 팲 U?ƗOmRSzs&Cf9B gm%L24RVdkmlA8xiһNćYW}CL% @7|oWxa^P p)|5  nN\(.+ . S# ټ܀8 n%Zջ6Ql9=fu K0yM"<*c駒HW`]s{9 z4Eb4zg4tqܓ ;VzMN,>o~}~o.a4#hTCi#BC<𽉫q8)ׂ~o'ĨC~fݾ08TØt6hvdTn`xsAזŝքVQbB%չ,Hb^k&K%(+7Z>vxYdvk7ث55}?ؗ߷R3NSUUeAg1aYvҩkbiW#u5@QcXa4xleYruK)`nnσاbU罛u jZ^ pUd19٭ r5kJ4 =7.N@-Cc!^}\A'_UbF'Sٚ +b>hCŬ܉Dec7O'/uhp[9SDv_D>c& vھu_[͈r"wY3G8V1SB͙v5mkWEch@PvI\ M#m$H^RPՓgj/8gksU')/G␥a$,q&xZl^y1cszߴ2KC[ nQ;q&}ы(LKiqKJJynJWBMxBFaڊ@z~q3rw*Jm㨝89w)'\Aw4- ^%yApϱMqjj| SoznB\?.!7g4:Jf}G.neX jamRk .Z3JpghR\Fr= <*gE!IrL*??G[g|l5 @U1>N9ؖ31Y k:_ǨiLRJqM Pp-LB}IeB2m[ieEAf2N!ٰL*sWjd ;n][kdBi>iV1>%Kq+%Sko򍓮;_-fsVw+&0gFNG1TJ2Yd꒨5a?E jpW"Y8CKʢ\BjEXI<߼DWb?Ĭ~~E[MB&甕B IܙCjn\H3$=1ro5 88}GKZy ݾ,Tc%v)ҺF~a:ׇ Ӄ;> <,k_JW%qX)`5EHJ5GadAA<Sх8xقyX +F8ׯH{~;c=oK6p.1pSmAۢw찟o :f: @5{<͢^cVY˾W扆w2"\w+n*gg|+3ybweQв–yRx5Ku8k#_Exo_$зu e u)o?1k A rTcU/akdRS[ẸzO`jpiLW@#IEz#ٞx_j"{'\/|X{'rg;ctq`S}$#.ˉ̈́oCV "QKnג?o}w$zO`A%eIi7@v6/!?b+Ui59֣k'6uILRL\JE{ b*SCKJoM;zov{_>Q0*5nhoţYѪ)!RJ=v!q1Mvө NxF0S?:!Q{ D4(bqb{Su0]@ȟ %JA-%2%>铘BovVB53i#GԖ:ș+3u-tl7 p`HEr\mmYIp3(gU6wH(޸O0kulZ=%D]*LVns~ۆ-IMk!WjsN|Msul+/>Y Ņ wxOݙ3@n}@n =܊ \+j,2ONhWWJ'-k3Eg_?S@(Bq`R0Ae!>m%<CK8'5Q1eހHcPoT!$jd갷u^uhufQ K!$9t + aҕ>DžuzM@N1C,X:ƺ U/. ltG1mk%5?#[)^JM wX~́A_6pX)X=!wȐ_xڈruZ/A+CtFӫY>j_'ۅT["Ҷ }=apI%׉}ƿ R?ixk}ˏ"T2UKDosկ.h\#_ؾ6_aX 1 wk$sBtN/YQ4x7ȣ8xTjIHXnфlݛ$5/%$)ާ#ZEg?\S6+*LrRሯ7IUoko^-ǐs"$FVoϸîyґ$ 7-bK ΢C XJuSdTgC :~=6J3T36Z6)v [qoL$/ɔ๋zoBΆ!j0/BTag%9oPJɤ.3-zAo]vS*ձ<;j}`* ZlugSruN3& ^BU+3Ĉ ݌2[ӔemڱOoYيA$h2s4CzA5Wv3b/nW@tNo!eU0xW ̈́jFd d~v9/+<-WzA!%٧ 9e18EO_duK wO@pώh0lr>6ƪM1=2JV /Dx]= E,{h9t}.yYT420vui-F4T#v;ɕE0Q=tWYDФ %:&:qylQ:M=0+z e8nmM5$:}1:ю O5_2HHǝeUBx$\m&#E>^ޖ4@q PvXx"q&`HOK1j7?it|!KW{*}#df`k`b*\v#q>]nxW/tOXS+kp['yR<Ӛ͋YD8gç89/Kn5C{|Kgd(j}V F)Գ$XxnòZLN I>8е\60g mBܹ<4ڭ]7k2TpDyhU Hԃ{Hᤒ|$(w`L֟qk{ǥlm |gei3 ~dDZ`RUQĵ}jo#Y3 R|T1gJ{+smk`Ě99a?Hq_FvGGϵqͣW }ބoJQKCq'PvUw Men'@Bi5৳s}U& '%#! */'PknE_g9U.<~sr`knTU6iЁ.թZo3BTDkz s6k*sF0Z72~]*s?Sf1w}Bv6/d4~1O .:e$&Z.sUo/n@@Bv#F+d;[sFQNɍU[g zBv?i190(#!T=0iHp43V )V% {TӐ SLt{6g*jpf8U,j4HvבG{PW?]%8ġXjن>ϝqu;zf_ͮ' Mn1p̂9+JL̲Vfs^JQS# R4  ]2k\w='ӹ]!q`k-n0+"ܫ/ͮ']y/C!暏m;lW$l=ZE:iTޭZ8lI1F2h[hitYD }sf)!)tRx`AP7F2'B̅mM ޞO)uJu"YBWi7-m.} kyhWkTOaw8=9nGl`Lxg5>}}g>,v]妆͝kmDT <&d-[j|MMG-,pOk1揙o?Іi5;A mߊ۔. oS߆ 1 dX[ ,0\hyݾ3lq5(>(E1EtoX70$苊AIa XD).G.K"D=18Cd&9&(4|A9&`}0ۣ4T:8}m\#}.vS(>xwaMz{WZUBF'> `Ot4t3ߪEHμMVh{!#z?ODU9-;ſT]>¹H}H]x*g|UڦVObmģsѠת}-;J:9S [L?q 8, un`zIB1A;ؙqR{>wj~QlgNy-G{I:.D\+Msj]E<q&񮁠zq1d-v@_Oߗ`,>+nlC55'ݽ;F& [MZ_dyܑL|~ʨ&P21=t0wxƧZd5UR1~I?-(M^x=M]ʤ#BR4&q#' " ح\ZsvkQ~@H[HڔѾy"!oVRp(2Vnl3.e5 )]h*#e<KϏ@@aCfWa[_;`qY,={1Ac_[IX {=qOjp 9ْ{i7E< ĝ{(j="0Hg5lbҏsܬ[.xlߋ#}{]JNlK=Z =L Ov7>pG-6,(PT"gsf"-w=> q!-H@ӫPcK9MezVQsXQ =}U=YU%б4ͧbk'kHƞqa\mCV(c1/@sRA+;cS«G ơoKMsTns?~{hy{Fq{9օr7y]?F="HmIѰT×A߄L* g3%\7oXtO2KO:8{'OSn$b$.CЂP=Q;˓j$tw*WS|CGu۱ϥ.?3g&9A|Dw-?;YVdɓk1;] -"}Hͳُ?kc>l%m>u tϫ5EͷZ*Uwx~PrgO3kF90TGŶɝwwқnh2%lRזǿmY%0͆,Ș8NI' vp՘Ne o||/8h x൒BJ2'S(PO am4Hf0+a :5 NW_E"!1ۓ8igLbzVxpzZPe bF^l~L|i Z:}t2 vi䰜L2ѨW o8zeS\%׊Z=T_<&+uԠ8s2&z@m4(5|P3c ?eamdݍ:j nWw.D]<\aaint5-3W _qN,l3-D/p'֟8+'Y&YsQ#̏wo-{MUotA0Dxƚ>|90'':£Jj4 SYTN< sb;lP\[PJe gKĸ=xE^7܇Nw * C|#.ΐ_ ~Q+rq:Q&2A[_ UzdU肛E]VtAYh(}wϩԌ&BV8]I XGGB~[ZeMbMZƚq+ ~h!A`gmkȔo)ŖYd^64zezWy0HR% Rm.D^+,6dbCx6Bis 8t}F_}n\FPצlzq5fn#]M_v_8HMiIo%6b8ȍMAk&LdN)$Owy`IOvAН챱Hm,|/5VQa8$!<88eSnRl]!HP\SQ2) /}&Ƴ;te6tj:{ XҽgAҔ}# P'NƿwoEXuڻ؍k)u>uX1@hejomBجd[#|V?W\,.MB\V:.] Šmr q4=)dV܍=(Eً8Nz8os87y2C3C2l( ?C6RQ/(. 逶QU? ӭ/u5{uNwq8wЖtw2TОZD+OFb؃Sl(:^u}WOnɶin xp7g"bJZǍOq:;Hq^DAфaY[&]1>O[Ia8nk txPjA!tUmϯ"N0Zܲr٥p㏻c\$!K&"t%1g*٪-Z*aj&EKD$%FGP 98 rC1U_lZ }&{sVh#[ XMa'P^{u IB 0ָ% 296fc_!@;( ow1hU~!{6C8jI"BP+[pKZچޓ*݋v cJPsc~'5NXh@&ymGRR o1-ǡ< ֓W8B1uiR9Z&8dь褫Oaca-)`5m4Qv` %ɏ;.g0<)7m7\%!;SP 66fq5aPM@r.=oZQˀE Dz`Eo !}w 4à #!0Xؗ~ɯС`UXYF@FnCKC|3dzqQGxoNNm f}#IBIr>A<xOHv5|wz11 t'H%s mtmj-|.Ĵ}c)J[J><0ƈ{z 8+#C;w%(Vf F*u[nK+!X"v$ʙ^PZƸdž^$[ggCa9X޳}I iŠ1B _X_?'ݸ!gˀ(gU ]AK8^3ݷ+e^ԜyKTj@Qrk&>(P-X%!g|b)9v!(b/?K;ʜa2Π@@ q?:?ƻ\0tb|ƩPcr2)g=ˊ4ƶg){*ajrc0Ȧ ,ztySʄgR# |w C.)pԻ^ҟYcvb\_(ln vJU~DAY.`Hi qPPڊb"F6xѪ*tUϞ%OZLpoVp@;Az}mEYXC\, YCct+`TiE]4ɸKp%h\^Wk4x~tsvm%5#u Ƭ>aO"vDlv45ׯ.#>~Dz]QQk9,W?qm1&@>h8Yo,,ΒE B8m ?j'mZcNhx8(˶eс%־7kU*xq=0՗Г;x~XAkFfdil 0XNrR ``r_Y3꒢D~'xSVWڭ#/逯ޒmR<:C<+ױZ~%]"e z>z ]yk`{6\ȅ-_6?z?Ƴ>ar'H5 ԰8 RG􊘏# ow?#zgN բ[` twe9qH\?\A?auEMq&r{^ť<rK wa~3qu$ ~J"DP<ТnNS |vyX&mqFkYjLr=p`@SED@A8K}]~_UHLx_h ncL8f7/Y|>,%@$iX,S#RW]>g27#;c,_ax@!sGpړ! 2~SLLӦqHc\D6XbK}q˹>\KCj1TD-@P">+uօ K %'V%-7IWnj6E.BF&3gRN Tjwt\^1oNUz^FtJm~m6[&oF1Ő#<"eW+T4ΔcFnX6Y(W>)_h"q7bm̏&F=u/𔿽 >[B?a޸ V,~Ȕ/܎ż]V~NC1?0 ER&Jq.Tn$ȉwyja.| ,!9"Yk7CRy1 1x.87eJX\4zx~|WXoF`}o9f]ma3wds8'$iP'f&5fE˘dALڥG.ۙ~*E`9Gp%L ϩ%l(G̶Ub@.<ާWjz[ɋ+-vA_MeL^sƈIxDڬ|X,5-@BGĆSRnb0vkx0THRvL?k~!hm3y&땃ɅBNF e hdt91,&-Ȧ,gMh)ضY#su>մi8E J2^ZzVT6 Kl<:LsV7r7*O1BWWX*+ @K] 4Y F3!tk0+ޕ?_3!S^QMRDۆDLoOʃɲn-OO.^/`gY*uId12;gp `_dCsYKM4>Cf%iҳ972=ԟPxm}cж XdL+ml_ؘT3D <8. SNI'WTi:,Uz;K7$.>{`ojf(.Bazgj y!Rv%/2i TNOZz@ yX= `H>3MnqG*v:>xͺZ󍎗WhB#NH)X28٭T_Y>`;DYFɆ%T(=8"`hsՐG=;PyBS6}ok#JyY]op-#K2}A1pڧm4f=x;Ff&~,zwP9 ='dZ_Sa 9Go SO|;ځ^l_R]n5<qX@Tn;>NR~upB Jq]MNii/O1IIH}6ŹQa5a7w/'5-9kbN**Ȍ"6M&h.GD( x+E%F9ғ:v'e9'|OW7܏ >@:YP'p4>qZ&~ h ӿ]/e35WIګF&NL"1?r%M|Ug^NS?'[G?J=5MTךTmyEyI Uf$@+~X  Zu@xyk5v1Үaq"􊳽lk ۻ".;:Uj1zD$ [9# 3gk~'zJ5}l?6f,(IzPtv-P"DPI[~n@O^6NO ]5jmG~sZ?vU1]`H RI ψJbD&c*ԕ66 É"~= a9Cz}| !a֌)V/^L.H5n0oȳRx 6=4_m_ C^y7@Z04D n2)۽9F視DK_ }#5Dg)kM( 0F7W &ytD1IY KUeiOzz dSxnsSi,e'/4*?}pxF~eA{Sv-C;l,"Xr;ܷLe'jp"CJGU2$L2m,{}RGG5>C @)nlr|Y6YsD9֏)m0Y1D\NyBejË'VsqS~e>m$_4_~(;~r1,8=`}$/>õW@VZxRx<ðׄF'Łv*Pq@^b`,A$qV2bk}F_9:gBTצXp|L`"QU]~{˥6l$UGKeVĸWvMj Cc'V1n׼*18~Hż R{ -pdH r*^GcD8?'^S< V,)Gׅ &)&{?F/7BrI^{ĸ0ŷNQ@y,/g'&WPՉ"9~BB9շWmo|ïd8Oc.iEv}డBaߵ4GF~ ~b1ܘ!Գs0J[q}+B V{X@ GފTE$F[?\|k9X7ݼ3Xd3%+1NX4WO[6鴳lQ\}|piY8m<ϧl~acݠzʴ\be) )TsoE< Nx) ]+d5{s^{8O Od@Tڮ6NWeېgi|rjyY ɸR8"H"!Ӿ8,RWm{>-w/rtc0ݸZTԤQb_gMF? ;:k%'5|0 2bug'ME.myyW8PR[B?!qu9?[/ ]E(jieJe7wǾ1_,_v)f+/_%KNTNm*Y[;|30e_-q?ev37A!RrL8?OkM^I]UA/LH1`3Vl\d28 ,ֽ~ze*74>˸`o,a4G^,Al'^l:uQ+%+fmZsMԶ1]IC.oqI^gG`JG<;I:IME6 B #vu?J]},5Hu>'Rq/ !WͶi7S}>d:=z*;]^W0~WZYtFxeE2k/GB@fE.#5`3zGI×UMՎ,fwVZ`bWlpODik\q{beG{"$"w2{ LR$0Ē#Հ,H|GQSOJ:jjhF^K*a'ŏ+$u郍:*’-wPmwa9od^>]KӢ$mg>; G5 mA?yQ7 7d[5 mecDzSo,Qfot 7Ч[\;^cQdqf;ÎA[bf~A= ,؆1 ]yO )!+ wE0 6_.w3 Ov<7N綠g5'~tB.yU)X⯉~`h/|dE}xOu}?ёf$ˎ[Ɔ{eb:-pL>pNGnm6{®Wt.!2% feY#JbA##Ē4+*ݬ4\ ۣ'-Shʻ#ǀ)@ħ[cuY k>esP_O>M^,W^LyKy~f֠<V:q~6<CZ|cZrCҪd60; Na=ųOguScs1=_I/Xbk`C?y/#YܰwZCCc&v: lC䛘`zJCBM槑}j"?)[GgochyvGdƇ`TY yNsN|ub[g]aKS>?~+oI0~3ʵdVRn0is NС#Z WqG')0^Իͮ%O fWvUߘ<$qDz_<j_ SDDDkM@n bcRvaܰ۫@ 6^ƃN#.vӕPh9 ^st﫳 RƙK)Xzh c,nݬO B3^̘6mJdxjP/캍'>\\eB!qCa 1b{PH~Vp<34+<+ -9hgR I|p7bB+Sұ%q], s6BMmae!mkI"˯ŋu{~z_IDKd^BOH}>hX;~?k empx(Ѭ{Oe)@|mjɟmFk -qqI в*M )+j5qh&Wm@nߘT=ƔJ [2gWd'R|xd&V~D(U !fA .҆Cl&-$$a^aؒM7HLGKH y:Xn3{ teK[ƧK\:SHA'y6Plrޙ k )~b6ܲ7RVt=4q5Xgie{E-ev"f&emU՞Mmp3Џ헢U`^ x|aܯ\ŋf4IHZsi/m@~imß;^\=W&(Qnsuv%,@QʩuE{ 9gT2bb! 燹Jp}蘅BQY  A \/VMg<1+Ÿb%뛫^byd+f-VLt@U_GEc=ΎN|\.><_*;zFa?l#57nGLlxu= 5:g"byhMt.8 s9?"Q ؆m\=I)d}\׌eWƲۙTl;/Gj]s R^"ywt=7@fwEMMS//nض7Zh+8'O]br6YCJBÎI۰g ҁbLy |/a{*7I^3բN {g"˺zU:xqߣ`%/4.뛪I?L>d32cX(r`3)FBnVd|W{ZC*D))Ӌ}O/ż ^) o, @ ' :$C*epA$pt\_YJ8P%_yIF#e&͠ž){i{IYoVt MDcܲ؅ X䧠؋=,}kyP`WH@gM{GGv^_H"ˎ_"y2?<􂴬_ל¿0WJ]dWqwi0WӅG=CV=K]wq(L^Pyw`Zٽ'Ja_2ﹻ#pyezf.hp^mTp.0=bw7؁ 7zU"lW& ~:"7U"jgBx|#))O6&;ur{b]q;\^x\qV'`\3 ;m%Lw#uFDqr&{)J3H_n^ ἖\$}Ov|gA,9Ǐ3Bf !w+%؈"O: 8l?8(< Sxr zB1OroDža%y(w>bGcs&牽KMF0@;$mJ`gT3B*r 'x{,xAyJD( oB^Kaၠ jumtm(O,$P[_1jߪOlK^Oxahfq_;.}qdE+HoFl>Xx7B+Q3+ Ѝd{r`]<$0/(5U_Tռ;wj I"036nG@]I{xV>r ̩0abK~VL45 ȓ俖!?N1"Mn0 HZG QPهpsPp)|9Nr-ryCBOs&_TakiL.1֢>4 xUvYzp)JDԙBl;ns83(< Or}+Y/v"gdz$Jpщc91/3hm,]Y9M2=++7ad<_[/ fi_|k}TlGϮ#Foyrxv7F8'F΄ErT?mX]:zMvE&rGBSgU r#bDZQ?id?6<^;&hs}1&TghxG6&'IiGO` o߽~Ǔzr^fYN+].[P VwCNyY3l';QYV/Sەڭڡ3#u1I/L^Ss'ÂW+f?wYAXمv:wFo8U(\[O}Ϲh/*żǶj6 B2)LϿa V1o.q^{[Fh3ap%w$Wz~K<+3nyh1$E@Ht2\rp b0]  gb|dP9鏼]Lci@o7~GF<=?^v)3s}ob\ב>~۹9IvӷƜhoK ^)GHa\ y#slaƃbx(K6IߞWo_TaH}_9ڋg~66(=O^[aE~6ҾxKOON?|VE}bRӄɺ[bD|BstҒӟi1B*-{aH^D5F9{z2pb4d.0HqH;9Fzovo'SG ?s$;S(G^]2/"^:{:GKﭐ &/rn-:,soWQ~v0)]`vMV k`Tm\l8Ui9ƨ&; ›O_˚0yEy>k_۠[?FzL@vľXkovo}־ iJX~_#e_N^ۡ{Y$R췧j ;x"ҞL05Fϻ?dgND+Ρ#ınk*|_//vcx7yE_gǼfx#)+DmH_Bl;0OW"U̔3qLw3yׄ-ʒ'9T+oԛ~rKNUjcRwwtUG /VsM#t[oJ(O\k8S9t,h_ۈ/W ^/KcS !clF`iOtN!?]bԒH|o71?%y;Mם)#O(P̕ы%;qGzj`><\]yr ytDNzT^gk6Q0H|Da"!]Wj린H)J/d}d4H+% # S[$[>3Xxz^].Y@lh/~H_NtyI{v 7o$E vGܼDoku8Gg*B1QXZV;ԟyjMOwD4_S=;f$^*F.mLm0N-~fmdRH.ެ-CEmsSrr[>Dzn>DŽ#UO).#́9Mr+o"Q{}a\).Eq4Ἰ)a!~Lo"` :]=W\zKapz xMyt0pj닐L4* @?i(υV` <f~{gVW+{co[S4kA) \E8voFt4n!0oo[o[?( 5E"BZB![9MMq+zl%m|`8Շx2U%Ãbad)@w?g⢣z(v"Eܧ$8s_"`$ u*?Lv+>]sW^pwRtԛ1rNo ]jM[.Ό~ĨЍu˟C"'e~'p`| a6Dƒqswg'}Jj+(kظlEJK_@FS_Qijh /qR޷2Jnrl]&(𚇟W_|޻)OS_֪Uz( ta^lLL̗Eu:^ iiiU"y &"#EVXY9v0 $K ~9#uΓ|UxEOAe|d+9: 1004^fvN󤦦ɄЙo)/╀xMa̰pC(/( ۫=ڟfffO+>P;.%>ƙwbGv;Îʅd@ I<L u]qf ~8Enl\~a[3,JtZ7c)-KLhzvπ-WXG;1A'<°7w~v>2qX| K_8CܧgTl*6IsMeh:l㨹^ޞT^waod׽ak\\%Ml\ci_D7%"z1 (ri#eZhso[cLSl"dvҟʷx #R'L1iC Ns]#OϊPgk[\X[8.e G6]wSVW3%|t4<<2^]GM,[7o3n9s췕+b.\73Lqt1˓_z&^# CM5 )Tjh71xyn5W( oDhO[߁Sn'ԥE[{B @9 Q7ӭgKEj *Y!d9z9CI0~cc/u.Vu f98SJǏߙfc6r[v5ltݎ (qߥv]lȬX:46|" qiZȦt&r I`tuf4PO0}mpo(t2($ـn/&&&IΥť5{;ݜQ⼼:FG>zD<[f(!G;?2 9B>һ^{A \[_P jąS\꟡GQ₂V5?/42 V~He#4~Yy6ciL|D`uu૵ӻliwOg [Zg  )5@,lg|1\GJݏfxJX@i@fXӲmUujV#Ӈ Qu$R7IDV3D_.ZQN +i9 ^\k 8s&jk vtˣ|*<.=RWWO~U[[__֠Z5b!GLXR: =q/e?Y4؀8cbf +x9l%9)=='WG qzbRS+{3R#zxVs=jnI+=<ز#ֲ ɣmMA@^[k[.鴏Ý}*ڴD~OzZZ[:;poյݖfϺl(w[|-絿ce| w.s'؜wdttKon.b}gBK1eǐAj'D=AR{ba^u5b}K/d11@μonUR|%/Ѽ{TcMnXK6CaRZS#?KG7-kі f;g/X|ݧ bG[%Hs2|_nyN"n&ɸnoooe*&uag.>^i =NnӶݭΔd醼7&-u K?E}dI aFc(O0clr 3ʔ3TN5 Ϟ3sruWt!H0 =5 5INNqNn#eB8ɡs2c֨[[9{{{f=/À]g6ӱAO^wz76Y%P+k"z@N/c<}VՀ*sTQlF(;lSuX+LWTwK+eeeӲsqQ?"'bUrqttz #g6@ k}/!kXe^w~~~BqwwC1==mgDw<=;sԜƴ5G,'*\%uI7͉r@ЕksObܰq_cW%"/uM?.2ա_}Qȳf9->>zG(^xDQxZXFiT5'벩!7U.G]OMJ^hPUtƑ HnJ >p@H?FjMp+f|(Ç7W٧CRs>*sN gWG ;7챽yMxuJqI`6>>>~ItY@3V dɾ K }khd4)uwD248~ٸWWg%ɃŦR7r=0<^׵ /7o$OPr@?Q7J{SSn-nogHݎ?Њ1p탃B-i2P ̉ hbtPDirC,%得rw!J+8E zz~]h @.g;:@C&+oodn鋋z@Q>ą]K]#}2p%cp5&o͈T5|.#:< 0'79Sa@@40}]g |%ˁs VSw`F+Θ"_ ""R[~ K ӂ  `v&Nʓ@^` K FBP`תB6hFSކŃ8Vu HIV"Aa<+ř`"}B lG 1F\P`Sn'o-7%j 0{<.8(ɞ *b"17#0䗬%F{359s6Қyч>99p$Ҏվ~DsXjln;=MN&`KtޤH5^(Vb r  _xbJ:\$!,*U4f1$`ˎBUbB$&aQm]Z+nHZu}mZD]*Emg}Z3BHﵟﳿ23g3 ۇck^XM8uڦw\z<|򌞋'&k~O 2wc_╮rm¦ph$p#ȻqW^¼訾siZATXgcπAhd*|sۃ++m7*̰NOv>WP=Sq{=څ3oϜ91f>](,[rS o[Y^mgC0&t_<0b/ztL1xzW sv\jucѺ~ǎhǜPXou[8l$nUc#s a1Qx1rȑoTR?Cun s^N92̷1T$A`pƔam x7aUߔ7m6ڦ t-*6vߵµ6n z 86 {J;=k{;0>#]lyEhȓ'OfZ'55ǯ~NU6.{ VO:C0۠y)bbZK]֦+enJEޙ7fS==};?ӦM;}^ӻm~n]/zL+'v])?gjI,ӦiΝ>euVxq՟USmuHh>=f|C d  fΚsu :v.^{mBEͪ 7GڛTqcy"gq(-LFر!cr~rC;`U_%E"#1ʼnn_F_BWEv6*W/\A#LZmjDE7畵_K KZ~5/i!^ g|eCŒ]q &7aEx*,x˛_=?L+?OxӳrO >~d{ĭ9{v}*cOoZL?wz)+p8D/+~/\^7?XSo ^Io 7*v#9@@@@@@@@@@@@@ $? zS/6 nldU 6c/XOlLN56y29Ŷscsw6bs Ps5,=l6=m=Ҡf N}Qt©r2)]Ryy WnSL{'wߣv.`0dr\q"}|A;t ?KQgO̱bF5{ިne62 StcrmEm0匲2KaN;jd ri3әz>ʉ=jKRMz3/0 -vd Zզ]S9xAm93: <q*HڷyS$u5i\!&K~Gc&p1hH}QHFh"AVdFȄ(Y A~RfBd@zdG(TR1Gy:_A"`z3 D kǡ!7F;h-ڊz@*LĔApjE){.U7α(J#- 4ب \l@dsE̦HҪ(RA+(VQkZ2['QJt\Y4 tL9/Rmu]dSsQU֊ BCK܅L#.`5ߺ tX}A?,?l2X]x@7tAZig!5P: .l(?ݢGբ\R߲Ѳ,pǡ'/A V;Ew@&~~f۟wrⷥ'ǧ`dN()i2Q*$E"9tԺ)i\xci}T/iiR#*z OAkt*6[IjJ8g33Z.)#oub-MT:l+b1b@UXm.[%4w̷5T- ( j27?F; ٦udkFזVN\^iTL),>rEE}rY\5QG.|^ 2&+CnlFs6+) /PۀxÄ˖My6qK>s=P$?bPII 'K!跿`7غϟr'_O@@@@@@@@@@@@@@@@@@@@@@߄4zWS^GjfŠ)57kzK^q5POra'9v-+ ܳS[O^UЃ_nƇ3ڞI9.nrn''*oXvo#UGG )]?ç;vz#% p{QO2}z厾T&SRR~ܤ ҌVy~20]Y?TBV7~GzsH>< |0Aq_ai ʉ=jKRMz3/0 -vd Zզ]S9xAm93: <] Pq9KpnMd8&3d6~ex_Ej.NJu I]xW`bշssiX,I&Gfߕ 9tUbi.Z "r{R68 @, ;Hw"`ma[A/9Gp(wR[WQs xEL"WIDrEH"O'd:**9191%Sz x.7Ps"Q4}fccMN11U6qBVw*&Cju2waK\kQ~p߻E9tEeYqeY>L5C7N>1 R_H! 4Rv ۉP8lx~f۟wrⷥ'ǧ`dN()i2Q*$E"9=aޚ)i\xci}T/iiR#*z OAkt*6[IjJ8g33Z.)#oub-MT:l+_( L$"B;F`K+*:(U[PA(`$vprk'Zq(#w}}w'P3B4Fz#?F4>sK痴?F|.]Jmť,;O>ΓfU/,5J7kR>ܲZ{.WF.%yxgI㲊WaSa+m;ړ2#\m{::\JXq]IZu5ą|0IzχJdэ%#ɸFVF鸒9jr-&L!4lg9V$q!<]ϕN6cBqx:wt KYzx,&iR;[,|[zL } V^~lپ́W aמ7Pŷ}߳ouy6szT꽁;S7N6_.}߹air]=8mUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU |z@! p +8 ($".j"!If'}hЉbjݻkE snGyEy:{p *k#@6 {p *k#; DQ@ߐA18$XT`hR۰ 6`b5MGC:PϿn)~oWpV4RE/Y/,ٱbef_yM[޻H&)fި/ܦ}/{p *k#@6 {p *k#@6 {p *k#@6J{s@jt@& ` lA@B-!H 1X01MI=,>i[[q>i|4"%,-gWʢ7vuD-=CDYQ_ޫ/oAEw swift-im-2.0+dev6/Swift/Packaging/WiX/0000755000175000017500000000000012227051774017352 5ustar kismithkismithswift-im-2.0+dev6/Swift/Packaging/WiX/Swift.wxs0000644000175000017500000000700012227051774021206 0ustar kismithkismith Not Installed And Not CRT_INSTALLED swift-im-2.0+dev6/Swift/Packaging/WiX/include.xslt0000644000175000017500000000065112227051774021713 0ustar kismithkismith INSTALLDIR swift-im-2.0+dev6/Swift/Packaging/Source/0000755000175000017500000000000012227051774020103 5ustar kismithkismithswift-im-2.0+dev6/Swift/Packaging/Source/package.sh0000755000175000017500000000137112227051774022037 0ustar kismithkismith#!/bin/sh ## For a Swiften-only tarball, try ## EXCLUDE_FOLDERS="Swift Sluift Swiftob Limber Slimber" ./package.sh export PYTHONPATH=../../../BuildTools/SCons VERSION=`../../../BuildTools/GetBuildVersion.py swift` DIRNAME=swift-$VERSION if [ -z "$VERSION" ]; then echo "Unable to determine version" exit -1 fi echo "Cleaning up old sources ..." rm -rf swift-* echo "Checking out a fresh copy ..." rm -rf $DIRNAME git clone ../../../.git $DIRNAME echo "$VERSION" > $DIRNAME/VERSION.swift rm -rf $DIRNAME/.git echo "Excluding folders based on environment variable EXCLUDE_FOLDERS='$EXCLUDE_FOLDERS'" for FOLDER in $EXCLUDE_FOLDERS; do echo "Excluding $FOLDER" rm -rf $DIRNAME/$FOLDER done echo "Creating tarball ..." tar czf $DIRNAME.tar.gz $DIRNAME swift-im-2.0+dev6/Swift/Packaging/nsis/0000755000175000017500000000000012227051774017617 5ustar kismithkismithswift-im-2.0+dev6/Swift/Packaging/nsis/swift.nsi0000644000175000017500000001365612227051774021501 0ustar kismithkismith# Ask to be an admin RequestExecutionLevel admin # Note - this doesn't mean you get it! # Use the newer, nicer installer style !include MUI2.nsh # set dependencies !define msvccRedistributableExe "vcredist_x86.exe" # define installer name Name "Swift" outFile "Swift-installer-win32-${buildVersion}.exe" # set default install directory installDir "$PROGRAMFILES\Swift" # Declare variables needed later Var StartMenuFolder SetCompressor lzma # How to do pages with the modern ui. !define MUI_ABORTWARNING !insertmacro MUI_PAGE_DIRECTORY # Remember the start menu folder for uninstall !define MUI_STARTMENUPAGE_REGISTRY_ROOT "HKCU" !define MUI_STARTMENUPAGE_REGISTRY_KEY "Software\Swift" !define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "Start Menu Folder" !insertmacro MUI_PAGE_STARTMENU Application $StartMenuFolder !insertmacro MUI_PAGE_INSTFILES !insertmacro MUI_UNPAGE_CONFIRM !insertmacro MUI_UNPAGE_INSTFILES !macro checkNotRunning FindProcDLL::FindProc "Swift.exe" IntCmp $R0 1 0 notRunning MessageBox MB_OK|MB_ICONEXCLAMATION "Swift is running. Please close it first" /SD IDOK Abort notRunning: !macroend Function .onInit !insertmacro checkNotRunning FunctionEnd Function un.onInit !insertmacro checkNotRunning FunctionEnd # default section start section "Main install" # define output path setOutPath $INSTDIR # Specify files to go in output path. # If you update this list, update the uninstall list too. File "..\..\QtUI\Swift\Swift.exe" #File "..\..\QtUI\Swift\ssleay32.dll" #File "..\..\QtUI\Swift\libeay32.dll" File "..\..\QtUI\Swift\phonon4.dll" File "..\..\QtUI\Swift\QtCore4.dll" File "..\..\QtUI\Swift\QtGui4.dll" File "..\..\QtUI\Swift\QtWebKit4.dll" File "..\..\QtUI\Swift\QtNetwork4.dll" File "..\..\QtUI\Swift\QtXMLPatterns4.dll" SetOutPath $INSTDIR\imageformats File "..\..\QtUI\Swift\imageformats\qgif4.dll" File "..\..\QtUI\Swift\imageformats\qico4.dll" File "..\..\QtUI\Swift\imageformats\qjpeg4.dll" File "..\..\QtUI\Swift\imageformats\qmng4.dll" File "..\..\QtUI\Swift\imageformats\qsvg4.dll" File "..\..\QtUI\Swift\imageformats\qtiff4.dll" SetOutPath $INSTDIR\sounds File "..\..\QtUI\Swift\sounds\message-received.wav" SetOutPath $INSTDIR\images File "..\..\QtUI\Swift\images\logo-icon-32.png" SetOutPath $INSTDIR\translations !include translations-install.nsh # create start menu item !insertmacro MUI_STARTMENU_WRITE_BEGIN Application CreateDirectory "$SMPROGRAMS\$StartMenuFolder" CreateShortCut "$SMPROGRAMS\$StartMenuFolder\Swift.lnk" "$INSTDIR\Swift.exe" CreateShortCut "$SMPROGRAMS\$StartMenuFolder\Uninstall Swift.lnk" "$INSTDIR\uninstaller.exe" !insertmacro MUI_STARTMENU_WRITE_END # Add the information to Add/Remove WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Swift" "DisplayName" "Swift" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Swift" "UninstallString" "$\"$INSTDIR\uninstaller.exe$\"" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Swift" "QuietUninstallString" "$\"$INSTDIR\uninstaller.exe$\"" # define uninstaller name writeUninstaller $INSTDIR\uninstaller.exe # default section end sectionEnd Section -Prerequisites # http://nsis.sourceforge.net/Embedding_other_installers FindFirst $R1 $R0 "$WINDIR\winsxs\x86_microsoft.vc90.crt_1fc8b3b9a1e18e3b_9.0.30729.*" ${If} $R0 != "" #Skip vc runtime, already installed ${Else} SetOutPath $INSTDIR\Prerequisites MessageBox MB_YESNO "Install C++ Runtime?" /SD IDYES IDNO endRuntime File ${msvccRedistributableDir}\${msvccRedistributableExe} ExecWait "$INSTDIR\Prerequisites\${msvccRedistributableExe}" Delete $INSTDIR\Prerequisites\${msvccRedistributableExe} RmDir $INSTDIR\Prerequisites ${EndIf} Goto endRuntime endRuntime: SectionEnd section "autostart" MessageBox MB_YESNO "Would you like Swift to run at startup?" /SD IDYES IDNO endAutostart WriteRegStr HKEY_CURRENT_USER "Software\Microsoft\Windows\CurrentVersion\Run" "Swift" "$INSTDIR\Swift.exe" Goto endAutostart endAutostart: sectionEnd # create a section to define what the uninstaller does. # the section will always be named "Uninstall" section "Uninstall" # Always delete uninstaller first delete $INSTDIR\uninstaller.exe # now delete installed files delete $INSTDIR\Swift.exe #delete $INSTDIR\ssleay32.dll #delete $INSTDIR\libeay32.dll delete $INSTDIR\phonon4.dll delete $INSTDIR\QtCore4.dll delete $INSTDIR\QtGui4.dll delete $INSTDIR\QtWebKit4.dll delete $INSTDIR\QtNetwork4.dll delete $INSTDIR\QtXMLPatterns4.dll delete $INSTDIR\imageformats\qgif4.dll delete $INSTDIR\imageformats\qico4.dll delete $INSTDIR\imageformats\qjpeg4.dll delete $INSTDIR\imageformats\qmng4.dll delete $INSTDIR\imageformats\qsvg4.dll delete $INSTDIR\imageformats\qtiff4.dll delete $INSTDIR\sounds\message-received.wav delete $INSTDIR\images\logo-icon-32.png" !include translations-uninstall.nsh RmDir $INSTDIR\translations RmDir $INSTDIR\Prerequisites RmDir $INSTDIR\imageformats RmDir $INSTDIR\images RmDir $INSTDIR\sounds RmDir $INSTDIR !insertmacro MUI_STARTMENU_GETFOLDER Application $StartMenuFolder Delete "$SMPROGRAMS\$StartMenuFolder\Swift.lnk" Delete "$SMPROGRAMS\$StartMenuFolder\Uninstall Swift.lnk" RmDir "$SMPROGRAMS\$StartMenuFolder" DeleteRegKey HKEY_CURRENT_USER "Software\Swift\Start Menu Folder" DeleteRegKey /ifempty HKEY_CURRENT_USER "Software\Swift" DeleteRegKey HKEY_CURRENT_USER "Software\Microsoft\Windows\CurrentVersion\Run\Swift" DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Swift" Goto endUninstall endUninstall: sectionEnd # TODO http://nsis.sourceforge.net/Check_whether_your_application_is_running_during_uninstallation # http://nsis.sourceforge.net/Date_and_time_in_installer_or_application_name # http://nsis.sourceforge.net/Removing_'Nullsoft_Install_System_vX.XX'_String_from_installer swift-im-2.0+dev6/Swift/ChangeLog.md0000644000175000017500000000575312227051773017141 0ustar kismithkismith2.1 --- - Fixed potential crash when using proxies on Mac OS X. 2.0-beta2 --------- - Enable auto-completion of nicknames that don't start with a letter. - Generate crash dumps on Windows. - Connection timeouts are now on each connection separately, instead of on the complete connection process. - Don't allow pressing `` in the roster to trigger logins. - Don't lose security labels when correcting a message. - Don't crash when completing user search without selection. - Always auto join MUC with a consistent nickname. - Renamed `swift` binary to `swift-im` on Linux. - Avoid woosh down and woosh up on Mac when opening chat windows. - Improved MUC invitation dialog. - Fixed problem with displaying group names containing '&' in the 'Edit' dialog. - Show stream encryption status in header. - Show extended certificate dialog when encryption errors occur. - Don't allow server command results to get interpreted as HTML. - Additional connection settings (such as manual host setting, proxy configuration and BOSH) can now be specified through a new dialog on the login window. Thanks to Tobias Markmann. 2.0-beta1 --------- - Windows packages are now built in Microsoft Installer's ".msi" format. Please uninstall any older NSIS-based Swift packages before upgrading to this release. - Suitable names will now be suggested from the contact's vcard when adding/editing their roster entry. - It is now possible for sysadmins to deploy files with policies for configuration options, such as making it impossible for users to save passwords or to force sound notifications off, or to set defaults. - Swift will now use appropriate algorithms when run on a Windows platform locked down in FIPS-140-2 mode. - Our TLS provider has been changed to the platform-provided one (Schannel) on Windows, allowing us to use certificates (both file and card-based) from the system store (CAPI). - Added support for message receipts in one-to-one conversations (XEP-0184). - Added support for several MUC operations (kicking, banning, invites, topic changes, room destruction, changing participant affiliations, ...). - It is now possible to resize the font in the chat window conversations. - Added support for message correction. Use 'up' to edit the previous message. - A list of recent chats is now kept in the 'Chats' tab of the main window. - Added support for server commands. - Chat tabs with unread messages from several chats will now be a little more descriptive. - Use a bar for showing where the last read messages in a chat are. - Added support for SOCKS5 and HTTPConnect proxies. These settings are taken from the platform preferences. - Retrieve incremental contact list on login on servers that support it (XEP-0237: Roster Versioning). - Lots of bugfixes, performance fixes, and other changes. Thanks to Tobias Markmann, Jan Kaluza, Thilo Cestonaro, Arnt Gulbrandsen, Vlad Voicu, Vitaly Takmazov, Yoann Blein, Catalin Badea, Pavol Babincak, Mateusz Piekos, Alexey Melnikov and Soren Dreijer. 1.0 --- - Initial release. swift-im-2.0+dev6/Swift/QtUI/0000755000175000017500000000000012227051774015601 5ustar kismithkismithswift-im-2.0+dev6/Swift/QtUI/CocoaApplicationActivateHelper.h0000644000175000017500000000104312227051774024001 0ustar kismithkismith/* * Copyright (c) 2012 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include namespace Swift { /** * This class is here as a workaround for a bug in Qt. * See Case #501. */ class CocoaApplicationActivateHelper : public QObject { public: CocoaApplicationActivateHelper(); ~CocoaApplicationActivateHelper(); private: bool eventFilter(QObject* o, QEvent* e); private: struct Private; Private* p; }; } swift-im-2.0+dev6/Swift/QtUI/QtFileTransferListWidget.cpp0000644000175000017500000000374712227051774023211 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include "QtFileTransferListWidget.h" #include #include #include #include #include namespace Swift { QtFileTransferListWidget::QtFileTransferListWidget() : fileTransferOverview(0) { QVBoxLayout* layout = new QVBoxLayout(this); layout->setSpacing(0); layout->setContentsMargins(0,0,0,0); treeView = new QTreeView(this); treeView->setRootIsDecorated(false); treeView->setItemsExpandable(false); layout->addWidget(treeView); itemModel = new QtFileTransferListItemModel(); treeView->setModel(itemModel); QWidget* bottom = new QWidget(this); layout->addWidget(bottom); bottom->setAutoFillBackground(true); QHBoxLayout* buttonLayout = new QHBoxLayout(bottom); buttonLayout->setContentsMargins(10,0,20,0); buttonLayout->setSpacing(0); QPushButton* clearFinished = new QPushButton(tr("Clear Finished Transfers"), bottom); clearFinished->setEnabled(false); //connect(clearButton, SIGNAL(clicked()), textEdit, SLOT(clear())); buttonLayout->addWidget(clearFinished); setWindowTitle(tr("File Transfer List")); emit titleUpdated(); } QtFileTransferListWidget::~QtFileTransferListWidget() { delete itemModel; } void QtFileTransferListWidget::showEvent(QShowEvent* event) { emit windowOpening(); emit titleUpdated(); /* This just needs to be somewhere after construction */ QWidget::showEvent(event); } void QtFileTransferListWidget::show() { QWidget::show(); emit windowOpening(); } void QtFileTransferListWidget::activate() { emit wantsToActivate(); } void QtFileTransferListWidget::setFileTransferOverview(FileTransferOverview *overview) { fileTransferOverview = overview; itemModel->setFileTransferOverview(overview); } void QtFileTransferListWidget::closeEvent(QCloseEvent* event) { emit windowClosing(); event->accept(); } } swift-im-2.0+dev6/Swift/QtUI/SConscript0000644000175000017500000003517112227051774017622 0ustar kismithkismithimport os, shutil, datetime, re, time import Version def generateDefaultTheme(dir) : sourceDir = dir.abspath result = "\n" result += "" result += "" for (path, dirs, files) in os.walk(sourceDir) : for file in files : filePath = os.path.join(path,file) result += "%(path)s" % { "alias": filePath[len(sourceDir)+1:], "path": filePath } result += "" result += "" return result Import("env") myenv = env.Clone() myenv["CXXFLAGS"] = filter(lambda x : x != "-Wfloat-equal", myenv["CXXFLAGS"]) myenv.UseFlags(env["SWIFT_CONTROLLERS_FLAGS"]) myenv.UseFlags(env["SWIFTOOLS_FLAGS"]) if myenv["HAVE_XSS"] : myenv.UseFlags(env["XSS_FLAGS"]) if env["PLATFORM"] == "posix" : myenv.Append(LIBS = ["X11"]) if myenv["HAVE_SPARKLE"] : myenv.UseFlags(env["SPARKLE_FLAGS"]) myenv.UseFlags(env["SWIFTEN_FLAGS"]) myenv.UseFlags(env["SWIFTEN_DEP_FLAGS"]) myenv.UseFlags(env["BREAKPAD_FLAGS"]) if myenv.get("HAVE_GROWL", False) : myenv.UseFlags(myenv["GROWL_FLAGS"]) myenv.Append(CPPDEFINES = ["HAVE_GROWL"]) if myenv["swift_mobile"] : myenv.Append(CPPDEFINES = ["SWIFT_MOBILE"]) if myenv.get("HAVE_SNARL", False) : myenv.UseFlags(myenv["SNARL_FLAGS"]) myenv.Append(CPPDEFINES = ["HAVE_SNARL"]) if env["PLATFORM"] == "win32" : myenv.Append(LIBS = ["cryptui"]) myenv.UseFlags(myenv["PLATFORM_FLAGS"]) myenv.Tool("qt4", toolpath = ["#/BuildTools/SCons/Tools"]) myenv.Tool("nsis", toolpath = ["#/BuildTools/SCons/Tools"]) myenv.Tool("wix", toolpath = ["#/BuildTools/SCons/Tools"]) myenv.Tool("textfile", toolpath = ["#/BuildTools/SCons/Tools"]) qt4modules = ['QtCore', 'QtGui', 'QtWebKit'] if env["PLATFORM"] == "posix" : qt4modules += ["QtDBus"] if env["PLATFORM"] != "win32" and env["PLATFORM"] != "darwin" : qt4modules += ["QtNetwork"] myenv.EnableQt4Modules(qt4modules, debug = False) myenv.Append(CPPPATH = ["."]) if env["PLATFORM"] == "win32" : #myenv["LINKFLAGS"] = ["/SUBSYSTEM:CONSOLE"] myenv.Append(LINKFLAGS = ["/SUBSYSTEM:WINDOWS"]) myenv.Append(LIBS = "qtmain") if myenv.get("HAVE_SCHANNEL", 0) : myenv.Append(LIBS = "Cryptui") myenv.Append(CPPDEFINES = "HAVE_SCHANNEL") if env["debug"] and not env["optimize"]: myenv.Append(LINKFLAGS = ["/NODEFAULTLIB:msvcrt"]) myenv.WriteVal("DefaultTheme.qrc", myenv.Value(generateDefaultTheme(myenv.Dir("#/Swift/resources/themes/Default")))) sources = [ "main.cpp", "QtAboutWidget.cpp", "QtAvatarWidget.cpp", "QtUIFactory.cpp", "QtChatWindowFactory.cpp", "QtChatWindow.cpp", "QtClickableLabel.cpp", "QtLoginWindow.cpp", "QtMainWindow.cpp", "QtProfileWindow.cpp", "QtNameWidget.cpp", "QtSettingsProvider.cpp", "QtStatusWidget.cpp", "QtScaledAvatarCache.cpp", "QtSwift.cpp", "QtURIHandler.cpp", "QtChatView.cpp", "QtChatTheme.cpp", "QtChatTabs.cpp", "QtSoundPlayer.cpp", "QtSystemTray.cpp", "QtCachedImageScaler.cpp", "QtTabbable.cpp", "QtTabWidget.cpp", "QtTextEdit.cpp", "QtXMLConsoleWidget.cpp", "QtHistoryWindow.cpp", "QtFileTransferListWidget.cpp", "QtFileTransferListItemModel.cpp", "QtAdHocCommandWindow.cpp", "QtUtilities.cpp", "QtBookmarkDetailWindow.cpp", "QtAddBookmarkWindow.cpp", "QtEditBookmarkWindow.cpp", "QtContactEditWindow.cpp", "QtContactEditWidget.cpp", "ChatSnippet.cpp", "MessageSnippet.cpp", "SystemMessageSnippet.cpp", "QtElidingLabel.cpp", "QtFormWidget.cpp", "QtFormResultItemModel.cpp", "QtLineEdit.cpp", "QtJoinMUCWindow.cpp", "QtInviteToChatWindow.cpp", "QtConnectionSettingsWindow.cpp", "Roster/RosterModel.cpp", "Roster/QtTreeWidget.cpp", # "Roster/QtTreeWidgetItem.cpp", "Roster/RosterDelegate.cpp", "Roster/GroupItemDelegate.cpp", "Roster/DelegateCommons.cpp", "Roster/QtRosterWidget.cpp", "Roster/QtOccupantListWidget.cpp", "EventViewer/EventModel.cpp", "EventViewer/EventDelegate.cpp", "EventViewer/TwoLineDelegate.cpp", "EventViewer/QtEventWindow.cpp", "EventViewer/QtEvent.cpp", "ChatList/QtChatListWindow.cpp", "ChatList/ChatListModel.cpp", "ChatList/ChatListDelegate.cpp", "ChatList/ChatListMUCItem.cpp", "ChatList/ChatListRecentItem.cpp", "ChatList/ChatListWhiteboardItem.cpp", "MUCSearch/QtMUCSearchWindow.cpp", "MUCSearch/MUCSearchModel.cpp", "MUCSearch/MUCSearchRoomItem.cpp", "MUCSearch/MUCSearchEmptyItem.cpp", "MUCSearch/MUCSearchDelegate.cpp", "UserSearch/QtUserSearchFirstPage.cpp", "UserSearch/QtUserSearchFieldsPage.cpp", "UserSearch/QtUserSearchResultsPage.cpp", "UserSearch/QtUserSearchDetailsPage.cpp", "UserSearch/QtUserSearchWindow.cpp", "UserSearch/UserSearchModel.cpp", "UserSearch/UserSearchDelegate.cpp", "Whiteboard/FreehandLineItem.cpp", "Whiteboard/GView.cpp", "Whiteboard/TextDialog.cpp", "Whiteboard/QtWhiteboardWindow.cpp", "Whiteboard/ColorWidget.cpp", "QtSubscriptionRequestWindow.cpp", "QtRosterHeader.cpp", "QtWebView.cpp", "qrc_DefaultTheme.cc", "qrc_Swift.cc", "QtChatWindowJSBridge.cpp", "QtMUCConfigurationWindow.cpp", "QtAffiliationEditor.cpp", "QtUISettingConstants.cpp", "QtURLValidator.cpp" ] # Determine the version myenv["SWIFT_VERSION"] = Version.getBuildVersion(env.Dir("#").abspath, "swift") if env["PLATFORM"] == "win32" : swift_windows_version = Version.convertToWindowsVersion(myenv["SWIFT_VERSION"]) myenv["SWIFT_VERSION_MAJOR"] = swift_windows_version[0] myenv["SWIFT_VERSION_MINOR"] = swift_windows_version[1] myenv["SWIFT_VERSION_PATCH"] = swift_windows_version[2] if env["PLATFORM"] == "win32" : res_env = myenv.Clone() res_env.Append(CPPDEFINES = [ ("SWIFT_COPYRIGHT_YEAR", "\"\\\"2010-%s\\\"\"" % str(time.localtime()[0])), ("SWIFT_VERSION_MAJOR", "${SWIFT_VERSION_MAJOR}"), ("SWIFT_VERSION_MINOR", "${SWIFT_VERSION_MINOR}"), ("SWIFT_VERSION_PATCH", "${SWIFT_VERSION_PATCH}"), ]) res = res_env.RES("#/Swift/resources/Windows/Swift.rc") # For some reason, SCons isn't picking up the dependency correctly # Adding it explicitly until i figure out why myenv.Depends(res, "../Controllers/BuildVersion.h") sources += [ "WinUIHelpers.cpp", "CAPICertificateSelector.cpp", "WindowsNotifier.cpp", "#/Swift/resources/Windows/Swift.res" ] if env["PLATFORM"] == "posix" : sources += [ "FreeDesktopNotifier.cpp", "QtDBUSURIHandler.cpp", ] if env["PLATFORM"] == "darwin" : sources += ["CocoaApplicationActivateHelper.mm"] sources += ["CocoaUIHelpers.mm"] if env["PLATFORM"] == "darwin" or env["PLATFORM"] == "win32" : swiftProgram = myenv.Program("Swift", sources) else : sources += ["QtCertificateViewerDialog.cpp"]; myenv.Uic4("QtCertificateViewerDialog.ui"); swiftProgram = myenv.Program("swift-im", sources) if env["PLATFORM"] != "darwin" and env["PLATFORM"] != "win32" : openURIProgram = myenv.Program("swift-open-uri", "swift-open-uri.cpp") else : openURIProgram = [] myenv.Uic4("MUCSearch/QtMUCSearchWindow.ui") myenv.Uic4("UserSearch/QtUserSearchWizard.ui") myenv.Uic4("UserSearch/QtUserSearchFirstPage.ui") myenv.Uic4("UserSearch/QtUserSearchFieldsPage.ui") myenv.Uic4("UserSearch/QtUserSearchResultsPage.ui") myenv.Uic4("QtBookmarkDetailWindow.ui") myenv.Uic4("QtAffiliationEditor.ui") myenv.Uic4("QtJoinMUCWindow.ui") myenv.Uic4("QtHistoryWindow.ui") myenv.Uic4("QtConnectionSettings.ui") myenv.Qrc("DefaultTheme.qrc") myenv.Qrc("Swift.qrc") # Resources commonResources = { "": ["#/Swift/resources/sounds"] } myenv["TEXTFILESUFFIX"] = "" myenv.MyTextfile(target = "COPYING", source = [myenv.File("../../COPYING.gpl"), myenv.File("../../COPYING.thirdparty")], LINESEPARATOR = "\n\n========\n\n\n") ################################################################################ # Translation ################################################################################ # Collect available languages translation_languages = [] for file in os.listdir(Dir("#/Swift/Translations").abspath) : if file.startswith("swift_") and file.endswith(".ts") : translation_languages.append(file[6:-3]) # Generate translation modules translation_sources = [env.File("#/Swift/Translations/swift.ts").abspath] translation_modules = [] for lang in translation_languages : translation_resource = "#/Swift/resources/translations/swift_" + lang + ".qm" translation_source = "#/Swift/Translations/swift_" + lang + ".ts" translation_sources.append(env.File(translation_source).abspath) translation_modules.append(env.File(translation_resource).abspath) myenv.Qm(translation_resource, translation_source) commonResources["translations"] = commonResources.get("translations", []) + [translation_resource] # LUpdate translation (if requested) if ARGUMENTS.get("update_translations", False) : myenv.Precious(translation_sources) remove_obsolete_option = "" if ARGUMENTS.get("remove_obsolete_translations", False) : remove_obsolete_option = " -no-obsolete" for translation_source in filter(lambda x: not x.endswith("_en.ts"), translation_sources) : t = myenv.Command([translation_source], [], [myenv.Action("$QT4_LUPDATE -I " + env.Dir("#").abspath + remove_obsolete_option + " -silent -codecfortr utf-8 -recursive Swift -ts " + translation_source, cmdstr = "$QT4_LUPDATECOMSTR")]) myenv.AlwaysBuild(t) # NSIS installation script if env["PLATFORM"] == "win32" : nsis_translation_install_script = "" nsis_translation_uninstall_script = "" for lang in translation_languages : nsis_translation_install_script += "File \"..\\..\\QtUI\\Swift\\translations\\swift_" + lang + ".qm\"\n" nsis_translation_uninstall_script += "delete $INSTDIR\\translations\\swift_" + lang + ".qm\n" myenv.WriteVal("../Packaging/nsis/translations-install.nsh", myenv.Value(nsis_translation_install_script)) myenv.WriteVal("../Packaging/nsis/translations-uninstall.nsh", myenv.Value(nsis_translation_uninstall_script)) ################################################################################ if env["PLATFORM"] == "darwin" : frameworks = [] if env["HAVE_SPARKLE"] : frameworks.append(env["SPARKLE_FRAMEWORK"]) if env["HAVE_GROWL"] : frameworks.append(env["GROWL_FRAMEWORK"]) commonResources[""] = commonResources.get("", []) + ["#/Swift/resources/MacOSX/Swift.icns"] app = myenv.AppBundle("Swift", version = myenv["SWIFT_VERSION"], resources = commonResources, frameworks = frameworks, handlesXMPPURIs = True) if env["DIST"] : myenv.Command(["#/Packages/Swift/Swift-${SWIFT_VERSION}.dmg"], [app], ["Swift/Packaging/MacOSX/package.sh " + app.path + " Swift/Packaging/MacOSX/Swift.dmg.gz $TARGET $QTDIR"]) dsym = myenv.Command(["Swift-${SWIFT_VERSION}.dSYM"], ["Swift"], ["dsymutil -o ${TARGET} ${SOURCE}"]) myenv.Command(["#/Packages/Swift/Swift-${SWIFT_VERSION}.dSYM.zip"], dsym, ["cd ${SOURCE.dir} && zip -r ${TARGET.abspath} ${SOURCE.name}"]) if env.get("SWIFT_INSTALLDIR", "") : env.Install(os.path.join(env["SWIFT_INSTALLDIR"], "bin"), swiftProgram + openURIProgram) env.InstallAs(os.path.join(env["SWIFT_INSTALLDIR"], "share", "pixmaps", "swift.xpm"), "#/Swift/resources/logo/logo-icon-32.xpm") icons_path = os.path.join(env["SWIFT_INSTALLDIR"], "share", "icons", "hicolor") env.InstallAs(os.path.join(icons_path, "32x32", "apps", "swift.xpm"), "#/Swift/resources/logo/logo-icon-32.xpm") env.InstallAs(os.path.join(icons_path, "scalable", "apps", "swift.svg"), "#/Swift/resources/logo/logo-icon.svg") for i in ["16", "22", "24", "64", "128"] : env.InstallAs(os.path.join(icons_path, i + "x" + i, "apps", "swift.png"), "#/Swift/resources/logo/logo-icon-" + i + ".png") env.Install(os.path.join(env["SWIFT_INSTALLDIR"], "share", "applications"), "#/Swift/resources/swift.desktop") for dir, resource in commonResources.items() : env.Install(os.path.join(env["SWIFT_INSTALLDIR"], "share", "swift", dir), resource) if env["PLATFORM"] == "win32" : if env["DIST"] : commonResources[""] = commonResources.get("", []) + [ #os.path.join(env["OPENSSL_DIR"], "bin", "ssleay32.dll"), #os.path.join(env["OPENSSL_DIR"], "bin", "libeay32.dll"), "#/Swift/resources/images", ] if env["SWIFTEN_DLL"] : commonResources[""] = commonResources.get("", []) + ["#/Swiften/${SWIFTEN_LIBRARY_FILE}"] qtimageformats = ["gif", "ico", "jpeg", "mng", "svg", "tiff"] qtlibs = ["QtCore4", "QtGui4", "QtNetwork4", "QtWebKit4", "QtXMLPatterns4", "phonon4"] windowsBundleFiles = myenv.WindowsBundle("Swift", resources = commonResources, qtimageformats = qtimageformats, qtlibs = qtlibs) #myenv.Append(NSIS_OPTIONS = [ # "/DmsvccRedistributableDir=\"" + env["vcredist"] + "\"", # "/DbuildVersion=" + myenv["SWIFT_VERSION"] # ]) #myenv.Nsis("../Packaging/nsis/swift.nsi") if env["SCONS_STAGE"] == "build" and env.get("wix_bindir", None): def convertToRTF(env, target, source) : infile = open(source[0].abspath, 'r') outfile = open(target[0].abspath, 'w') outfile.write('{\\rtf1\\ansi{\\fonttbl\\f0\\fswiss Helvetica;}\\fs16\\f0\\pard\n') for line in infile: for char in line.decode("utf-8") : if ord(char) > 127 : # FIXME: This is incorrect, because it only works for latin1. # The correct way is \u? , but this is more # work outfile.write("\\'%X" % ord(char)) else : outfile.write(char) outfile.write('\\par ') outfile.write('}') outfile.close() infile.close() env.Command(["Swift/COPYING.rtf"], ["COPYING"], convertToRTF) wixvariables = { 'VCCRTFile': env["vcredist"], 'Version': str(myenv["SWIFT_VERSION_MAJOR"]) + "." + str(myenv["SWIFT_VERSION_MINOR"]) + "." + str(myenv["SWIFT_VERSION_PATCH"]) } wixincludecontent = "" for key in wixvariables: wixincludecontent += "" % (key, wixvariables[key]) wixincludecontent += "" myenv.WriteVal("..\\Packaging\\Wix\\variables.wxs", env.Value(wixincludecontent)) myenv.WiX_Heat('..\\Packaging\\WiX\\gen_files.wxs', windowsBundleFiles) myenv.WiX_Candle('..\\Packaging\\WiX\\Swift.wixobj', '..\\Packaging\\WiX\\Swift.wxs') myenv.WiX_Candle('..\\Packaging\\WiX\\gen_files.wixobj', '..\\Packaging\\WiX\\gen_files.wxs') myenv.WiX_Light('#/Packages/Swift/Swift-' + myenv["SWIFT_VERSION"] + '.msi', ['..\\Packaging\\WiX\\gen_files.wixobj','..\\Packaging\\WiX\\Swift.wixobj']) if myenv["debug"] : myenv.InstallAs('#/Packages/Swift/Swift-' + myenv["SWIFT_VERSION"] + '.pdb', "Swift.pdb") swift-im-2.0+dev6/Swift/QtUI/QtScaledAvatarCache.cpp0000644000175000017500000000276312227051774022100 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "QtScaledAvatarCache.h" #include #include #include #include #include #include #include namespace Swift { QtScaledAvatarCache::QtScaledAvatarCache(int size) : size(size) { } QString QtScaledAvatarCache::getScaledAvatarPath(const QString& path) { QFileInfo avatarFile(path); if (avatarFile.exists()) { if (!avatarFile.dir().exists(QString::number(size))) { if (!avatarFile.dir().mkdir(QString::number(size))) { return path; } } QDir targetDir(avatarFile.dir().absoluteFilePath(QString::number(size))); QString targetFile = targetDir.absoluteFilePath(avatarFile.baseName()); if (!QFileInfo(targetFile).exists()) { QPixmap avatarPixmap; avatarPixmap.load(path); QPixmap maskedAvatar(avatarPixmap.size()); maskedAvatar.fill(QColor(0, 0, 0, 0)); QPainter maskPainter(&maskedAvatar); maskPainter.setBrush(Qt::black); maskPainter.drawRoundedRect(maskedAvatar.rect(), 25.0, 25.0, Qt::RelativeSize); maskPainter.setCompositionMode(QPainter::CompositionMode_SourceIn); maskPainter.drawPixmap(0, 0, avatarPixmap); maskPainter.end(); if (!maskedAvatar.scaled(size, size, Qt::KeepAspectRatio, Qt::SmoothTransformation).save(targetFile, "PNG")) { return path; } } return targetFile; } else { return path; } } } swift-im-2.0+dev6/Swift/QtUI/QtAboutWidget.cpp0000644000175000017500000000577512227051774021046 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Swift/QtUI/QtAboutWidget.h" #include #include #include #include #include #include #include #include #include namespace Swift { QtAboutWidget::QtAboutWidget() : QDialog() { #ifndef Q_WS_MAC setWindowTitle(QString(tr("About %1")).arg("Swift")); #endif setWindowIcon(QIcon(":/logo-icon-16.png")); resize(180, 240); QVBoxLayout *mainLayout = new QVBoxLayout(this); mainLayout->setAlignment(Qt::AlignHCenter); setLayout(mainLayout); QLabel* iconLabel = new QLabel(this); iconLabel->setPixmap(QIcon(":/logo-shaded-text.256.png").pixmap(90, 90)); iconLabel->setAlignment(Qt::AlignHCenter); mainLayout->addWidget(iconLabel); QLabel* appNameLabel = new QLabel("
" + QCoreApplication::applicationName() + "
", this); mainLayout->addWidget(appNameLabel); QLabel* versionLabel = new QLabel(QString("
") + tr("Version %1").arg(QCoreApplication::applicationVersion()) + "
", this); mainLayout->addWidget(versionLabel); QString buildString = QString("
") + QString(tr("Built with Qt %1")).arg(QT_VERSION_STR); buildString += QString("
") + QString(tr("Running with Qt %1")).arg(qVersion()); buildString += "
"; QLabel* buildLabel = new QLabel(buildString, this); mainLayout->addWidget(buildLabel); if (QCoreApplication::translate("TRANSLATION_INFO", "TRANSLATION_AUTHOR") != "TRANSLATION_AUTHOR") { mainLayout->addWidget(new QLabel(QString("
") + QString(tr("Using the English translation by\n%1")).arg(QCoreApplication::translate("TRANSLATION_INFO", "TRANSLATION_AUTHOR")).replace("\n", "
") + "
", this)); } QCoreApplication::translate("TRANSLATION_INFO", "TRANSLATION_LICENSE", "This string contains the license under which this translation is licensed. We ask you to license the translation under the BSD license. Please read http://www.opensource.org/licenses/bsd-license.php, and if you agree to release your translation under this license, use the following (untranslated) text: 'This translation is licensed under the BSD License. See http://www.opensource.org/licenses/bsd-license.php'"); QPushButton* licenseButton = new QPushButton(tr("View License"), this); mainLayout->addWidget(licenseButton); connect(licenseButton, SIGNAL(clicked()), this, SLOT(handleLicenseClicked())); setFixedSize(minimumSizeHint()); } void QtAboutWidget::handleLicenseClicked() { QTextEdit* text = new QTextEdit(); text->setAttribute(Qt::WA_DeleteOnClose); text->setReadOnly(true); QFile file(":/COPYING"); file.open(QIODevice::ReadOnly); QTextStream in(&file); in.setCodec("UTF-8"); text->setPlainText(in.readAll()); file.close(); text->resize(500, 600); text->show(); text->activateWindow(); } } swift-im-2.0+dev6/Swift/QtUI/QtJoinMUCWindow.cpp0000644000175000017500000000367312227051774021257 0ustar kismithkismith/* * Copyright (c) 2010-2012 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "QtJoinMUCWindow.h" #include #include #include namespace Swift { QtJoinMUCWindow::QtJoinMUCWindow(UIEventStream* uiEventStream) : uiEventStream(uiEventStream) { ui.setupUi(this); #if QT_VERSION >= 0x040700 ui.room->setPlaceholderText(tr("someroom@rooms.example.com")); #endif connect(ui.room, SIGNAL(returnPressed()), this, SLOT(handleJoin())); connect(ui.searchButton, SIGNAL(clicked()), this, SLOT(handleSearch())); connect(ui.joinButton, SIGNAL(clicked()), this, SLOT(handleJoin())); // FIXME: Temporarily set focus on the nickName field first, so that the // placeholder for the room is visible. This is just because Qt hides // the placeholder when a widget is focused for some reason. ui.nickName->setFocus(); ui.instantRoom->setChecked(true); ui.nickName->setValidator(new NickValidator(this)); } void QtJoinMUCWindow::handleJoin() { if (ui.room->text().isEmpty()) { // TODO: Error return; } if (ui.nickName->text().isEmpty()) { // TODO: Error return; } lastSetNick = Q2PSTRING(ui.nickName->text()); std::string password = Q2PSTRING(ui.password->text()); JID room(Q2PSTRING(ui.room->text())); uiEventStream->send(boost::make_shared(room, password, lastSetNick, ui.joinAutomatically->isChecked(), !ui.instantRoom->isChecked())); hide(); } void QtJoinMUCWindow::handleSearch() { onSearchMUC(); } void QtJoinMUCWindow::setNick(const std::string& nick) { ui.nickName->setText(P2QSTRING(nick)); lastSetNick = nick; } void QtJoinMUCWindow::setMUC(const std::string& nick) { ui.room->setText(P2QSTRING(nick)); } void QtJoinMUCWindow::show() { QWidget::show(); QWidget::activateWindow(); ui.password->setText(""); } } swift-im-2.0+dev6/Swift/QtUI/QtStatusWidget.cpp0000644000175000017500000001740612227051774021251 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "QtStatusWidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "Swift/QtUI/QtElidingLabel.h" #include "Swift/QtUI/QtLineEdit.h" #include "Swift/QtUI/QtSwiftUtil.h" #include namespace Swift { QtStatusWidget::QtStatusWidget(QWidget *parent) : QWidget(parent), editCursor_(Qt::IBeamCursor), viewCursor_(Qt::PointingHandCursor) { isClicking_ = false; connecting_ = false; setMaximumHeight(24); connectingMovie_ = new QMovie(":/icons/connecting.mng"); QHBoxLayout* mainLayout = new QHBoxLayout(this); mainLayout->setSpacing(0); mainLayout->setContentsMargins(0,0,0,0); stack_ = new QStackedWidget(this); stack_->setLineWidth(2); stack_->setFrameShape(QFrame::StyledPanel); mainLayout->addWidget(stack_); QWidget* page1 = new QWidget(this); stack_->addWidget(page1); QHBoxLayout* page1Layout = new QHBoxLayout(page1); page1Layout->setSpacing(0); page1Layout->setContentsMargins(0,0,0,0); page1->setCursor(viewCursor_); statusIcon_ = new QLabel(this); statusIcon_->setMinimumSize(16, 16); statusIcon_->setMaximumSize(16, 16); page1Layout->addWidget(statusIcon_); statusTextLabel_ = new QtElidingLabel(this); QFont font = statusTextLabel_->font(); font.setItalic(true); statusTextLabel_->setFont(font); page1Layout->addWidget(statusTextLabel_); icons_[StatusShow::Online] = QIcon(":/icons/online.png"); icons_[StatusShow::Away] = QIcon(":/icons/away.png"); icons_[StatusShow::DND] = QIcon(":/icons/dnd.png"); icons_[StatusShow::None] = QIcon(":/icons/offline.png"); setStatusType(StatusShow::None); QWidget* page2 = new QWidget(this); QHBoxLayout* page2Layout = new QHBoxLayout(page2); page2Layout->setSpacing(0); page2Layout->setContentsMargins(0,0,0,0); stack_->addWidget(page2); statusEdit_ = new QtLineEdit(this); page2Layout->addWidget(statusEdit_); connect(statusEdit_, SIGNAL(returnPressed()), this, SLOT(handleEditComplete())); connect(statusEdit_, SIGNAL(escapePressed()), this, SLOT(handleEditCancelled())); connect(statusEdit_, SIGNAL(textChanged(const QString&)), this, SLOT(generateList())); setStatusText(""); menu_ = new QListWidget(); menu_->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint ); menu_->setAlternatingRowColors(true); menu_->setFocusProxy(statusEdit_); menu_->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); menu_->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); QSizePolicy policy(menu_->sizePolicy()); policy.setVerticalPolicy(QSizePolicy::Expanding); menu_->setSizePolicy(policy); connect(menu_, SIGNAL(itemClicked(QListWidgetItem*)), this, SLOT(handleItemClicked(QListWidgetItem*))); viewMode(); } QtStatusWidget::~QtStatusWidget() { delete menu_; delete connectingMovie_; } void QtStatusWidget::handleApplicationFocusChanged(QWidget* /*old*/, QWidget* /*now*/) { QWidget* now = qApp->focusWidget(); if (!editing_ || stack_->currentIndex() == 0) { return; } if (!now || (now != menu_ && now != statusEdit_ && !now->isAncestorOf(statusEdit_) && !now->isAncestorOf(menu_) && !statusEdit_->isAncestorOf(now) && !menu_->isAncestorOf(now))) { handleEditCancelled(); } } void QtStatusWidget::mousePressEvent(QMouseEvent*) { if (stack_->currentIndex() == 0) { handleClicked(); } } void QtStatusWidget::generateList() { if (!editing_) { return; } QString text = statusEdit_->text(); newStatusText_ = text; menu_->clear(); foreach (StatusShow::Type type, icons_.keys()) { QListWidgetItem* item = new QListWidgetItem(text == "" ? getNoMessage() : text, menu_); item->setIcon(icons_[type]); item->setToolTip(P2QSTRING(statusShowTypeToFriendlyName(type)) + ": " + item->text()); item->setStatusTip(item->toolTip()); item->setData(Qt::UserRole, QVariant(type)); } foreach (StatusShow::Type type, icons_.keys()) { QListWidgetItem* item = new QListWidgetItem(P2QSTRING(statusShowTypeToFriendlyName(type)), menu_); item->setIcon(icons_[type]); item->setToolTip(item->text()); item->setStatusTip(item->toolTip()); item->setData(Qt::UserRole, QVariant(type)); } } void QtStatusWidget::handleClicked() { editing_ = true; QDesktopWidget* desktop = QApplication::desktop(); int screen = desktop->screenNumber(this); QPoint point = mapToGlobal(QPoint(0, height())); QRect geometry = desktop->availableGeometry(screen); int x = point.x(); int y = point.y(); int width = 200; int height = 80; int screenWidth = geometry.x() + geometry.width(); if (x + width > screenWidth) { x = screenWidth - width; } std::vector types; types.push_back(StatusShow::Online); types.push_back(StatusShow::FFC); types.push_back(StatusShow::Away); types.push_back(StatusShow::XA); types.push_back(StatusShow::DND); types.push_back(StatusShow::None); foreach (StatusShow::Type type, types) { if (statusEdit_->text() == P2QSTRING(statusShowTypeToFriendlyName(type))) { statusEdit_->setText(""); } } generateList(); height = menu_->sizeHintForRow(0) * menu_->count(); int marginLeft; int marginTop; int marginRight; int marginBottom; menu_->getContentsMargins(&marginLeft, &marginTop, &marginRight, &marginBottom); height += marginTop + marginBottom; width += marginLeft + marginRight; menu_->setGeometry(x, y, width, height); menu_->move(x, y); menu_->setMaximumWidth(width); menu_->show(); activateWindow(); statusEdit_->selectAll(); stack_->setCurrentIndex(1); statusEdit_->setFocus(); connect(qApp, SIGNAL(focusChanged(QWidget*, QWidget*)), this, SLOT(handleApplicationFocusChanged(QWidget*, QWidget*)), Qt::QueuedConnection); } void QtStatusWidget::viewMode() { disconnect(qApp, SIGNAL(focusChanged(QWidget*, QWidget*)), this, SLOT(handleApplicationFocusChanged(QWidget*, QWidget*))); editing_ = false; menu_->hide(); stack_->setCurrentIndex(0); } void QtStatusWidget::handleEditComplete() { editing_ = false; statusText_ = newStatusText_; viewMode(); emit onChangeStatusRequest(selectedStatusType_, statusText_); } void QtStatusWidget::handleEditCancelled() { editing_ = false; setStatusText(statusText_); viewMode(); } StatusShow::Type QtStatusWidget::getSelectedStatusShow() { return selectedStatusType_; } void QtStatusWidget::handleItemClicked(QListWidgetItem* item) { editing_ = false; selectedStatusType_ = static_cast(item->data(Qt::UserRole).toInt()); QString message = item->data(Qt::DisplayRole).toString(); newStatusText_ = message == getNoMessage() ? "" : message; statusEdit_->setText(newStatusText_); handleEditComplete(); } void QtStatusWidget::setNewToolTip() { if (connecting_) { statusTextLabel_->setToolTip(tr("Connecting")); } else { statusTextLabel_->setToolTip(P2QSTRING(statusShowTypeToFriendlyName(selectedStatusType_)) + ": " + statusTextLabel_->text()); } } void QtStatusWidget::setStatusText(const QString& text) { connectingMovie_->stop(); statusText_ = text; statusEdit_->setText(text); QString escapedText(text.isEmpty() ? getNoMessage() : text); statusTextLabel_->setText(escapedText); setNewToolTip(); } void QtStatusWidget::setConnecting() { connecting_ = true; statusIcon_->setMovie(connectingMovie_); connectingMovie_->start(); setNewToolTip(); } void QtStatusWidget::setStatusType(StatusShow::Type type) { connecting_ = false; selectedStatusType_ = icons_.contains(type) ? type : StatusShow::Online; statusIcon_->setPixmap(icons_[selectedStatusType_].pixmap(16, 16)); setNewToolTip(); } QString QtStatusWidget::getNoMessage() { return QString(tr("(No message)")); } } swift-im-2.0+dev6/Swift/QtUI/QtJoinMUCWindow.h0000644000175000017500000000231312227051774020712 0ustar kismithkismith/* * 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 #include #include "QtSwiftUtil.h" #include #include namespace Swift { class UIEventStream; class NickValidator : public QValidator { Q_OBJECT public: NickValidator(QObject* parent) : QValidator(parent) { } virtual QValidator::State validate(QString& input, int& /*pos*/) const { if (input.isEmpty()) { return QValidator::Acceptable; } JID test("alice", "wonderland.lit", Q2PSTRING(input)); return test.isValid() ? QValidator::Acceptable : QValidator::Invalid; } }; class QtJoinMUCWindow : public QWidget, public JoinMUCWindow { Q_OBJECT public: QtJoinMUCWindow(UIEventStream* uiEventStream); virtual void setNick(const std::string& nick); virtual void setMUC(const std::string& nick); virtual void show(); private slots: void handleJoin(); void handleSearch(); private: Ui::QtJoinMUCWindow ui; std::string lastSetNick; UIEventStream* uiEventStream; }; } swift-im-2.0+dev6/Swift/QtUI/QtAffiliationEditor.ui0000644000175000017500000000720512227051774022045 0ustar kismithkismith QtAffiliationEditor 0 0 575 466 Edit Affiliations Affiliation: Owner Administrator Member Outcast (Banned) Add User Remove User Qt::Vertical 20 40 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() QtAffiliationEditor accept() 224 444 157 274 buttonBox rejected() QtAffiliationEditor reject() 292 450 286 274 swift-im-2.0+dev6/Swift/QtUI/QtInviteToChatWindow.cpp0000644000175000017500000000636212227051774022352 0ustar kismithkismith/* * Copyright (c) 2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include namespace Swift { QtInviteToChatWindow::QtInviteToChatWindow(QWidget* parent) : QDialog(parent) { QBoxLayout *layout = new QBoxLayout(QBoxLayout::TopToBottom, this); //layout->setContentsMargins(0,0,0,0); //layout->setSpacing(2); QLabel* description = new QLabel(tr("Users to invite to this chat (one per line):")); layout->addWidget(description); jidsLayout_ = new QBoxLayout(QBoxLayout::TopToBottom); layout->addLayout(jidsLayout_); QLabel* reasonLabel = new QLabel(tr("If you want to provide a reason for the invitation, enter it here")); layout->addWidget(reasonLabel); reason_ = new QLineEdit(this); layout->addWidget(reason_); connect(this, SIGNAL(accepted()), this, SLOT(handleAccepting())); connect(this, SIGNAL(rejected()), this, SLOT(handleRejecting())); buttonBox_ = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); connect(buttonBox_, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox_, SIGNAL(rejected()), this, SLOT(reject())); layout->addWidget(buttonBox_); addJIDLine(); jids_[0]->setFocus(); setModal(false); show(); } QtInviteToChatWindow::~QtInviteToChatWindow() { } void QtInviteToChatWindow::handleAccepting() { onCompleted(); } void QtInviteToChatWindow::handleRejecting() { onDismissed(); } std::string QtInviteToChatWindow::getReason() const { return Q2PSTRING(reason_->text()); } std::vector QtInviteToChatWindow::getJIDs() const { std::vector results; foreach (QLineEdit* jidEdit, jids_) { QStringList parts = jidEdit->text().split(" "); if (parts.size() > 0) { JID jid(Q2PSTRING(parts.last())); if (jid.isValid() && !jid.getNode().empty()) { results.push_back(jid); } } } return results; } void QtInviteToChatWindow::addJIDLine() { QLineEdit* jid = new QLineEdit(this); QCompleter* completer = new QCompleter(&completions_, this); completer->setCaseSensitivity(Qt::CaseInsensitive); jid->setCompleter(completer); jidsLayout_->addWidget(jid); connect(jid, SIGNAL(textChanged(const QString&)), this, SLOT(handleJIDTextChanged())); if (!jids_.empty()) { setTabOrder(jids_.back(), jid); } jids_.push_back(jid); setTabOrder(jid, reason_); setTabOrder(reason_, buttonBox_); //setTabOrder(buttonBox_, jids_[0]); } void QtInviteToChatWindow::handleJIDTextChanged() { bool gotEmpty = false; foreach(QLineEdit* edit, jids_) { if (edit->text().isEmpty()) { gotEmpty = true; } } if (!gotEmpty) { addJIDLine(); } } typedef std::pair JIDString; void QtInviteToChatWindow::setAutoCompletions(std::vector > completions) { QStringList list; foreach (JIDString jidPair, completions) { QString line = P2QSTRING(jidPair.first.toString()); if (jidPair.second != jidPair.first.toString() && !jidPair.second.empty()) { line = P2QSTRING(jidPair.second) + " - " + line; } list.append(line); } completions_.setStringList(list); } } swift-im-2.0+dev6/Swift/QtUI/Roster/0000755000175000017500000000000012227051774017057 5ustar kismithkismithswift-im-2.0+dev6/Swift/QtUI/Roster/QtRosterWidget.h0000644000175000017500000000117412227051774022162 0ustar kismithkismith/* * Copyright (c) 2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include "Swift/QtUI/Roster/QtTreeWidget.h" namespace Swift { class QtUIPreferences; class QtRosterWidget : public QtTreeWidget { Q_OBJECT public: QtRosterWidget(UIEventStream* eventStream, SettingsProvider* settings, QWidget* parent = 0); virtual ~QtRosterWidget(); public slots: void handleEditUserActionTriggered(bool checked); protected: void contextMenuEvent(QContextMenuEvent* event); private: void renameGroup(GroupRosterItem* group); }; } swift-im-2.0+dev6/Swift/QtUI/Roster/QtRosterWidget.cpp0000644000175000017500000000762712227051774022526 0ustar kismithkismith/* * Copyright (c) 2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Roster/QtRosterWidget.h" #include #include #include #include #include "Swift/Controllers/UIEvents/RequestContactEditorUIEvent.h" #include "Swift/Controllers/UIEvents/RemoveRosterItemUIEvent.h" #include "Swift/Controllers/UIEvents/RenameGroupUIEvent.h" #include "Swift/Controllers/UIEvents/SendFileUIEvent.h" #include "Swift/Controllers/UIEvents/RequestWhiteboardUIEvent.h" #include "QtContactEditWindow.h" #include "Swift/Controllers/Roster/ContactRosterItem.h" #include "Swift/Controllers/Roster/GroupRosterItem.h" #include "Swift/Controllers/UIEvents/UIEventStream.h" #include "QtSwiftUtil.h" namespace Swift { QtRosterWidget::QtRosterWidget(UIEventStream* eventStream, SettingsProvider* settings, QWidget* parent) : QtTreeWidget(eventStream, settings, parent) { } QtRosterWidget::~QtRosterWidget() { } void QtRosterWidget::handleEditUserActionTriggered(bool /*checked*/) { QModelIndexList selectedIndexList = getSelectedIndexes(); if (selectedIndexList.empty()) { return; } QModelIndex index = selectedIndexList[0]; if (!index.isValid()) { return; } RosterItem* item = static_cast(index.internalPointer()); if (ContactRosterItem* contact = dynamic_cast(item)) { eventStream_->send(boost::make_shared(contact->getJID())); } } void QtRosterWidget::contextMenuEvent(QContextMenuEvent* event) { QModelIndex index = indexAt(event->pos()); if (!index.isValid()) { return; } RosterItem* item = static_cast(index.internalPointer()); QMenu contextMenu; if (ContactRosterItem* contact = dynamic_cast(item)) { QAction* editContact = contextMenu.addAction(tr("Edit…")); QAction* removeContact = contextMenu.addAction(tr("Remove")); #ifdef SWIFT_EXPERIMENTAL_FT QAction* sendFile = NULL; if (contact->supportsFeature(ContactRosterItem::FileTransferFeature)) { sendFile = contextMenu.addAction(tr("Send File")); } #endif #ifdef SWIFT_EXPERIMENTAL_WB QAction* startWhiteboardChat = NULL; if (contact->supportsFeature(ContactRosterItem::WhiteboardFeature)) { startWhiteboardChat = contextMenu.addAction(tr("Start Whiteboard Chat")); } #endif QAction* result = contextMenu.exec(event->globalPos()); if (result == editContact) { eventStream_->send(boost::make_shared(contact->getJID())); } else if (result == removeContact) { if (QtContactEditWindow::confirmContactDeletion(contact->getJID())) { eventStream_->send(boost::make_shared(contact->getJID())); } } #ifdef SWIFT_EXPERIMENTAL_FT else if (sendFile && result == sendFile) { QString fileName = QFileDialog::getOpenFileName(this, tr("Send File"), "", tr("All Files (*);;")); if (!fileName.isEmpty()) { eventStream_->send(boost::make_shared(contact->getJID(), Q2PSTRING(fileName))); } } #endif #ifdef SWIFT_EXPERIMENTAL_WB else if (startWhiteboardChat && result == startWhiteboardChat) { eventStream_->send(boost::make_shared(contact->getJID())); } #endif } else if (GroupRosterItem* group = dynamic_cast(item)) { QAction* renameGroupAction = contextMenu.addAction(tr("Rename")); QAction* result = contextMenu.exec(event->globalPos()); if (result == renameGroupAction) { renameGroup(group); } } } void QtRosterWidget::renameGroup(GroupRosterItem* group) { bool ok; QString newName = QInputDialog::getText(NULL, tr("Rename group"), tr("Enter a new name for group '%1':").arg(P2QSTRING(group->getDisplayName())), QLineEdit::Normal, P2QSTRING(group->getDisplayName()), &ok); if (ok) { eventStream_->send(boost::make_shared(group->getDisplayName(), Q2PSTRING(newName))); } } } swift-im-2.0+dev6/Swift/QtUI/Roster/QtTreeWidget.h0000644000175000017500000000325312227051774021603 0ustar kismithkismith/* * Copyright (c) 2010-2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include #include #include "Swift/QtUI/Roster/RosterModel.h" #include "Swift/QtUI/Roster/RosterDelegate.h" namespace Swift { class UIEventStream; class SettingsProvider; class QtTreeWidget : public QTreeView{ Q_OBJECT public: QtTreeWidget(UIEventStream* eventStream, SettingsProvider* settings, QWidget* parent = 0); ~QtTreeWidget(); void show(); QtTreeWidgetItem* getRoot(); void setRosterModel(Roster* roster); Roster* getRoster() {return roster_;} boost::signal onSomethingSelectedChanged; private slots: void handleItemActivated(const QModelIndex&); void handleModelItemExpanded(const QModelIndex&, bool expanded); void handleExpanded(const QModelIndex&); void handleCollapsed(const QModelIndex&); void handleClicked(const QModelIndex&); void handleSettingChanged(const std::string& setting); protected: void dragEnterEvent(QDragEnterEvent* event); void dropEvent(QDropEvent* event); void dragMoveEvent(QDragMoveEvent* event); protected: QModelIndexList getSelectedIndexes() const; private: void drawBranches(QPainter*, const QRect&, const QModelIndex&) const; protected slots: virtual void currentChanged(const QModelIndex& current, const QModelIndex& previous); protected: UIEventStream* eventStream_; private: RosterModel* model_; Roster* roster_; RosterDelegate* delegate_; QtTreeWidgetItem* treeRoot_; SettingsProvider* settings_; }; } swift-im-2.0+dev6/Swift/QtUI/Roster/DelegateCommons.h0000644000175000017500000000264712227051774022307 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include #include #include #include namespace Swift { class DelegateCommons { public: DelegateCommons() : nameFont(QApplication::font()), detailFont(QApplication::font()) { detailFontSizeDrop = nameFont.pointSize() >= 10 ? 2 : 0; detailFont.setStyle(QFont::StyleItalic); detailFont.setPointSize(nameFont.pointSize() - detailFontSizeDrop); } static void drawElidedText(QPainter* painter, const QRect& region, const QString& text, int flags = Qt::AlignTop); QSize contactSizeHint(const QStyleOptionViewItem& option, const QModelIndex& index, bool compact) const; void paintContact(QPainter* painter, const QStyleOptionViewItem& option, const QColor& nameColor, const QString& avatarPath, const QIcon& presenceIcon, const QString& name, const QString& statusText, int unreadCount, bool compact) const; int detailFontSizeDrop; QFont nameFont; QFont detailFont; static const int horizontalMargin; static const int verticalMargin; static const int farLeftMargin; static const int avatarSize; static const int presenceIconHeight; static const int presenceIconWidth; static const int unreadCountSize; }; } swift-im-2.0+dev6/Swift/QtUI/Roster/RosterModel.h0000644000175000017500000000335612227051774021476 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include "Swift/Controllers/Roster/Roster.h" #include #include namespace Swift { enum RosterRoles { StatusTextRole = Qt::UserRole, AvatarRole = Qt::UserRole + 1, PresenceIconRole = Qt::UserRole + 2, StatusShowTypeRole = Qt::UserRole + 3, ChildCountRole = Qt::UserRole + 4, }; class QtTreeWidget; class RosterModel : public QAbstractItemModel { Q_OBJECT public: RosterModel(QtTreeWidget* view); ~RosterModel(); void setRoster(Roster* swiftRoster); int columnCount(const QModelIndex& parent = QModelIndex()) const; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const; QModelIndex index(RosterItem* item) const; QModelIndex parent(const QModelIndex& index) const; int rowCount(const QModelIndex& parent = QModelIndex()) const; signals: void itemExpanded(const QModelIndex& item, bool expanded); private: void handleDataChanged(RosterItem* item); void handleChildrenChanged(GroupRosterItem* item); RosterItem* getItem(const QModelIndex& index) const; QColor intToColor(int color) const; QColor getTextColor(RosterItem* item) const; QColor getBackgroundColor(RosterItem* item) const; QString getToolTip(RosterItem* item) const; QString getAvatar(RosterItem* item) const; QString getStatusText(RosterItem* item) const; QIcon getPresenceIcon(RosterItem* item) const; int getChildCount(RosterItem* item) const; void reLayout(); Roster* roster_; QtTreeWidget* view_; }; } swift-im-2.0+dev6/Swift/QtUI/Roster/GroupItemDelegate.cpp0000644000175000017500000001152412227051774023134 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "GroupItemDelegate.h" #include #include #include namespace Swift { GroupItemDelegate::GroupItemDelegate() : groupFont_(QApplication::font()) { groupFont_.setPointSize(common_.nameFont.pointSize() - common_.detailFontSizeDrop); groupFont_.setWeight(QFont::Bold); } QSize GroupItemDelegate::sizeHint(const QStyleOptionViewItem& /*option*/, const QModelIndex& /*index*/) const { QFontMetrics groupMetrics(groupFont_); return QSize(150, groupMetrics.height() + common_.verticalMargin + 2); } void GroupItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QString& name, int rowCount, bool expanded) const { painter->save(); painter->setPen(QPen(QColor(189, 189, 189))); //FIXME: It looks like Qt is passing us a rectangle that's too small //This deliberately draws outside the lines, and we need to find a better solution. int correctionAmount = groupCornerRadius_ > 0 ? 0 : 1; QRect region(QPoint(option.rect.left() - correctionAmount, option.rect.top()), QSize(option.rect.width() + correctionAmount, option.rect.height() - common_.verticalMargin)); QLinearGradient fillGradient(region.topLeft(), region.bottomLeft()); fillGradient.setColorAt(0, QColor(244, 244, 244)); fillGradient.setColorAt(0.1, QColor(231, 231, 231)); fillGradient.setColorAt(1, QColor(209, 209, 209)); QBrush backgroundBrush = QBrush(fillGradient); QPainterPath fillPath; fillPath.addRoundedRect(region, groupCornerRadius_, groupCornerRadius_); QPainterPath linePath; linePath.addRoundedRect(region, groupCornerRadius_, groupCornerRadius_); painter->fillPath(fillPath, backgroundBrush); painter->drawPath(linePath); int triangleHorizontalOffset = 1; int triangleWidth = 9; int triangleHeight = 5; paintExpansionTriangle(painter, region.adjusted(common_.horizontalMargin + triangleHorizontalOffset + 1, 0, 0, 0), triangleWidth, triangleHeight, expanded); int textLeftOffset = 3 * common_.horizontalMargin + 1 + triangleWidth + triangleHorizontalOffset; QFontMetrics fontMetrics(groupFont_); int textTopOffset = (region.height() - fontMetrics.height()) / 2; painter->setFont(groupFont_); int contactCountWidth = 0; QRect textRect = region.adjusted(textLeftOffset, textTopOffset, -1 * textLeftOffset, -1 * textTopOffset); if (!expanded) { QFontMetrics groupMetrics(groupFont_); int contactCount = rowCount; QString countString = QString("%1").arg(contactCount); contactCountWidth = groupMetrics.width(countString) + 2 * common_.horizontalMargin; int offsetAmount = textRect.width() - contactCountWidth + common_.horizontalMargin; QRect countRect = textRect.adjusted(offsetAmount, 0, 0/*-1 * offsetAmount*/, 0); paintShadowText(painter, countRect, countString); } QRect nameTextRect = expanded ? textRect : textRect.adjusted(0, 0, -contactCountWidth, 0); QString elidedName = fontMetrics.elidedText(name, Qt::ElideRight, nameTextRect.width(), Qt::TextShowMnemonic); paintShadowText(painter, nameTextRect, elidedName); painter->restore(); } void GroupItemDelegate::paintExpansionTriangle(QPainter* painter, const QRect& region, int width, int height, bool expanded) const { // height is the height of the downward pointing triangle QPolygonF triangle; if (expanded) { QPointF triangleTopLeft(region.left(), region.top() + region.height() / 2 - height / 2); triangle << triangleTopLeft; triangle << triangleTopLeft + QPointF(width, 0); triangle << triangleTopLeft + QPointF(width / 2, height); // The expanded triangle should be a little lower, because its pointy shape makes it feel // as if it's too high. triangle.translate(QPointF(0,1)); } else { QPointF triangleTopLeft(region.left() + ((width - height) / 2), region.top() + region.height() / 2 - width / 2); triangle << triangleTopLeft; triangle << triangleTopLeft + QPointF(height, width / 2); triangle << triangleTopLeft + QPointF(0, width); } //qDebug() << "Painting triangle: " << triangle; QPolygonF triangleShadow(triangle); triangleShadow.translate(QPointF(0, -1)); QPainterPath trianglePath; QPainterPath triangleShadowPath; QBrush triangleBrush(QColor(110, 110, 110)); QBrush triangleShadowBrush(QColor(47, 47, 47)); trianglePath.addPolygon(triangle); triangleShadowPath.addPolygon(triangleShadow); painter->fillPath(triangleShadowPath, triangleShadowBrush); painter->fillPath(trianglePath, triangleBrush); } void GroupItemDelegate::paintShadowText(QPainter* painter, const QRect& region, const QString& text) const { painter->setPen(QPen(QColor(254, 254, 254))); painter->drawText(region.adjusted(0, 1, 0, 0), Qt::AlignTop, text); painter->setPen(QPen(QColor(115, 115, 115))); painter->drawText(region, Qt::AlignTop, text); } const int GroupItemDelegate::groupCornerRadius_ = 0; } swift-im-2.0+dev6/Swift/QtUI/Roster/RosterDelegate.h0000644000175000017500000000216112227051774022141 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include "GroupItemDelegate.h" #include "DelegateCommons.h" namespace Swift { class QtTreeWidget; class RosterDelegate : public QStyledItemDelegate { public: RosterDelegate(QtTreeWidget* tree, bool compact); ~RosterDelegate(); QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const; void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; public slots: void setCompact(bool compact); private: QSize contactSizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const; void paintGroup(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; void paintContact(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; bool compact_; DelegateCommons common_; GroupItemDelegate* groupDelegate_; QtTreeWidget* tree_; }; } swift-im-2.0+dev6/Swift/QtUI/Roster/DelegateCommons.cpp0000644000175000017500000001114412227051774022632 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "DelegateCommons.h" #include #include namespace Swift { void DelegateCommons::drawElidedText(QPainter* painter, const QRect& region, const QString& text, int flags) { QString adjustedText(painter->fontMetrics().elidedText(text, Qt::ElideRight, region.width(), Qt::TextShowMnemonic)); painter->drawText(region, flags, adjustedText.simplified()); } void DelegateCommons::paintContact(QPainter* painter, const QStyleOptionViewItem& option, const QColor& nameColor, const QString& avatarPath, const QIcon& presenceIcon, const QString& name, const QString& statusText, int unreadCount, bool compact) const { painter->save(); QRect fullRegion(option.rect); if ( option.state & QStyle::State_Selected ) { painter->fillRect(fullRegion, option.palette.highlight()); painter->setPen(option.palette.highlightedText().color()); } else { painter->setPen(QPen(nameColor)); } QRect presenceIconRegion(QPoint(farLeftMargin, fullRegion.top()), QSize(presenceIconWidth, fullRegion.height() - verticalMargin)); int calculatedAvatarSize = presenceIconRegion.height(); //This overlaps the presenceIcon, so must be painted first QRect avatarRegion(QPoint(presenceIconRegion.right() - presenceIconWidth / 2, presenceIconRegion.top()), QSize(calculatedAvatarSize, calculatedAvatarSize)); QPixmap avatarPixmap; if (!compact && !avatarPath.isEmpty()) { QString scaledAvatarPath = QtScaledAvatarCache(avatarRegion.height()).getScaledAvatarPath(avatarPath); if (QFileInfo(scaledAvatarPath).exists()) { avatarPixmap.load(scaledAvatarPath); } } if (!compact && avatarPixmap.isNull()) { avatarPixmap = QPixmap(":/icons/avatar.png").scaled(avatarRegion.height(), avatarRegion.width(), Qt::KeepAspectRatio, Qt::SmoothTransformation); } if (!compact) { painter->drawPixmap(avatarRegion.topLeft() + QPoint(((avatarRegion.width() - avatarPixmap.width()) / 2), (avatarRegion.height() - avatarPixmap.height()) / 2), avatarPixmap); } //Paint the presence icon over the top of the avatar presenceIcon.paint(painter, presenceIconRegion, Qt::AlignBottom | Qt::AlignHCenter); QFontMetrics nameMetrics(nameFont); painter->setFont(nameFont); int extraFontWidth = nameMetrics.width("H"); int leftOffset = (compact ? presenceIconRegion : avatarRegion).right() + horizontalMargin * 2 + extraFontWidth / 2; QRect textRegion(fullRegion.adjusted(leftOffset, 0, 0/*-leftOffset*/, 0)); int nameHeight = nameMetrics.height() + verticalMargin; QRect nameRegion(textRegion.adjusted(0, verticalMargin, 0, 0)); DelegateCommons::drawElidedText(painter, nameRegion, name); if (!compact) { painter->setFont(detailFont); painter->setPen(QPen(QColor(160,160,160))); QRect statusTextRegion(textRegion.adjusted(0, nameHeight, 0, 0)); DelegateCommons::drawElidedText(painter, statusTextRegion, statusText); } if (unreadCount > 0) { QRect unreadRect(fullRegion.right() - unreadCountSize - horizontalMargin, fullRegion.top() + (fullRegion.height() - unreadCountSize) / 2, unreadCountSize, unreadCountSize); QPen pen(QColor("black")); pen.setWidth(1); painter->setRenderHint(QPainter::Antialiasing, true); painter->setPen(pen); painter->setBrush(QBrush(QColor("red"), Qt::SolidPattern)); //painter->setBackgroundMode(Qt::OpaqueMode); painter->drawEllipse(unreadRect); painter->setBackgroundMode(Qt::TransparentMode); painter->setPen(QColor("white")); drawElidedText(painter, unreadRect, QString("%1").arg(unreadCount), Qt::AlignCenter); } painter->restore(); } QSize DelegateCommons::contactSizeHint(const QStyleOptionViewItem& /*option*/, const QModelIndex& /*index*/, bool compact ) const { int heightByAvatar = (compact ? presenceIconHeight : avatarSize) + verticalMargin * 2; QFontMetrics nameMetrics(nameFont); QFontMetrics statusMetrics(detailFont); int sizeByText = 2 * verticalMargin + nameMetrics.height() + (compact ? 0 : statusMetrics.height()); //Doesn't work, yay! FIXME: why? //QSize size = (option.state & QStyle::State_Selected) ? QSize(150, 80) : QSize(150, avatarSize_ + margin_ * 2); //qDebug() << "Returning size" << size; return QSize(150, sizeByText > heightByAvatar ? sizeByText : heightByAvatar); } const int DelegateCommons::horizontalMargin = 2; const int DelegateCommons::verticalMargin = 2; const int DelegateCommons::farLeftMargin = 2; const int DelegateCommons::avatarSize = 20; const int DelegateCommons::presenceIconHeight = 16; const int DelegateCommons::presenceIconWidth = 16; const int DelegateCommons::unreadCountSize = 16; } swift-im-2.0+dev6/Swift/QtUI/Roster/Roster.pri0000644000175000017500000000064612227051774021057 0ustar kismithkismithSOURCES += $$PWD/RosterDelegate.cpp \ # $$PWD/RosterItem.cpp \ $$PWD/RosterModel.cpp \ $$PWD/QtTreeWidget.cpp \ $$PWD/QtTreeWidgetItem.cpp \ $$PWD/DelegateCommons.cpp \ $$PWD/GroupItemDelegate.cpp HEADERS += $$PWD/RosterDelegate.h \ # $$PWD/RosterItem.h \ $$PWD/RosterModel.h \ $$PWD/QtTreeWidget.h \ $$PWD/QtTreeWidgetFactory.h \ $$PWD/QtTreeWidgetItem.h \ $$PWD/DelegateCommons.h \ $$PWD/GroupItemDelegate.h swift-im-2.0+dev6/Swift/QtUI/Roster/RosterModel.cpp0000644000175000017500000001535112227051774022027 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "RosterModel.h" #include #include #include #include #include "Swiften/Elements/StatusShow.h" #include "Swift/Controllers/Roster/ContactRosterItem.h" #include "Swift/Controllers/Roster/GroupRosterItem.h" #include #include "QtSwiftUtil.h" #include "Swift/QtUI/Roster/QtTreeWidget.h" namespace Swift { RosterModel::RosterModel(QtTreeWidget* view) : view_(view) { roster_ = NULL; } RosterModel::~RosterModel() { } void RosterModel::setRoster(Roster* roster) { roster_ = roster; if (roster_) { roster->onChildrenChanged.connect(boost::bind(&RosterModel::handleChildrenChanged, this, _1)); roster->onDataChanged.connect(boost::bind(&RosterModel::handleDataChanged, this, _1)); } reLayout(); } void RosterModel::reLayout() { //emit layoutChanged(); reset(); if (!roster_) { return; } foreach (RosterItem* item, roster_->getRoot()->getDisplayedChildren()) { GroupRosterItem* child = dynamic_cast(item); if (!child) continue; emit itemExpanded(index(child), child->isExpanded()); } } void RosterModel::handleChildrenChanged(GroupRosterItem* /*group*/) { reLayout(); } void RosterModel::handleDataChanged(RosterItem* item) { Q_ASSERT(item); QModelIndex modelIndex = index(item); if (modelIndex.isValid()) { emit dataChanged(modelIndex, modelIndex); } } int RosterModel::columnCount(const QModelIndex& /*parent*/) const { return 1; } RosterItem* RosterModel::getItem(const QModelIndex& index) const { return index.isValid() ? static_cast(index.internalPointer()) : NULL; } QVariant RosterModel::data(const QModelIndex& index, int role) const { RosterItem* item = getItem(index); if (!item) return QVariant(); switch (role) { case Qt::DisplayRole: return P2QSTRING(item->getDisplayName()); case Qt::TextColorRole: return getTextColor(item); case Qt::BackgroundColorRole: return getBackgroundColor(item); case Qt::ToolTipRole: return getToolTip(item); case StatusTextRole: return getStatusText(item); case AvatarRole: return getAvatar(item); case PresenceIconRole: return getPresenceIcon(item); case ChildCountRole: return getChildCount(item); default: return QVariant(); } } int RosterModel::getChildCount(RosterItem* item) const { GroupRosterItem* group = dynamic_cast(item); return group ? group->getDisplayedChildren().size() : 0; } QColor RosterModel::intToColor(int color) const { return QColor( ((color & 0xFF0000)>>16), ((color & 0xFF00)>>8), (color & 0xFF)); } QColor RosterModel::getTextColor(RosterItem* item) const { ContactRosterItem* contact = dynamic_cast(item); int color = 0; if (contact) { switch (contact->getStatusShow()) { case StatusShow::Online: color = 0x000000; break; case StatusShow::Away: color = 0x336699; break; case StatusShow::XA: color = 0x336699; break; case StatusShow::FFC: color = 0x000000; break; case StatusShow::DND: color = 0x990000; break; case StatusShow::None: color = 0x7F7F7F;break; } } return intToColor(color); } QColor RosterModel::getBackgroundColor(RosterItem* item) const { return dynamic_cast(item) ? intToColor(0xFFFFFF) : intToColor(0x969696); } QString RosterModel::getToolTip(RosterItem* item) const { QString tip(P2QSTRING(item->getDisplayName())); ContactRosterItem* contact = dynamic_cast(item); if (contact) { if (contact->getDisplayJID().isValid()) { tip += "\n" + P2QSTRING(contact->getDisplayJID().toBare().toString()); } tip += "\n " + P2QSTRING(statusShowTypeToFriendlyName(contact->getStatusShow())); if (!getStatusText(item).isEmpty()) { tip += ": " + getStatusText(item); } } return tip; } QString RosterModel::getAvatar(RosterItem* item) const { ContactRosterItem* contact = dynamic_cast(item); if (!contact) { return ""; } return QString(contact->getAvatarPath().c_str()); } QString RosterModel::getStatusText(RosterItem* item) const { ContactRosterItem* contact = dynamic_cast(item); if (!contact) return ""; return P2QSTRING(contact->getStatusText()); } QIcon RosterModel::getPresenceIcon(RosterItem* item) const { ContactRosterItem* contact = dynamic_cast(item); if (!contact) return QIcon(); QString iconString; switch (contact->getStatusShow()) { case StatusShow::Online: iconString = "online";break; case StatusShow::Away: iconString = "away";break; case StatusShow::XA: iconString = "away";break; case StatusShow::FFC: iconString = "online";break; case StatusShow::DND: iconString = "dnd";break; case StatusShow::None: iconString = "offline";break; } return QIcon(":/icons/" + iconString + ".png"); } QModelIndex RosterModel::index(int row, int column, const QModelIndex& parent) const { if (!roster_) { return QModelIndex(); } GroupRosterItem* parentItem; if (!parent.isValid()) { //top level parentItem = roster_->getRoot(); } else { parentItem = dynamic_cast(getItem(parent)); if (!parentItem) return QModelIndex(); } return static_cast(row) < parentItem->getDisplayedChildren().size() ? createIndex(row, column, parentItem->getDisplayedChildren()[row]) : QModelIndex(); } QModelIndex RosterModel::index(RosterItem* item) const { GroupRosterItem* parent = item->getParent(); /* Recursive check that it's ok to create such an item Assuming there are more contacts in a group than groups in a group, this could save a decent chunk of search time at startup.*/ if (parent == NULL || roster_ == NULL || (parent != roster_->getRoot() && !index(parent).isValid())) { return QModelIndex(); } for (size_t i = 0; i < parent->getDisplayedChildren().size(); i++) { if (parent->getDisplayedChildren()[i] == item) { return createIndex(i, 0, item); } } return QModelIndex(); } QModelIndex RosterModel::parent(const QModelIndex& child) const { if (!roster_ || !child.isValid()) { return QModelIndex(); } GroupRosterItem* parent = getItem(child)->getParent(); return (parent != roster_->getRoot()) ? index(parent) : QModelIndex(); } int RosterModel::rowCount(const QModelIndex& parent) const { if (!roster_) return 0; RosterItem* item = parent.isValid() ? static_cast(parent.internalPointer()) : roster_->getRoot(); Q_ASSERT(item); GroupRosterItem* group = dynamic_cast(item); int count = group ? group->getDisplayedChildren().size() : 0; // qDebug() << "rowCount = " << count << " where parent.isValid() == " << parent.isValid() << ", group == " << (group ? P2QSTRING(group->getDisplayName()) : "*contact*"); return count; } } swift-im-2.0+dev6/Swift/QtUI/Roster/GroupItemDelegate.h0000644000175000017500000000157112227051774022602 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include "DelegateCommons.h" namespace Swift { class QtTreeWidgetItem; class GroupItemDelegate { public: GroupItemDelegate(); QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const; void paint(QPainter* painter, const QStyleOptionViewItem& option, const QString& name, int rowCount, bool expanded) const; private: void paintShadowText(QPainter* painter, const QRect& region, const QString& text) const; void paintExpansionTriangle(QPainter* painter, const QRect& region, int width, int height, bool expanded) const; QFont groupFont_; static const int groupCornerRadius_; DelegateCommons common_; }; } swift-im-2.0+dev6/Swift/QtUI/Roster/QtOccupantListWidget.h0000644000175000017500000000161112227051774023310 0ustar kismithkismith/* * Copyright (c) 2011-2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include "Swift/QtUI/Roster/QtTreeWidget.h" #include "Swiften/Base/boost_bsignals.h" #include "Swift/Controllers/UIInterfaces/ChatWindow.h" namespace Swift { class SettingsProvider; class QtOccupantListWidget : public QtTreeWidget { Q_OBJECT public: QtOccupantListWidget(UIEventStream* eventStream, SettingsProvider* settings, QWidget* parent = 0); virtual ~QtOccupantListWidget(); void setAvailableOccupantActions(const std::vector& actions); boost::signal onOccupantActionSelected; protected: void contextMenuEvent(QContextMenuEvent* event); private: std::vector availableOccupantActions_; }; } swift-im-2.0+dev6/Swift/QtUI/Roster/main.cpp0000644000175000017500000000446312227051774020516 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include "QtTreeWidget.h" #include "QtTreeWidgetFactory.h" #include "Swiften/Elements/StatusShow.h" int main(int argc, char *argv[]) { QApplication app(argc, argv); //Swift::RosterModel model; //QTreeView view; //view.setModel(&model); //view.setWindowTitle("A roster"); //view.show(); Swift::QtTreeWidgetFactory treeWidgetFactory; Swift::QtTreeWidget* tree = dynamic_cast(treeWidgetFactory.createTreeWidget()); tree->show(); QList item3s; for (int i = 0; i < 500; i++) { Swift::QtTreeWidgetItem* group = dynamic_cast(treeWidgetFactory.createTreeWidgetItem(tree)); group->setText("People"); group->setBackgroundColor(0xBBBBBB); Swift::QtTreeWidgetItem* item1 = dynamic_cast(treeWidgetFactory.createTreeWidgetItem(group)); Swift::QtTreeWidgetItem* item2 = dynamic_cast(treeWidgetFactory.createTreeWidgetItem(group)); Swift::QtTreeWidgetItem* item3 = dynamic_cast(treeWidgetFactory.createTreeWidgetItem(group)); Swift::QtTreeWidgetItem* item4 = dynamic_cast(treeWidgetFactory.createTreeWidgetItem(group)); item1->setText("Remko"); item2->setText("Kevin"); item3->setText("Cath"); item4->setText("KimTypo"); item4->setText("Kim"); item3s.push_back(item3); } Swift::QtTreeWidgetItem* group = dynamic_cast(treeWidgetFactory.createTreeWidgetItem(tree)); group->setText("Many People"); Swift::QtTreeWidgetItem* person350; Swift::QtTreeWidgetItem* person1200; for (int i = 0; i < 1500; i++) { Swift::QtTreeWidgetItem* item = dynamic_cast(treeWidgetFactory.createTreeWidgetItem(group)); item->setText(Q2PSTRING(QString("Some person %1").arg(i))); item->setStatusShow(Swift::StatusShow::Away); if (i == 350) person350 = item; if (i == 1200) person1200 = item; } for (int i = 0; i < item3s.size(); i++) { item3s[i]->setStatusShow(Swift::StatusShow::XA); } person350->setStatusShow(Swift::StatusShow::DND); person1200->setStatusShow(Swift::StatusShow::Online); return app.exec(); } swift-im-2.0+dev6/Swift/QtUI/Roster/QtTreeWidget.cpp0000644000175000017500000001474312227051774022144 0ustar kismithkismith/* * Copyright (c) 2010-2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Roster/QtTreeWidget.h" #include #include #include #include #include #include #include #include #include #include #include #include namespace Swift { QtTreeWidget::QtTreeWidget(UIEventStream* eventStream, SettingsProvider* settings, QWidget* parent) : QTreeView(parent) { eventStream_ = eventStream; settings_ = settings; model_ = new RosterModel(this); setModel(model_); delegate_ = new RosterDelegate(this, settings_->getSetting(QtUISettingConstants::COMPACT_ROSTER)); setItemDelegate(delegate_); setHeaderHidden(true); #ifdef SWIFT_PLATFORM_MACOSX setAlternatingRowColors(true); #endif setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); expandAll(); setAnimated(true); setIndentation(0); #ifdef SWIFT_EXPERIMENTAL_FT setAcceptDrops(true); #endif setRootIsDecorated(true); connect(this, SIGNAL(activated(const QModelIndex&)), this, SLOT(handleItemActivated(const QModelIndex&))); connect(model_, SIGNAL(itemExpanded(const QModelIndex&, bool)), this, SLOT(handleModelItemExpanded(const QModelIndex&, bool))); connect(this, SIGNAL(expanded(const QModelIndex&)), this, SLOT(handleExpanded(const QModelIndex&))); connect(this, SIGNAL(collapsed(const QModelIndex&)), this, SLOT(handleCollapsed(const QModelIndex&))); connect(this, SIGNAL(clicked(const QModelIndex&)), this, SLOT(handleClicked(const QModelIndex&))); settings_->onSettingChanged.connect(boost::bind(&QtTreeWidget::handleSettingChanged, this, _1)); } QtTreeWidget::~QtTreeWidget() { settings_->onSettingChanged.disconnect(boost::bind(&QtTreeWidget::handleSettingChanged, this, _1)); delete model_; delete delegate_; } void QtTreeWidget::handleSettingChanged(const std::string& setting) { if (setting == QtUISettingConstants::COMPACT_ROSTER.getKey()) { delegate_->setCompact(settings_->getSetting(QtUISettingConstants::COMPACT_ROSTER)); repaint(); } } void QtTreeWidget::setRosterModel(Roster* roster) { roster_ = roster; model_->setRoster(roster); expandAll(); } QtTreeWidgetItem* QtTreeWidget::getRoot() { return treeRoot_; } void QtTreeWidget::handleClicked(const QModelIndex& index) { GroupRosterItem* item = dynamic_cast(static_cast(index.internalPointer())); if (item) { setExpanded(index, !isExpanded(index)); } currentChanged(index, QModelIndex()); } QModelIndexList QtTreeWidget::getSelectedIndexes() const { // Not using selectedIndexes(), because this seems to cause a crash in Qt (4.7.0) in the // QModelIndexList destructor. // This is a workaround posted in http://www.qtcentre.org/threads/16933 (although this case // was resolved by linking against the debug libs, ours isn't, and we're not alone) QItemSelection ranges = selectionModel()->selection(); QModelIndexList selectedIndexList; for (int i = 0; i < ranges.count(); ++i) { QModelIndex parent = ranges.at(i).parent(); int right = ranges.at(i).model()->columnCount(parent) - 1; if (ranges.at(i).left() == 0 && ranges.at(i).right() == right) { for (int r = ranges.at(i).top(); r <= ranges.at(i).bottom(); ++r) { selectedIndexList.append(ranges.at(i).model()->index(r, 0, parent)); } } } return selectedIndexList; } void QtTreeWidget::currentChanged(const QModelIndex& current, const QModelIndex& previous) { RosterItem* item = NULL; QModelIndexList selectedIndexList = getSelectedIndexes(); if (selectedIndexList.empty() || !selectedIndexList[0].isValid()) { /* I didn't quite understand why using current didn't seem to work here.*/ } else if (current.isValid()) { item = static_cast(current.internalPointer()); item = dynamic_cast(item); } onSomethingSelectedChanged(item); QTreeView::currentChanged(current, previous); } void QtTreeWidget::handleItemActivated(const QModelIndex& index) { RosterItem* item = static_cast(index.internalPointer()); ContactRosterItem* contact = dynamic_cast(item); if (contact) { eventStream_->send(boost::shared_ptr(new RequestChatUIEvent(contact->getJID()))); } } void QtTreeWidget::dragEnterEvent(QDragEnterEvent *event) { if (event->mimeData()->hasUrls() && event->mimeData()->urls().size() == 1) { event->acceptProposedAction(); } } void QtTreeWidget::dropEvent(QDropEvent *event) { QModelIndex index = indexAt(event->pos()); if (index.isValid()) { RosterItem* item = static_cast(index.internalPointer()); if (ContactRosterItem* contact = dynamic_cast(item)) { if (contact->supportsFeature(ContactRosterItem::FileTransferFeature)) { QString filename = event->mimeData()->urls().at(0).toLocalFile(); if (!filename.isEmpty()) { eventStream_->send(boost::make_shared(contact->getJID(), Q2PSTRING(filename))); } } } } } void QtTreeWidget::dragMoveEvent(QDragMoveEvent* event) { QModelIndex index = indexAt(event->pos()); if (index.isValid()) { RosterItem* item = static_cast(index.internalPointer()); if (ContactRosterItem* contact = dynamic_cast(item)) { if (contact->supportsFeature(ContactRosterItem::FileTransferFeature)) { event->accept(); return; } } } event->ignore(); } void QtTreeWidget::handleExpanded(const QModelIndex& index) { GroupRosterItem* item = dynamic_cast(static_cast(index.internalPointer())); if (item) { item->setExpanded(true); } } void QtTreeWidget::handleCollapsed(const QModelIndex& index) { GroupRosterItem* item = dynamic_cast(static_cast(index.internalPointer())); if (item) { item->setExpanded(false); } } void QtTreeWidget::handleModelItemExpanded(const QModelIndex& index, bool shouldExpand) { if (!index.isValid()) { return; } bool alreadyRight = this->isExpanded(index) == shouldExpand; if (alreadyRight) { return; } setExpanded(index, shouldExpand); } void QtTreeWidget::drawBranches(QPainter*, const QRect&, const QModelIndex&) const { } void QtTreeWidget::show() { QWidget::show(); } } swift-im-2.0+dev6/Swift/QtUI/Roster/Roster.pro0000644000175000017500000000112312227051774021054 0ustar kismithkismithinclude(Roster.pri) SOURCES += main.cpp DEPENDPATH += ../. ../../.. ../../../3rdParty/Boost/src INCLUDEPATH += ../. ../../.. ../../../3rdParty/Boost/src LIBS += ../../../Swiften/libSwiften.a LIBS += ../../../3rdParty/Boost/libBoost.a LIBS += ../../../3rdParty/LibIDN/libIDN.a mac { DEFINES += SWIFT_PLATFORM_MACOSX } CONFIG -= app_bundle DEFINES += BOOST_SIGNALS_NAMESPACE=bsignals BOOST_ALL_NO_LIB exists(../config.pri) { LIBS += ../../Controllers/Controllers.a ../../../Swiften/Swiften.a include(../config.pri) } mac { DEFINES += SWIFT_PLATFORM_MACOSX } RESOURCES += ../Swift.qrc swift-im-2.0+dev6/Swift/QtUI/Roster/RosterDelegate.cpp0000644000175000017500000000526612227051774022505 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "RosterDelegate.h" #include #include #include #include #include #include #include #include #include #include "Swift/Controllers/Roster/ContactRosterItem.h" #include "Swift/Controllers/Roster/GroupRosterItem.h" #include "QtTreeWidget.h" #include "RosterModel.h" namespace Swift { RosterDelegate::RosterDelegate(QtTreeWidget* tree, bool compact) : compact_(compact) { tree_ = tree; groupDelegate_ = new GroupItemDelegate(); } RosterDelegate::~RosterDelegate() { delete groupDelegate_; } void RosterDelegate::setCompact(bool compact) { compact_ = compact; } QSize RosterDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index ) const { RosterItem* item = static_cast(index.internalPointer()); if (dynamic_cast(item)) { return groupDelegate_->sizeHint(option, index); } return contactSizeHint(option, index); } QSize RosterDelegate::contactSizeHint(const QStyleOptionViewItem& option, const QModelIndex& index ) const { return common_.contactSizeHint(option, index, compact_); } void RosterDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { RosterItem* item = static_cast(index.internalPointer()); if (dynamic_cast(item)) { paintGroup(painter, option, index); } else { paintContact(painter, option, index); } } void RosterDelegate::paintGroup(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { if (index.isValid()) { groupDelegate_->paint(painter, option, index.data(Qt::DisplayRole).toString(), index.data(ChildCountRole).toInt(), tree_->isExpanded(index)); } } void RosterDelegate::paintContact(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { QColor nameColor = index.data(Qt::TextColorRole).value(); QString avatarPath; if (index.data(AvatarRole).isValid() && !index.data(AvatarRole).value().isNull()) { avatarPath = index.data(AvatarRole).value(); } QIcon presenceIcon = index.data(PresenceIconRole).isValid() && !index.data(PresenceIconRole).value().isNull() ? index.data(PresenceIconRole).value() : QIcon(":/icons/offline.png"); QString name = index.data(Qt::DisplayRole).toString(); QString statusText = index.data(StatusTextRole).toString(); common_.paintContact(painter, option, nameColor, avatarPath, presenceIcon, name, statusText, 0, compact_); } } swift-im-2.0+dev6/Swift/QtUI/Roster/QtOccupantListWidget.cpp0000644000175000017500000000444612227051774023654 0ustar kismithkismith/* * Copyright (c) 2011-2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Roster/QtOccupantListWidget.h" #include #include #include #include #include "Swift/Controllers/Roster/ContactRosterItem.h" #include "Swift/Controllers/Roster/GroupRosterItem.h" #include "Swift/Controllers/UIEvents/UIEventStream.h" #include "QtSwiftUtil.h" namespace Swift { QtOccupantListWidget::QtOccupantListWidget(UIEventStream* eventStream, SettingsProvider* settings, QWidget* parent) : QtTreeWidget(eventStream, settings, parent) { } QtOccupantListWidget::~QtOccupantListWidget() { } void QtOccupantListWidget::setAvailableOccupantActions(const std::vector& actions) { availableOccupantActions_ = actions; } void QtOccupantListWidget::contextMenuEvent(QContextMenuEvent* event) { QModelIndex index = indexAt(event->pos()); if (!index.isValid()) { return; } RosterItem* item = static_cast(index.internalPointer()); ContactRosterItem* contact = dynamic_cast(item); if (contact) { onSomethingSelectedChanged(contact); QMenu contextMenu; if (availableOccupantActions_.empty()) { QAction* noAction = contextMenu.addAction(tr("No actions for this user")); noAction->setEnabled(false); contextMenu.exec(event->globalPos()); } else { std::map actions; foreach (ChatWindow::OccupantAction availableAction, availableOccupantActions_) { QString text = "Error: missing string"; switch (availableAction) { case ChatWindow::Kick: text = tr("Kick user"); break; case ChatWindow::Ban: text = tr("Kick and ban user"); break; case ChatWindow::MakeModerator: text = tr("Make moderator"); break; case ChatWindow::MakeParticipant: text = tr("Make participant"); break; case ChatWindow::MakeVisitor: text = tr("Remove voice"); break; case ChatWindow::AddContact: text = tr("Add to contacts"); break; } QAction* action = contextMenu.addAction(text); actions[action] = availableAction; } QAction* result = contextMenu.exec(event->globalPos()); if (result) { onOccupantActionSelected(actions[result], contact); } } } } } swift-im-2.0+dev6/Swift/QtUI/QtAddBookmarkWindow.cpp0000644000175000017500000000112012227051774022152 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "QtAddBookmarkWindow.h" #include namespace Swift { QtAddBookmarkWindow::QtAddBookmarkWindow(UIEventStream* eventStream) : eventStream_(eventStream) { } bool QtAddBookmarkWindow::commit() { boost::optional bookmark = createBookmarkFromForm(); if (bookmark) { eventStream_->send(boost::shared_ptr(new AddMUCBookmarkUIEvent(*bookmark))); return true; } else { return false; } } } swift-im-2.0+dev6/Swift/QtUI/QtScaledAvatarCache.h0000644000175000017500000000060412227051774021535 0ustar kismithkismith/* * 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 #include namespace Swift { class QtScaledAvatarCache { public: QtScaledAvatarCache(int size); QString getScaledAvatarPath(const QString& path); private: int size; }; } swift-im-2.0+dev6/Swift/QtUI/CAPICertificateSelector.h0000644000175000017500000000046012227051774022332 0ustar kismithkismith/* * Copyright (c) 2012 Isode Limited, London, England. * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include namespace Swift { std::string selectCAPICertificate(); bool isCAPIURI(std::string uri); } swift-im-2.0+dev6/Swift/QtUI/ChatSnippet.cpp0000644000175000017500000000160512227051774020531 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include "ChatSnippet.h" namespace Swift { ChatSnippet::ChatSnippet(bool appendToPrevious) : appendToPrevious_(appendToPrevious) { } ChatSnippet::~ChatSnippet() { } QString ChatSnippet::timeToEscapedString(const QDateTime& time) { QDate now(QDate::currentDate()); QString date = ""; if (time.date().daysTo(now) > 0) { date = "ddd "; } if (time.date().month() != now.month()) { date = date + "MMMM "; } if (time.date().daysTo(now) > 6) { date = date + "d "; } if (time.date().year() != now.year()) { date = date + "yy "; } date += "h:mm"; return escape(time.toString(date)); } QString ChatSnippet::wrapResizable(const QString& text) { return "" + text + ""; } }; swift-im-2.0+dev6/Swift/QtUI/QtRosterHeader.cpp0000644000175000017500000000733412227051774021210 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "QtRosterHeader.h" #include #include #include #include #include #include #include #include #include #include "QtStatusWidget.h" #include #include #include #include "QtScaledAvatarCache.h" namespace Swift { QtRosterHeader::QtRosterHeader(SettingsProvider* settings, QWidget* parent) : QWidget(parent) { QHBoxLayout* topLayout = new QHBoxLayout(); topLayout->setSpacing(3); topLayout->setContentsMargins(4,4,4,4); setLayout(topLayout); setMinimumHeight(50); setMaximumHeight(50); avatarLabel_ = new QtClickableLabel(this); avatarLabel_->setMinimumSize(avatarSize_, avatarSize_); avatarLabel_->setMaximumSize(avatarSize_, avatarSize_); avatarLabel_->setAlignment(Qt::AlignCenter); setAvatar(":/icons/avatar.png"); avatarLabel_->setScaledContents(false); topLayout->addWidget(avatarLabel_); connect(avatarLabel_, SIGNAL(clicked()), this, SIGNAL(onEditProfileRequest())); QVBoxLayout* rightLayout = new QVBoxLayout(); rightLayout->setSpacing(4); rightLayout->setContentsMargins(4,0,0,0); topLayout->addLayout(rightLayout); QHBoxLayout* nameAndSecurityLayout = new QHBoxLayout(); nameAndSecurityLayout->setContentsMargins(4,0,0,0); nameWidget_ = new QtNameWidget(settings, this); connect(nameWidget_, SIGNAL(onChangeNickRequest()), this, SIGNAL(onEditProfileRequest())); nameAndSecurityLayout->addWidget(nameWidget_); securityInfoButton_ = new QToolButton(this); securityInfoButton_->setStyleSheet("QToolButton { border: none; } QToolButton:hover { border: 1px solid #bebebe; } QToolButton:pressed { border: 1px solid #757575; background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #777777, stop: 1 #d4d4d4);}"); //securityInfoButton_->setAutoRaise(true); securityInfoButton_->setIcon(QIcon(":/icons/lock.png")); securityInfoButton_->setToolTip(tr("Connection is secured")); connect(securityInfoButton_, SIGNAL(clicked()), this, SIGNAL(onShowCertificateInfo())); nameAndSecurityLayout->addWidget(securityInfoButton_); rightLayout->addLayout(nameAndSecurityLayout); statusWidget_ = new QtStatusWidget(this); connect(statusWidget_, SIGNAL(onChangeStatusRequest(StatusShow::Type, const QString&)), this, SLOT(handleChangeStatusRequest(StatusShow::Type, const QString&))); rightLayout->addWidget(statusWidget_); show(); } void QtRosterHeader::handleChangeStatusRequest(StatusShow::Type type, const QString& text) { emit onChangeStatusRequest(type, text); } void QtRosterHeader::setStatusText(const QString& statusMessage) { statusWidget_->setStatusText(statusMessage); } void QtRosterHeader::setStatusType(StatusShow::Type type) { statusWidget_->setStatusType(type); } void QtRosterHeader::setConnecting() { statusWidget_->setConnecting(); } void QtRosterHeader::setStreamEncryptionStatus(bool tlsInPlace) { securityInfoButton_->setVisible(tlsInPlace); } void QtRosterHeader::setAvatar(const QString& path) { QString scaledAvatarPath = QtScaledAvatarCache(avatarSize_).getScaledAvatarPath(path); QPixmap avatar; if (QFileInfo(scaledAvatarPath).exists()) { avatar.load(scaledAvatarPath); } else { avatar = QPixmap(":/icons/avatar.png").scaled(avatarSize_, avatarSize_, Qt::KeepAspectRatio, Qt::SmoothTransformation); } avatarLabel_->setPixmap(avatar); } void QtRosterHeader::setNick(const QString& nick) { nameWidget_->setNick(nick); } void QtRosterHeader::setJID(const QString& jid) { nameWidget_->setJID(jid); } const int QtRosterHeader::avatarSize_ = 40; } swift-im-2.0+dev6/Swift/QtUI/QtHistoryWindow.cpp0000644000175000017500000002144312227051774021447 0ustar kismithkismith/* * Copyright (c) 2012 Catalin Badea * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Swift { QtHistoryWindow::QtHistoryWindow(SettingsProvider* settings, UIEventStream* eventStream) : previousTopMessageWasSelf_(false), previousBottomMessageWasSelf_(false) { ui_.setupUi(this); theme_ = new QtChatTheme(""); idCounter_ = 0; delete ui_.conversation_; conversation_ = new QtChatView(theme_, this, true); QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); sizePolicy.setHorizontalStretch(80); sizePolicy.setVerticalStretch(0); conversation_->setSizePolicy(sizePolicy); ui_.conversation_ = conversation_; ui_.bottomLayout_->addWidget(conversation_); delete ui_.conversationRoster_; conversationRoster_ = new QtTreeWidget(eventStream, settings, this); QSizePolicy sizePolicy2(QSizePolicy::Preferred, QSizePolicy::Expanding); sizePolicy2.setVerticalStretch(80); conversationRoster_->setSizePolicy(sizePolicy2); ui_.conversationRoster_ = conversationRoster_; ui_.bottomLeftLayout_->setDirection(QBoxLayout::BottomToTop); ui_.bottomLeftLayout_->addWidget(conversationRoster_); setWindowTitle(tr("History")); conversationRoster_->onSomethingSelectedChanged.connect(boost::bind(&QtHistoryWindow::handleSomethingSelectedChanged, this, _1)); connect(conversation_, SIGNAL(scrollRequested(int)), this, SLOT(handleScrollRequested(int))); connect(conversation_, SIGNAL(scrollReachedTop()), this, SLOT(handleScrollReachedTop())); connect(conversation_, SIGNAL(scrollReachedBottom()), this, SLOT(handleScrollReachedBottom())); connect(conversation_, SIGNAL(fontResized(int)), this, SLOT(handleFontResized(int))); connect(ui_.searchBox_->lineEdit(), SIGNAL(returnPressed()), this, SLOT(handleReturnPressed())); connect(ui_.calendarWidget_, SIGNAL(clicked(const QDate&)), this, SLOT(handleCalendarClicked(const QDate&))); connect(ui_.calendarWidget_, SIGNAL(activated(const QDate&)), this, SLOT(handleCalendarClicked(const QDate&))); connect(ui_.previousButton_, SIGNAL(clicked(bool)), this, SLOT(handlePreviousButtonClicked())); connect(ui_.nextButton_, SIGNAL(clicked(bool)), this, SLOT(handleNextButtonClicked())); } QtHistoryWindow::~QtHistoryWindow() { disconnect(conversation_, SIGNAL(scrollRequested(int)), this, SLOT(handleScrollRequested(int))); disconnect(conversation_, SIGNAL(scrollReachedTop()), this, SLOT(handleScrollReachedTop())); disconnect(conversation_, SIGNAL(scrollReachedBottom()), this, SLOT(handleScrollReachedBottom())); disconnect(conversation_, SIGNAL(fontResized(int)), this, SLOT(handleFontResized(int))); disconnect(ui_.searchBox_->lineEdit(), SIGNAL(returnPressed()), this, SLOT(handleReturnPressed())); disconnect(ui_.calendarWidget_, SIGNAL(clicked(const QDate&)), this, SLOT(handleCalendarClicked(const QDate&))); disconnect(ui_.calendarWidget_, SIGNAL(activated(const QDate&)), this, SLOT(handleCalendarClicked(const QDate&))); disconnect(ui_.previousButton_, SIGNAL(clicked(bool)), this, SLOT(handlePreviousButtonClicked())); disconnect(ui_.nextButton_, SIGNAL(clicked(bool)), this, SLOT(handleNextButtonClicked())); delete theme_; delete conversation_; // TODO: delete ui_ } void QtHistoryWindow::activate() { emit wantsToActivate(); } void QtHistoryWindow::showEvent(QShowEvent* event) { emit windowOpening(); emit titleUpdated(); QWidget::showEvent(event); } void QtHistoryWindow::closeEvent(QCloseEvent* event) { emit windowClosing(); event->accept(); } void QtHistoryWindow::setRosterModel(Roster* model) { conversationRoster_->setRosterModel(model); } void QtHistoryWindow::addMessage(const std::string &message, const std::string &senderName, bool senderIsSelf, const std::string& avatarPath, const boost::posix_time::ptime& time, bool addAtTheTop) { QString scaledAvatarPath = QtScaledAvatarCache(32).getScaledAvatarPath(avatarPath.c_str()); QString messageHTML(P2QSTRING(message)); messageHTML = Qt::escape(messageHTML); QString searchTerm = ui_.searchBox_->lineEdit()->text(); if (searchTerm.length()) { messageHTML.replace(searchTerm, "" + searchTerm + ""); } // note: time uses localtime QDate date = QDate(time.date().year(), time.date().month(), time.date().day()); QTime dayTime = QTime(time.time_of_day().hours(), time.time_of_day().minutes(), time.time_of_day().seconds()); QDateTime qTime = QDateTime(date, dayTime); std::string id = "id" + boost::lexical_cast(idCounter_++); QString qAvatarPath = scaledAvatarPath.isEmpty() ? "qrc:/icons/avatar.png" : QUrl::fromLocalFile(scaledAvatarPath).toEncoded(); if (addAtTheTop) { bool appendToPrevious = ((senderIsSelf && previousTopMessageWasSelf_) || (!senderIsSelf && !previousTopMessageWasSelf_&& previousTopSenderName_ == P2QSTRING(senderName))); conversation_->addMessageTop(boost::shared_ptr(new MessageSnippet(messageHTML, Qt::escape(P2QSTRING(senderName)), qTime, qAvatarPath, senderIsSelf, appendToPrevious, theme_, P2QSTRING(id)))); previousTopMessageWasSelf_ = senderIsSelf; previousTopSenderName_ = P2QSTRING(senderName); } else { bool appendToPrevious = ((senderIsSelf && previousBottomMessageWasSelf_) || (!senderIsSelf && !previousBottomMessageWasSelf_&& previousBottomSenderName_ == P2QSTRING(senderName))); conversation_->addMessageBottom(boost::shared_ptr(new MessageSnippet(messageHTML, Qt::escape(P2QSTRING(senderName)), qTime, qAvatarPath, senderIsSelf, appendToPrevious, theme_, P2QSTRING(id)))); previousBottomMessageWasSelf_ = senderIsSelf; previousBottomSenderName_ = P2QSTRING(senderName); } // keep track of the days viewable in the chatView if (!dates_.count(date)) { dates_.insert(date); } } void QtHistoryWindow::handleSomethingSelectedChanged(RosterItem* item) { onSelectedContactChanged(item); } void QtHistoryWindow::resetConversationView() { previousTopMessageWasSelf_ = false; previousBottomMessageWasSelf_ = false; previousTopSenderName_.clear(); previousBottomSenderName_.clear(); dates_.clear(); conversation_->resetView(); } void QtHistoryWindow::handleScrollRequested(int pos) { // first message starts with offset 5 if (pos < 5) { pos = 5; } QDate currentDate; foreach (const QDate& date, dates_) { int snippetPosition = conversation_->getSnippetPositionByDate(date); if (snippetPosition <= pos) { currentDate = date; } } if (ui_.calendarWidget_->selectedDate() != currentDate) { ui_.calendarWidget_->setSelectedDate(currentDate); } } void QtHistoryWindow::handleScrollReachedTop() { if (dates_.empty()) { return; } int year, month, day; QDate firstDate = *dates_.begin(); firstDate.getDate(&year, &month, &day); onScrollReachedTop(boost::gregorian::date(year, month, day)); } void QtHistoryWindow::handleScrollReachedBottom() { if (dates_.empty()) { return; } int year, month, day; QDate lastDate = *dates_.rbegin(); lastDate.getDate(&year, &month, &day); onScrollReachedBottom(boost::gregorian::date(year, month, day)); } void QtHistoryWindow::handleReturnPressed() { onReturnPressed(ui_.searchBox_->lineEdit()->text().toStdString()); } void QtHistoryWindow::handleCalendarClicked(const QDate& date) { int year, month, day; QDate tempDate = date; // getDate discards const qualifier tempDate.getDate(&year, &month, &day); onCalendarClicked(boost::gregorian::date(year, month, day)); } void QtHistoryWindow::setDate(const boost::gregorian::date& date) { ui_.calendarWidget_->setSelectedDate(QDate::fromJulianDay(date.julian_day())); } void QtHistoryWindow::handleNextButtonClicked() { onNextButtonClicked(); } void QtHistoryWindow::handlePreviousButtonClicked() { onPreviousButtonClicked(); } void QtHistoryWindow::handleFontResized(int fontSizeSteps) { conversation_->resizeFont(fontSizeSteps); emit fontResized(fontSizeSteps); } void QtHistoryWindow::resetConversationViewTopInsertPoint() { previousTopMessageWasSelf_ = false; previousTopSenderName_ = QString(); conversation_->resetTopInsertPoint(); } std::string QtHistoryWindow::getSearchBoxText() { return ui_.searchBox_->lineEdit()->text().toStdString(); } boost::gregorian::date QtHistoryWindow::getLastVisibleDate() { if (!dates_.empty()) { QDate lastDate = *dates_.rbegin(); int year, month, day; lastDate.getDate(&year, &month, &day); return boost::gregorian::date(year, month, day); } return boost::gregorian::date(boost::gregorian::not_a_date_time); } } swift-im-2.0+dev6/Swift/QtUI/QtTabWidget.h0000644000175000017500000000054112227051774020131 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include namespace Swift { class QtTabWidget : public QTabWidget { Q_OBJECT public: QtTabWidget(QWidget* parent); ~QtTabWidget(); QTabBar* tabBar(); }; } swift-im-2.0+dev6/Swift/QtUI/QtChatTheme.cpp0000644000175000017500000000601112227051774020452 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith. * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "QtChatTheme.h" #include #include namespace Swift { /** * Load Adium themes, as http://trac.adium.im/wiki/CreatingMessageStyles */ QtChatTheme::QtChatTheme(const QString& themePath) : qrc_(themePath.isEmpty()), themePath_(qrc_ ? ":/themes/Default/" : themePath + "/Contents/Resources/") { QString fileNames[EndMarker]; fileNames[Header] = "Header.html"; fileNames[Footer] = "Footer.html"; fileNames[Content] = "Content.html"; fileNames[Status] = "Status.html"; fileNames[Topic] = "Topic.html"; fileNames[FileTransferRequest] = "FileTransferRequest.html"; fileNames[IncomingContent] = "Incoming/Content.html"; fileNames[IncomingNextContent] = "Incoming/NextContent.html"; fileNames[IncomingContext] = "Incoming/Context.html"; fileNames[IncomingNextContext] = "Incoming/NextContext.html"; fileNames[OutgoingContent] = "Outgoing/Content.html"; fileNames[OutgoingNextContent] = "Outgoing/NextContent.html"; fileNames[OutgoingContext] = "Outgoing/Context.html"; fileNames[OutgoingNextContext] = "Outgoing/NextContext.html"; fileNames[Template] = "Template.html"; fileNames[MainCSS] = "main.css"; fileNames[TemplateDefault] = ":/themes/Template.html"; for (int i = 0; i < EndMarker; i++) { QString source; QFile sourceFile((i != TemplateDefault ? themePath_ : "") + fileNames[i]); if (sourceFile.exists() && sourceFile.open(QIODevice::ReadOnly)) { source = sourceFile.readAll(); sourceFile.close(); } else { //qWarning() << "Couldn't load file " << sourceFile.fileName(); } fileContents_.append(source); } /* Fallbacks */ if (fileContents_[Template].isEmpty()) fileContents_[Template] = fileContents_[TemplateDefault]; if (fileContents_[Status].isEmpty()) fileContents_[Status] = fileContents_[Content]; if (fileContents_[IncomingContent].isEmpty()) fileContents_[IncomingContent] = fileContents_[Content]; if (fileContents_[IncomingNextContent].isEmpty()) fileContents_[IncomingNextContent] = fileContents_[IncomingContent]; if (fileContents_[FileTransferRequest].isEmpty()) fileContents_[FileTransferRequest] = fileContents_[Status]; if (fileContents_[IncomingContext].isEmpty()) fileContents_[IncomingContext] = fileContents_[IncomingContent]; if (fileContents_[IncomingNextContext].isEmpty()) fileContents_[IncomingNextContext] = fileContents_[IncomingNextContent]; if (fileContents_[OutgoingContent].isEmpty()) fileContents_[OutgoingContent] = fileContents_[IncomingContent]; if (fileContents_[OutgoingContext].isEmpty()) fileContents_[OutgoingContext] = fileContents_[OutgoingContent]; if (fileContents_[OutgoingNextContent].isEmpty()) fileContents_[OutgoingNextContent] = fileContents_[OutgoingContent]; if (fileContents_[OutgoingNextContext].isEmpty()) fileContents_[OutgoingNextContext] = fileContents_[OutgoingNextContent]; } QString QtChatTheme::getBase() const { return qrc_ ? "qrc" + themePath_ : "file://" + themePath_; } } swift-im-2.0+dev6/Swift/QtUI/QtUIFactory.cpp0000644000175000017500000001370212227051774020462 0ustar kismithkismith/* * Copyright (c) 2010-2012 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "QtUIFactory.h" #include #include "QtXMLConsoleWidget.h" #include "QtChatTabs.h" #include "QtMainWindow.h" #include "QtLoginWindow.h" #include "QtSystemTray.h" #include "QtSettingsProvider.h" #include "QtMainWindow.h" #include "QtChatWindow.h" #include "QtJoinMUCWindow.h" #include "QtChatWindowFactory.h" #include "QtSwiftUtil.h" #include "MUCSearch/QtMUCSearchWindow.h" #include "UserSearch/QtUserSearchWindow.h" #include "QtProfileWindow.h" #include "QtContactEditWindow.h" #include "QtAdHocCommandWindow.h" #include "QtFileTransferListWidget.h" #include "Whiteboard/QtWhiteboardWindow.h" #include #include #include #include namespace Swift { QtUIFactory::QtUIFactory(SettingsProviderHierachy* settings, QtSettingsProvider* qtOnlySettings, QtChatTabs* tabs, QSplitter* netbookSplitter, QtSystemTray* systemTray, QtChatWindowFactory* chatWindowFactory, TimerFactory* timerFactory, bool startMinimized, bool emoticonsExist) : settings(settings), qtOnlySettings(qtOnlySettings), tabs(tabs), netbookSplitter(netbookSplitter), systemTray(systemTray), chatWindowFactory(chatWindowFactory), timerFactory_(timerFactory), lastMainWindow(NULL), loginWindow(NULL), startMinimized(startMinimized), emoticonsExist_(emoticonsExist) { chatFontSize = settings->getSetting(QtUISettingConstants::CHATWINDOW_FONT_SIZE); historyFontSize_ = settings->getSetting(QtUISettingConstants::HISTORYWINDOW_FONT_SIZE); } XMLConsoleWidget* QtUIFactory::createXMLConsoleWidget() { QtXMLConsoleWidget* widget = new QtXMLConsoleWidget(); tabs->addTab(widget); if (!tabs->isVisible()) { tabs->show(); } widget->show(); return widget; } HistoryWindow* QtUIFactory::createHistoryWindow(UIEventStream* uiEventStream) { QtHistoryWindow* window = new QtHistoryWindow(settings, uiEventStream); tabs->addTab(window); if (!tabs->isVisible()) { tabs->show(); } connect(window, SIGNAL(fontResized(int)), this, SLOT(handleHistoryWindowFontResized(int))); window->handleFontResized(historyFontSize_); window->show(); return window; } void QtUIFactory::handleHistoryWindowFontResized(int size) { historyFontSize_ = size; settings->storeSetting(QtUISettingConstants::HISTORYWINDOW_FONT_SIZE, size); } FileTransferListWidget* QtUIFactory::createFileTransferListWidget() { QtFileTransferListWidget* widget = new QtFileTransferListWidget(); tabs->addTab(widget); if (!tabs->isVisible()) { tabs->show(); } widget->show(); return widget; } MainWindow* QtUIFactory::createMainWindow(UIEventStream* eventStream) { lastMainWindow = new QtMainWindow(settings, eventStream, loginWindow->getMenus(), emoticonsExist_); return lastMainWindow; } LoginWindow* QtUIFactory::createLoginWindow(UIEventStream* eventStream) { loginWindow = new QtLoginWindow(eventStream, settings, timerFactory_); if (netbookSplitter) { netbookSplitter->insertWidget(0, loginWindow); } connect(systemTray, SIGNAL(clicked()), loginWindow, SLOT(toggleBringToFront())); #ifndef SWIFT_MOBILE QVariant loginWindowGeometryVariant = qtOnlySettings->getQSettings()->value("loginWindowGeometry"); if (loginWindowGeometryVariant.isValid()) { loginWindow->restoreGeometry(loginWindowGeometryVariant.toByteArray()); } connect(loginWindow, SIGNAL(geometryChanged()), this, SLOT(handleLoginWindowGeometryChanged())); if (startMinimized) loginWindow->hide(); #endif return loginWindow; } void QtUIFactory::handleLoginWindowGeometryChanged() { qtOnlySettings->getQSettings()->setValue("loginWindowGeometry", loginWindow->saveGeometry()); } EventWindow* QtUIFactory::createEventWindow() { return lastMainWindow->getEventWindow(); } ChatListWindow* QtUIFactory::createChatListWindow(UIEventStream*) { return lastMainWindow->getChatListWindow(); } MUCSearchWindow* QtUIFactory::createMUCSearchWindow() { return new QtMUCSearchWindow(); } ChatWindow* QtUIFactory::createChatWindow(const JID& contact, UIEventStream* eventStream) { QtChatWindow* window = dynamic_cast(chatWindowFactory->createChatWindow(contact, eventStream)); chatWindows.push_back(window); std::vector > deletions; foreach (QPointer existingWindow, chatWindows) { if (existingWindow.isNull()) { deletions.push_back(existingWindow); } else { connect(window, SIGNAL(fontResized(int)), existingWindow, SLOT(handleFontResized(int))); connect(existingWindow, SIGNAL(fontResized(int)), window, SLOT(handleFontResized(int))); } } foreach (QPointer deletedWindow, deletions) { chatWindows.erase(std::remove(chatWindows.begin(), chatWindows.end(), deletedWindow), chatWindows.end()); } connect(window, SIGNAL(fontResized(int)), this, SLOT(handleChatWindowFontResized(int))); window->handleFontResized(chatFontSize); return window; } void QtUIFactory::handleChatWindowFontResized(int size) { chatFontSize = size; settings->storeSetting(QtUISettingConstants::CHATWINDOW_FONT_SIZE, size); } UserSearchWindow* QtUIFactory::createUserSearchWindow(UserSearchWindow::Type type, UIEventStream* eventStream, const std::set& groups) { return new QtUserSearchWindow(eventStream, type, groups); }; JoinMUCWindow* QtUIFactory::createJoinMUCWindow(UIEventStream* uiEventStream) { return new QtJoinMUCWindow(uiEventStream); } ProfileWindow* QtUIFactory::createProfileWindow() { return new QtProfileWindow(); } ContactEditWindow* QtUIFactory::createContactEditWindow() { return new QtContactEditWindow(); } WhiteboardWindow* QtUIFactory::createWhiteboardWindow(boost::shared_ptr whiteboardSession) { return new QtWhiteboardWindow(whiteboardSession); } void QtUIFactory::createAdHocCommandWindow(boost::shared_ptr command) { new QtAdHocCommandWindow(command); } } swift-im-2.0+dev6/Swift/QtUI/QtClickableLabel.cpp0000644000175000017500000000055112227051774021424 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "QtClickableLabel.h" namespace Swift { QtClickableLabel::QtClickableLabel(QWidget* parent) : QLabel(parent) { } void QtClickableLabel::mousePressEvent(QMouseEvent*) { emit clicked(); } } swift-im-2.0+dev6/Swift/QtUI/QtProfileWindow.cpp0000644000175000017500000000647412227051774021415 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "QtProfileWindow.h" #include #include #include #include #include #include #include #include #include "QtSwiftUtil.h" #include "QtAvatarWidget.h" namespace Swift { QtProfileWindow::QtProfileWindow() { setWindowTitle(tr("Edit Profile")); QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum); sizePolicy.setHorizontalStretch(0); sizePolicy.setVerticalStretch(0); sizePolicy.setHeightForWidth(this->sizePolicy().hasHeightForWidth()); setSizePolicy(sizePolicy); QVBoxLayout* layout = new QVBoxLayout(this); layout->setContentsMargins(10, 10, 10, 10); QHBoxLayout* topLayout = new QHBoxLayout(); avatar = new QtAvatarWidget(this); topLayout->addWidget(avatar); QVBoxLayout* fieldsLayout = new QVBoxLayout(); QHBoxLayout* horizontalLayout_2 = new QHBoxLayout(); nicknameLabel = new QLabel(tr("Nickname:"), this); horizontalLayout_2->addWidget(nicknameLabel); nickname = new QLineEdit(this); horizontalLayout_2->addWidget(nickname); fieldsLayout->addLayout(horizontalLayout_2); errorLabel = new QLabel(this); errorLabel->setAlignment(Qt::AlignHCenter); fieldsLayout->addWidget(errorLabel); fieldsLayout->addItem(new QSpacerItem(198, 17, QSizePolicy::Minimum, QSizePolicy::Expanding)); topLayout->addLayout(fieldsLayout); layout->addLayout(topLayout); QHBoxLayout* horizontalLayout = new QHBoxLayout(); horizontalLayout->setContentsMargins(0, 0, 0, 0); horizontalLayout->addItem(new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum)); throbberLabel = new QLabel(this); throbberLabel->setMovie(new QMovie(":/icons/throbber.gif", QByteArray(), this)); horizontalLayout->addWidget(throbberLabel); saveButton = new QPushButton(tr("Save"), this); saveButton->setDefault( true ); connect(saveButton, SIGNAL(clicked()), SLOT(handleSave())); horizontalLayout->addWidget(saveButton); fieldsLayout->addLayout(horizontalLayout); resize(360, 120); } void QtProfileWindow::setVCard(Swift::VCard::ref vcard) { this->vcard = vcard; nickname->setText(P2QSTRING(vcard->getNickname())); avatar->setAvatar(vcard->getPhoto(), vcard->getPhotoType()); } void QtProfileWindow::setEnabled(bool b) { nickname->setEnabled(b); nicknameLabel->setEnabled(b); avatar->setEnabled(b); saveButton->setEnabled(b); } void QtProfileWindow::setProcessing(bool processing) { if (processing) { throbberLabel->movie()->start(); throbberLabel->show(); } else { throbberLabel->hide(); throbberLabel->movie()->stop(); } } void QtProfileWindow::show() { QWidget::show(); QWidget::activateWindow(); } void QtProfileWindow::hideEvent(QHideEvent* event) { QWidget::hideEvent(event); } void QtProfileWindow::hide() { QWidget::hide(); } void QtProfileWindow::handleSave() { assert(vcard); vcard->setNickname(Q2PSTRING(nickname->text())); vcard->setPhoto(avatar->getAvatarData()); vcard->setPhotoType(avatar->getAvatarType()); onVCardChangeRequest(vcard); } void QtProfileWindow::setError(const std::string& error) { if (!error.empty()) { errorLabel->setText("" + P2QSTRING(error) + ""); } else { errorLabel->setText(""); } } } swift-im-2.0+dev6/Swift/QtUI/QtURIHandler.h0000644000175000017500000000065112227051774020216 0ustar kismithkismith/* * 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 #include class QUrl; namespace Swift { class QtURIHandler : public QObject, public URIHandler { public: QtURIHandler(); private: bool eventFilter(QObject* obj, QEvent* event); }; } swift-im-2.0+dev6/Swift/QtUI/QtAffiliationEditor.cpp0000644000175000017500000000506412227051774022213 0ustar kismithkismith/* * Copyright (c) 2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "QtAffiliationEditor.h" #include #include #include "QtSwiftUtil.h" namespace Swift { QtAffiliationEditor::QtAffiliationEditor(QWidget* parent) : QDialog(parent){ ui_.setupUi(this); setAttribute(Qt::WA_DeleteOnClose); connect(ui_.affiliation, SIGNAL(currentIndexChanged(int)), this, SLOT(handleCurrentIndexChanged(int))); connect(ui_.addJID, SIGNAL(clicked()), this, SLOT(handleAddClicked())); connect(ui_.removeJID, SIGNAL(clicked()), this, SLOT(handleRemoveClicked())); } QtAffiliationEditor::~QtAffiliationEditor() { } void QtAffiliationEditor::setAffiliations(MUCOccupant::Affiliation affiliation, const std::vector& jids) { affiliations_[affiliation] = jids; if (affiliationFromIndex(ui_.affiliation->currentIndex()) == affiliation) { handleCurrentIndexChanged(ui_.affiliation->currentIndex()); } } const std::vector >& QtAffiliationEditor::getChanges() const { return changes_; } void QtAffiliationEditor::handleCurrentIndexChanged(int index) { ui_.list->clear(); foreach (const JID& jid, affiliations_[affiliationFromIndex(index)]) { ui_.list->addItem(P2QSTRING(jid.toString())); } } void QtAffiliationEditor::handleAddClicked() { bool ok = false; JID jid = JID(Q2PSTRING(QInputDialog::getText(this, tr("Add User"), tr("Added User's Address:"), QLineEdit::Normal, "", &ok))); if (ok && jid.isValid()) { //FIXME: validation MUCOccupant::Affiliation affiliation = affiliationFromIndex(ui_.affiliation->currentIndex()); changes_.push_back(ChangePair(affiliation, jid)); ui_.list->addItem(P2QSTRING(jid.toString())); affiliations_[affiliation].push_back(jid); } } void QtAffiliationEditor::handleRemoveClicked() { QListWidgetItem* item = ui_.list->currentItem(); if (item) { JID jid(Q2PSTRING(item->text())); changes_.push_back(ChangePair(MUCOccupant::NoAffiliation, jid)); std::vector& jids = affiliations_[affiliationFromIndex(ui_.affiliation->currentIndex())]; jids.erase(std::remove(jids.begin(), jids.end(), jid), jids.end()); handleCurrentIndexChanged(ui_.affiliation->currentIndex()); } } MUCOccupant::Affiliation QtAffiliationEditor::affiliationFromIndex(int affiliation) { switch (affiliation) { case 0: return MUCOccupant::Owner; case 1: return MUCOccupant::Admin; case 2: return MUCOccupant::Member; case 3: return MUCOccupant::Outcast; default: return MUCOccupant::Outcast; } } }swift-im-2.0+dev6/Swift/QtUI/QtLoginWindow.cpp0000644000175000017500000004333612227051774021063 0ustar kismithkismith/* * Copyright (c) 2010-2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "QtLoginWindow.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_SCHANNEL #include "CAPICertificateSelector.h" #include #endif #include namespace Swift{ QtLoginWindow::QtLoginWindow(UIEventStream* uiEventStream, SettingsProvider* settings, TimerFactory* timerFactory) : QMainWindow(), settings_(settings), timerFactory_(timerFactory) { uiEventStream_ = uiEventStream; setWindowTitle("Swift"); #ifndef Q_WS_MAC setWindowIcon(QIcon(":/logo-icon-16.png")); #endif QtUtilities::setX11Resource(this, "Main"); resize(200, 500); setContentsMargins(0,0,0,0); QWidget *centralWidget = new QWidget(this); setCentralWidget(centralWidget); QBoxLayout *topLayout = new QBoxLayout(QBoxLayout::TopToBottom, centralWidget); stack_ = new QStackedWidget(centralWidget); topLayout->addWidget(stack_); topLayout->setMargin(0); loginWidgetWrapper_ = new QWidget(this); loginWidgetWrapper_->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); QBoxLayout *layout = new QBoxLayout(QBoxLayout::TopToBottom, loginWidgetWrapper_); layout->addStretch(2); QLabel* logo = new QLabel(this); logo->setPixmap(QPixmap(":/logo-shaded-text.256.png")); logo->setScaledContents(true); logo->setFixedSize(192,192); QWidget *logoWidget = new QWidget(this); QHBoxLayout *logoLayout = new QHBoxLayout(); logoLayout->setMargin(0); logoLayout->addStretch(0); logoLayout->addWidget(logo); logoLayout->addStretch(0); logoWidget->setLayout(logoLayout); layout->addWidget(logoWidget); layout->addStretch(2); QLabel* jidLabel = new QLabel(this); jidLabel->setText("" + tr("User address:") + ""); layout->addWidget(jidLabel); username_ = new QComboBox(this); username_->setEditable(true); username_->setWhatsThis(tr("User address - looks like someuser@someserver.com")); username_->setToolTip(tr("User address - looks like someuser@someserver.com")); username_->view()->installEventFilter(this); layout->addWidget(username_); QLabel* jidHintLabel = new QLabel(this); jidHintLabel->setText("" + tr("Example: alice@wonderland.lit") + ""); jidHintLabel->setAlignment(Qt::AlignRight); layout->addWidget(jidHintLabel); QLabel* passwordLabel = new QLabel(); passwordLabel->setText("" + tr("Password:") + ""); layout->addWidget(passwordLabel); QWidget* w = new QWidget(this); w->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); layout->addWidget(w); QHBoxLayout* credentialsLayout = new QHBoxLayout(w); credentialsLayout->setMargin(0); credentialsLayout->setSpacing(3); password_ = new QLineEdit(this); password_->setEchoMode(QLineEdit::Password); connect(password_, SIGNAL(returnPressed()), this, SLOT(loginClicked())); connect(username_->lineEdit(), SIGNAL(returnPressed()), password_, SLOT(setFocus())); connect(username_, SIGNAL(editTextChanged(const QString&)), this, SLOT(handleUsernameTextChanged())); credentialsLayout->addWidget(password_); certificateButton_ = new QToolButton(this); certificateButton_->setCheckable(true); certificateButton_->setIcon(QIcon(":/icons/certificate.png")); certificateButton_->setToolTip(tr("Click if you have a personal certificate used for login to the service.")); certificateButton_->setWhatsThis(tr("Click if you have a personal certificate used for login to the service.")); credentialsLayout->addWidget(certificateButton_); connect(certificateButton_, SIGNAL(clicked(bool)), SLOT(handleCertficateChecked(bool))); loginButton_ = new QPushButton(this); loginButton_->setText(tr("Connect")); loginButton_->setAutoDefault(true); loginButton_->setDefault(true); layout->addWidget(loginButton_); QLabel* connectionOptionsLabel = new QLabel(this); connectionOptionsLabel->setText("" + QObject::tr("Connection Options") + ""); connectionOptionsLabel->setTextFormat(Qt::RichText); connectionOptionsLabel->setAlignment(Qt::AlignRight|Qt::AlignVCenter); layout->addWidget(connectionOptionsLabel); connect(connectionOptionsLabel, SIGNAL(linkActivated(const QString&)), SLOT(handleOpenConnectionOptions())); message_ = new QLabel(this); message_->setTextFormat(Qt::RichText); message_->setWordWrap(true); layout->addWidget(message_); layout->addStretch(2); remember_ = new QCheckBox(tr("Remember Password?"), this); layout->addWidget(remember_); loginAutomatically_ = new QCheckBox(tr("Login Automatically?"), this); layout->addWidget(loginAutomatically_); connect(loginButton_, SIGNAL(clicked()), SLOT(loginClicked())); stack_->addWidget(loginWidgetWrapper_); #ifdef SWIFTEN_PLATFORM_MACOSX menuBar_ = new QMenuBar(NULL); #else menuBar_ = menuBar(); #endif QApplication::setQuitOnLastWindowClosed(false); swiftMenu_ = new QMenu(tr("&Swift"), this); #ifdef SWIFTEN_PLATFORM_MACOSX generalMenu_ = new QMenu(tr("&General"), this); #else generalMenu_ = swiftMenu_; #endif #ifdef SWIFTEN_PLATFORM_MACOSX QAction* aboutAction = new QAction(QString("&About %1").arg("Swift"), this); #else QAction* aboutAction = new QAction(QString(tr("&About %1")).arg("Swift"), this); #endif connect(aboutAction, SIGNAL(triggered()), SLOT(handleAbout())); swiftMenu_->addAction(aboutAction); xmlConsoleAction_ = new QAction(tr("&Show Debug Console"), this); connect(xmlConsoleAction_, SIGNAL(triggered()), SLOT(handleShowXMLConsole())); generalMenu_->addAction(xmlConsoleAction_); #ifdef SWIFT_EXPERIMENTAL_FT fileTransferOverviewAction_ = new QAction(tr("Show &File Transfer Overview"), this); connect(fileTransferOverviewAction_, SIGNAL(triggered()), SLOT(handleShowFileTransferOverview())); generalMenu_->addAction(fileTransferOverviewAction_); #endif toggleSoundsAction_ = new QAction(tr("&Play Sounds"), this); toggleSoundsAction_->setCheckable(true); toggleSoundsAction_->setChecked(settings_->getSetting(SettingConstants::PLAY_SOUNDS)); connect(toggleSoundsAction_, SIGNAL(toggled(bool)), SLOT(handleToggleSounds(bool))); generalMenu_->addAction(toggleSoundsAction_); toggleNotificationsAction_ = new QAction(tr("Display Pop-up &Notifications"), this); toggleNotificationsAction_->setCheckable(true); toggleNotificationsAction_->setChecked(settings_->getSetting(SettingConstants::SHOW_NOTIFICATIONS)); connect(toggleNotificationsAction_, SIGNAL(toggled(bool)), SLOT(handleToggleNotifications(bool))); #ifndef SWIFTEN_PLATFORM_MACOSX swiftMenu_->addSeparator(); #endif #ifdef SWIFTEN_PLATFORM_MACOSX QAction* quitAction = new QAction("&Quit", this); #else QAction* quitAction = new QAction(tr("&Quit"), this); #endif connect(quitAction, SIGNAL(triggered()), SLOT(handleQuit())); swiftMenu_->addAction(quitAction); setInitialMenus(); settings_->onSettingChanged.connect(boost::bind(&QtLoginWindow::handleSettingChanged, this, _1)); bool eagle = settings_->getSetting(SettingConstants::FORGET_PASSWORDS); remember_->setEnabled(!eagle); loginAutomatically_->setEnabled(!eagle); xmlConsoleAction_->setEnabled(!eagle); if (eagle) { remember_->setChecked(false); loginAutomatically_->setChecked(false); } #ifdef SWIFTEN_PLATFORM_MACOSX // Temporary workaround for case 501. Could be that this code is still // needed when Qt provides a proper fix qApp->installEventFilter(this); #endif this->show(); } void QtLoginWindow::setShowNotificationToggle(bool toggle) { if (toggle) { QList< QAction* > generalMenuActions = generalMenu_->actions(); generalMenu_->insertAction(generalMenuActions.at(generalMenuActions.count()-2), toggleNotificationsAction_); } else { generalMenu_->removeAction(toggleNotificationsAction_); } } bool QtLoginWindow::eventFilter(QObject *obj, QEvent *event) { if (obj == username_->view() && event->type() == QEvent::KeyPress) { QKeyEvent *keyEvent = static_cast(event); if (keyEvent->key() == Qt::Key_Delete || keyEvent->key() == Qt::Key_Backspace) { QString jid(username_->view()->currentIndex().data().toString()); int result = QMessageBox::question(this, tr("Remove profile"), tr("Remove the profile '%1'?").arg(jid), QMessageBox::Yes | QMessageBox::No); if (result == QMessageBox::Yes) { onPurgeSavedLoginRequest(Q2PSTRING(jid)); } return true; } } #ifdef SWIFTEN_PLATFORM_MACOSX // Dock clicked // Temporary workaround for case 501. Could be that this code is still // needed when Qt provides a proper fix if (obj == qApp && event->type() == QEvent::ApplicationActivate && !isVisible()) { bringToFront(); } #endif return QObject::eventFilter(obj, event); } void QtLoginWindow::handleSettingChanged(const std::string& settingPath) { if (settingPath == SettingConstants::PLAY_SOUNDS.getKey()) { toggleSoundsAction_->setChecked(settings_->getSetting(SettingConstants::PLAY_SOUNDS)); } if (settingPath == SettingConstants::SHOW_NOTIFICATIONS.getKey()) { toggleNotificationsAction_->setChecked(settings_->getSetting(SettingConstants::SHOW_NOTIFICATIONS)); } } void QtLoginWindow::selectUser(const std::string& username) { for (int i = 0; i < usernames_.count(); i++) { if (P2QSTRING(username) == usernames_[i]) { username_->setCurrentIndex(i); password_->setFocus(); break; } } } void QtLoginWindow::removeAvailableAccount(const std::string& jid) { QString username = P2QSTRING(jid); int index = -1; for (int i = 0; i < usernames_.count(); i++) { if (username == usernames_[i]) { index = i; } } if (index >= 0) { usernames_.removeAt(index); passwords_.removeAt(index); certificateFiles_.removeAt(index); username_->removeItem(index); } } void QtLoginWindow::addAvailableAccount(const std::string& defaultJID, const std::string& defaultPassword, const std::string& defaultCertificate, const ClientOptions& options) { QString username = P2QSTRING(defaultJID); int index = -1; for (int i = 0; i < usernames_.count(); i++) { if (username == usernames_[i]) { index = i; } } if (index == -1) { usernames_.append(username); passwords_.append(P2QSTRING(defaultPassword)); certificateFiles_.append(P2QSTRING(defaultCertificate)); options_.push_back(options); username_->addItem(username); } else { usernames_[index] = username; passwords_[index] = P2QSTRING(defaultPassword); certificateFiles_[index] = P2QSTRING(defaultCertificate); options_[index] = options; } } void QtLoginWindow::handleUsernameTextChanged() { QString username = username_->currentText(); for (int i = 0; i < usernames_.count(); i++) { if (username_->currentText() == usernames_[i]) { certificateFile_ = certificateFiles_[i]; password_->setText(passwords_[i]); remember_->setChecked(password_->text() != ""); currentOptions_ = options_[i]; } } certificateButton_->setChecked(!certificateFile_.isEmpty()); } void QtLoginWindow::loggedOut() { stack_->removeWidget(stack_->currentWidget()); stack_->addWidget(loginWidgetWrapper_); stack_->setCurrentWidget(loginWidgetWrapper_); setInitialMenus(); setIsLoggingIn(false); } void QtLoginWindow::setIsLoggingIn(bool loggingIn) { /* Change the for loop as well if you add to this.*/ QWidget* widgets[5] = {username_, password_, remember_, loginAutomatically_, certificateButton_}; loginButton_->setText(loggingIn ? tr("Cancel") : tr("Connect")); for (int i = 0; i < 5; i++) { widgets[i]->setEnabled(!loggingIn); } bool eagle = settings_->getSetting(SettingConstants::FORGET_PASSWORDS); remember_->setEnabled(!eagle); loginAutomatically_->setEnabled(!eagle); } void QtLoginWindow::loginClicked() { if (username_->isEnabled()) { std::string banner = settings_->getSetting(QtUISettingConstants::CLICKTHROUGH_BANNER); if (!banner.empty()) { QMessageBox msgBox; msgBox.setWindowTitle(tr("Confirm terms of use")); msgBox.setText(""); msgBox.setInformativeText(P2QSTRING(banner)); msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); msgBox.setDefaultButton(QMessageBox::No); if (msgBox.exec() != QMessageBox::Yes) { return; } } CertificateWithKey::ref certificate; std::string certificateString = Q2PSTRING(certificateFile_); #if defined(HAVE_SCHANNEL) if (isCAPIURI(certificateString)) { certificate = boost::make_shared(certificateString, timerFactory_); } else { certificate = boost::make_shared(certificateString, createSafeByteArray(Q2PSTRING(password_->text()))); } #else certificate = boost::make_shared(certificateString, createSafeByteArray(Q2PSTRING(password_->text()))); #endif onLoginRequest(Q2PSTRING(username_->currentText()), Q2PSTRING(password_->text()), certificateString, certificate, currentOptions_, remember_->isChecked(), loginAutomatically_->isChecked()); if (settings_->getSetting(SettingConstants::FORGET_PASSWORDS)) { /* Mustn't remember logins */ username_->clearEditText(); password_->setText(""); } } else { onCancelLoginRequest(); } } void QtLoginWindow::setLoginAutomatically(bool loginAutomatically) { loginAutomatically_->setChecked(loginAutomatically); } void QtLoginWindow::handleCertficateChecked(bool checked) { if (checked) { #ifdef HAVE_SCHANNEL certificateFile_ = P2QSTRING(selectCAPICertificate()); if (certificateFile_.isEmpty()) { certificateButton_->setChecked(false); } #else certificateFile_ = QFileDialog::getOpenFileName(this, tr("Select an authentication certificate"), QString(), tr("P12 files (*.cert *.p12 *.pfx);;All files (*.*)")); if (certificateFile_.isEmpty()) { certificateButton_->setChecked(false); } #endif } else { certificateFile_ = ""; } } void QtLoginWindow::handleAbout() { if (!aboutDialog_) { aboutDialog_ = new QtAboutWidget(); aboutDialog_->show(); } else { aboutDialog_->show(); aboutDialog_->raise(); aboutDialog_->activateWindow(); } } void QtLoginWindow::handleShowXMLConsole() { uiEventStream_->send(boost::make_shared()); } void QtLoginWindow::handleShowFileTransferOverview() { uiEventStream_->send(boost::make_shared()); } void QtLoginWindow::handleToggleSounds(bool enabled) { settings_->storeSetting(SettingConstants::PLAY_SOUNDS, enabled); } void QtLoginWindow::handleToggleNotifications(bool enabled) { settings_->storeSetting(SettingConstants::SHOW_NOTIFICATIONS, enabled); } void QtLoginWindow::handleQuit() { onQuitRequest(); } void QtLoginWindow::quit() { QApplication::quit(); } void QtLoginWindow::setInitialMenus() { menuBar_->clear(); menuBar_->addMenu(swiftMenu_); #ifdef SWIFTEN_PLATFORM_MACOSX menuBar_->addMenu(generalMenu_); #endif } void QtLoginWindow::morphInto(MainWindow *mainWindow) { setEnabled(false); QtMainWindow *qtMainWindow = dynamic_cast(mainWindow); assert(qtMainWindow); stack_->removeWidget(loginWidgetWrapper_); stack_->addWidget(qtMainWindow); stack_->setCurrentWidget(qtMainWindow); setEnabled(true); setInitialMenus(); foreach (QMenu* menu, qtMainWindow->getMenus()) { menuBar_->addMenu(menu); } setFocus(); } void QtLoginWindow::setMessage(const std::string& message) { if (!message.empty()) { message_->setText("
" + P2QSTRING(message) + "
"); } else { message_->setText(""); } } void QtLoginWindow::toggleBringToFront() { if (!isVisible()) { bringToFront(); } else { window()->hide(); } } void QtLoginWindow::bringToFront() { window()->showNormal(); window()->raise(); window()->activateWindow(); } void QtLoginWindow::hide() { window()->hide(); } QtLoginWindow::QtMenus QtLoginWindow::getMenus() const { return QtMenus(swiftMenu_, generalMenu_); } void QtLoginWindow::resizeEvent(QResizeEvent*) { emit geometryChanged(); } void QtLoginWindow::moveEvent(QMoveEvent*) { emit geometryChanged(); } bool QtLoginWindow::askUserToTrustCertificatePermanently(const std::string& message, const std::vector& certificates) { QMessageBox dialog(this); dialog.setText(tr("The certificate presented by the server is not valid.")); dialog.setInformativeText(P2QSTRING(message) + "\n\n" + tr("Would you like to permanently trust this certificate? This must only be done if you know it is correct.")); dialog.addButton(tr("Show Certificate"), QMessageBox::HelpRole); dialog.addButton(QMessageBox::Yes); dialog.addButton(QMessageBox::No); dialog.setDefaultButton(QMessageBox::No); while (true) { int result = dialog.exec(); if (result == QMessageBox::Yes || result == QMessageBox::No) { return result == QMessageBox::Yes; } // FIXME: This isn't very nice, because the dialog disappears every time. We actually need a real // dialog with a custom button. QtMainWindow::openCertificateDialog(certificates, &dialog); } } void QtLoginWindow::handleOpenConnectionOptions() { QtConnectionSettingsWindow connectionSettings(currentOptions_); if (connectionSettings.exec() == QDialog::Accepted) { currentOptions_ = connectionSettings.getOptions(); } } } swift-im-2.0+dev6/Swift/QtUI/ApplicationTest/0000755000175000017500000000000012227051774020704 5ustar kismithkismithswift-im-2.0+dev6/Swift/QtUI/ApplicationTest/main.cpp0000644000175000017500000000200112227051774022325 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include "../QtSwiftUtil.h" #include #include "SwifTools/Application/Platform/PlatformApplication.h" using namespace Swift; class MyWidget : public QWidget { Q_OBJECT public: MyWidget() : application_("MyApplication") { QVBoxLayout *layout = new QVBoxLayout(this); input_ = new QLineEdit(this); layout->addWidget(input_); connect(input_, SIGNAL(returnPressed()), SLOT(handleReturnPressed())); } private slots: void handleReturnPressed() { application_.getApplicationMessageDisplay()->setMessage(Q2PSTRING(input_->text())); } private: PlatformApplication application_; QLineEdit* input_; }; int main(int argc, char* argv[]) { QApplication app(argc, argv); MyWidget widget; widget.show(); return app.exec(); } #include "main.moc" swift-im-2.0+dev6/Swift/QtUI/ApplicationTest/ApplicationTest.pro0000644000175000017500000000036612227051774024536 0ustar kismithkismithTEMPLATE = app TARGET = DEPENDPATH += . INCLUDEPATH += . ../../.. ../../../Swiften/3rdParty/Boost mac { ICON = ../../../../resources/MacOSX/Swift.icns } HEADERS += \ ../QtSwiftUtil.h SOURCES += \ main.cpp LIBS += \ ../../../Swiften/Swift.a swift-im-2.0+dev6/Swift/QtUI/QtCachedImageScaler.h0000644000175000017500000000060512227051774021524 0ustar kismithkismith/* * 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 namespace Swift { class QtCachedImageScaler { public: QtCachedImageScaler(); boost::filesystem::path getScaledImage(const boost::filesystem::path& image, int size); }; } swift-im-2.0+dev6/Swift/QtUI/QtContactEditWidget.h0000644000175000017500000000240712227051774021627 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include #include #include class QLabel; class QLineEdit; class QCheckBox; class QHBoxLayout; class QRadioButton; namespace Swift { class QtContactEditWidget : public QWidget { Q_OBJECT public: QtContactEditWidget(const std::set& allGroups, QWidget* parent); void setName(const std::string&); std::string getName() const; void setSelectedGroups(const std::vector& groups); std::set getSelectedGroups() const; void setNameSuggestions(const std::vector& suggestions); void clear(); private: QString doubleAmpersand(const std::string& name) const; std::string singleAmpersand(const QString& name) const; private: typedef std::map CheckBoxMap; CheckBoxMap checkBoxes_; QHBoxLayout* nameLayout_; QHBoxLayout* suggestionsLayout_; QRadioButton* nameRadioButton_; QLineEdit* name_; QWidget* groups_; QCheckBox* newGroup_; QLineEdit* newGroupName_; QLabel* throbberLabel_; }; } swift-im-2.0+dev6/Swift/QtUI/EventViewer/0000755000175000017500000000000012227051774020044 5ustar kismithkismithswift-im-2.0+dev6/Swift/QtUI/EventViewer/EventView.pri0000644000175000017500000000052112227051774022472 0ustar kismithkismithSOURCES += $$PWD/EventDelegate.cpp \ $$PWD/EventModel.cpp \ $$PWD/EventView.cpp \ $$PWD/QtEventWindow.cpp \ $$PWD/QtEvent.cpp \ $$PWD/../QtSubscriptionRequestWindow.cpp HEADERS += $$PWD/EventDelegate.h \ $$PWD/EventModel.h \ $$PWD/EventView.h \ $$PWD/QtEventWindow.h \ $$PWD/QtEvent.h \ $$PWD/../QtSubscriptionRequestWindow.h swift-im-2.0+dev6/Swift/QtUI/EventViewer/EventDelegate.h0000644000175000017500000000171012227051774022730 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include "Swift/QtUI/Roster/DelegateCommons.h" #include "Swift/QtUI/EventViewer/TwoLineDelegate.h" namespace Swift { enum EventType {MessageEventType, SubscriptionEventType, ErrorEventType, MUCInviteEventType}; class EventDelegate : public QStyledItemDelegate { Q_OBJECT public: EventDelegate(); QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const; void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; private: EventType getEventType(boost::shared_ptr event) const; DelegateCommons common_; TwoLineDelegate messageDelegate_; TwoLineDelegate subscriptionDelegate_; TwoLineDelegate errorDelegate_; TwoLineDelegate mucInviteDelegate_; }; } swift-im-2.0+dev6/Swift/QtUI/EventViewer/EventModel.h0000644000175000017500000000174112227051774022262 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include "Swift/Controllers/XMPPEvents/StanzaEvent.h" #include "Swift/QtUI/EventViewer/QtEvent.h" namespace Swift { class EventModel : public QAbstractListModel { Q_OBJECT public: EventModel(); ~EventModel(); void addEvent(boost::shared_ptr event, bool active); void removeEvent(boost::shared_ptr event); QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; int rowCount(const QModelIndex& parent = QModelIndex()) const; QtEvent* getItem(int row) const; int getNewEventCount(); protected: QModelIndex index ( int row, int column = 0, const QModelIndex & parent = QModelIndex() ) const; private: QList activeEvents_; QList inactiveEvents_; }; } swift-im-2.0+dev6/Swift/QtUI/EventViewer/QtEvent.cpp0000644000175000017500000000655012227051774022144 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Swift/QtUI/EventViewer/QtEvent.h" #include #include "Swift/Controllers/XMPPEvents/MessageEvent.h" #include "Swift/Controllers/XMPPEvents/ErrorEvent.h" #include "Swift/Controllers/XMPPEvents/SubscriptionRequestEvent.h" #include "Swift/Controllers/XMPPEvents/MUCInviteEvent.h" #include "Swift/QtUI/QtSwiftUtil.h" namespace Swift { QtEvent::QtEvent(boost::shared_ptr event, bool active) : event_(event) { active_ = active; } QVariant QtEvent::data(int role) { switch (role) { case Qt::ToolTipRole: return QVariant(text()).toString() + "\n" + B2QDATE(event_->getTime()).toString(); case Qt::DisplayRole: return QVariant(text()); case Qt::TextColorRole: return active_ ? Qt::black : Qt::darkGray; case Qt::BackgroundColorRole: return active_ ? Qt::white : Qt::lightGray; case SenderRole: return QVariant(sender()); /*case StatusTextRole: return statusText_; case AvatarRole: return avatar_; case PresenceIconRole: return getPresenceIcon();*/ default: return QVariant(); } } QString QtEvent::sender() { boost::shared_ptr messageEvent = boost::dynamic_pointer_cast(event_); if (messageEvent) { return P2QSTRING(messageEvent->getStanza()->getFrom().toString()); } boost::shared_ptr subscriptionRequestEvent = boost::dynamic_pointer_cast(event_); if (subscriptionRequestEvent) { return P2QSTRING(subscriptionRequestEvent->getJID().toBare().toString()); } boost::shared_ptr errorEvent = boost::dynamic_pointer_cast(event_); if (errorEvent) { return P2QSTRING(errorEvent->getJID().toBare().toString()); } boost::shared_ptr mucInviteEvent = boost::dynamic_pointer_cast(event_); if (mucInviteEvent) { return P2QSTRING(mucInviteEvent->getInviter().toString()); } return ""; } QString QtEvent::text() { boost::shared_ptr messageEvent = boost::dynamic_pointer_cast(event_); if (messageEvent) { return P2QSTRING(messageEvent->getStanza()->getBody()); } boost::shared_ptr subscriptionRequestEvent = boost::dynamic_pointer_cast(event_); if (subscriptionRequestEvent) { std::string reason = subscriptionRequestEvent->getReason(); QString message; if (reason.empty()) { message = QString(QObject::tr("%1 would like to add you to their contact list.")).arg(subscriptionRequestEvent->getJID().toBare().toString().c_str()); } else { message = QString(QObject::tr("%1 would like to add you to their contact list, saying '%2'")).arg(subscriptionRequestEvent->getJID().toBare().toString().c_str()).arg(reason.c_str()); } return message; } boost::shared_ptr errorEvent = boost::dynamic_pointer_cast(event_); if (errorEvent) { return P2QSTRING(errorEvent->getText()); } boost::shared_ptr mucInviteEvent = boost::dynamic_pointer_cast(event_); if (mucInviteEvent) { QString message = QString(QObject::tr("%1 has invited you to enter the %2 room.")).arg(P2QSTRING(mucInviteEvent->getInviter().toBare().toString())).arg(P2QSTRING(mucInviteEvent->getRoomJID().toString())); return message; } return ""; } } swift-im-2.0+dev6/Swift/QtUI/EventViewer/QtEvent.h0000644000175000017500000000122012227051774021576 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include "Swift/Controllers/XMPPEvents/StanzaEvent.h" namespace Swift { class QtEvent { public: QtEvent(boost::shared_ptr event, bool active); QVariant data(int role); boost::shared_ptr getEvent() { return event_; }; enum EventRoles { SenderRole = Qt::UserRole }; private: QString text(); QString sender(); boost::shared_ptr event_; bool active_; }; } swift-im-2.0+dev6/Swift/QtUI/EventViewer/EventView.pro0000644000175000017500000000074512227051774022510 0ustar kismithkismithCONFIG -= app_bundle include(EventView.pri) SOURCES += main.cpp DEPENDPATH += ../. ../../.. ../../../3rdParty/Boost/src INCLUDEPATH += ../. ../../.. ../../../3rdParty/Boost/src DEFINES += BOOST_SIGNALS_NAMESPACE=bsignals BOOST_ALL_NO_LIB LIBS += ../../Controllers/libSwiftControllers.a LIBS += ../../../Swiften/libSwiften.a LIBS += ../../../3rdParty/Boost/libBoost.a LIBS += ../../../3rdParty/LibIDN/libIDN.a mac { DEFINES += SWIFT_PLATFORM_MACOSX } RESOURCES += ../Swift.qrc swift-im-2.0+dev6/Swift/QtUI/EventViewer/QtEventWindow.h0000644000175000017500000000220312227051774022770 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include "Swift/Controllers/UIInterfaces/EventWindow.h" #include "Swift/Controllers/UIEvents/UIEventStream.h" #include "Swift/QtUI/EventViewer/EventView.h" #include "Swift/QtUI/EventViewer/EventModel.h" #include "Swift/QtUI/EventViewer/EventDelegate.h" class QPushButton; namespace Swift { class QtEventWindow : public QWidget, public EventWindow { Q_OBJECT public: QtEventWindow(UIEventStream* eventStream); ~QtEventWindow(); void addEvent(boost::shared_ptr event, bool active); void removeEvent(boost::shared_ptr event); signals: void onNewEventCountUpdated(int count); private slots: void handleItemActivated(const QModelIndex& item); void handleItemClicked(const QModelIndex& item); void handleReadClicked(); private: EventModel* model_; EventDelegate* delegate_; UIEventStream* eventStream_; QTreeView* view_; QPushButton* readButton_; }; } swift-im-2.0+dev6/Swift/QtUI/EventViewer/EventDelegate.cpp0000644000175000017500000000547712227051774023301 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "EventDelegate.h" #include #include "Swift/Controllers/XMPPEvents/MessageEvent.h" #include "Swift/Controllers/XMPPEvents/ErrorEvent.h" #include "Swift/Controllers/XMPPEvents/SubscriptionRequestEvent.h" #include "Swift/Controllers/XMPPEvents/MUCInviteEvent.h" namespace Swift { EventDelegate::EventDelegate() : QStyledItemDelegate(), messageDelegate_(QtEvent::SenderRole, Qt::DisplayRole, false), subscriptionDelegate_(QtEvent::SenderRole, Qt::DisplayRole, true), errorDelegate_(QtEvent::SenderRole, Qt::DisplayRole, true), mucInviteDelegate_(QtEvent::SenderRole, Qt::DisplayRole, false) { } QSize EventDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index ) const { QtEvent* item = static_cast(index.internalPointer()); if (!item) { return QStyledItemDelegate::sizeHint(option, index); } switch (getEventType(item->getEvent())) { case MessageEventType: return messageDelegate_.sizeHint(option, item); case SubscriptionEventType: return subscriptionDelegate_.sizeHint(option, item); case ErrorEventType: return errorDelegate_.sizeHint(option, item); case MUCInviteEventType: return mucInviteDelegate_.sizeHint(option, item); default: return QStyledItemDelegate::sizeHint(option, index); } } void EventDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { QtEvent* item = static_cast(index.internalPointer()); if (!item) { QStyledItemDelegate::paint(painter, option, index); return; } switch (getEventType(item->getEvent())) { case MessageEventType: messageDelegate_.paint(painter, option, item);break; case SubscriptionEventType: subscriptionDelegate_.paint(painter, option, item);break; case ErrorEventType: errorDelegate_.paint(painter, option, item);break; case MUCInviteEventType: mucInviteDelegate_.paint(painter, option, item);break; default: QStyledItemDelegate::paint(painter, option, index); } } EventType EventDelegate::getEventType(boost::shared_ptr event) const { boost::shared_ptr messageEvent = boost::dynamic_pointer_cast(event); if (messageEvent) return MessageEventType; boost::shared_ptr subscriptionEvent = boost::dynamic_pointer_cast(event); if (subscriptionEvent) return SubscriptionEventType; boost::shared_ptr errorEvent = boost::dynamic_pointer_cast(event); if (errorEvent) return ErrorEventType; boost::shared_ptr mucInviteEvent = boost::dynamic_pointer_cast(event); if (mucInviteEvent) return MUCInviteEventType; //I don't know what this is. assert(false); return MessageEventType; } } swift-im-2.0+dev6/Swift/QtUI/EventViewer/main.cpp0000644000175000017500000000217612227051774021502 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include "EventView.h" #include "EventModel.h" #include "QtEventWindow.h" #include "Swiften/Events/MessageEvent.h" #include "Swiften/Events/ErrorEvent.h" #include "Swiften/JID/JID.h" int main(int argc, char *argv[]) { QApplication app(argc, argv); Swift::UIEventStream eventStream; Swift::QtEventWindow* viewer = new Swift::QtEventWindow(&eventStream); viewer->show(); boost::shared_ptr message1(new Swift::Message()); message1->setBody("Oooh, shiny"); boost::shared_ptr event1(new Swift::MessageEvent(message1)); viewer->addEvent(boost::dynamic_pointer_cast(event1), true); for (int i = 0; i < 100; i++) { viewer->addEvent(boost::dynamic_pointer_cast(event1), false); } viewer->addEvent(boost::dynamic_pointer_cast(boost::make_shared(Swift::JID("me@example.com"), "Something bad did happen to you.")), true); return app.exec(); } swift-im-2.0+dev6/Swift/QtUI/EventViewer/TwoLineDelegate.h0000644000175000017500000000125012227051774023227 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include "Swift/QtUI/Roster/DelegateCommons.h" #include "QtEvent.h" namespace Swift { class TwoLineDelegate { public: TwoLineDelegate(int firstRole, int secondRole, bool wrap); ~TwoLineDelegate(); QSize sizeHint(const QStyleOptionViewItem& option, QtEvent* event) const; void paint(QPainter* painter, const QStyleOptionViewItem& option, QtEvent* event) const; private: DelegateCommons common_; int firstRole_; int secondRole_; bool wrap_; }; } swift-im-2.0+dev6/Swift/QtUI/EventViewer/TwoLineDelegate.cpp0000644000175000017500000000367712227051774023601 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "TwoLineDelegate.h" #include #include #include namespace Swift { TwoLineDelegate::TwoLineDelegate(int firstRole, int secondRole, bool wrap) { firstRole_ = firstRole; secondRole_ = secondRole; wrap_ = wrap; } TwoLineDelegate::~TwoLineDelegate() { } QSize TwoLineDelegate::sizeHint(const QStyleOptionViewItem& /*option*/, QtEvent* /*event*/ ) const { QFontMetrics nameMetrics(common_.nameFont); QFontMetrics statusMetrics(common_.detailFont); int sizeByText = 2 * common_.verticalMargin + nameMetrics.height() + statusMetrics.height(); return QSize(150, sizeByText); } void TwoLineDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, QtEvent* event) const { painter->save(); QRect fullRegion(option.rect); if ( option.state & QStyle::State_Selected ) { painter->fillRect(fullRegion, option.palette.highlight()); painter->setPen(option.palette.highlightedText().color()); } else { QColor nameColor = event->data(Qt::TextColorRole).value(); painter->setPen(QPen(nameColor)); } QFontMetrics nameMetrics(common_.nameFont); painter->setFont(common_.nameFont); int extraFontWidth = nameMetrics.width("H"); int leftOffset = common_.horizontalMargin * 2 + extraFontWidth / 2; QRect textRegion(fullRegion.adjusted(leftOffset, 0, 0, 0)); int nameHeight = nameMetrics.height() + common_.verticalMargin; QRect nameRegion(textRegion.adjusted(0, common_.verticalMargin, 0, 0)); DelegateCommons::drawElidedText(painter, nameRegion, event->data(firstRole_).toString()); painter->setFont(common_.detailFont); painter->setPen(QPen(QColor(160,160,160))); QRect detailRegion(textRegion.adjusted(0, nameHeight, 0, 0)); DelegateCommons::drawElidedText(painter, detailRegion, event->data(secondRole_).toString()); painter->restore(); } } swift-im-2.0+dev6/Swift/QtUI/EventViewer/QtEventWindow.cpp0000644000175000017500000000765412227051774023342 0ustar kismithkismith /* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Swift/QtUI/EventViewer/QtEventWindow.h" #include #include #include #include #include "Swift/Controllers/XMPPEvents/MessageEvent.h" #include "Swift/Controllers/XMPPEvents/ErrorEvent.h" #include "Swift/QtUI/QtSubscriptionRequestWindow.h" #include "Swift/Controllers/XMPPEvents/SubscriptionRequestEvent.h" #include "Swift/Controllers/XMPPEvents/MUCInviteEvent.h" #include "Swift/Controllers/UIEvents/RequestChatUIEvent.h" #include "Swift/Controllers/UIEvents/JoinMUCUIEvent.h" #include "Swiften/Base/Platform.h" namespace Swift { QtEventWindow::QtEventWindow(UIEventStream* eventStream) : EventWindow(false) { QBoxLayout* layout = new QBoxLayout(QBoxLayout::TopToBottom, this); layout->setContentsMargins(0,0,0,0); layout->setSpacing(0); view_ = new QTreeView(this); layout->addWidget(view_); eventStream_ = eventStream; model_ = new EventModel(); view_->setModel(model_); delegate_ = new EventDelegate(); view_->setItemDelegate(delegate_); view_->setHeaderHidden(true); #ifdef SWIFT_PLATFORM_MACOSX view_->setAlternatingRowColors(true); #endif view_->setAnimated(true); view_->setIndentation(0); view_->setRootIsDecorated(true); readButton_ = new QPushButton(tr("Display Notice"), this); layout->addWidget(readButton_); readButton_->setEnabled(false); connect(readButton_, SIGNAL(clicked()), this, SLOT(handleReadClicked())); connect(view_, SIGNAL(clicked(const QModelIndex&)), this, SLOT(handleItemClicked(const QModelIndex&))); connect(view_, SIGNAL(activated(const QModelIndex&)), this, SLOT(handleItemActivated(const QModelIndex&))); } QtEventWindow::~QtEventWindow() { delete model_; delete delegate_; /* Not view_ because this is the parent */ } void QtEventWindow::handleItemClicked(const QModelIndex&) { readButton_->setEnabled(true); } void QtEventWindow::handleReadClicked() { QModelIndex index = view_->currentIndex(); if (!index.isValid()) { return; } handleItemActivated(index); } void QtEventWindow::handleItemActivated(const QModelIndex& item) { QtEvent* event = model_->getItem(item.row()); boost::shared_ptr messageEvent = boost::dynamic_pointer_cast(event->getEvent()); boost::shared_ptr subscriptionEvent = boost::dynamic_pointer_cast(event->getEvent()); boost::shared_ptr mucInviteEvent = boost::dynamic_pointer_cast(event->getEvent()); boost::shared_ptr errorEvent = boost::dynamic_pointer_cast(event->getEvent()); if (messageEvent) { if (messageEvent->getStanza()->getType() == Message::Groupchat) { eventStream_->send(boost::shared_ptr(new JoinMUCUIEvent(messageEvent->getStanza()->getFrom().toBare(), messageEvent->getStanza()->getTo().getResource()))); } else { eventStream_->send(boost::shared_ptr(new RequestChatUIEvent(messageEvent->getStanza()->getFrom()))); } } else if (subscriptionEvent) { QtSubscriptionRequestWindow* window = QtSubscriptionRequestWindow::getWindow(subscriptionEvent, this); window->show(); } else if (mucInviteEvent) { eventStream_->send(boost::shared_ptr(new RequestChatUIEvent(mucInviteEvent->getInviter()))); mucInviteEvent->conclude(); } else { if (errorEvent) { errorEvent->conclude(); } QMessageBox msgBox; msgBox.setText(model_->data(item, Qt::DisplayRole).toString()); msgBox.exec(); } } void QtEventWindow::addEvent(boost::shared_ptr event, bool active) { view_->clearSelection(); model_->addEvent(event, active); emit onNewEventCountUpdated(model_->getNewEventCount()); } void QtEventWindow::removeEvent(boost::shared_ptr event) { view_->clearSelection(); model_->removeEvent(event); emit onNewEventCountUpdated(model_->getNewEventCount()); } } swift-im-2.0+dev6/Swift/QtUI/EventViewer/EventModel.cpp0000644000175000017500000000467412227051774022625 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "EventModel.h" #include namespace Swift { EventModel::EventModel() { } EventModel::~EventModel() { foreach (QtEvent* event, activeEvents_) { delete event; } foreach (QtEvent* event, inactiveEvents_) { delete event; } } QtEvent* EventModel::getItem(int row) const { return row < activeEvents_.size() ? activeEvents_[row] : inactiveEvents_[row - activeEvents_.size()]; } int EventModel::getNewEventCount() { return activeEvents_.size(); } QVariant EventModel::data(const QModelIndex& index, int role) const { if (!index.isValid()) { return QVariant(); } QtEvent* item = getItem(index.row()); QVariant result = item ? item->data(role) : QVariant(); return result; } /* * We only reimplement this to get the pointers inside the indices. */ QModelIndex EventModel::index(int row, int column, const QModelIndex & parent) const { if (!hasIndex(row, column, parent) || parent.isValid()) { return QModelIndex(); } return row < rowCount() ? createIndex(row, column, getItem(row)) : QModelIndex(); } int EventModel::rowCount(const QModelIndex& parent) const { /* Invalid parent = root, valid parent = child, and we're a list not a tree.*/ int count = parent.isValid() ? 0 : activeEvents_.size() + inactiveEvents_.size(); return count; } void EventModel::addEvent(boost::shared_ptr event, bool active) { if (active) { activeEvents_.push_front(new QtEvent(event, active)); emit dataChanged(createIndex(0, 0), createIndex(1, 0)); } else { inactiveEvents_.push_front(new QtEvent(event, active)); emit dataChanged(createIndex(activeEvents_.size() -1, 0), createIndex(activeEvents_.size(), 0)); if (inactiveEvents_.size() > 50) { removeEvent(inactiveEvents_[20]->getEvent()); } } emit layoutChanged(); } void EventModel::removeEvent(boost::shared_ptr event) { for (int i = inactiveEvents_.size() - 1; i >= 0; i--) { if (event == inactiveEvents_[i]->getEvent()) { inactiveEvents_.removeAt(i); emit dataChanged(createIndex(activeEvents_.size() + i - 1, 0), createIndex(activeEvents_.size() + i - 1, 0)); return; } } for (int i = 0; i < activeEvents_.size(); i++) { if (event == activeEvents_[i]->getEvent()) { activeEvents_.removeAt(i); emit dataChanged(createIndex(i - 1, 0), createIndex(i - 1, 0)); return; } } } } swift-im-2.0+dev6/Swift/QtUI/EventViewer/EventView.h0000644000175000017500000000046312227051774022134 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include namespace Swift { class EventView : public QListView { Q_OBJECT public: EventView(QWidget* parent); }; } swift-im-2.0+dev6/Swift/QtUI/EventViewer/EventView.cpp0000644000175000017500000000044112227051774022463 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Swift/QtUI/EventViewer/EventView.h" namespace Swift { EventView::EventView(QWidget* parent) : QListView(parent) { } } swift-im-2.0+dev6/Swift/QtUI/QtMUCConfigurationWindow.cpp0000644000175000017500000000324612227051774023163 0ustar kismithkismith/* * Copyright (c) 2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include namespace Swift { QtMUCConfigurationWindow::QtMUCConfigurationWindow(Form::ref form) : closed_(false) { setAttribute(Qt::WA_DeleteOnClose); QBoxLayout* layout = new QBoxLayout(QBoxLayout::TopToBottom, this); layout->setContentsMargins(0,0,0,0); layout->setSpacing(2); //QLabel* label = new QLabel(this); //label->setText(tr("Room configuration")); //layout->addWidget(label); formWidget_ = NULL; formWidget_ = new QtFormWidget(form, this); layout->addWidget(formWidget_); QWidget* buttonsWidget = new QWidget(this); layout->addWidget(buttonsWidget); QBoxLayout* buttonsLayout = new QBoxLayout(QBoxLayout::LeftToRight, buttonsWidget); cancelButton_ = new QPushButton(tr("Cancel"), buttonsWidget); buttonsLayout->addWidget(cancelButton_); connect(cancelButton_, SIGNAL(clicked()), this, SLOT(handleCancelClicked())); okButton_ = new QPushButton(tr("OK"), buttonsWidget); buttonsLayout->addWidget(okButton_); connect(okButton_, SIGNAL(clicked()), this, SLOT(handleOKClicked())); show(); } QtMUCConfigurationWindow::~QtMUCConfigurationWindow() { } void QtMUCConfigurationWindow::closeEvent(QCloseEvent* /*event*/) { if (!closed_) { onFormCancelled(); } } void QtMUCConfigurationWindow::handleCancelClicked() { close(); } void QtMUCConfigurationWindow::handleOKClicked() { onFormComplete(formWidget_->getCompletedForm()); closed_ = true; close(); } } swift-im-2.0+dev6/Swift/QtUI/QtAboutWidget.h0000644000175000017500000000053412227051774020477 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include namespace Swift { class QtAboutWidget : public QDialog { Q_OBJECT public: QtAboutWidget(); private slots: void handleLicenseClicked(); }; } swift-im-2.0+dev6/Swift/QtUI/QtSoundPlayer.h0000644000175000017500000000114512227051774020525 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include "Swift/Controllers/SoundPlayer.h" #include namespace Swift { class ApplicationPathProvider; class QtSoundPlayer : public QObject, public SoundPlayer { Q_OBJECT public: QtSoundPlayer(ApplicationPathProvider* applicationPathProvider); void playSound(SoundEffect sound); private: void playSound(const std::string& soundResource); private: ApplicationPathProvider* applicationPathProvider; }; } swift-im-2.0+dev6/Swift/QtUI/QtAdHocCommandWindow.h0000644000175000017500000000247412227051774021733 0ustar kismithkismith/* * Copyright (c) 2010-2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include #include class QBoxLayout; namespace Swift { class QtFormWidget; class QtAdHocCommandWindow : public QWidget, public AdHocCommandWindow { Q_OBJECT public: QtAdHocCommandWindow(boost::shared_ptr command); virtual ~QtAdHocCommandWindow(); private: void handleNextStageReceived(Command::ref command); void handleError(ErrorPayload::ref error); void setForm(Form::ref); void setNoForm(bool andHide); void setAvailableActions(Command::ref commandResult); private slots: void handleCancelClicked(); void handlePrevClicked(); void handleNextClicked(); void handleCompleteClicked(); private: boost::shared_ptr command_; QtFormWidget* formWidget_; Form::ref form_; QLabel* label_; QPushButton* backButton_; QPushButton* nextButton_; QPushButton* completeButton_; QPushButton* cancelButton_; std::map actions_; QBoxLayout* layout_; }; } swift-im-2.0+dev6/Swift/QtUI/FreeDesktopNotifier.h0000644000175000017500000000127412227051774021671 0ustar kismithkismith/* * 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 "SwifTools/Notifier/Notifier.h" #include "QtCachedImageScaler.h" namespace Swift { class FreeDesktopNotifier : public Notifier { public: FreeDesktopNotifier(const std::string& name); virtual void showMessage(Type type, const std::string& subject, const std::string& description, const boost::filesystem::path& picture, boost::function callback); virtual void purgeCallbacks() { #warning FIXME implement. }; private: std::string applicationName; QtCachedImageScaler imageScaler; }; } swift-im-2.0+dev6/Swift/QtUI/QtChatTabs.h0000644000175000017500000000204512227051774017751 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include "QtTabbable.h" #include "QtTabWidget.h" #include #include class QTabWidget; namespace Swift { class QtChatTabs : public QWidget { Q_OBJECT public: QtChatTabs(); void addTab(QtTabbable* tab); void minimise(); QtTabbable* getCurrentTab(); signals: void geometryChanged(); protected slots: void closeEvent(QCloseEvent* event); void resizeEvent(QResizeEvent* event); void moveEvent(QMoveEvent* event); private slots: void handleTabClosing(); void handleTabTitleUpdated(); void handleTabTitleUpdated(QWidget* widget); void handleTabCloseRequested(int index); void handleWidgetShown(); void handleWantsToActivate(); void handleRequestedPreviousTab(); void handleRequestedNextTab(); void handleRequestedActiveTab(); void flash(); private: void checkForFirstShow(); QtTabWidget* tabs_; }; } swift-im-2.0+dev6/Swift/QtUI/QtHistoryWindow.ui0000644000175000017500000000545612227051774021310 0ustar kismithkismith QtHistoryWindow 0 0 608 522 History Search: 0 0 true Next false Previous Qt::Horizontal 5 0 0 0 85 0 swift-im-2.0+dev6/Swift/QtUI/QtContactEditWindow.h0000644000175000017500000000215512227051774021653 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include #include class QLabel; class QVBoxLayout; namespace Swift { class QtContactEditWidget; class QtContactEditWindow : public QWidget, public ContactEditWindow { Q_OBJECT public: QtContactEditWindow(); virtual ~QtContactEditWindow(); virtual void setNameSuggestions(const std::vector& nameSuggestions); virtual void setContact(const JID& jid, const std::string& name, const std::vector& groups, const std::set& allGroups); void setEnabled(bool); void show(); void hide(); static bool confirmContactDeletion(const JID& jid); private slots: void handleRemoveContact(); void handleUpdateContact(); private: JID jid_; QVBoxLayout* groupsLayout_; QLabel* jidLabel_; QtContactEditWidget* contactEditWidget_; }; } swift-im-2.0+dev6/Swift/QtUI/WindowsNotifier.h0000644000175000017500000000170612227051774021110 0ustar kismithkismith/* * 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 #include #include #include class QSystemTrayIcon; namespace Swift { class WindowsNotifier : public QObject, public Notifier { Q_OBJECT public: WindowsNotifier(const std::string& name, const boost::filesystem::path& icon, QSystemTrayIcon* tray); ~WindowsNotifier(); virtual void showMessage(Type type, const std::string& subject, const std::string& description, const boost::filesystem::path& picture, boost::function callback); virtual void purgeCallbacks(); private slots: void handleMessageClicked(); private: QSystemTrayIcon* tray; Win32NotifierWindow* notifierWindow; SnarlNotifier* snarlNotifier; boost::function lastCallback; }; } swift-im-2.0+dev6/Swift/QtUI/QtMainWindow.cpp0000644000175000017500000003234012227051774020670 0ustar kismithkismith/* * Copyright (c) 2010-2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "QtMainWindow.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(SWIFTEN_PLATFORM_MACOSX) #include #elif defined(SWIFTEN_PLATFORM_WINDOWS) #include #else #include #endif namespace Swift { QtMainWindow::QtMainWindow(SettingsProvider* settings, UIEventStream* uiEventStream, QtLoginWindow::QtMenus loginMenus, bool emoticonsExist) : QWidget(), MainWindow(false), loginMenus_(loginMenus) { uiEventStream_ = uiEventStream; settings_ = settings; setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); QBoxLayout *mainLayout = new QBoxLayout(QBoxLayout::TopToBottom, this); mainLayout->setContentsMargins(0,0,0,0); mainLayout->setSpacing(0); meView_ = new QtRosterHeader(settings, this); mainLayout->addWidget(meView_); connect(meView_, SIGNAL(onChangeStatusRequest(StatusShow::Type, const QString&)), this, SLOT(handleStatusChanged(StatusShow::Type, const QString&))); connect(meView_, SIGNAL(onEditProfileRequest()), this, SLOT(handleEditProfileRequest())); connect(meView_, SIGNAL(onShowCertificateInfo()), this, SLOT(handleShowCertificateInfo())); tabs_ = new QtTabWidget(this); #if QT_VERSION >= 0x040500 tabs_->setDocumentMode(true); #endif tabs_->setTabPosition(QTabWidget::South); mainLayout->addWidget(tabs_); contactsTabWidget_ = new QWidget(this); contactsTabWidget_->setContentsMargins(0, 0, 0, 0); QBoxLayout *contactTabLayout = new QBoxLayout(QBoxLayout::TopToBottom, contactsTabWidget_); contactsTabWidget_->setLayout(contactTabLayout); contactTabLayout->setSpacing(0); contactTabLayout->setContentsMargins(0, 0, 0, 0); treeWidget_ = new QtRosterWidget(uiEventStream_, settings_, this); contactTabLayout->addWidget(treeWidget_); tabs_->addTab(contactsTabWidget_, tr("&Contacts")); eventWindow_ = new QtEventWindow(uiEventStream_); connect(eventWindow_, SIGNAL(onNewEventCountUpdated(int)), this, SLOT(handleEventCountUpdated(int))); chatListWindow_ = new QtChatListWindow(uiEventStream_, settings_); connect(chatListWindow_, SIGNAL(onCountUpdated(int)), this, SLOT(handleChatCountUpdated(int))); tabs_->addTab(chatListWindow_, tr("C&hats")); tabs_->addTab(eventWindow_, tr("&Notices")); tabs_->setCurrentIndex(settings_->getSetting(QtUISettingConstants::CURRENT_ROSTER_TAB)); connect(tabs_, SIGNAL(currentChanged(int)), this, SLOT(handleTabChanged(int))); this->setLayout(mainLayout); QMenu* viewMenu = new QMenu(tr("&View"), this); menus_.push_back(viewMenu); showOfflineAction_ = new QAction(tr("&Show offline contacts"), this); showOfflineAction_->setCheckable(true); showOfflineAction_->setChecked(false); connect(showOfflineAction_, SIGNAL(toggled(bool)), SLOT(handleShowOfflineToggled(bool))); viewMenu->addAction(showOfflineAction_); handleShowOfflineToggled(settings_->getSetting(SettingConstants::SHOW_OFFLINE)); if (emoticonsExist) { showEmoticonsAction_ = new QAction(tr("&Show Emoticons"), this); showEmoticonsAction_->setCheckable(true); showEmoticonsAction_->setChecked(false); connect(showEmoticonsAction_, SIGNAL(toggled(bool)), SLOT(handleShowEmoticonsToggled(bool))); viewMenu->addAction(showEmoticonsAction_); handleShowEmoticonsToggled(settings_->getSetting(QtUISettingConstants::SHOW_EMOTICONS)); } //QAction* compactRosterAction_ = new QAction(tr("&Compact Roster"), this); //compactRosterAction_->setCheckable(true); //compactRosterAction_->setChecked(false); //connect(compactRosterAction_, SIGNAL(toggled(bool)), uiPreferences_, SLOT(setCompactRosters(bool))); //viewMenu->addAction(compactRosterAction_); QMenu* actionsMenu = new QMenu(tr("&Actions"), this); menus_.push_back(actionsMenu); QAction* editProfileAction = new QAction(tr("Edit &Profile…"), this); connect(editProfileAction, SIGNAL(triggered()), SLOT(handleEditProfileAction())); actionsMenu->addAction(editProfileAction); QAction* joinMUCAction = new QAction(tr("Enter &Room…"), this); connect(joinMUCAction, SIGNAL(triggered()), SLOT(handleJoinMUCAction())); actionsMenu->addAction(joinMUCAction); #ifdef SWIFT_EXPERIMENTAL_HISTORY QAction* viewLogsAction = new QAction(tr("&View History…"), this); connect(viewLogsAction, SIGNAL(triggered()), SLOT(handleViewLogsAction())); actionsMenu->addAction(viewLogsAction); #endif addUserAction_ = new QAction(tr("&Add Contact…"), this); connect(addUserAction_, SIGNAL(triggered(bool)), this, SLOT(handleAddUserActionTriggered(bool))); actionsMenu->addAction(addUserAction_); editUserAction_ = new QAction(tr("&Edit Selected Contact…"), this); connect(editUserAction_, SIGNAL(triggered(bool)), treeWidget_, SLOT(handleEditUserActionTriggered(bool))); actionsMenu->addAction(editUserAction_); editUserAction_->setEnabled(false); chatUserAction_ = new QAction(tr("Start &Chat…"), this); connect(chatUserAction_, SIGNAL(triggered(bool)), this, SLOT(handleChatUserActionTriggered(bool))); actionsMenu->addAction(chatUserAction_); serverAdHocMenu_ = new QMenu(tr("Run Server Command"), this); actionsMenu->addMenu(serverAdHocMenu_); actionsMenu->addSeparator(); QAction* signOutAction = new QAction(tr("&Sign Out"), this); connect(signOutAction, SIGNAL(triggered()), SLOT(handleSignOutAction())); actionsMenu->addAction(signOutAction); toggleRequestDeliveryReceipts_ = new QAction(tr("&Request Delivery Receipts"), this); toggleRequestDeliveryReceipts_->setCheckable(true); toggleRequestDeliveryReceipts_->setChecked(settings_->getSetting(SettingConstants::REQUEST_DELIVERYRECEIPTS)); connect(toggleRequestDeliveryReceipts_, SIGNAL(toggled(bool)), SLOT(handleToggleRequestDeliveryReceipts(bool))); QList< QAction* > generalMenuActions = loginMenus_.generalMenu->actions(); loginMenus_.generalMenu->insertAction(generalMenuActions.at(generalMenuActions.count()-2),toggleRequestDeliveryReceipts_); treeWidget_->onSomethingSelectedChanged.connect(boost::bind(&QAction::setEnabled, editUserAction_, _1)); setAvailableAdHocCommands(std::vector()); QAction* adHocAction = new QAction(tr("Collecting commands..."), this); adHocAction->setEnabled(false); serverAdHocMenu_->addAction(adHocAction); serverAdHocCommandActions_.append(adHocAction); settings_->onSettingChanged.connect(boost::bind(&QtMainWindow::handleSettingChanged, this, _1)); } QtMainWindow::~QtMainWindow() { settings_->onSettingChanged.disconnect(boost::bind(&QtMainWindow::handleSettingChanged, this, _1)); } void QtMainWindow::handleTabChanged(int index) { settings_->storeSetting(QtUISettingConstants::CURRENT_ROSTER_TAB, index); } void QtMainWindow::handleToggleRequestDeliveryReceipts(bool enabled) { settings_->storeSetting(SettingConstants::REQUEST_DELIVERYRECEIPTS, enabled); } void QtMainWindow::handleShowCertificateInfo() { onShowCertificateRequest(); } QtEventWindow* QtMainWindow::getEventWindow() { return eventWindow_; } QtChatListWindow* QtMainWindow::getChatListWindow() { return chatListWindow_; } void QtMainWindow::setRosterModel(Roster* roster) { treeWidget_->setRosterModel(roster); } void QtMainWindow::handleEditProfileRequest() { uiEventStream_->send(boost::make_shared()); } void QtMainWindow::handleEventCountUpdated(int count) { QColor eventTabColor = (count == 0) ? QColor() : QColor(255, 0, 0); // invalid resets to default int eventIndex = 2; tabs_->tabBar()->setTabTextColor(eventIndex, eventTabColor); QString text = tr("&Notices"); if (count > 0) { text += QString(" (%1)").arg(count); } tabs_->setTabText(eventIndex, text); } void QtMainWindow::handleChatCountUpdated(int count) { QColor chatTabColor = (count == 0) ? QColor() : QColor(255, 0, 0); // invalid resets to default int chatIndex = 1; tabs_->tabBar()->setTabTextColor(chatIndex, chatTabColor); QString text = tr("&Chats"); if (count > 0) { text += QString(" (%1)").arg(count); } tabs_->setTabText(chatIndex, text); } void QtMainWindow::handleAddUserActionTriggered(bool /*checked*/) { boost::shared_ptr event(new RequestAddUserDialogUIEvent()); uiEventStream_->send(event); } void QtMainWindow::handleChatUserActionTriggered(bool /*checked*/) { boost::shared_ptr event(new RequestChatWithUserDialogUIEvent()); uiEventStream_->send(event); } void QtMainWindow::handleSignOutAction() { loginMenus_.generalMenu->removeAction(toggleRequestDeliveryReceipts_); onSignOutRequest(); } void QtMainWindow::handleEditProfileAction() { uiEventStream_->send(boost::make_shared()); } void QtMainWindow::handleJoinMUCAction() { uiEventStream_->send(boost::make_shared()); } void QtMainWindow::handleViewLogsAction() { uiEventStream_->send(boost::make_shared()); } void QtMainWindow::handleStatusChanged(StatusShow::Type showType, const QString &statusMessage) { onChangeStatusRequest(showType, Q2PSTRING(statusMessage)); } void QtMainWindow::handleSettingChanged(const std::string& settingPath) { if (settingPath == SettingConstants::SHOW_OFFLINE.getKey()) { handleShowOfflineToggled(settings_->getSetting(SettingConstants::SHOW_OFFLINE)); } if (settingPath == QtUISettingConstants::SHOW_EMOTICONS.getKey()) { handleShowEmoticonsToggled(settings_->getSetting(QtUISettingConstants::SHOW_EMOTICONS)); } if (settingPath == SettingConstants::REQUEST_DELIVERYRECEIPTS.getKey()) { toggleRequestDeliveryReceipts_->setChecked(settings_->getSetting(SettingConstants::REQUEST_DELIVERYRECEIPTS)); } } void QtMainWindow::handleShowOfflineToggled(bool state) { settings_->storeSetting(SettingConstants::SHOW_OFFLINE, state); showOfflineAction_->setChecked(settings_->getSetting(SettingConstants::SHOW_OFFLINE)); } void QtMainWindow::handleShowEmoticonsToggled(bool state) { settings_->storeSetting(QtUISettingConstants::SHOW_EMOTICONS, state); showEmoticonsAction_->setChecked(settings_->getSetting(QtUISettingConstants::SHOW_EMOTICONS)); } void QtMainWindow::setMyNick(const std::string& nick) { meView_->setNick(P2QSTRING(nick)); } void QtMainWindow::setMyJID(const JID& jid) { meView_->setJID(P2QSTRING(jid.toBare().toString())); } void QtMainWindow::setMyAvatarPath(const std::string& path) { meView_->setAvatar(P2QSTRING(path)); } void QtMainWindow::setMyStatusText(const std::string& status) { meView_->setStatusText(P2QSTRING(status)); } void QtMainWindow::setMyStatusType(StatusShow::Type type) { meView_->setStatusType(type); } void QtMainWindow::setConnecting() { meView_->setConnecting(); } void QtMainWindow::setStreamEncryptionStatus(bool tlsInPlaceAndValid) { meView_->setStreamEncryptionStatus(tlsInPlaceAndValid); } void QtMainWindow::openCertificateDialog(const std::vector& chain) { openCertificateDialog(chain, this); } void QtMainWindow::openCertificateDialog(const std::vector& chain, QWidget* parent) { #if defined(SWIFTEN_PLATFORM_MACOSX) CocoaUIHelpers::displayCertificateChainAsSheet(parent, chain); #elif defined(SWIFTEN_PLATFORM_WINDOWS) WinUIHelpers::displayCertificateChainAsSheet(parent, chain); #else QtCertificateViewerDialog::displayCertificateChainAsSheet(parent, chain); #endif } void QtMainWindow::handleAdHocActionTriggered(bool /*checked*/) { QAction* action = qobject_cast(sender()); assert(action); DiscoItems::Item command = serverAdHocCommands_[serverAdHocCommandActions_.indexOf(action)]; uiEventStream_->send(boost::shared_ptr(new RequestAdHocUIEvent(command))); } void QtMainWindow::setAvailableAdHocCommands(const std::vector& commands) { serverAdHocCommands_ = commands; foreach (QAction* action, serverAdHocCommandActions_) { delete action; } serverAdHocMenu_->clear(); serverAdHocCommandActions_.clear(); foreach (DiscoItems::Item command, commands) { QAction* action = new QAction(P2QSTRING(command.getName()), this); connect(action, SIGNAL(triggered(bool)), this, SLOT(handleAdHocActionTriggered(bool))); serverAdHocMenu_->addAction(action); serverAdHocCommandActions_.append(action); } if (serverAdHocCommandActions_.isEmpty()) { QAction* action = new QAction(tr("No Available Commands"), this); action->setEnabled(false); serverAdHocMenu_->addAction(action); serverAdHocCommandActions_.append(action); } } } swift-im-2.0+dev6/Swift/QtUI/QtCertificateViewerDialog.cpp0000644000175000017500000001207412227051774023342 0ustar kismithkismith/* * Copyright (c) 2012 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include "QtCertificateViewerDialog.h" #include "ui_QtCertificateViewerDialog.h" #include #include #include #include namespace Swift { QtCertificateViewerDialog::QtCertificateViewerDialog(QWidget* parent) : QDialog(parent), ui(new Ui::QtCertificateViewerDialog) { ui->setupUi(this); connect(ui->certChainTreeWidget, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), SLOT(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*))); setAttribute(Qt::WA_DeleteOnClose); } QtCertificateViewerDialog::~QtCertificateViewerDialog() { delete ui; } void QtCertificateViewerDialog::setCertificateChain(const std::vector& chain) { // clean widgets ui->certChainTreeWidget->clear(); if (chain.empty()) return; // convert Swift certificate chain to qt certificate chain (root goes first) currentChain.clear(); foreach(Certificate::ref cert, chain) { ByteArray certAsDer = cert->toDER(); QByteArray dataArray(reinterpret_cast(certAsDer.data()), certAsDer.size()); currentChain.push_front(QSslCertificate(dataArray, QSsl::Der)); } // fill treeWidget QTreeWidgetItem* root = new QTreeWidgetItem(ui->certChainTreeWidget, QStringList(currentChain.at(0).subjectInfo(QSslCertificate::CommonName))); root->setData(0, Qt::UserRole, QVariant(0)); root->setExpanded(true); ui->certChainTreeWidget->addTopLevelItem(root); if (currentChain.size() > 1) { QTreeWidgetItem* parent = root; for (int n = 1; n < currentChain.size(); n++) { QTreeWidgetItem* link = new QTreeWidgetItem(parent, QStringList(QString("↳ ") + currentChain.at(n).subjectInfo(QSslCertificate::CommonName))); link->setExpanded(true); link->setData(0, Qt::UserRole, QVariant(n)); parent = link; } ui->certChainTreeWidget->setCurrentItem(parent); } else { ui->certChainTreeWidget->setCurrentItem(root); } } void QtCertificateViewerDialog::displayCertificateChainAsSheet(QWidget* parent, const std::vector& chain) { QtCertificateViewerDialog* dialog = new QtCertificateViewerDialog(parent); dialog->setCertificateChain(chain); dialog->show(); } void QtCertificateViewerDialog::currentItemChanged(QTreeWidgetItem* current, QTreeWidgetItem*) { setCertificateDetails(currentChain.at(current->data(0, Qt::UserRole).toInt())); } #define ADD_SECTION( TITLE ) \ ui->certGridLayout->addWidget(new QLabel("" + TITLE + ""), rowCount++, 0, 1, 2); #define ADD_FIELD( TITLE, VALUE) \ ui->certGridLayout->addWidget(new QLabel(TITLE), rowCount, 0, 1, 1, Qt::AlignRight); \ valueLabel = new QLabel(VALUE); \ valueLabel->setTextFormat(Qt::PlainText); \ valueLabel->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); \ ui->certGridLayout->addWidget(valueLabel, rowCount++, 1, 1, 1, Qt::AlignLeft); void QtCertificateViewerDialog::setCertificateDetails(const QSslCertificate& cert) { QLayoutItem* item; while ((item = ui->certGridLayout->takeAt(0)) != NULL ) { delete item->widget(); delete item; } int rowCount = 0; ui->certGridLayout->setColumnStretch(2, 1); QLabel* valueLabel = 0; ADD_SECTION(tr("General")); ADD_FIELD(tr("Valid From"), cert.effectiveDate().toString(Qt::TextDate)); ADD_FIELD(tr("Valid To"), cert.expiryDate().toString(Qt::TextDate)); ADD_FIELD(tr("Serial Number"), QString(cert.serialNumber().toHex())); ADD_FIELD(tr("Version"), QString(cert.version())); ADD_SECTION(tr("Subject")); ADD_FIELD(tr("Organization"), cert.subjectInfo(QSslCertificate::Organization)); ADD_FIELD(tr("Common Name"), cert.subjectInfo(QSslCertificate::CommonName)); ADD_FIELD(tr("Locality"), cert.subjectInfo(QSslCertificate::LocalityName)); ADD_FIELD(tr("Organizational Unit"), cert.subjectInfo(QSslCertificate::OrganizationalUnitName)); ADD_FIELD(tr("Country"), cert.subjectInfo(QSslCertificate::CountryName)); ADD_FIELD(tr("State"), cert.subjectInfo(QSslCertificate::StateOrProvinceName)); if (!cert.alternateSubjectNames().empty()) { ADD_SECTION(tr("Alternate Subject Names")); QMultiMap altNames = cert.alternateSubjectNames(); foreach (const QSsl::AlternateNameEntryType &type, altNames.uniqueKeys()) { foreach (QString name, altNames.values(type)) { if (type == QSsl::EmailEntry) { ADD_FIELD(tr("E-mail Address"), name); } else { ADD_FIELD(tr("DNS Name"), name); } } } } ADD_SECTION(tr("Issuer")); ADD_FIELD(tr("Organization"), cert.issuerInfo(QSslCertificate::Organization)); ADD_FIELD(tr("Common Name"), cert.issuerInfo(QSslCertificate::CommonName)); ADD_FIELD(tr("Locality"), cert.issuerInfo(QSslCertificate::LocalityName)); ADD_FIELD(tr("Organizational Unit"), cert.issuerInfo(QSslCertificate::OrganizationalUnitName)); ADD_FIELD(tr("Country"), cert.issuerInfo(QSslCertificate::CountryName)); ADD_FIELD(tr("State"), cert.issuerInfo(QSslCertificate::StateOrProvinceName)); ui->certGridLayout->setRowStretch(rowCount + 1, 1); } } swift-im-2.0+dev6/Swift/QtUI/QtStrings.h0000644000175000017500000000612112227051774017710 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ /* * This file contains strings from the Qt library that need to be * translated. * * This header is not included by our sources, but will be picked up * by lupdate. */ #pragma once QT_TRANSLATE_NOOP("CloseButton", "Close Tab"); QT_TRANSLATE_NOOP3("QApplication", "QT_LAYOUT_DIRECTION", "Translate this to LTR for left-to-right or RTL for right-to-left languages"); QT_TRANSLATE_NOOP("QLineEdit", "Select All"); QT_TRANSLATE_NOOP("QLineEdit", "&Undo"); QT_TRANSLATE_NOOP("QLineEdit", "&Redo"); QT_TRANSLATE_NOOP("QLineEdit", "Cu&t"); QT_TRANSLATE_NOOP("QLineEdit", "&Copy"); QT_TRANSLATE_NOOP("QLineEdit", "&Paste"); QT_TRANSLATE_NOOP("QLineEdit", "Delete"); QT_TRANSLATE_NOOP("QScrollBar", "Scroll here"); QT_TRANSLATE_NOOP("QScrollBar", "Top"); QT_TRANSLATE_NOOP("QScrollBar", "Bottom"); QT_TRANSLATE_NOOP("QScrollBar", "Page up"); QT_TRANSLATE_NOOP("QScrollBar", "Page down"); QT_TRANSLATE_NOOP("QScrollBar", "Scroll up"); QT_TRANSLATE_NOOP("QScrollBar", "Scroll down"); QT_TRANSLATE_NOOP("QTextControl", "Select All"); QT_TRANSLATE_NOOP("QTextControl", "&Copy"); QT_TRANSLATE_NOOP("QTextControl", "&Undo"); QT_TRANSLATE_NOOP("QTextControl", "&Redo"); QT_TRANSLATE_NOOP("QTextControl", "Cu&t"); QT_TRANSLATE_NOOP("QTextControl", "&Paste"); QT_TRANSLATE_NOOP("QTextControl", "Delete"); QT_TRANSLATE_NOOP("QWebPage", "Copy Link"); QT_TRANSLATE_NOOP("QWebPage", "Copy"); QT_TRANSLATE_NOOP("QWebPage", "Copy Image"); QT_TRANSLATE_NOOP("QWebPage", "Scroll here"); QT_TRANSLATE_NOOP("QWebPage", "Top"); QT_TRANSLATE_NOOP("QWebPage", "Bottom"); QT_TRANSLATE_NOOP("QWebPage", "Page up"); QT_TRANSLATE_NOOP("QWebPage", "Page down"); QT_TRANSLATE_NOOP("QWebPage", "Scroll up"); QT_TRANSLATE_NOOP("QWebPage", "Scroll down"); QT_TRANSLATE_NOOP("QWizard", "< &Back"); QT_TRANSLATE_NOOP("QWizard", "&Finish"); QT_TRANSLATE_NOOP("QWizard", "&Help"); QT_TRANSLATE_NOOP("QWizard", "Go Back"); QT_TRANSLATE_NOOP("QWizard", "Continue"); QT_TRANSLATE_NOOP("QWizard", "Commit"); QT_TRANSLATE_NOOP("QWizard", "Done"); QT_TRANSLATE_NOOP("QWizard", "Quit"); QT_TRANSLATE_NOOP("QWizard", "Help"); QT_TRANSLATE_NOOP("QWizard", "Cancel"); QT_TRANSLATE_NOOP("QWizard", "&Next"); QT_TRANSLATE_NOOP("QWizard", "&Next >"); QT_TRANSLATE_NOOP("QDialogButtonBox", "&Yes"); QT_TRANSLATE_NOOP("QDialogButtonBox", "&No"); QT_TRANSLATE_NOOP("QDialogButtonBox", "&OK"); QT_TRANSLATE_NOOP("QDialogButtonBox", "OK"); QT_TRANSLATE_NOOP("QDialogButtonBox", "&Cancel"); QT_TRANSLATE_NOOP("QDialogButtonBox", "Cancel"); QT_TRANSLATE_NOOP("QMessageBox", "Show Details..."); QT_TRANSLATE_NOOP("QMessageBox", "Hide Details..."); QT_TRANSLATE_NOOP("MAC_APPLICATION_MENU", "Services"); QT_TRANSLATE_NOOP("MAC_APPLICATION_MENU", "Hide %1"); QT_TRANSLATE_NOOP("MAC_APPLICATION_MENU", "Hide Others"); QT_TRANSLATE_NOOP("MAC_APPLICATION_MENU", "Show All"); QT_TRANSLATE_NOOP("MAC_APPLICATION_MENU", "Preferences..."); QT_TRANSLATE_NOOP("MAC_APPLICATION_MENU", "Quit %1"); QT_TRANSLATE_NOOP("MAC_APPLICATION_MENU", "About %1"); swift-im-2.0+dev6/Swift/QtUI/QtAffiliationEditor.h0000644000175000017500000000206212227051774021653 0ustar kismithkismith/* * Copyright (c) 2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include #include #include namespace Swift { class QtAffiliationEditor : public QDialog { Q_OBJECT public: QtAffiliationEditor(QWidget* parent = NULL); ~QtAffiliationEditor(); void setAffiliations(MUCOccupant::Affiliation, const std::vector& jids); const std::vector >& getChanges() const; private slots: void handleCurrentIndexChanged(int); void handleAddClicked(); void handleRemoveClicked(); private: typedef std::pair ChangePair; MUCOccupant::Affiliation affiliationFromIndex(int affiliation); Ui::QtAffiliationEditor ui_; std::map > affiliations_; std::vector changes_; }; }swift-im-2.0+dev6/Swift/QtUI/QtContactEditWindow.cpp0000644000175000017500000000605112227051774022205 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "QtContactEditWindow.h" #include #include #include #include #include #include #include #include #include #include "Swift/QtUI/QtSwiftUtil.h" #include "QtContactEditWidget.h" namespace Swift { QtContactEditWindow::QtContactEditWindow() : contactEditWidget_(NULL) { resize(400,300); setWindowTitle(tr("Edit contact")); setContentsMargins(0,0,0,0); QBoxLayout* layout = new QVBoxLayout(this); jidLabel_ = new QLabel(this); jidLabel_->setAlignment(Qt::AlignHCenter); layout->addWidget(jidLabel_); groupsLayout_ = new QVBoxLayout(); groupsLayout_->setContentsMargins(0,0,0,0); layout->addLayout(groupsLayout_); QHBoxLayout* buttonLayout = new QHBoxLayout(); layout->addLayout(buttonLayout); QPushButton* removeButton = new QPushButton(tr("Remove contact"), this); connect(removeButton, SIGNAL(clicked()), this, SLOT(handleRemoveContact())); buttonLayout->addWidget(removeButton); QPushButton* okButton = new QPushButton(tr("OK"), this); okButton->setDefault( true ); connect(okButton, SIGNAL(clicked()), this, SLOT(handleUpdateContact())); buttonLayout->addStretch(); buttonLayout->addWidget(okButton); } QtContactEditWindow::~QtContactEditWindow() { } void QtContactEditWindow::setNameSuggestions(const std::vector& nameSuggestions) { if (contactEditWidget_) { contactEditWidget_->setNameSuggestions(nameSuggestions); } } void QtContactEditWindow::setContact(const JID& jid, const std::string& name, const std::vector& groups, const std::set& allGroups) { delete contactEditWidget_; jid_ = jid; jidLabel_->setText("" + P2QSTRING(jid.toString()) + ""); contactEditWidget_ = new QtContactEditWidget(allGroups, this); groupsLayout_->addWidget(contactEditWidget_); contactEditWidget_->setName(name); contactEditWidget_->setSelectedGroups(groups); } void QtContactEditWindow::setEnabled(bool b) { QWidget::setEnabled(b); } void QtContactEditWindow::show() { QWidget::show(); QWidget::activateWindow(); } void QtContactEditWindow::hide() { QWidget::hide(); } void QtContactEditWindow::handleRemoveContact() { if (confirmContactDeletion(jid_)) { onRemoveContactRequest(); } } bool QtContactEditWindow::confirmContactDeletion(const JID& jid) { QMessageBox msgBox; msgBox.setWindowTitle(tr("Confirm contact deletion")); msgBox.setText(tr("Are you sure you want to delete this contact?")); msgBox.setInformativeText(QString(tr("This will remove the contact '%1' from all groups they may be in.")).arg(P2QSTRING(jid.toString()))); msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); msgBox.setDefaultButton(QMessageBox::Yes); return msgBox.exec() == QMessageBox::Yes; } void QtContactEditWindow::handleUpdateContact() { onChangeContactRequest(contactEditWidget_->getName(), contactEditWidget_->getSelectedGroups()); } } swift-im-2.0+dev6/Swift/QtUI/QtChatTheme.h0000644000175000017500000000354612227051774020131 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith. * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include namespace Swift { class QtChatTheme { public: QtChatTheme(const QString& themePath); QString getHeader() const {return fileContents_[Header];}; QString getFooter() const {return fileContents_[Footer];}; QString getContent() const {return fileContents_[Content];}; QString getStatus() const {return fileContents_[Status];}; QString getTopic() const {return fileContents_[Topic];}; QString getFileTransferRequest() const {return fileContents_[FileTransferRequest];}; QString getIncomingContent() const {return fileContents_[IncomingContent];}; QString getIncomingNextContent() const {return fileContents_[IncomingNextContent];}; QString getIncomingContext() const {return fileContents_[IncomingContext];}; QString getIncomingNextContext() const {return fileContents_[IncomingNextContext];}; QString getOutgoingContent() const {return fileContents_[OutgoingContent];}; QString getOutgoingNextContent() const {return fileContents_[OutgoingNextContent];}; QString getOutgoingContext() const {return fileContents_[OutgoingContext];}; QString getOutgoingNextContext() const {return fileContents_[OutgoingNextContext];}; QString getTemplate() const {return fileContents_[Template];} QString getMainCSS() const {return fileContents_[MainCSS];} QString getBase() const; private: enum files {Header = 0, Footer, Content, Status, Topic, FileTransferRequest, IncomingContent, IncomingNextContent, IncomingContext, IncomingNextContext, OutgoingContent, OutgoingNextContent, OutgoingContext, OutgoingNextContext, Template, MainCSS, TemplateDefault, EndMarker}; bool qrc_; QList fileContents_; QString themePath_; }; } swift-im-2.0+dev6/Swift/QtUI/QtUIFactory.h0000644000175000017500000000515512227051774020132 0ustar kismithkismith/* * Copyright (c) 2010-2012 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include class QSplitter; namespace Swift { class QtSettingsProvider; class SettingsProviderHierachy; class QtChatTabs; class QtSystemTray; class QtLoginWindow; class QtMainWindow; class QtChatTheme; class QtChatWindowFactory; class QtChatWindow; class TimerFactory; class historyWindow_; class WhiteboardSession; class QtUIFactory : public QObject, public UIFactory { Q_OBJECT public: QtUIFactory(SettingsProviderHierachy* settings, QtSettingsProvider* qtOnlySettings, QtChatTabs* tabs, QSplitter* netbookSplitter, QtSystemTray* systemTray, QtChatWindowFactory* chatWindowFactory, TimerFactory* timerFactory, bool startMinimized, bool emoticonsExist); virtual XMLConsoleWidget* createXMLConsoleWidget(); virtual HistoryWindow* createHistoryWindow(UIEventStream*); virtual MainWindow* createMainWindow(UIEventStream* eventStream); virtual LoginWindow* createLoginWindow(UIEventStream* eventStream); virtual EventWindow* createEventWindow(); virtual ChatListWindow* createChatListWindow(UIEventStream*); virtual MUCSearchWindow* createMUCSearchWindow(); virtual ChatWindow* createChatWindow(const JID &contact, UIEventStream* eventStream); virtual UserSearchWindow* createUserSearchWindow(UserSearchWindow::Type type, UIEventStream* eventStream, const std::set& groups); virtual JoinMUCWindow* createJoinMUCWindow(UIEventStream* uiEventStream); virtual ProfileWindow* createProfileWindow(); virtual ContactEditWindow* createContactEditWindow(); virtual FileTransferListWidget* createFileTransferListWidget(); virtual WhiteboardWindow* createWhiteboardWindow(boost::shared_ptr whiteboardSession); virtual void createAdHocCommandWindow(boost::shared_ptr command); private slots: void handleLoginWindowGeometryChanged(); void handleChatWindowFontResized(int); void handleHistoryWindowFontResized(int); private: SettingsProviderHierachy* settings; QtSettingsProvider* qtOnlySettings; QtChatTabs* tabs; QSplitter* netbookSplitter; QtSystemTray* systemTray; QtChatWindowFactory* chatWindowFactory; TimerFactory* timerFactory_; QtMainWindow* lastMainWindow; QtLoginWindow* loginWindow; std::vector > chatWindows; bool startMinimized; int chatFontSize; int historyFontSize_; bool emoticonsExist_; }; } swift-im-2.0+dev6/Swift/QtUI/SystemMessageSnippet.cpp0000644000175000017500000000165512227051774022450 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "SystemMessageSnippet.h" #include namespace Swift { SystemMessageSnippet::SystemMessageSnippet(const QString& message, const QDateTime& time, bool appendToPrevious, QtChatTheme* theme) : ChatSnippet(appendToPrevious) { if (appendToPrevious) { setContinuationFallbackSnippet(boost::shared_ptr(new SystemMessageSnippet(message, time, false, theme))); } content_ = theme->getStatus(); content_.replace("%message%", wrapResizable("" + escape(message) + "")); content_.replace("%shortTime%", wrapResizable(escape(time.toString("h:mm")))); content_.replace("%time%", wrapResizable("" + timeToEscapedString(time) + "")); } SystemMessageSnippet::~SystemMessageSnippet() { } } swift-im-2.0+dev6/Swift/QtUI/QtCertificateViewerDialog.ui0000644000175000017500000000637712227051774023206 0ustar kismithkismith QtCertificateViewerDialog 0 0 655 514 Certificate Viewer true Qt::Horizontal QDialogButtonBox::Ok false Qt::ScrollBarAlwaysOff true 0 0 629 368 16777215 70 QAbstractItemView::NoEditTriggers false false false false false false 1 buttonBox accepted() QtCertificateViewerDialog accept() 248 254 157 274 buttonBox rejected() QtCertificateViewerDialog reject() 316 260 286 274 swift-im-2.0+dev6/Swift/QtUI/QtXMLConsoleWidget.h0000644000175000017500000000156012227051774021410 0ustar kismithkismith/* * 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 "Swift/Controllers/UIInterfaces/XMLConsoleWidget.h" #include "QtTabbable.h" class QTextEdit; class QCheckBox; class QColor; namespace Swift { class QtXMLConsoleWidget : public QtTabbable, public XMLConsoleWidget { Q_OBJECT public: QtXMLConsoleWidget(); ~QtXMLConsoleWidget(); void show(); void activate(); virtual void handleDataRead(const SafeByteArray& data); virtual void handleDataWritten(const SafeByteArray& data); private: virtual void closeEvent(QCloseEvent* event); virtual void showEvent(QShowEvent* event); void appendTextIfEnabled(const std::string& data, const QColor& color); private: QTextEdit* textEdit; QCheckBox* enabled; }; } swift-im-2.0+dev6/Swift/QtUI/QtSubscriptionRequestWindow.cpp0000644000175000017500000000544712227051774024051 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Swift/QtUI/QtSubscriptionRequestWindow.h" #include #include #include #include #include "Swift/QtUI/QtSwiftUtil.h" namespace Swift { QtSubscriptionRequestWindow::QtSubscriptionRequestWindow(boost::shared_ptr event, QWidget* parent) : QDialog(parent), event_(event) { QString text = QString(tr("%1 would like to add you to their contact list.\n Would you like to add them to your contact list and share your status when you're online? \n\nIf you choose to defer this choice, you will be asked again when you next login.")).arg(event->getJID().toString().c_str()); QVBoxLayout* layout = new QVBoxLayout(); QLabel* label = new QLabel(text, this); layout->addWidget(label); if (event_->getConcluded()) { QLabel* doneLabel = new QLabel(tr("You have already replied to this request")); QPushButton* okButton = new QPushButton(tr("OK"), this); connect(okButton, SIGNAL(clicked()), this, SLOT(handleDefer())); layout->addWidget(doneLabel); layout->addWidget(okButton); } else { QPushButton* yesButton = new QPushButton(tr("Yes"), this); yesButton->setDefault(true); connect(yesButton, SIGNAL(clicked()), this, SLOT(handleYes())); QPushButton* noButton = new QPushButton(tr("No"), this); connect(noButton, SIGNAL(clicked()), this, SLOT(handleNo())); QPushButton* deferButton = new QPushButton(tr("Defer"), this); deferButton->setShortcut(QKeySequence(Qt::Key_Escape)); connect(deferButton, SIGNAL(clicked()), this, SLOT(handleDefer())); QHBoxLayout* buttonLayout = new QHBoxLayout(); buttonLayout->addWidget(yesButton); buttonLayout->addWidget(noButton); buttonLayout->addWidget(deferButton); layout->addLayout(buttonLayout); } setLayout(layout); } void QtSubscriptionRequestWindow::handleYes() { event_->accept(); delete this; } void QtSubscriptionRequestWindow::handleNo() { event_->decline(); delete this; } void QtSubscriptionRequestWindow::handleDefer() { event_->defer(); delete this; } QtSubscriptionRequestWindow::~QtSubscriptionRequestWindow() { windows_.removeOne(this); } QtSubscriptionRequestWindow* QtSubscriptionRequestWindow::getWindow(boost::shared_ptr event, QWidget* parent) { foreach (QtSubscriptionRequestWindow* window, windows_) { if (window->getEvent() == event) { return window; } } QtSubscriptionRequestWindow* window = new QtSubscriptionRequestWindow(event, parent); windows_.append(window); return window; } boost::shared_ptr QtSubscriptionRequestWindow::getEvent() { return event_; } QList QtSubscriptionRequestWindow::windows_; } swift-im-2.0+dev6/Swift/QtUI/QtChatWindowFactory.cpp0000644000175000017500000000546112227051774022217 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "QtChatWindowFactory.h" #include #include "QtChatTabs.h" #include "QtChatWindow.h" #include "QtSwiftUtil.h" #include "QtChatTheme.h" #include namespace Swift { static const QString SPLITTER_STATE = "mucSplitterState"; static const QString CHAT_TABS_GEOMETRY = "chatTabsGeometry"; QtChatWindowFactory::QtChatWindowFactory(QSplitter* splitter, SettingsProvider* settings, QtSettingsProvider* qtSettings, QtChatTabs* tabs, const QString& themePath, QMap emoticons) : themePath_(themePath), emoticons_(emoticons) { qtOnlySettings_ = qtSettings; settings_ = settings; tabs_ = tabs; theme_ = NULL; if (splitter) { splitter->addWidget(tabs_); } else if (tabs_) { QVariant chatTabsGeometryVariant = qtOnlySettings_->getQSettings()->value(CHAT_TABS_GEOMETRY); if (chatTabsGeometryVariant.isValid()) { tabs_->restoreGeometry(chatTabsGeometryVariant.toByteArray()); } connect(tabs_, SIGNAL(geometryChanged()), this, SLOT(handleWindowGeometryChanged())); } } QtChatWindowFactory::~QtChatWindowFactory() { delete theme_; } ChatWindow* QtChatWindowFactory::createChatWindow(const JID &contact,UIEventStream* eventStream) { if (!theme_) { theme_ = new QtChatTheme(themePath_); if (theme_->getIncomingContent().isEmpty()) { delete theme_; theme_ = new QtChatTheme(""); /* Use the inbuilt theme */ } } QtChatWindow *chatWindow = new QtChatWindow(P2QSTRING(contact.toString()), theme_, eventStream, settings_, emoticons_); connect(chatWindow, SIGNAL(splitterMoved()), this, SLOT(handleSplitterMoved())); connect(this, SIGNAL(changeSplitterState(QByteArray)), chatWindow, SLOT(handleChangeSplitterState(QByteArray))); QVariant splitterState = qtOnlySettings_->getQSettings()->value(SPLITTER_STATE); if(splitterState.isValid()) { chatWindow->handleChangeSplitterState(splitterState.toByteArray()); } if (tabs_) { tabs_->addTab(chatWindow); } else { QVariant chatGeometryVariant = qtOnlySettings_->getQSettings()->value(CHAT_TABS_GEOMETRY); if (chatGeometryVariant.isValid()) { chatWindow->restoreGeometry(chatGeometryVariant.toByteArray()); } connect(chatWindow, SIGNAL(geometryChanged()), this, SLOT(handleWindowGeometryChanged())); } return chatWindow; } void QtChatWindowFactory::handleWindowGeometryChanged() { qtOnlySettings_->getQSettings()->setValue(CHAT_TABS_GEOMETRY, qobject_cast(sender())->saveGeometry()); } void QtChatWindowFactory::handleSplitterMoved() { QByteArray splitterState = qobject_cast(sender())->getSplitterState(); qtOnlySettings_->getQSettings()->setValue(SPLITTER_STATE, QVariant(splitterState)); emit changeSplitterState(splitterState); } } swift-im-2.0+dev6/Swift/QtUI/Swift.qrc0000644000175000017500000000400312227051774017401 0ustar kismithkismith ../resources/logo/logo-icon-16.png ../resources/logo/logo-chat-16.png ../resources/logo/logo-shaded-text.256.png ../resources/icons/online.png ../resources/icons/connecting.mng ../resources/icons/away.png ../resources/icons/dnd.png ../resources/icons/offline.png ../resources/icons/certificate.png ../resources/icons/lock.png ../resources/icons/error.png ../resources/icons/warn.png ../resources/icons/check.png ../resources/icons/throbber.gif ../resources/icons/avatar.png ../resources/icons/no-avatar.png ../resources/icons/tray-standard.png ../resources/icons/new-chat.png ../resources/icons/actions.png COPYING ../resources/icons/line.png ../resources/icons/rect.png ../resources/icons/circle.png ../resources/icons/handline.png ../resources/icons/text.png ../resources/icons/polygon.png ../resources/icons/cursor.png ../resources/icons/eraser.png swift-im-2.0+dev6/Swift/QtUI/QtFileTransferListItemModel.h0000644000175000017500000000225412227051774023302 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include namespace Swift { class FileTransferController; class FileTransferOverview; class QtFileTransferListItemModel : public QAbstractItemModel { Q_OBJECT public: explicit QtFileTransferListItemModel(QObject *parent = 0); void setFileTransferOverview(FileTransferOverview*); QVariant headerData(int section, Qt::Orientation orientation, int role) const; int columnCount(const QModelIndex &parent) const; QVariant data(const QModelIndex &index, int role) const; QModelIndex parent(const QModelIndex &child) const; int rowCount(const QModelIndex &parent) const; QModelIndex index(int row, int column, const QModelIndex &parent) const; private: enum Columns { Direction = 0, OtherParty, State, Progress, OverallSize, NoOfColumns, }; private: void handleNewFileTransferController(FileTransferController*); void handleStateChange(int index); void handleProgressChange(int index); signals: public slots: private: FileTransferOverview *fileTransferOverview; }; } swift-im-2.0+dev6/Swift/QtUI/QtElidingLabel.cpp0000644000175000017500000000242012227051774021123 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Swift/QtUI/QtElidingLabel.h" namespace Swift { QtElidingLabel::QtElidingLabel(QWidget* parent, Qt::WindowFlags f) : QLabel(parent, f) { fullText_ = ""; dirty_ = true; setSizes(); setTextFormat(Qt::PlainText); } QtElidingLabel::QtElidingLabel(const QString& text, QWidget* parent, Qt::WindowFlags f) : QLabel(text, parent, f) { fullText_ = text; dirty_ = true; setSizes(); setTextFormat(Qt::PlainText); } QtElidingLabel::~QtElidingLabel() { } void QtElidingLabel::setSizes() { setMinimumSize(1, minimumHeight()); } void QtElidingLabel::setText(const QString& text) { fullText_ = text; QLabel::setText(text); dirty_ = true; } void QtElidingLabel::paintEvent(QPaintEvent* event) { QRect rect = contentsRect(); dirty_ = dirty_ || rect != lastRect_; if (dirty_) { lastRect_ = rect; int fontWidth = fontMetrics().width(fullText_); if (fontWidth > rect.width()) { QString elidedText(fontMetrics().elidedText(fullText_, Qt::ElideRight, rect.width(), Qt::TextShowMnemonic)); QLabel::setText(elidedText); } else { QLabel::setText(fullText_); } dirty_ = false; } QLabel::paintEvent(event); } } swift-im-2.0+dev6/Swift/QtUI/QtFormWidget.cpp0000644000175000017500000002444212227051774020667 0ustar kismithkismith/* * Copyright (c) 2010-2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include #include namespace Swift { QtFormWidget::QtFormWidget(Form::ref form, QWidget* parent) : QWidget(parent), form_(form) { QGridLayout* thisLayout = new QGridLayout(this); int row = 0; if (!form->getTitle().empty()) { QLabel* instructions = new QLabel(("" + form->getTitle() + "").c_str(), this); thisLayout->addWidget(instructions, row++, 0, 1, 2); } if (!form->getInstructions().empty()) { QLabel* instructions = new QLabel(form->getInstructions().c_str(), this); thisLayout->addWidget(instructions, row++, 0, 1, 2); } QScrollArea* scrollArea = new QScrollArea(this); thisLayout->addWidget(scrollArea); QWidget* scroll = new QWidget(this); QGridLayout* layout = new QGridLayout(scroll); foreach (boost::shared_ptr field, form->getFields()) { QWidget* widget = createWidget(field); if (widget) { layout->addWidget(new QLabel(field->getLabel().c_str(), this), row, 0); layout->addWidget(widget, row++, 1); } } scrollArea->setWidget(scroll); scrollArea->setWidgetResizable(true); setEditable(form->getType() != Form::CancelType && form->getType() != Form::ResultType); } QtFormWidget::~QtFormWidget() { } QListWidget* QtFormWidget::createList(FormField::ref field) { QListWidget* listWidget = new QListWidget(this); listWidget->setSortingEnabled(false); listWidget->setSelectionMode(boost::dynamic_pointer_cast(field) ? QAbstractItemView::MultiSelection : QAbstractItemView::SingleSelection); boost::shared_ptr listMultiField = boost::dynamic_pointer_cast(field); boost::shared_ptr listSingleField = boost::dynamic_pointer_cast(field); std::vector selected; foreach (FormField::Option option, field->getOptions()) { listWidget->addItem(option.label.c_str()); if (listSingleField) { selected.push_back(option.value == listSingleField->getValue()); } else if (listMultiField) { std::string text = option.value; selected.push_back(std::find(listMultiField->getValue().begin(), listMultiField->getValue().end(), text) != listMultiField->getValue().end()); } } for (int i = 0; i < listWidget->count(); i++) { QListWidgetItem* item = listWidget->item(i); item->setSelected(selected[i]); } return listWidget; } QWidget* QtFormWidget::createWidget(FormField::ref field) { QWidget* widget = NULL; boost::shared_ptr booleanField = boost::dynamic_pointer_cast(field); if (booleanField) { QCheckBox* checkWidget = new QCheckBox(this); checkWidget->setCheckState(booleanField->getValue() ? Qt::Checked : Qt::Unchecked); widget = checkWidget; } boost::shared_ptr fixedField = boost::dynamic_pointer_cast(field); if (fixedField) { QString value = fixedField->getValue().c_str(); widget = new QLabel(value, this); } boost::shared_ptr listSingleField = boost::dynamic_pointer_cast(field); if (listSingleField) { widget = createList(field); } boost::shared_ptr textMultiField = boost::dynamic_pointer_cast(field); if (textMultiField) { QString value = textMultiField->getValue().c_str(); QTextEdit* textWidget = new QTextEdit(this); textWidget->setPlainText(value); widget = textWidget; } boost::shared_ptr textPrivateField = boost::dynamic_pointer_cast(field); if (textPrivateField) { QString value = textPrivateField->getValue().c_str(); QLineEdit* lineWidget = new QLineEdit(value, this); lineWidget->setEchoMode(QLineEdit::Password); widget = lineWidget; } boost::shared_ptr textSingleField = boost::dynamic_pointer_cast(field); if (textSingleField) { QString value = textSingleField->getValue().c_str(); widget = new QLineEdit(value, this); } boost::shared_ptr jidSingleField = boost::dynamic_pointer_cast(field); if (jidSingleField) { QString value = jidSingleField->getValue().toString().c_str(); widget = new QLineEdit(value, this); } boost::shared_ptr jidMultiField = boost::dynamic_pointer_cast(field); if (jidMultiField) { QString text; bool prev = false; foreach (JID line, jidMultiField->getValue()) { if (prev) { text += "\n"; } prev = true; text += line.toString().c_str(); } QTextEdit* textWidget = new QTextEdit(this); textWidget->setPlainText(text); widget = textWidget; } boost::shared_ptr listMultiField = boost::dynamic_pointer_cast(field); if (listMultiField) { widget = createList(field); } boost::shared_ptr hiddenField = boost::dynamic_pointer_cast(field); if (hiddenField) { } fields_[field->getName()] = widget; return widget; } Form::ref QtFormWidget::getCompletedForm() { Form::ref result(new Form(Form::SubmitType)); foreach (boost::shared_ptr field, form_->getFields()) { boost::shared_ptr resultField; boost::shared_ptr booleanField = boost::dynamic_pointer_cast(field); if (booleanField) { resultField = FormField::ref(BooleanFormField::create(qobject_cast(fields_[field->getName()])->checkState() == Qt::Checked)); } boost::shared_ptr fixedField = boost::dynamic_pointer_cast(field); if (fixedField) { resultField = FormField::ref(FixedFormField::create(fixedField->getValue())); } boost::shared_ptr listSingleField = boost::dynamic_pointer_cast(field); if (listSingleField) { QListWidget* listWidget = qobject_cast(fields_[field->getName()]); if (listWidget->selectedItems().size() > 0) { int i = listWidget->row(listWidget->selectedItems()[0]); resultField = FormField::ref(ListSingleFormField::create(field->getOptions()[i].value)); } else { resultField = FormField::ref(ListSingleFormField::create()); } } boost::shared_ptr textMultiField = boost::dynamic_pointer_cast(field); if (textMultiField) { QTextEdit* widget = qobject_cast(fields_[field->getName()]); QString string = widget->toPlainText(); if (string.isEmpty()) { resultField = FormField::ref(TextMultiFormField::create()); } else { resultField = FormField::ref(TextMultiFormField::create(Q2PSTRING(string))); } } boost::shared_ptr textPrivateField = boost::dynamic_pointer_cast(field); if (textPrivateField) { QLineEdit* widget = qobject_cast(fields_[field->getName()]); QString string = widget->text(); if (string.isEmpty()) { resultField = FormField::ref(TextPrivateFormField::create()); } else { resultField = FormField::ref(TextPrivateFormField::create(Q2PSTRING(string))); } } boost::shared_ptr textSingleField = boost::dynamic_pointer_cast(field); if (textSingleField) { QLineEdit* widget = qobject_cast(fields_[field->getName()]); QString string = widget->text(); if (string.isEmpty()) { resultField = FormField::ref(TextSingleFormField::create()); } else { resultField = FormField::ref(TextSingleFormField::create(Q2PSTRING(string))); } } boost::shared_ptr jidSingleField = boost::dynamic_pointer_cast(field); if (jidSingleField) { QLineEdit* widget = qobject_cast(fields_[field->getName()]); QString string = widget->text(); JID jid(Q2PSTRING(string)); if (string.isEmpty()) { resultField = FormField::ref(JIDSingleFormField::create()); } else { resultField = FormField::ref(JIDSingleFormField::create(jid)); } } boost::shared_ptr jidMultiField = boost::dynamic_pointer_cast(field); if (jidMultiField) { QTextEdit* widget = qobject_cast(fields_[field->getName()]); QString string = widget->toPlainText(); if (string.isEmpty()) { resultField = FormField::ref(JIDMultiFormField::create()); } else { QStringList lines = string.split("\n"); std::vector value; foreach (QString line, lines) { value.push_back(JID(Q2PSTRING(line))); } resultField = FormField::ref(JIDMultiFormField::create(value)); } } boost::shared_ptr listMultiField = boost::dynamic_pointer_cast(field); if (listMultiField) { QListWidget* listWidget = qobject_cast(fields_[field->getName()]); std::vector values; foreach (QListWidgetItem* item, listWidget->selectedItems()) { values.push_back(field->getOptions()[listWidget->row(item)].value); } resultField = FormField::ref(ListMultiFormField::create(values)); } boost::shared_ptr hiddenField = boost::dynamic_pointer_cast(field); if (hiddenField) { resultField = FormField::ref(HiddenFormField::create(hiddenField->getValue())); } resultField->setName(field->getName()); result->addField(resultField); } return result; } template void QtFormWidget::setEnabled(QWidget* rawWidget, bool editable) { T* widget = qobject_cast(rawWidget); if (widget) { widget->setEnabled(editable); } } template void QtFormWidget::setEditable(QWidget* rawWidget, bool editable) { T* widget = qobject_cast(rawWidget); if (widget) { widget->setReadOnly(!editable); } } void QtFormWidget::setEditable(bool editable) { if (!form_) { return; } foreach (boost::shared_ptr field, form_->getFields()) { QWidget* widget = NULL; if (field) { widget = fields_[field->getName()]; } setEnabled(widget, editable); setEnabled(widget, editable); setEditable(widget, editable); setEditable(widget, editable); } } } swift-im-2.0+dev6/Swift/QtUI/QtFileTransferListItemModel.cpp0000644000175000017500000001064012227051774023633 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include "QtFileTransferListItemModel.h" #include #include #include #include #include namespace Swift { extern std::string formatSize(const boost::uintmax_t bytes); QtFileTransferListItemModel::QtFileTransferListItemModel(QObject *parent) : QAbstractItemModel(parent), fileTransferOverview(0) { } void QtFileTransferListItemModel::setFileTransferOverview(FileTransferOverview *overview) { fileTransferOverview = overview; fileTransferOverview->onNewFileTransferController.connect(boost::bind(&QtFileTransferListItemModel::handleNewFileTransferController, this, _1)); } void QtFileTransferListItemModel::handleNewFileTransferController(FileTransferController* newController) { emit layoutAboutToBeChanged(); emit layoutChanged(); dataChanged(createIndex(0,0), createIndex(fileTransferOverview->getFileTransfers().size(),4)); newController->onStateChage.connect(boost::bind(&QtFileTransferListItemModel::handleStateChange, this, fileTransferOverview->getFileTransfers().size() - 1)); newController->onProgressChange.connect(boost::bind(&QtFileTransferListItemModel::handleProgressChange, this, fileTransferOverview->getFileTransfers().size() - 1)); } void QtFileTransferListItemModel::handleStateChange(int index) { emit dataChanged(createIndex(index, 2), createIndex(index, 2)); } void QtFileTransferListItemModel::handleProgressChange(int index) { emit dataChanged(createIndex(index, 3), createIndex(index, 3)); } QVariant QtFileTransferListItemModel::headerData(int section, Qt::Orientation /* orientation */, int role) const { if (role != Qt::DisplayRole) return QVariant(); if (section == Direction) return QVariant(QObject::tr("Direction")); if (section == OtherParty) return QVariant(QObject::tr("Other Party")); if (section == State) return QVariant(QObject::tr("State")); if (section == Progress) return QVariant(QObject::tr("Progress")); if (section == OverallSize) return QVariant(QObject::tr("Size")); return QVariant(); } int QtFileTransferListItemModel::columnCount(const QModelIndex& /* parent */) const { return NoOfColumns; } QVariant QtFileTransferListItemModel::data(const QModelIndex &index, int role) const { if (role != Qt::DisplayRole || !index.isValid() || !fileTransferOverview || static_cast(index.row()) >= fileTransferOverview->getFileTransfers().size()) { return QVariant(); } FileTransferController* controller = fileTransferOverview->getFileTransfers().at(index.row()); if (index.column() == Direction) { return controller->isIncoming() ? QVariant(QObject::tr("Incoming")) : QVariant(QObject::tr("Outgoing")); } if (index.column() == OtherParty) { return QVariant(QString::fromStdString(controller->getOtherParty().toString())); } if (index.column() == State) { FileTransfer::State state = controller->getState(); switch(state.state) { case FileTransfer::State::WaitingForStart: return QVariant(QObject::tr("Waiting for start")); case FileTransfer::State::WaitingForAccept: return QVariant(QObject::tr("Waiting for other side to accept")); case FileTransfer::State::Negotiating: return QVariant(QObject::tr("Negotiating")); case FileTransfer::State::Transferring: return QVariant(QObject::tr("Transferring")); case FileTransfer::State::Finished: return QVariant(QObject::tr("Finished")); case FileTransfer::State::Failed: return QVariant(QObject::tr("Failed")); case FileTransfer::State::Canceled: return QVariant(QObject::tr("Canceled")); } } if (index.column() == Progress) { return QVariant(QString::number(controller->getProgress())); } if (index.column() == OverallSize) { return QVariant(QString::fromStdString(formatSize((controller->getSize())))); } return QVariant(); } QModelIndex QtFileTransferListItemModel::parent(const QModelIndex& /* child */) const { return createIndex(0,0); } int QtFileTransferListItemModel::rowCount(const QModelIndex& /* parent */) const { return fileTransferOverview ? fileTransferOverview->getFileTransfers().size() : 0; } QModelIndex QtFileTransferListItemModel::index(int row, int column, const QModelIndex& /* parent */) const { return createIndex(row, column, 0); } } swift-im-2.0+dev6/Swift/QtUI/QtUISettingConstants.h0000644000175000017500000000141512227051774022030 0ustar kismithkismith/* * Copyright (c) 2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include namespace Swift { class QtUISettingConstants { public: static const SettingsProvider::Setting COMPACT_ROSTER; static const SettingsProvider::Setting CLICKTHROUGH_BANNER; static const SettingsProvider::Setting CURRENT_ROSTER_TAB; static const SettingsProvider::Setting SHOW_NICK_IN_ROSTER_HEADER; static const SettingsProvider::Setting CHATWINDOW_FONT_SIZE; static const SettingsProvider::Setting HISTORYWINDOW_FONT_SIZE; static const SettingsProvider::Setting SHOW_EMOTICONS; }; } swift-im-2.0+dev6/Swift/QtUI/config.pri.in0000644000175000017500000000003512227051774020165 0ustar kismithkismithLIBS += @CONFIG_LIBS@ @LIBS@ swift-im-2.0+dev6/Swift/QtUI/QtProfileWindow.h0000644000175000017500000000170512227051774021052 0ustar kismithkismith/* * 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 #include class QLabel; class QLineEdit; class QHBoxLayout; class QPushButton; namespace Swift { class QtAvatarWidget; class QtProfileWindow : public QWidget, public ProfileWindow { Q_OBJECT public: QtProfileWindow(); void setVCard(Swift::VCard::ref); void setEnabled(bool); void setProcessing(bool); virtual void setError(const std::string&); void show(); void hide(); void hideEvent (QHideEvent* event); private slots: void handleSave(); private: VCard::ref vcard; QtAvatarWidget* avatar; QLabel* nicknameLabel; QLineEdit* nickname; QLabel* throbberLabel; QLabel* errorLabel; QHBoxLayout* horizontalLayout; QPushButton* saveButton; }; } swift-im-2.0+dev6/Swift/QtUI/QtChatTabs.cpp0000644000175000017500000002104612227051774020306 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "QtChatTabs.h" #include #include #include #include #include #include #include #include #include #include #include #include namespace Swift { QtChatTabs::QtChatTabs() : QWidget() { #ifndef Q_WS_MAC setWindowIcon(QIcon(":/logo-chat-16.png")); #else setAttribute(Qt::WA_ShowWithoutActivating); #endif tabs_ = new QtTabWidget(this); tabs_->setUsesScrollButtons(true); tabs_->setElideMode(Qt::ElideRight); #if QT_VERSION >= 0x040500 /*For Macs, change the tab rendering.*/ tabs_->setDocumentMode(true); /*Closable tabs are only in Qt4.5 and later*/ tabs_->setTabsClosable(true); connect(tabs_, SIGNAL(tabCloseRequested(int)), this, SLOT(handleTabCloseRequested(int))); #else #warning Qt 4.5 or later is needed. Trying anyway, some things will be disabled. #endif QVBoxLayout *layout = new QVBoxLayout; layout->setSpacing(0); layout->setContentsMargins(0, 3, 0, 0); layout->addWidget(tabs_); setLayout(layout); //resize(400, 300); } void QtChatTabs::closeEvent(QCloseEvent* event) { //Hide first to prevent flickering as each tab is removed. hide(); for (int i = tabs_->count() - 1; i >= 0; i--) { tabs_->widget(i)->close(); } event->accept(); } QtTabbable* QtChatTabs::getCurrentTab() { return qobject_cast(tabs_->currentWidget()); } void QtChatTabs::addTab(QtTabbable* tab) { QSizePolicy policy = sizePolicy(); /* Chat windows like to grow - don't let them */ setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); tabs_->addTab(tab, tab->windowTitle()); connect(tab, SIGNAL(titleUpdated()), this, SLOT(handleTabTitleUpdated()), Qt::UniqueConnection); connect(tab, SIGNAL(countUpdated()), this, SLOT(handleTabTitleUpdated()), Qt::UniqueConnection); connect(tab, SIGNAL(windowClosing()), this, SLOT(handleTabClosing()), Qt::UniqueConnection); connect(tab, SIGNAL(windowOpening()), this, SLOT(handleWidgetShown()), Qt::UniqueConnection); connect(tab, SIGNAL(wantsToActivate()), this, SLOT(handleWantsToActivate()), Qt::UniqueConnection); connect(tab, SIGNAL(requestNextTab()), this, SLOT(handleRequestedNextTab()), Qt::UniqueConnection); connect(tab, SIGNAL(requestActiveTab()), this, SLOT(handleRequestedActiveTab()), Qt::UniqueConnection); connect(tab, SIGNAL(requestPreviousTab()), this, SLOT(handleRequestedPreviousTab()), Qt::UniqueConnection); connect(tab, SIGNAL(requestFlash()), this, SLOT(flash()), Qt::UniqueConnection); setSizePolicy(policy); } void QtChatTabs::handleWidgetShown() { QtTabbable* widget = qobject_cast(sender()); if (!widget) { return; } checkForFirstShow(); if (tabs_->indexOf(widget) >= 0) { handleTabTitleUpdated(widget); return; } addTab(widget); show(); } void QtChatTabs::handleWantsToActivate() { QtTabbable* widget = qobject_cast(sender()); Q_ASSERT(widget); //Un-minimize and bring to front. setWindowState(windowState() & ~Qt::WindowMinimized); setWindowState(windowState() | Qt::WindowActive); show(); widget->show(); tabs_->setCurrentWidget(widget); handleTabTitleUpdated(widget); widget->setFocus(); raise(); activateWindow(); } void QtChatTabs::handleTabClosing() { QWidget* widget = qobject_cast(sender()); int index; if (widget && ((index = tabs_->indexOf(widget)) >= 0)) { tabs_->removeTab(index); if (tabs_->count() == 0) { hide(); } else { handleTabTitleUpdated(tabs_->currentWidget()); } } } void QtChatTabs::handleRequestedPreviousTab() { int newIndex = tabs_->currentIndex() - 1; tabs_->setCurrentIndex(newIndex >= 0 ? newIndex : tabs_->count() - 1); } void QtChatTabs::handleRequestedNextTab() { int newIndex = tabs_->currentIndex() + 1; tabs_->setCurrentIndex(newIndex < tabs_->count() ? newIndex : 0); } void QtChatTabs::handleRequestedActiveTab() { QtTabbable::AlertType types[] = {QtTabbable::WaitingActivity, QtTabbable::ImpendingActivity}; bool finished = false; for (int j = 0; j < 2; j++) { bool looped = false; for (int i = tabs_->currentIndex() + 1; !finished && i != tabs_->currentIndex(); i++) { if (i >= tabs_->count()) { if (looped) { break; } looped = true; i = 0; } if (qobject_cast(tabs_->widget(i))->getWidgetAlertState() == types[j]) { tabs_->setCurrentIndex(i); finished = true; break; } } } } void QtChatTabs::handleTabCloseRequested(int index) { QWidget* widget = tabs_->widget(index); widget->close(); } void QtChatTabs::handleTabTitleUpdated() { QWidget* widget = qobject_cast(sender()); handleTabTitleUpdated(widget); } void QtChatTabs::handleTabTitleUpdated(QWidget* widget) { if (!widget) { return; } QtTabbable* tabbable = qobject_cast(widget); int index = tabs_->indexOf(widget); if (index < 0) { return; } QString tabText = tabbable->windowTitle().simplified(); // look for spectrum-generated and other long JID localparts, and // try to abbreviate the resulting long tab texts QRegExp hasTrailingGarbage("^(.[-\\w\\s&]+)([^\\s\\w].*)$"); if (hasTrailingGarbage.exactMatch(tabText) && hasTrailingGarbage.cap(1).simplified().length() >= 2 && hasTrailingGarbage.cap(2).length() >= 7) { // there may be some trailing garbage, and it's long // enough to be worth removing, and we'd leave at // least a couple of characters. tabText = hasTrailingGarbage.cap(1).simplified(); } // QTabBar interprets &, so escape that tabText.replace("&", "&&"); // see which alt[a-z] keys other tabs use bool accelsTaken[26]; int i = 0; while (i < 26) { accelsTaken[i] = (i == 0); //A is used for 'switch to active tab' i++; } int other = tabs_->tabBar()->count(); while (other >= 0) { other--; if (other != index) { QString t = tabs_->tabBar()->tabText(other).toLower(); int r = t.indexOf('&'); if (r >= 0 && t[r+1] >= 'a' && t[r+1] <= 'z') { accelsTaken[t[r+1].unicode()-'a'] = true; } } } // then look to see which letters in tabText may be used i = 0; int accelPos = -1; while (i < tabText.length() && accelPos < 0) { if (tabText[i] >= 'A' && tabText[i] <= 'Z' && !accelsTaken[tabText[i].unicode()-'A']) { accelPos = i; } if (tabText[i] >= 'a' && tabText[i] <= 'z' && !accelsTaken[tabText[i].unicode()-'a']) { accelPos = i; } ++i; } if (accelPos >= 0) { tabText = tabText.mid(0, accelPos) + "&" + tabText.mid(accelPos); } // this could be improved on some european keyboards, such as // the German one (where alt-Sz-Ligature is available) and basically // doesn't work on Arabic/Indic keyboards (where Latin letters // aren't available), but I don't care to deal with those. tabs_->setTabText(index, tabbable->getCount() > 0 ? QString("(%1) %2").arg(tabbable->getCount()).arg(tabText) : tabText); QColor tabTextColor; switch (tabbable->getWidgetAlertState()) { case QtTabbable::WaitingActivity : tabTextColor = QColor(217, 20, 43); break; case QtTabbable::ImpendingActivity : tabTextColor = QColor(27, 171, 32); break; default : tabTextColor = QColor(); } tabs_->tabBar()->setTabTextColor(index, tabTextColor); std::vector > unreads; for (int i = 0; i < tabs_->count(); i++) { QtTabbable* tab = qobject_cast(tabs_->widget(i)); if (tab) { unreads.push_back(std::pair(Q2PSTRING(tab->windowTitle()), tab->getCount())); } } std::string current(Q2PSTRING(qobject_cast(tabs_->currentWidget())->windowTitle())); ChatMessageSummarizer summary; setWindowTitle(summary.getSummary(current, unreads).c_str()); } void QtChatTabs::flash() { #ifndef SWIFTEN_PLATFORM_MACOSX QApplication::alert(this, 0); #endif } void QtChatTabs::resizeEvent(QResizeEvent*) { emit geometryChanged(); } void QtChatTabs::moveEvent(QMoveEvent*) { emit geometryChanged(); } void QtChatTabs::checkForFirstShow() { if (!isVisible()) { #ifndef Q_WS_MAC showMinimized(); #else /* https://bugreports.qt-project.org/browse/QTBUG-19194 * ^ When the above is fixed we can swap the below for just show(); * WA_ShowWithoutActivating seems to helpfully not work, so... */ QWidget* currentWindow = QApplication::activeWindow(); /* Remember who had focus if we're the current application*/ show(); QCoreApplication::processEvents(); /* Run through the eventloop to clear the show() */ if (currentWindow) { currentWindow->activateWindow(); /* Set focus back */ } #endif } } } swift-im-2.0+dev6/Swift/QtUI/QtTranslator.h0000644000175000017500000000104312227051774020406 0ustar kismithkismith/* * 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 #include class QtTranslator : public Swift::Translator { public: QtTranslator() { } virtual std::string translate(const std::string& text, const std::string& context) { return std::string(QCoreApplication::translate(context.c_str(), text.c_str(), 0, QCoreApplication::UnicodeUTF8).toUtf8()); } }; swift-im-2.0+dev6/Swift/QtUI/QtChatWindow.cpp0000644000175000017500000011000012227051774020651 0ustar kismithkismith/* * Copyright (c) 2010-2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "QtChatWindow.h" #include "Swift/Controllers/Roster/Roster.h" #include "Swift/Controllers/Roster/RosterItem.h" #include "Swift/Controllers/Roster/ContactRosterItem.h" #include "Roster/QtOccupantListWidget.h" #include "SwifTools/Linkify.h" #include "QtChatView.h" #include "MessageSnippet.h" #include "SystemMessageSnippet.h" #include "QtTextEdit.h" #include "QtSettingsProvider.h" #include "QtScaledAvatarCache.h" #include "QtInviteToChatWindow.h" #include #include #include "SwifTools/TabComplete.h" #include #include #include #include "QtChatWindowJSBridge.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Swift { const QString QtChatWindow::ButtonWhiteboardSessionCancel = QString("whiteboard-cancel"); const QString QtChatWindow::ButtonWhiteboardSessionAcceptRequest = QString("whiteboard-acceptrequest"); const QString QtChatWindow::ButtonWhiteboardShowWindow = QString("whiteboard-showwindow"); const QString QtChatWindow::ButtonFileTransferCancel = QString("filetransfer-cancel"); const QString QtChatWindow::ButtonFileTransferSetDescription = QString("filetransfer-setdescription"); const QString QtChatWindow::ButtonFileTransferSendRequest = QString("filetransfer-sendrequest"); const QString QtChatWindow::ButtonFileTransferAcceptRequest = QString("filetransfer-acceptrequest"); const QString QtChatWindow::ButtonMUCInvite = QString("mucinvite"); QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventStream* eventStream, SettingsProvider* settings, QMap emoticons) : QtTabbable(), contact_(contact), previousMessageWasSelf_(false), previousMessageKind_(PreviosuMessageWasNone), eventStream_(eventStream), emoticons_(emoticons) { settings_ = settings; unreadCount_ = 0; idCounter_ = 0; inputEnabled_ = true; completer_ = NULL; affiliationEditor_ = NULL; theme_ = theme; isCorrection_ = false; labelModel_ = NULL; correctionEnabled_ = Maybe; showEmoticons_ = true; updateTitleWithUnreadCount(); #ifdef SWIFT_EXPERIMENTAL_FT setAcceptDrops(true); #endif alertStyleSheet_ = "background: rgb(255, 255, 153); color: black"; QBoxLayout *layout = new QBoxLayout(QBoxLayout::TopToBottom, this); layout->setContentsMargins(0,0,0,0); layout->setSpacing(2); alertWidget_ = new QWidget(this); QHBoxLayout* alertLayout = new QHBoxLayout(alertWidget_); layout->addWidget(alertWidget_); alertLabel_ = new QLabel(this); alertLayout->addWidget(alertLabel_); alertButton_ = new QPushButton(this); connect (alertButton_, SIGNAL(clicked()), this, SLOT(handleAlertButtonClicked())); alertLayout->addWidget(alertButton_); QPalette palette = alertWidget_->palette(); palette.setColor(QPalette::Window, QColor(Qt::yellow)); palette.setColor(QPalette::WindowText, QColor(Qt::black)); alertWidget_->setStyleSheet(alertStyleSheet_); alertLabel_->setStyleSheet(alertStyleSheet_); alertWidget_->hide(); QBoxLayout* subjectLayout = new QBoxLayout(QBoxLayout::LeftToRight); subject_ = new QLineEdit(this); subjectLayout->addWidget(subject_); setSubject(""); subject_->setReadOnly(true); actionButton_ = new QPushButton(this); actionButton_->setIcon(QIcon(":/icons/actions.png")); connect(actionButton_, SIGNAL(clicked()), this, SLOT(handleActionButtonClicked())); subjectLayout->addWidget(actionButton_); subject_->hide(); actionButton_->hide(); layout->addLayout(subjectLayout); logRosterSplitter_ = new QSplitter(this); logRosterSplitter_->setAutoFillBackground(true); layout->addWidget(logRosterSplitter_); messageLog_ = new QtChatView(theme, this); logRosterSplitter_->addWidget(messageLog_); treeWidget_ = new QtOccupantListWidget(eventStream_, settings_, this); treeWidget_->hide(); logRosterSplitter_->addWidget(treeWidget_); logRosterSplitter_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); connect(logRosterSplitter_, SIGNAL(splitterMoved(int, int)), this, SLOT(handleSplitterMoved(int, int))); midBar_ = new QWidget(this); //layout->addWidget(midBar); midBar_->setAutoFillBackground(true); QHBoxLayout *midBarLayout = new QHBoxLayout(midBar_); midBarLayout->setContentsMargins(0,0,0,0); midBarLayout->setSpacing(2); //midBarLayout->addStretch(); labelsWidget_ = new QComboBox(this); labelsWidget_->setFocusPolicy(Qt::NoFocus); labelsWidget_->hide(); labelsWidget_->setSizeAdjustPolicy(QComboBox::AdjustToContents); midBarLayout->addWidget(labelsWidget_,0); connect(labelsWidget_, SIGNAL(currentIndexChanged(int)), this, SLOT(handleCurrentLabelChanged(int))); defaultLabelsPalette_ = labelsWidget_->palette(); QHBoxLayout* inputBarLayout = new QHBoxLayout(); inputBarLayout->setContentsMargins(0,0,0,0); inputBarLayout->setSpacing(2); input_ = new QtTextEdit(this); input_->setAcceptRichText(false); inputBarLayout->addWidget(midBar_); inputBarLayout->addWidget(input_); correctingLabel_ = new QLabel(tr("Correcting"), this); inputBarLayout->addWidget(correctingLabel_); correctingLabel_->hide(); layout->addLayout(inputBarLayout); inputClearing_ = false; contactIsTyping_ = false; tabCompletion_ = false; connect(input_, SIGNAL(unhandledKeyPressEvent(QKeyEvent*)), this, SLOT(handleKeyPressEvent(QKeyEvent*))); connect(input_, SIGNAL(returnPressed()), this, SLOT(returnPressed())); connect(input_, SIGNAL(textChanged()), this, SLOT(handleInputChanged())); connect(input_, SIGNAL(cursorPositionChanged()), this, SLOT(handleCursorPositionChanged())); setFocusProxy(input_); logRosterSplitter_->setFocusProxy(input_); midBar_->setFocusProxy(input_); messageLog_->setFocusProxy(input_); connect(qApp, SIGNAL(focusChanged(QWidget*, QWidget*)), this, SLOT(qAppFocusChanged(QWidget*, QWidget*))); connect(messageLog_, SIGNAL(gotFocus()), input_, SLOT(setFocus())); resize(400,300); connect(messageLog_, SIGNAL(fontResized(int)), this, SIGNAL(fontResized(int))); connect(messageLog_, SIGNAL(logCleared()), this, SLOT(handleLogCleared())); treeWidget_->onSomethingSelectedChanged.connect(boost::bind(&QtChatWindow::handleOccupantSelectionChanged, this, _1)); treeWidget_->onOccupantActionSelected.connect(boost::bind(boost::ref(onOccupantActionSelected), _1, _2)); jsBridge = new QtChatWindowJSBridge(); messageLog_->addToJSEnvironment("chatwindow", jsBridge); connect(jsBridge, SIGNAL(buttonClicked(QString,QString,QString,QString)), this, SLOT(handleHTMLButtonClicked(QString,QString,QString,QString))); settings_->onSettingChanged.connect(boost::bind(&QtChatWindow::handleSettingChanged, this, _1)); showEmoticons_ = settings_->getSetting(QtUISettingConstants::SHOW_EMOTICONS); } QtChatWindow::~QtChatWindow() { delete jsBridge; if (mucConfigurationWindow_) { delete mucConfigurationWindow_.data(); } } void QtChatWindow::handleSettingChanged(const std::string& setting) { if (setting == QtUISettingConstants::SHOW_EMOTICONS.getKey()) { showEmoticons_ = settings_->getSetting(QtUISettingConstants::SHOW_EMOTICONS); messageLog_->showEmoticons(showEmoticons_); } } void QtChatWindow::handleLogCleared() { onLogCleared(); } void QtChatWindow::handleOccupantSelectionChanged(RosterItem* item) { onOccupantSelectionChanged(dynamic_cast(item)); } bool QtChatWindow::appendToPreviousCheck(QtChatWindow::PreviousMessageKind messageKind, const std::string& senderName, bool senderIsSelf) const { return previousMessageKind_ == messageKind && ((senderIsSelf && previousMessageWasSelf_) || (!senderIsSelf && !previousMessageWasSelf_&& previousSenderName_ == P2QSTRING(senderName))); } void QtChatWindow::handleFontResized(int fontSizeSteps) { messageLog_->resizeFont(fontSizeSteps); } void QtChatWindow::handleAlertButtonClicked() { onAlertButtonClicked(); } void QtChatWindow::setAlert(const std::string& alertText, const std::string& buttonText) { alertLabel_->setText(alertText.c_str()); if (buttonText.empty()) { alertButton_->hide(); } else { alertButton_->setText(buttonText.c_str()); alertButton_->show(); } alertWidget_->show(); } void QtChatWindow::cancelAlert() { alertWidget_->hide(); } void QtChatWindow::setTabComplete(TabComplete* completer) { completer_ = completer; } void QtChatWindow::handleKeyPressEvent(QKeyEvent* event) { event->ignore(); QtTabbable::handleKeyPressEvent(event); if (event->isAccepted()) { return; } event->accept(); int key = event->key(); if (key == Qt::Key_Tab) { tabComplete(); } else if ((key == Qt::Key_Up) && input_->toPlainText().isEmpty() && !(lastSentMessage_.isEmpty())) { beginCorrection(); } else if (key == Qt::Key_Down && isCorrection_ && input_->textCursor().atBlockEnd()) { cancelCorrection(); } else if (key == Qt::Key_Down || key == Qt::Key_Up) { /* Drop */ } else { messageLog_->handleKeyPressEvent(event); } } void QtChatWindow::beginCorrection() { if (correctionEnabled_ == ChatWindow::Maybe) { setAlert(Q2PSTRING(tr("This chat may not support message correction. If you send a correction anyway, it may appear as a duplicate message"))); } else if (correctionEnabled_ == ChatWindow::No) { setAlert(Q2PSTRING(tr("This chat does not support message correction. If you send a correction anyway, it will appear as a duplicate message"))); } QTextCursor cursor = input_->textCursor(); cursor.select(QTextCursor::Document); cursor.beginEditBlock(); cursor.insertText(QString(lastSentMessage_)); cursor.endEditBlock(); isCorrection_ = true; correctingLabel_->show(); input_->setStyleSheet(alertStyleSheet_); labelsWidget_->setEnabled(false); } void QtChatWindow::cancelCorrection() { cancelAlert(); QTextCursor cursor = input_->textCursor(); cursor.select(QTextCursor::Document); cursor.removeSelectedText(); isCorrection_ = false; correctingLabel_->hide(); input_->setStyleSheet(qApp->styleSheet()); labelsWidget_->setEnabled(true); } QByteArray QtChatWindow::getSplitterState() { return logRosterSplitter_->saveState(); } void QtChatWindow::handleChangeSplitterState(QByteArray state) { logRosterSplitter_->restoreState(state); } void QtChatWindow::handleSplitterMoved(int, int) { emit splitterMoved(); } void QtChatWindow::tabComplete() { if (!completer_) { return; } QTextCursor cursor; if (tabCompleteCursor_.hasSelection()) { cursor = tabCompleteCursor_; } else { cursor = input_->textCursor(); while(cursor.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor) && cursor.document()->characterAt(cursor.position() - 1) != ' ') { } } QString root = cursor.selectedText(); if (root.isEmpty()) { return; } QString suggestion = P2QSTRING(completer_->completeWord(Q2PSTRING(root))); if (root == suggestion) { return; } tabCompletion_ = true; cursor.beginEditBlock(); cursor.removeSelectedText(); int oldPosition = cursor.position(); cursor.insertText(suggestion); tabCompleteCursor_ = cursor; tabCompleteCursor_.setPosition(oldPosition, QTextCursor::KeepAnchor); cursor.endEditBlock(); tabCompletion_ = false; } void QtChatWindow::setRosterModel(Roster* roster) { treeWidget_->setRosterModel(roster); } void QtChatWindow::setAvailableSecurityLabels(const std::vector& labels) { delete labelModel_; labelModel_ = new LabelModel(); labelModel_->availableLabels_ = labels; int i = 0; int defaultIndex = 0; labelsWidget_->setModel(labelModel_); foreach (SecurityLabelsCatalog::Item label, labels) { if (label.getIsDefault()) { defaultIndex = i; break; } i++; } labelsWidget_->setCurrentIndex(defaultIndex); } void QtChatWindow::handleCurrentLabelChanged(int index) { if (static_cast(index) >= labelModel_->availableLabels_.size()) { qDebug() << "User selected a label that doesn't exist"; return; } const SecurityLabelsCatalog::Item& label = labelModel_->availableLabels_[index]; if (label.getLabel()) { QPalette palette = labelsWidget_->palette(); //palette.setColor(QPalette::Base, P2QSTRING(label.getLabel()->getBackgroundColor())); palette.setColor(labelsWidget_->backgroundRole(), P2QSTRING(label.getLabel()->getBackgroundColor())); palette.setColor(labelsWidget_->foregroundRole(), P2QSTRING(label.getLabel()->getForegroundColor())); labelsWidget_->setPalette(palette); midBar_->setPalette(palette); labelsWidget_->setAutoFillBackground(true); } else { labelsWidget_->setAutoFillBackground(false); labelsWidget_->setPalette(defaultLabelsPalette_); midBar_->setPalette(defaultLabelsPalette_); } } void QtChatWindow::setSecurityLabelsError() { labelsWidget_->setEnabled(false); } void QtChatWindow::setSecurityLabelsEnabled(bool enabled) { if (enabled) { labelsWidget_->setEnabled(true); labelsWidget_->show(); } else { labelsWidget_->hide(); } } void QtChatWindow::setCorrectionEnabled(Tristate enabled) { correctionEnabled_ = enabled; } SecurityLabelsCatalog::Item QtChatWindow::getSelectedSecurityLabel() { assert(labelsWidget_->isEnabled()); assert(labelsWidget_->currentIndex() >= 0 && static_cast(labelsWidget_->currentIndex()) < labelModel_->availableLabels_.size()); return labelModel_->availableLabels_[labelsWidget_->currentIndex()]; } void QtChatWindow::closeEvent(QCloseEvent* event) { event->accept(); emit windowClosing(); onClosed(); } void QtChatWindow::convertToMUC() { setAcceptDrops(false); treeWidget_->show(); subject_->show(); actionButton_->show(); } void QtChatWindow::qAppFocusChanged(QWidget* /*old*/, QWidget* /*now*/) { if (isWidgetSelected()) { lastLineTracker_.setHasFocus(true); input_->setFocus(); onAllMessagesRead(); } else { lastLineTracker_.setHasFocus(false); } } void QtChatWindow::setInputEnabled(bool enabled) { inputEnabled_ = enabled; if (!enabled) { if (mucConfigurationWindow_) { delete mucConfigurationWindow_.data(); } if (affiliationEditor_) { delete affiliationEditor_.data(); } } } void QtChatWindow::showEvent(QShowEvent* event) { emit windowOpening(); QWidget::showEvent(event); } void QtChatWindow::setUnreadMessageCount(int count) { if (unreadCount_ != count) { unreadCount_ = count; updateTitleWithUnreadCount(); emit countUpdated(); } } void QtChatWindow::setContactChatState(ChatState::ChatStateType state) { contactIsTyping_ = (state == ChatState::Composing); emit titleUpdated(); } QtTabbable::AlertType QtChatWindow::getWidgetAlertState() { if (contactIsTyping_) { return ImpendingActivity; } if (unreadCount_ > 0) { return WaitingActivity; } return NoActivity; } void QtChatWindow::setName(const std::string& name) { contact_ = P2QSTRING(name); updateTitleWithUnreadCount(); } void QtChatWindow::updateTitleWithUnreadCount() { if (isWindow()) { setWindowTitle(unreadCount_ > 0 ? QString("(%1) %2").arg(unreadCount_).arg(contact_) : contact_); } else { setWindowTitle(contact_); } emit titleUpdated(); } std::string QtChatWindow::addMessage(const std::string &message, const std::string &senderName, bool senderIsSelf, boost::shared_ptr label, const std::string& avatarPath, const boost::posix_time::ptime& time) { return addMessage(linkimoticonify(P2QSTRING(message)), senderName, senderIsSelf, label, avatarPath, "", time); } QString QtChatWindow::linkimoticonify(const QString& message) const { QString messageHTML(message); messageHTML = Qt::escape(messageHTML); QMapIterator it(emoticons_); QString textStyle = showEmoticons_ ? "style='display:none'" : ""; QString imageStyle = showEmoticons_ ? "" : "style='display:none'"; if (messageHTML.length() < 500) { while (it.hasNext()) { it.next(); messageHTML.replace(it.key(), ""+it.key() + ""); } messageHTML = P2QSTRING(Linkify::linkify(Q2PSTRING(messageHTML))); } messageHTML.replace("\n","
"); return messageHTML; } std::string QtChatWindow::addMessage(const QString &message, const std::string &senderName, bool senderIsSelf, boost::shared_ptr label, const std::string& avatarPath, const QString& style, const boost::posix_time::ptime& time) { if (isWidgetSelected()) { onAllMessagesRead(); } QString scaledAvatarPath = QtScaledAvatarCache(32).getScaledAvatarPath(avatarPath.c_str()); QString htmlString; if (label) { htmlString = QString("").arg(Qt::escape(P2QSTRING(label->getForegroundColor()))).arg(Qt::escape(P2QSTRING(label->getBackgroundColor()))); htmlString += QString("%1 ").arg(Qt::escape(P2QSTRING(label->getDisplayMarking()))); } QString messageHTML(message); QString styleSpanStart = style == "" ? "" : ""; QString styleSpanEnd = style == "" ? "" : ""; htmlString += "" + styleSpanStart + messageHTML + styleSpanEnd + "" ; bool appendToPrevious = appendToPreviousCheck(PreviousMessageWasMessage, senderName, senderIsSelf); if (lastLineTracker_.getShouldMoveLastLine()) { /* should this be queued? */ messageLog_->addLastSeenLine(); /* if the line is added we should break the snippet */ appendToPrevious = false; } QString qAvatarPath = scaledAvatarPath.isEmpty() ? "qrc:/icons/avatar.png" : QUrl::fromLocalFile(scaledAvatarPath).toEncoded(); std::string id = "id" + boost::lexical_cast(idCounter_++); messageLog_->addMessageBottom(boost::shared_ptr(new MessageSnippet(htmlString, Qt::escape(P2QSTRING(senderName)), B2QDATE(time), qAvatarPath, senderIsSelf, appendToPrevious, theme_, P2QSTRING(id)))); previousMessageWasSelf_ = senderIsSelf; previousSenderName_ = P2QSTRING(senderName); previousMessageKind_ = PreviousMessageWasMessage; return id; } void QtChatWindow::flash() { emit requestFlash(); } void QtChatWindow::setAckState(std::string const& id, ChatWindow::AckState state) { QString xml; switch (state) { case ChatWindow::Pending: xml = ""; messageLog_->displayReceiptInfo(P2QSTRING(id), false); break; case ChatWindow::Received: xml = ""; messageLog_->displayReceiptInfo(P2QSTRING(id), true); break; case ChatWindow::Failed: xml = ""; break; } messageLog_->setAckXML(P2QSTRING(id), xml); } void QtChatWindow::setMessageReceiptState(const std::string& id, ChatWindow::ReceiptState state) { QString xml; switch (state) { case ChatWindow::ReceiptReceived: xml = ""; break; case ChatWindow::ReceiptRequested: xml = ""; break; } messageLog_->setReceiptXML(P2QSTRING(id), xml); } int QtChatWindow::getCount() { return unreadCount_; } std::string QtChatWindow::addAction(const std::string &message, const std::string &senderName, bool senderIsSelf, boost::shared_ptr label, const std::string& avatarPath, const boost::posix_time::ptime& time) { return addMessage(" *" + linkimoticonify(P2QSTRING(message)) + "*", senderName, senderIsSelf, label, avatarPath, "font-style:italic ", time); } std::string formatSize(const boost::uintmax_t bytes) { static const char *siPrefix[] = {"k", "M", "G", "T", "P", "E", "Z", "Y", NULL}; int power = 0; double engBytes = bytes; while (engBytes >= 1000) { ++power; engBytes = engBytes / 1000.0; } return str( boost::format("%.1lf %sB") % engBytes % (power > 0 ? siPrefix[power-1] : "") ); } QString encodeButtonArgument(const QString& str) { return Qt::escape(P2QSTRING(Base64::encode(createByteArray(Q2PSTRING(str))))); } QString decodeButtonArgument(const QString& str) { return P2QSTRING(byteArrayToString(Base64::decode(Q2PSTRING(str)))); } QString QtChatWindow::buildChatWindowButton(const QString& name, const QString& id, const QString& arg1, const QString& arg2, const QString& arg3) { QRegExp regex("[A-Za-z][A-Za-z0-9\\-\\_]+"); Q_ASSERT(regex.exactMatch(id)); QString html = QString("").arg(name).arg(id).arg(encodeButtonArgument(arg1)).arg(encodeButtonArgument(arg2)).arg(encodeButtonArgument(arg3)); return html; } std::string QtChatWindow::addFileTransfer(const std::string& senderName, bool senderIsSelf, const std::string& filename, const boost::uintmax_t sizeInBytes) { SWIFT_LOG(debug) << "addFileTransfer" << std::endl; QString ft_id = QString("ft%1").arg(P2QSTRING(boost::lexical_cast(idCounter_++))); QString htmlString; QString formattedFileSize = P2QSTRING(formatSize(sizeInBytes)); if (senderIsSelf) { // outgoing htmlString = tr("Send file") + ": " + P2QSTRING(filename) + " ( " + formattedFileSize + ")
" + "
" + buildChatWindowButton(tr("Cancel"), ButtonFileTransferCancel, ft_id) + buildChatWindowButton(tr("Set Description"), ButtonFileTransferSetDescription, ft_id) + buildChatWindowButton(tr("Send"), ButtonFileTransferSendRequest, ft_id) + "
"; } else { // incoming htmlString = tr("Receiving file") + ": " + P2QSTRING(filename) + " ( " + formattedFileSize + ")
" + "
" + buildChatWindowButton(tr("Cancel"), ButtonFileTransferCancel, ft_id) + buildChatWindowButton(tr("Accept"), ButtonFileTransferAcceptRequest, ft_id, P2QSTRING(filename)) + "
"; } //addMessage(message, senderName, senderIsSelf, boost::shared_ptr(), "", boost::posix_time::second_clock::local_time()); bool appendToPrevious = appendToPreviousCheck(PreviousMessageWasFileTransfer, senderName, senderIsSelf); if (lastLineTracker_.getShouldMoveLastLine()) { /* should this be queued? */ messageLog_->addLastSeenLine(); /* if the line is added we should break the snippet */ appendToPrevious = false; } QString qAvatarPath = "qrc:/icons/avatar.png"; std::string id = "ftmessage" + boost::lexical_cast(idCounter_++); messageLog_->addMessageBottom(boost::shared_ptr(new MessageSnippet(htmlString, Qt::escape(P2QSTRING(senderName)), B2QDATE(boost::posix_time::second_clock::local_time()), qAvatarPath, senderIsSelf, appendToPrevious, theme_, P2QSTRING(id)))); previousMessageWasSelf_ = senderIsSelf; previousSenderName_ = P2QSTRING(senderName); previousMessageKind_ = PreviousMessageWasFileTransfer; return Q2PSTRING(ft_id); } void QtChatWindow::setFileTransferProgress(std::string id, const int percentageDone) { messageLog_->setFileTransferProgress(QString::fromStdString(id), percentageDone); } void QtChatWindow::setFileTransferStatus(std::string id, const FileTransferState state, const std::string& msg) { messageLog_->setFileTransferStatus(QString::fromStdString(id), state, QString::fromStdString(msg)); } std::string QtChatWindow::addWhiteboardRequest(bool senderIsSelf) { QString wb_id = QString("wb%1").arg(P2QSTRING(boost::lexical_cast(idCounter_++))); QString htmlString; if (senderIsSelf) { htmlString = "
" + tr("Starting whiteboard chat") + "
"+ buildChatWindowButton(tr("Cancel"), ButtonWhiteboardSessionCancel, wb_id) + "
"; } else { htmlString = "
" + tr("%1 would like to start a whiteboard chat").arg(Qt::escape(contact_)) + ":
" + buildChatWindowButton(tr("Cancel"), ButtonWhiteboardSessionCancel, wb_id) + buildChatWindowButton(tr("Accept"), ButtonWhiteboardSessionAcceptRequest, wb_id) + "
"; } if (lastLineTracker_.getShouldMoveLastLine()) { /* should this be queued? */ messageLog_->addLastSeenLine(); /* if the line is added we should break the snippet */ // appendToPrevious = false; } QString qAvatarPath = "qrc:/icons/avatar.png"; std::string id = "wbmessage" + boost::lexical_cast(idCounter_++); messageLog_->addMessageBottom(boost::shared_ptr(new MessageSnippet(htmlString, Qt::escape(contact_), B2QDATE(boost::posix_time::second_clock::local_time()), qAvatarPath, false, false, theme_, P2QSTRING(id)))); previousMessageWasSelf_ = false; previousSenderName_ = contact_; return Q2PSTRING(wb_id); } void QtChatWindow::setWhiteboardSessionStatus(std::string id, const ChatWindow::WhiteboardSessionState state) { messageLog_->setWhiteboardSessionStatus(QString::fromStdString(id), state); } void QtChatWindow::handleHTMLButtonClicked(QString id, QString encodedArgument1, QString encodedArgument2, QString encodedArgument3) { QString arg1 = decodeButtonArgument(encodedArgument1); QString arg2 = decodeButtonArgument(encodedArgument2); QString arg3 = decodeButtonArgument(encodedArgument3); if (id.startsWith(ButtonFileTransferCancel)) { QString ft_id = arg1; onFileTransferCancel(Q2PSTRING(ft_id)); } else if (id.startsWith(ButtonFileTransferSetDescription)) { QString ft_id = arg1; bool ok = false; QString text = QInputDialog::getText(this, tr("File transfer description"), tr("Description:"), QLineEdit::Normal, "", &ok); if (ok) { descriptions[ft_id] = text; } } else if (id.startsWith(ButtonFileTransferSendRequest)) { QString ft_id = arg1; QString text = descriptions.find(ft_id) == descriptions.end() ? QString() : descriptions[ft_id]; onFileTransferStart(Q2PSTRING(ft_id), Q2PSTRING(text)); } else if (id.startsWith(ButtonFileTransferAcceptRequest)) { QString ft_id = arg1; QString filename = arg2; QString path = QFileDialog::getSaveFileName(this, tr("Save File"), filename); if (!path.isEmpty()) { onFileTransferAccept(Q2PSTRING(ft_id), Q2PSTRING(path)); } } else if (id.startsWith(ButtonWhiteboardSessionAcceptRequest)) { QString id = arg1; messageLog_->setWhiteboardSessionStatus(QString::fromStdString(Q2PSTRING(id)), ChatWindow::WhiteboardAccepted); onWhiteboardSessionAccept(); } else if (id.startsWith(ButtonWhiteboardSessionCancel)) { QString id = arg1; messageLog_->setWhiteboardSessionStatus(QString::fromStdString(Q2PSTRING(id)), ChatWindow::WhiteboardTerminated); onWhiteboardSessionCancel(); } else if (id.startsWith(ButtonWhiteboardShowWindow)) { QString id = arg1; onWhiteboardWindowShow(); } else if (id.startsWith(ButtonMUCInvite)) { QString roomJID = arg1; QString password = arg2; QString elementID = arg3; eventStream_->send(boost::make_shared(Q2PSTRING(roomJID), Q2PSTRING(password))); messageLog_->setMUCInvitationJoined(elementID); } else { SWIFT_LOG(debug) << "Unknown HTML button! ( " << Q2PSTRING(id) << " )" << std::endl; } } void QtChatWindow::addErrorMessage(const std::string& errorMessage) { if (isWidgetSelected()) { onAllMessagesRead(); } QString errorMessageHTML(Qt::escape(P2QSTRING(errorMessage))); errorMessageHTML.replace("\n","
"); messageLog_->addMessageBottom(boost::shared_ptr(new SystemMessageSnippet("" + errorMessageHTML + "", QDateTime::currentDateTime(), false, theme_))); previousMessageWasSelf_ = false; previousMessageKind_ = PreviousMessageWasSystem; } void QtChatWindow::addSystemMessage(const std::string& message) { if (isWidgetSelected()) { onAllMessagesRead(); } QString messageHTML(P2QSTRING(message)); messageHTML = linkimoticonify(messageHTML); messageLog_->addMessageBottom(boost::shared_ptr(new SystemMessageSnippet(messageHTML, QDateTime::currentDateTime(), false, theme_))); previousMessageKind_ = PreviousMessageWasSystem; } void QtChatWindow::replaceWithAction(const std::string& message, const std::string& id, const boost::posix_time::ptime& time) { replaceMessage(" *" + linkimoticonify(P2QSTRING(message)) + "*", id, time, "font-style:italic "); } void QtChatWindow::replaceMessage(const std::string& message, const std::string& id, const boost::posix_time::ptime& time) { replaceMessage(linkimoticonify(P2QSTRING(message)), id, time, ""); } void QtChatWindow::replaceMessage(const QString& message, const std::string& id, const boost::posix_time::ptime& time, const QString& style) { if (!id.empty()) { if (isWidgetSelected()) { onAllMessagesRead(); } QString messageHTML(message); QString styleSpanStart = style == "" ? "" : ""; QString styleSpanEnd = style == "" ? "" : ""; messageHTML = styleSpanStart + messageHTML + styleSpanEnd; messageLog_->replaceMessage(messageHTML, P2QSTRING(id), B2QDATE(time)); } else { std::cerr << "Trying to replace a message with no id"; } } void QtChatWindow::addPresenceMessage(const std::string& message) { if (isWidgetSelected()) { onAllMessagesRead(); } QString messageHTML(P2QSTRING(message)); messageHTML = linkimoticonify(messageHTML); messageLog_->addMessageBottom(boost::shared_ptr(new SystemMessageSnippet(messageHTML, QDateTime::currentDateTime(), false, theme_))); previousMessageKind_ = PreviousMessageWasPresence; } void QtChatWindow::returnPressed() { if (!inputEnabled_) { return; } messageLog_->scrollToBottom(); lastSentMessage_ = QString(input_->toPlainText()); onSendMessageRequest(Q2PSTRING(input_->toPlainText()), isCorrection_); inputClearing_ = true; input_->clear(); cancelCorrection(); inputClearing_ = false; } void QtChatWindow::handleInputChanged() { if (inputClearing_) { return; } if (input_->toPlainText().isEmpty()) { onUserCancelsTyping(); } else { onUserTyping(); } } void QtChatWindow::handleCursorPositionChanged() { if (tabCompletion_) { return; } tabCompleteCursor_.clearSelection(); } void QtChatWindow::show() { if (parentWidget() == NULL) { QWidget::show(); } emit windowOpening(); } void QtChatWindow::activate() { if (isWindow()) { QWidget::show(); } emit wantsToActivate(); input_->setFocus(); } void QtChatWindow::resizeEvent(QResizeEvent*) { emit geometryChanged(); } void QtChatWindow::moveEvent(QMoveEvent*) { emit geometryChanged(); } void QtChatWindow::dragEnterEvent(QDragEnterEvent *event) { if (event->mimeData()->hasUrls() && event->mimeData()->urls().size() == 1) { // TODO: check whether contact actually supports file transfer event->acceptProposedAction(); } } void QtChatWindow::dropEvent(QDropEvent *event) { if (event->mimeData()->urls().size() == 1) { onSendFileRequest(Q2PSTRING(event->mimeData()->urls().at(0).toLocalFile())); } else { addSystemMessage("Sending of multiple files at once isn't supported at this time."); } } void QtChatWindow::replaceLastMessage(const std::string& message) { messageLog_->replaceLastMessage(linkimoticonify(P2QSTRING(message))); } void QtChatWindow::setAvailableOccupantActions(const std::vector& actions) { treeWidget_->setAvailableOccupantActions(actions); } void QtChatWindow::setSubject(const std::string& subject) { //subject_->setVisible(!subject.empty()); subject_->setText(P2QSTRING(subject)); subject_->setToolTip(P2QSTRING(subject)); subject_->setCursorPosition(0); } void QtChatWindow::handleActionButtonClicked() { QMenu contextMenu; QAction* changeSubject = NULL; QAction* configure = NULL; QAction* affiliations = NULL; QAction* destroy = NULL; QAction* invite = NULL; foreach(ChatWindow::RoomAction availableAction, availableRoomActions_) { switch(availableAction) { case ChatWindow::ChangeSubject: changeSubject = contextMenu.addAction(tr("Change subject…")); break; case ChatWindow::Configure: configure = contextMenu.addAction(tr("Configure room…")); break; case ChatWindow::Affiliations: affiliations = contextMenu.addAction(tr("Edit affiliations…")); break; case ChatWindow::Destroy: destroy = contextMenu.addAction(tr("Destroy room")); break; case ChatWindow::Invite: invite = contextMenu.addAction(tr("Invite person to this room…")); break; } } QAction* result = contextMenu.exec(QCursor::pos()); if (result == NULL) { /* Skip processing. Note that otherwise, because the actions could be null they could match */ } else if (result == changeSubject) { bool ok; QString subject = QInputDialog::getText(this, tr("Change room subject"), tr("New subject:"), QLineEdit::Normal, subject_->text(), &ok); if (ok) { onChangeSubjectRequest(Q2PSTRING(subject)); } } else if (result == configure) { onConfigureRequest(Form::ref()); } else if (result == affiliations) { if (!affiliationEditor_) { onGetAffiliationsRequest(); affiliationEditor_ = new QtAffiliationEditor(this); connect(affiliationEditor_, SIGNAL(accepted()), this, SLOT(handleAffiliationEditorAccepted())); } affiliationEditor_->show(); } else if (result == destroy) { QMessageBox msgBox; msgBox.setWindowTitle(tr("Confirm room destruction")); msgBox.setText(tr("Are you sure you want to destroy the room?")); msgBox.setInformativeText(tr("This will destroy the room.")); msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); msgBox.setDefaultButton(QMessageBox::No); if (msgBox.exec() == QMessageBox::Yes) { onDestroyRequest(); } } else if (result == invite) { onInvitePersonToThisMUCRequest(); } } void QtChatWindow::handleAffiliationEditorAccepted() { onChangeAffiliationsRequest(affiliationEditor_->getChanges()); } void QtChatWindow::setAffiliations(MUCOccupant::Affiliation affiliation, const std::vector& jids) { if (!affiliationEditor_) return; affiliationEditor_->setAffiliations(affiliation, jids); } void QtChatWindow::setAvailableRoomActions(const std::vector &actions) { availableRoomActions_ = actions; } void QtChatWindow::showRoomConfigurationForm(Form::ref form) { if (mucConfigurationWindow_) { delete mucConfigurationWindow_.data(); } mucConfigurationWindow_ = new QtMUCConfigurationWindow(form); mucConfigurationWindow_->onFormComplete.connect(boost::bind(boost::ref(onConfigureRequest), _1)); mucConfigurationWindow_->onFormCancelled.connect(boost::bind(boost::ref(onConfigurationFormCancelled))); } void QtChatWindow::addMUCInvitation(const std::string& senderName, const JID& jid, const std::string& reason, const std::string& password, bool direct) { if (isWidgetSelected()) { onAllMessagesRead(); } QString htmlString = QObject::tr("You've been invited to enter the %1 room.").arg(P2QSTRING(jid.toString())) + "
"; if (!reason.empty()) { htmlString += QObject::tr("Reason: %1").arg(P2QSTRING(reason)) + "
"; } if (!direct) { htmlString += QObject::tr("This person may not have really sent this invitation!") + "
"; } QString id = QString(ButtonMUCInvite + "%1").arg(P2QSTRING(boost::lexical_cast(idCounter_++))); htmlString += "
" + buildChatWindowButton(tr("Accept Invite"), ButtonMUCInvite, Qt::escape(P2QSTRING(jid.toString())), Qt::escape(P2QSTRING(password)), id) + "
"; bool appendToPrevious = appendToPreviousCheck(PreviousMessageWasMUCInvite, senderName, false); if (lastLineTracker_.getShouldMoveLastLine()) { /* should this be queued? */ messageLog_->addLastSeenLine(); /* if the line is added we should break the snippet */ appendToPrevious = false; } QString qAvatarPath = "qrc:/icons/avatar.png"; messageLog_->addMessageBottom(boost::shared_ptr(new MessageSnippet(htmlString, Qt::escape(P2QSTRING(senderName)), B2QDATE(boost::posix_time::second_clock::local_time()), qAvatarPath, false, appendToPrevious, theme_, id))); previousMessageWasSelf_ = false; previousSenderName_ = P2QSTRING(senderName); previousMessageKind_ = PreviousMessageWasMUCInvite; } InviteToChatWindow* QtChatWindow::createInviteToChatWindow() { return new QtInviteToChatWindow(this); } } swift-im-2.0+dev6/Swift/QtUI/QtContactEditWidget.cpp0000644000175000017500000001136112227051774022161 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "QtContactEditWidget.h" #include #include #include #include #include #include #include #include #include "Swift/QtUI/QtSwiftUtil.h" namespace Swift { QtContactEditWidget::QtContactEditWidget(const std::set& allGroups, QWidget* parent) : QWidget(parent), nameRadioButton_(NULL), groups_(NULL) { QBoxLayout* layout = new QVBoxLayout(this); setContentsMargins(0,0,0,0); layout->setContentsMargins(0,0,0,0); nameLayout_ = new QHBoxLayout(); suggestionsLayout_ = new QHBoxLayout(); nameLayout_->addLayout(suggestionsLayout_); name_ = new QLineEdit(this); nameLayout_->addWidget(name_); throbberLabel_ = new QLabel(this); throbberLabel_->setMovie(new QMovie(":/icons/throbber.gif", QByteArray(), this)); throbberLabel_->movie()->start(); nameLayout_->addWidget(throbberLabel_); layout->addLayout(nameLayout_); layout->addWidget(new QLabel(tr("Groups:"), this)); QScrollArea* groupsArea = new QScrollArea(this); layout->addWidget(groupsArea); groupsArea->setWidgetResizable(true); groupsArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); groupsArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); QWidget* groups = new QWidget(groupsArea); groupsArea->setWidget(groups); QVBoxLayout* scrollLayout = new QVBoxLayout(groups); foreach (std::string group, allGroups) { QString groupName = doubleAmpersand(group); QCheckBox* check = new QCheckBox(groups); check->setText(groupName); check->setCheckState(Qt::Unchecked); checkBoxes_[group] = check; scrollLayout->addWidget(check); } QHBoxLayout* newGroupLayout = new QHBoxLayout(); newGroup_ = new QCheckBox(groups); newGroup_->setText(tr("New Group:")); newGroup_->setCheckState(Qt::Unchecked); newGroupLayout->addWidget(newGroup_); newGroupName_ = new QLineEdit(groups); newGroupLayout->addWidget(newGroupName_); scrollLayout->addLayout(newGroupLayout); scrollLayout->addItem(new QSpacerItem(20, 73, QSizePolicy::Minimum, QSizePolicy::Expanding)); } void QtContactEditWidget::setName(const std::string& name) { name_->setText(P2QSTRING(name)); } std::string QtContactEditWidget::getName() const { std::string name = Q2PSTRING(name_->text()); QList buttons = findChildren(); foreach(const QRadioButton* button, buttons) { if (button->isChecked()) { if (button == nameRadioButton_) { name = Q2PSTRING(name_->text()); } else { name = singleAmpersand(button->text()); } break; } } return name; } void QtContactEditWidget::setSelectedGroups(const std::vector& groups) { foreach (std::string group, groups) { checkBoxes_[group]->setCheckState(Qt::Checked); } } std::set QtContactEditWidget::getSelectedGroups() const { std::set groups; foreach(const CheckBoxMap::value_type& group, checkBoxes_) { if (group.second->checkState() == Qt::Checked) { groups.insert(group.first); } } if (newGroup_->checkState() == Qt::Checked && !newGroupName_->text().isEmpty()) { groups.insert(Q2PSTRING(newGroupName_->text())); } return groups; } void QtContactEditWidget::setNameSuggestions(const std::vector& suggestions) { throbberLabel_->movie()->stop(); throbberLabel_->hide(); foreach(const std::string& name, suggestions) { suggestionsLayout_->insertWidget(nameLayout_->count() - 2, new QRadioButton(doubleAmpersand(name), this)); } nameRadioButton_ = new QRadioButton(tr("Name:"), this); suggestionsLayout_->insertWidget(nameLayout_->count(), nameRadioButton_); QRadioButton* suggestedRadioButton = 0; QList radioButtons = findChildren(); foreach (QRadioButton* candidate, radioButtons) { if (candidate->text() == name_->text()) { suggestedRadioButton = candidate; break; } } if (suggestedRadioButton) { suggestedRadioButton->setChecked(true); } else { nameRadioButton_->setChecked(true); } } QString QtContactEditWidget::doubleAmpersand(const std::string& name) const { return P2QSTRING(name).replace("&", "&&"); } std::string QtContactEditWidget::singleAmpersand(const QString& name) const { return Q2PSTRING(QString(name).replace("&&", "&")); } void QtContactEditWidget::clear() { name_->clear(); setSelectedGroups(std::vector()); newGroup_->setChecked(false); newGroupName_->clear(); throbberLabel_->movie()->start(); throbberLabel_->show(); // clear suggestions while(suggestionsLayout_->count() != 0) { QLayoutItem *layoutItem = suggestionsLayout_->takeAt(0); delete layoutItem->widget(); delete layoutItem; } nameRadioButton_ = NULL; } } swift-im-2.0+dev6/Swift/QtUI/ChatView/0000755000175000017500000000000012227051774017313 5ustar kismithkismithswift-im-2.0+dev6/Swift/QtUI/ChatView/ChatView.pro0000644000175000017500000000045312227051774021551 0ustar kismithkismithTEMPLATE = app TARGET = ChatView DEPENDPATH += . INCLUDEPATH += . ../../../Swiften/3rdParty/Boost QT += webkit network HEADERS += ../QtChatView.h SOURCES += main.cpp ../QtChatView.cpp ../ChatSnippet.cpp ../MessageSnippet.cpp ../SystemMessageSnippet.cpp RESOURCES += ../DefaultTheme.qrc ../Swift.qrc swift-im-2.0+dev6/Swift/QtUI/ChatView/main.cpp0000644000175000017500000001204312227051774020743 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../QtChatView.h" #include "../MessageSnippet.h" #include "../SystemMessageSnippet.h" using namespace Swift; /* class MyNetworkReply : public QNetworkReply { public: MyNetworkReply() { } qint64 readData(char*, qint64) { return 0; } virtual void abort() { } }; class MyNetworkAccessManager : public QNetworkAccessManager { public: MyNetworkAccessManager() { } QNetworkReply * createRequest (Operation op, const QNetworkRequest& request, QIODevice* outgoingData = 0) { assert(op == QNetworkAccessManager::GetOperation); qDebug() << "Requesting: " << request.url(); return QNetworkAccessManager::createRequest(op, request, outgoingData); //return new MyNetworkReply(); } }; QVBoxLayout* mainLayout = new QVBoxLayout(this); webView_ = new QWebView(this); QFile file(":/themes/Stockholm/Contents/Resources/Incoming/Content.html"); file.open(QIODevice::ReadOnly); QString content = QString::fromUtf8(file.readAll()); webPage_ = new QWebPage(this); webPage_->setNetworkAccessManager(new MyNetworkAccessManager()); webView_->setPage(webPage_); QString pagehtml = "" //"" "" "" "" "" + content + ""; qDebug() << pagehtml; webPage_->mainFrame()->setHtml(pagehtml); */ /* class ChatView : public QWidget { public: ChatView(QWidget* parent) : QWidget(parent) { setFocusPolicy(Qt::NoFocus); QVBoxLayout* mainLayout = new QVBoxLayout(this); mainLayout->setSpacing(0); mainLayout->setContentsMargins(0,0,0,0); webView_ = new QWebView(this); webView_->setFocusPolicy(Qt::NoFocus); mainLayout->addWidget(webView_); webPage_ = new QWebPage(this); webPage_->setLinkDelegationPolicy(QWebPage::DelegateAllLinks); webView_->setPage(webPage_); connect(webPage_, SIGNAL(selectionChanged()), SLOT(copySelectionToClipboard())); QString pageHTML = "
"; webPage_->mainFrame()->setHtml(pageHTML); } void appendHTML(const QString& html) { webPage_->mainFrame()->evaluateJavaScript( "newNode = document.createElement(\"div\");" "newNode.innerHTML = \"" + html + "\";" "chatElement = document.getElementById(\"chat\");" "chatElement.appendChild(newNode);"); webPage_->mainFrame()->setScrollBarValue(Qt::Vertical, webPage_->mainFrame()->scrollBarMaximum(Qt::Vertical)); } private: QWebView* webView_; QWebPage* webPage_; }; */ class MyWidget : public QWidget { Q_OBJECT public: MyWidget() : previousWasIncoming_(false), previousWasOutgoing_(false), previousWasSystem_(false) { QVBoxLayout* mainLayout = new QVBoxLayout(this); chatView_ = new QtChatView(this); mainLayout->addWidget(chatView_); input1_ = new QLineEdit(this); connect(input1_, SIGNAL(returnPressed()), SLOT(addIncoming())); mainLayout->addWidget(input1_); input2_ = new QLineEdit(this); connect(input2_, SIGNAL(returnPressed()), SLOT(addOutgoing())); mainLayout->addWidget(input2_); input3_ = new QLineEdit(this); connect(input3_, SIGNAL(returnPressed()), SLOT(addSystem())); mainLayout->addWidget(input3_); resize(300,200); } public slots: void addIncoming() { chatView_->addMessage(MessageSnippet(input1_->text(), "Me", QDateTime::currentDateTime(), "qrc:/icons/avatar.png", true, previousWasIncoming_)); previousWasIncoming_ = true; previousWasOutgoing_ = false; previousWasSystem_ = false; input1_->clear(); } void addOutgoing() { chatView_->addMessage(MessageSnippet(input2_->text(), "You", QDateTime::currentDateTime(), "qrc:/icons/avatar.png", false, previousWasOutgoing_)); previousWasIncoming_ = false; previousWasOutgoing_ = true; previousWasSystem_ = false; input2_->clear(); } void addSystem() { chatView_->addMessage(SystemMessageSnippet(input3_->text(), QDateTime::currentDateTime(), previousWasSystem_)); previousWasIncoming_ = false; previousWasOutgoing_ = false; previousWasSystem_ = true; input3_->clear(); } private: bool previousWasIncoming_; bool previousWasOutgoing_; bool previousWasSystem_; QtChatView* chatView_; QLineEdit* input1_; QLineEdit* input2_; QLineEdit* input3_; }; int main(int argc, char* argv[]) { QApplication app(argc, argv); MyWidget w; w.show(); return app.exec(); } #include "main.moc" swift-im-2.0+dev6/Swift/QtUI/QtConnectionSettingsWindow.cpp0000644000175000017500000001564112227051774023631 0ustar kismithkismith/* * Copyright (c) 2012 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Swift/QtUI/QtConnectionSettingsWindow.h" #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Swift { QtConnectionSettingsWindow::QtConnectionSettingsWindow(const ClientOptions& options) : QDialog() { ui.setupUi(this); connect(ui.connectionMethod, SIGNAL(currentIndexChanged(int)), ui.stackedWidget, SLOT(setCurrentIndex(int))); connect(ui.manual_manualHost, SIGNAL(toggled(bool)), ui.manual_manualHostNameLabel, SLOT(setEnabled(bool))); connect(ui.manual_manualHost, SIGNAL(toggled(bool)), ui.manual_manualHostName, SLOT(setEnabled(bool))); connect(ui.manual_manualHost, SIGNAL(toggled(bool)), ui.manual_manualHostPortLabel, SLOT(setEnabled(bool))); connect(ui.manual_manualHost, SIGNAL(toggled(bool)), ui.manual_manualHostPort, SLOT(setEnabled(bool))); connect(ui.manual_manualProxy, SIGNAL(toggled(bool)), ui.manual_manualProxyHostLabel, SLOT(setEnabled(bool))); connect(ui.manual_manualProxy, SIGNAL(toggled(bool)), ui.manual_manualProxyHost, SLOT(setEnabled(bool))); connect(ui.manual_manualProxy, SIGNAL(toggled(bool)), ui.manual_manualProxyPortLabel, SLOT(setEnabled(bool))); connect(ui.manual_manualProxy, SIGNAL(toggled(bool)), ui.manual_manualProxyPort, SLOT(setEnabled(bool))); connect(ui.bosh_manualProxy, SIGNAL(toggled(bool)), ui.bosh_manualProxyHostLabel, SLOT(setEnabled(bool))); connect(ui.bosh_manualProxy, SIGNAL(toggled(bool)), ui.bosh_manualProxyHost, SLOT(setEnabled(bool))); connect(ui.bosh_manualProxy, SIGNAL(toggled(bool)), ui.bosh_manualProxyPortLabel, SLOT(setEnabled(bool))); connect(ui.bosh_manualProxy, SIGNAL(toggled(bool)), ui.bosh_manualProxyPort, SLOT(setEnabled(bool))); connect(ui.manual_proxyType, SIGNAL(currentIndexChanged(int)), SLOT(handleProxyTypeChanged(int))); connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(handleAcceptRequested())); QtURLValidator* urlValidator = new QtURLValidator(this); ui.bosh_uri->setValidator(urlValidator); ui.manual_useTLS->setCurrentIndex(2); ui.manual_proxyType->setCurrentIndex(0); ClientOptions defaults; if (options.boshURL.isEmpty()) { bool isDefault = options.useStreamCompression == defaults.useStreamCompression; isDefault &= options.useTLS == defaults.useTLS; isDefault &= options.allowPLAINWithoutTLS == defaults.allowPLAINWithoutTLS; isDefault &= options.useStreamCompression == defaults.useStreamCompression; isDefault &= options.useAcks == defaults.useAcks; isDefault &= options.manualHostname == defaults.manualHostname; isDefault &= options.manualPort == defaults.manualPort; isDefault &= options.proxyType == defaults.proxyType; isDefault &= options.manualProxyHostname == defaults.manualProxyHostname; isDefault &= options.manualProxyPort == defaults.manualProxyPort; if (isDefault) { ui.connectionMethod->setCurrentIndex(0); } else { ui.connectionMethod->setCurrentIndex(1); ui.manual_useTLS->setCurrentIndex(options.useTLS); ui.manual_allowPLAINWithoutTLS->setChecked(options.allowPLAINWithoutTLS); ui.manual_allowCompression->setChecked(options.useStreamCompression); if (!options.manualHostname.empty()) { ui.manual_manualHost->setChecked(true); ui.manual_manualHostName->setText(P2QSTRING(options.manualHostname)); if (options.manualPort >=0) { ui.manual_manualHostPort->setText(P2QSTRING(boost::lexical_cast(options.manualPort))); } } ui.manual_proxyType->setCurrentIndex(options.proxyType); if (!options.manualProxyHostname.empty()) { ui.manual_manualProxy->setChecked(true); ui.manual_manualProxyHost->setText(P2QSTRING(options.manualProxyHostname)); ui.manual_manualProxyPort->setText(P2QSTRING(boost::lexical_cast(options.manualProxyPort))); } } } else { ui.connectionMethod->setCurrentIndex(2); ui.bosh_uri->setText(P2QSTRING(options.boshURL.toString())); if (!options.boshHTTPConnectProxyURL.isEmpty()) { ui.bosh_manualProxy->setChecked(true); ui.bosh_manualProxyHost->setText(P2QSTRING(options.boshHTTPConnectProxyURL.getHost())); if (options.boshHTTPConnectProxyURL.getPort()) { ui.bosh_manualProxyPort->setText(P2QSTRING(boost::lexical_cast(*options.boshHTTPConnectProxyURL.getPort()))); } } } } void QtConnectionSettingsWindow::handleProxyTypeChanged(int index) { bool proxySettingsVisible = index != NoProxy && index != SystemProxy; ui.manual_manualProxy->setVisible(proxySettingsVisible); ui.manual_manualProxyHostLabel->setVisible(proxySettingsVisible); ui.manual_manualProxyHost->setVisible(proxySettingsVisible); ui.manual_manualProxyPortLabel->setVisible(proxySettingsVisible); ui.manual_manualProxyPort->setVisible(proxySettingsVisible); } void QtConnectionSettingsWindow::handleAcceptRequested() { if (ui.connectionMethod->currentIndex() != 2 || ui.bosh_uri->hasAcceptableInput()) { accept(); } else { QMessageBox::critical(this, tr("Configuration invalid"), tr("The provided BOSH URL is not valid.")); } } ClientOptions QtConnectionSettingsWindow::getOptions() { ClientOptions options; if (ui.connectionMethod->currentIndex() > 0) { /* Not automatic */ if (ui.connectionMethod->currentIndex() == 1) { /* Manual */ options.useTLS = static_cast(ui.manual_useTLS->currentIndex()); options.useStreamCompression = ui.manual_allowCompression->isChecked(); options.allowPLAINWithoutTLS = ui.manual_allowPLAINWithoutTLS->isChecked(); if (ui.manual_manualHost->isChecked()) { options.manualHostname = Q2PSTRING(ui.manual_manualHostName->text()); try { options.manualPort = boost::lexical_cast(Q2PSTRING(ui.manual_manualHostPort->text())); } catch (const boost::bad_lexical_cast&) { options.manualPort = -1; } } options.proxyType = static_cast(ui.manual_proxyType->currentIndex()); if (ui.manual_manualProxy->isChecked()) { options.manualProxyHostname = Q2PSTRING(ui.manual_manualProxyHost->text()); try { options.manualProxyPort = boost::lexical_cast(Q2PSTRING(ui.manual_manualProxyPort->text())); } catch (const boost::bad_lexical_cast&) {} } } else { /* BOSH */ options.boshURL = URL::fromString(Q2PSTRING(ui.bosh_uri->text())); if (ui.bosh_manualProxy->isChecked()) { std::string host = Q2PSTRING(ui.bosh_manualProxyHost->text()); try { int port = boost::lexical_cast(Q2PSTRING(ui.bosh_manualProxyPort->text())); options.boshHTTPConnectProxyURL = URL("http", host, port, ""); } catch (const boost::bad_lexical_cast&) { options.boshHTTPConnectProxyURL = URL("http", host, ""); } } } } return options; } } swift-im-2.0+dev6/Swift/QtUI/QtChatWindow.h0000644000175000017500000002154212227051774020332 0ustar kismithkismith/* * Copyright (c) 2010-2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include #include #include #include #include #include #include class QTextEdit; class QLineEdit; class QComboBox; class QLabel; class QSplitter; class QPushButton; namespace Swift { class QtChatView; class QtOccupantListWidget; class QtChatTheme; class TreeWidget; class QtTextEdit; class UIEventStream; class QtChatWindowJSBridge; class SettingsProvider; class LabelModel : public QAbstractListModel { Q_OBJECT public: LabelModel(QObject* parent = NULL) : QAbstractListModel(parent) {} virtual int rowCount(const QModelIndex& /*index*/) const { return static_cast(availableLabels_.size()); } virtual QVariant data(const QModelIndex& index, int role) const { if (!index.isValid()) { return QVariant(); } SecurityLabel::ref label = availableLabels_[index.row()].getLabel(); if (label && role == Qt::TextColorRole) { return P2QSTRING(label->getForegroundColor()); } if (label && role == Qt::TextColorRole) { return P2QSTRING(label->getBackgroundColor()); } if (role == Qt::DisplayRole) { std::string selector = availableLabels_[index.row()].getSelector(); std::string displayMarking = label ? label->getDisplayMarking() : ""; QString labelName = selector.empty() ? displayMarking.c_str() : selector.c_str(); return labelName; } return QVariant(); } std::vector availableLabels_; }; class QtChatWindow : public QtTabbable, public ChatWindow { Q_OBJECT public: static const QString ButtonWhiteboardSessionCancel; static const QString ButtonWhiteboardSessionAcceptRequest; static const QString ButtonWhiteboardShowWindow; static const QString ButtonFileTransferCancel; static const QString ButtonFileTransferSetDescription; static const QString ButtonFileTransferSendRequest; static const QString ButtonFileTransferAcceptRequest; static const QString ButtonMUCInvite; public: QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventStream* eventStream, SettingsProvider* settings, QMap emoticons); ~QtChatWindow(); std::string addMessage(const std::string &message, const std::string &senderName, bool senderIsSelf, boost::shared_ptr label, const std::string& avatarPath, const boost::posix_time::ptime& time); std::string addAction(const std::string &message, const std::string &senderName, bool senderIsSelf, boost::shared_ptr label, const std::string& avatarPath, const boost::posix_time::ptime& time); void addSystemMessage(const std::string& message); void addPresenceMessage(const std::string& message); void addErrorMessage(const std::string& errorMessage); void replaceMessage(const std::string& message, const std::string& id, const boost::posix_time::ptime& time); void replaceWithAction(const std::string& message, const std::string& id, const boost::posix_time::ptime& time); // File transfer related stuff std::string addFileTransfer(const std::string& senderName, bool senderIsSelf, const std::string& filename, const boost::uintmax_t sizeInBytes); void setFileTransferProgress(std::string id, const int percentageDone); void setFileTransferStatus(std::string id, const FileTransferState state, const std::string& msg); std::string addWhiteboardRequest(bool senderIsSelf); void setWhiteboardSessionStatus(std::string id, const ChatWindow::WhiteboardSessionState state); void show(); void activate(); void setUnreadMessageCount(int count); void convertToMUC(); // TreeWidget *getTreeWidget(); void setAvailableSecurityLabels(const std::vector& labels); void setSecurityLabelsEnabled(bool enabled); void setSecurityLabelsError(); SecurityLabelsCatalog::Item getSelectedSecurityLabel(); void setName(const std::string& name); void setInputEnabled(bool enabled); QtTabbable::AlertType getWidgetAlertState(); void setContactChatState(ChatState::ChatStateType state); void setRosterModel(Roster* roster); void setTabComplete(TabComplete* completer); int getCount(); void replaceLastMessage(const std::string& message); void setAckState(const std::string& id, AckState state); // message receipts void setMessageReceiptState(const std::string& id, ChatWindow::ReceiptState state); void flash(); QByteArray getSplitterState(); virtual void setAvailableOccupantActions(const std::vector& actions); void setSubject(const std::string& subject); void showRoomConfigurationForm(Form::ref); void addMUCInvitation(const std::string& senderName, const JID& jid, const std::string& reason, const std::string& password, bool direct = true); void setAffiliations(MUCOccupant::Affiliation, const std::vector&); void setAvailableRoomActions(const std::vector &actions); InviteToChatWindow* createInviteToChatWindow(); static QString buildChatWindowButton(const QString& name, const QString& id, const QString& arg1 = QString(), const QString& arg2 = QString(), const QString& arg3 = QString()); public slots: void handleChangeSplitterState(QByteArray state); void handleFontResized(int fontSizeSteps); void setAlert(const std::string& alertText, const std::string& buttonText = ""); void cancelAlert(); void setCorrectionEnabled(Tristate enabled); signals: void geometryChanged(); void splitterMoved(); void fontResized(int); protected slots: void qAppFocusChanged(QWidget* old, QWidget* now); void closeEvent(QCloseEvent* event); void resizeEvent(QResizeEvent* event); void moveEvent(QMoveEvent* event); void dragEnterEvent(QDragEnterEvent *event); void dropEvent(QDropEvent *event); protected: void showEvent(QShowEvent* event); private slots: void handleLogCleared(); void returnPressed(); void handleInputChanged(); void handleCursorPositionChanged(); void handleKeyPressEvent(QKeyEvent* event); void handleSplitterMoved(int pos, int index); void handleAlertButtonClicked(); void handleActionButtonClicked(); void handleHTMLButtonClicked(QString id, QString arg1, QString arg2, QString arg3); void handleAffiliationEditorAccepted(); void handleCurrentLabelChanged(int); private: enum PreviousMessageKind { PreviosuMessageWasNone, PreviousMessageWasMessage, PreviousMessageWasSystem, PreviousMessageWasPresence, PreviousMessageWasFileTransfer, PreviousMessageWasMUCInvite }; private: void updateTitleWithUnreadCount(); void tabComplete(); void beginCorrection(); void cancelCorrection(); void handleSettingChanged(const std::string& setting); std::string addMessage(const QString& message, const std::string& senderName, bool senderIsSelf, boost::shared_ptr label, const std::string& avatarPath, const QString& style, const boost::posix_time::ptime& time); void replaceMessage(const QString& message, const std::string& id, const boost::posix_time::ptime& time, const QString& style); void handleOccupantSelectionChanged(RosterItem* item); bool appendToPreviousCheck(PreviousMessageKind messageKind, const std::string& senderName, bool senderIsSelf) const; QString linkimoticonify(const QString& message) const; int unreadCount_; bool contactIsTyping_; LastLineTracker lastLineTracker_; QString contact_; QString lastSentMessage_; QTextCursor tabCompleteCursor_; QtChatView* messageLog_; QtChatTheme* theme_; QtTextEdit* input_; QWidget* midBar_; QComboBox* labelsWidget_; QtOccupantListWidget* treeWidget_; QLabel* correctingLabel_; QLabel* alertLabel_; QWidget* alertWidget_; QPushButton* alertButton_; TabComplete* completer_; QLineEdit* subject_; QPushButton* actionButton_; bool isCorrection_; bool previousMessageWasSelf_; PreviousMessageKind previousMessageKind_; QString previousSenderName_; bool inputClearing_; bool tabCompletion_; UIEventStream* eventStream_; bool inputEnabled_; QSplitter *logRosterSplitter_; Tristate correctionEnabled_; QString alertStyleSheet_; std::map descriptions; QtChatWindowJSBridge* jsBridge; QPointer mucConfigurationWindow_; QPointer affiliationEditor_; int idCounter_; SettingsProvider* settings_; std::vector availableRoomActions_; QMap emoticons_; bool showEmoticons_; QPalette defaultLabelsPalette_; LabelModel* labelModel_; }; } swift-im-2.0+dev6/Swift/QtUI/QtSwift.cpp0000644000175000017500000002007412227051774017711 0ustar kismithkismith/* * Copyright (c) 2010-2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "QtSwift.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "Swiften/Base/Paths.h" #if defined(SWIFTEN_PLATFORM_WINDOWS) #include "WindowsNotifier.h" #elif defined(HAVE_GROWL) #include "SwifTools/Notifier/GrowlNotifier.h" #elif defined(SWIFTEN_PLATFORM_LINUX) #include "FreeDesktopNotifier.h" #else #include "SwifTools/Notifier/NullNotifier.h" #endif #if defined(SWIFTEN_PLATFORM_MACOSX) #include "SwifTools/Dock/MacOSXDock.h" #else #include "SwifTools/Dock/NullDock.h" #endif #if defined(SWIFTEN_PLATFORM_MACOSX) #include "QtURIHandler.h" #elif defined(SWIFTEN_PLATFORM_WIN32) #include #else #include "QtDBUSURIHandler.h" #endif namespace Swift{ #if defined(SWIFTEN_PLATFORM_MACOSX) #define SWIFT_APPCAST_URL "http://swift.im/appcast/swift-mac-dev.xml" #else #define SWIFT_APPCAST_URL "" #endif po::options_description QtSwift::getOptionsDescription() { po::options_description result("Options"); result.add_options() ("debug", "Turn on debug logging") ("help", "Show this help message") ("version", "Show version information") ("netbook-mode", "Use netbook mode display (unsupported)") ("no-tabs", "Don't manage chat windows in tabs (unsupported)") ("latency-debug", "Use latency debugging (unsupported)") ("multi-account", po::value()->default_value(1), "Number of accounts to open windows for (unsupported)") ("start-minimized", "Don't show the login/roster window at startup") #if QT_VERSION >= 0x040800 ("language", po::value(), "Use a specific language, instead of the system-wide one") #endif ; return result; } XMLSettingsProvider* QtSwift::loadSettingsFile(const QString& fileName) { QFile configFile(fileName); if (configFile.exists() && configFile.open(QIODevice::ReadOnly)) { QString xmlString; while (!configFile.atEnd()) { QByteArray line = configFile.readLine(); xmlString += line + "\n"; } return new XMLSettingsProvider(Q2PSTRING(xmlString)); } return new XMLSettingsProvider(""); } QMap QtSwift::loadEmoticonsFile(const QString& fileName) { QMap emoticons; QFile file(fileName); if (file.exists() && file.open(QIODevice::ReadOnly)) { while (!file.atEnd()) { QString line = file.readLine(); line.replace("\n", ""); line.replace("\r", ""); qDebug() << "Parsing line : " << line; QStringList tokens = line.split(" "); if (tokens.size() == 2) { emoticons[tokens[0]] = "file://" + tokens[1]; qDebug() << "Adding mapping from " << tokens[0] << " to " << tokens[1]; } } } return emoticons; } QtSwift::QtSwift(const po::variables_map& options) : networkFactories_(&clientMainThreadCaller_), autoUpdater_(NULL), idleDetector_(&idleQuerier_, networkFactories_.getTimerFactory(), 1000) { if (options.count("netbook-mode")) { splitter_ = new QSplitter(); } else { splitter_ = NULL; } QCoreApplication::setApplicationName(SWIFT_APPLICATION_NAME); QCoreApplication::setOrganizationName(SWIFT_ORGANIZATION_NAME); QCoreApplication::setOrganizationDomain(SWIFT_ORGANIZATION_DOMAIN); QCoreApplication::setApplicationVersion(buildVersion); qtSettings_ = new QtSettingsProvider(); xmlSettings_ = loadSettingsFile(P2QSTRING((Paths::getExecutablePath() / "system-settings.xml").string())); settingsHierachy_ = new SettingsProviderHierachy(); settingsHierachy_->addProviderToTopOfStack(xmlSettings_); settingsHierachy_->addProviderToTopOfStack(qtSettings_); QMap emoticons = loadEmoticonsFile(P2QSTRING((Paths::getExecutablePath() / "emoticons.txt").string())); int numberOfAccounts = 1; try { numberOfAccounts = options["multi-account"].as(); } catch (...) { /* This seems to fail on a Mac when the .app is launched directly (the usual path).*/ numberOfAccounts = 1; } if (options.count("debug")) { Swift::logging = true; } tabs_ = options.count("no-tabs") && !splitter_ ? NULL : new QtChatTabs(); bool startMinimized = options.count("start-minimized") > 0; applicationPathProvider_ = new PlatformApplicationPathProvider(SWIFT_APPLICATION_NAME); storagesFactory_ = new FileStoragesFactory(applicationPathProvider_->getDataDir()); certificateStorageFactory_ = new CertificateFileStorageFactory(applicationPathProvider_->getDataDir(), tlsFactories_.getCertificateFactory()); chatWindowFactory_ = new QtChatWindowFactory(splitter_, settingsHierachy_, qtSettings_, tabs_, "", emoticons); soundPlayer_ = new QtSoundPlayer(applicationPathProvider_); // Ugly, because the dock depends on the tray, but the temporary // multi-account hack creates one tray per account. QtSystemTray* systemTray = new QtSystemTray(); systemTrays_.push_back(systemTray); #if defined(HAVE_GROWL) notifier_ = new GrowlNotifier(SWIFT_APPLICATION_NAME); #elif defined(SWIFTEN_PLATFORM_WINDOWS) notifier_ = new WindowsNotifier(SWIFT_APPLICATION_NAME, applicationPathProvider_->getResourcePath("/images/logo-icon-32.png"), systemTray->getQSystemTrayIcon()); #elif defined(SWIFTEN_PLATFORM_LINUX) notifier_ = new FreeDesktopNotifier(SWIFT_APPLICATION_NAME); #else notifier_ = new NullNotifier(); #endif #if defined(SWIFTEN_PLATFORM_MACOSX) dock_ = new MacOSXDock(&cocoaApplication_); #else dock_ = new NullDock(); #endif #if defined(SWIFTEN_PLATFORM_MACOSX) uriHandler_ = new QtURIHandler(); #elif defined(SWIFTEN_PLATFORM_WIN32) uriHandler_ = new NullURIHandler(); #else uriHandler_ = new QtDBUSURIHandler(); #endif if (splitter_) { splitter_->show(); } for (int i = 0; i < numberOfAccounts; i++) { if (i > 0) { // Don't add the first tray (see note above) systemTrays_.push_back(new QtSystemTray()); } QtUIFactory* uiFactory = new QtUIFactory(settingsHierachy_, qtSettings_, tabs_, splitter_, systemTrays_[i], chatWindowFactory_, networkFactories_.getTimerFactory(), startMinimized, !emoticons.empty()); uiFactories_.push_back(uiFactory); MainController* mainController = new MainController( &clientMainThreadCaller_, &networkFactories_, uiFactory, settingsHierachy_, systemTrays_[i], soundPlayer_, storagesFactory_, certificateStorageFactory_, dock_, notifier_, uriHandler_, &idleDetector_, options.count("latency-debug") > 0); mainControllers_.push_back(mainController); } // PlatformAutoUpdaterFactory autoUpdaterFactory; // if (autoUpdaterFactory.isSupported()) { // autoUpdater_ = autoUpdaterFactory.createAutoUpdater(SWIFT_APPCAST_URL); // autoUpdater_->checkForUpdates(); // } } QtSwift::~QtSwift() { delete autoUpdater_; foreach (QtUIFactory* factory, uiFactories_) { delete factory; } foreach (MainController* controller, mainControllers_) { delete controller; } delete notifier_; delete settingsHierachy_; delete qtSettings_; delete xmlSettings_; foreach (QtSystemTray* tray, systemTrays_) { delete tray; } delete tabs_; delete splitter_; delete uriHandler_; delete dock_; delete soundPlayer_; delete chatWindowFactory_; delete certificateStorageFactory_; delete storagesFactory_; } } swift-im-2.0+dev6/Swift/QtUI/QtSystemTray.cpp0000644000175000017500000000554212227051774020744 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma GCC diagnostic ignored "-Wredundant-decls" #include "Swift/QtUI/QtSystemTray.h" #include #ifdef Q_WS_X11 #include #endif #include #include #include #include #include namespace Swift { QtSystemTray::QtSystemTray() : QObject(), trayMenu_(0), onlineIcon_(":icons/online.png"), awayIcon_(":icons/away.png"), dndIcon_(":icons/dnd.png"), offlineIcon_(":icons/offline.png"), newMessageIcon_(":icons/new-chat.png"), throbberMovie_(":/icons/connecting.mng"), unreadMessages_(false), connecting_(false) { trayIcon_ = new QSystemTrayIcon(offlineIcon_); trayIcon_->setToolTip("Swift"); connect(trayIcon_, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(handleIconActivated(QSystemTrayIcon::ActivationReason))); connect(&throbberMovie_, SIGNAL(frameChanged(int)), this, SLOT(handleThrobberFrameChanged(int))); #ifdef Q_WS_X11 bool isUnity = QDBusInterface("com.canonical.Unity.Launcher", "/com/canonical/Unity/Launcher").isValid(); if (isUnity) { // Add an activation menu, because this is the only way to get the application to the // front on Unity. See the README for sni-qt (which handles Qt tray icons for Unity) trayMenu_ = new QMenu(); QAction* showAction = new QAction(QString("Show/Hide"), this); connect(showAction, SIGNAL(triggered()), SIGNAL(clicked())); trayMenu_->addAction(showAction); trayIcon_->setContextMenu(trayMenu_); } #endif trayIcon_->show(); } QtSystemTray::~QtSystemTray() { delete trayMenu_; delete trayIcon_; } void QtSystemTray::setUnreadMessages(bool some) { unreadMessages_ = some; updateStatusIcon(); } void QtSystemTray::handleThrobberFrameChanged(int /*frameNumber*/) { trayIcon_->setIcon(QIcon(throbberMovie_.currentPixmap())); } void QtSystemTray::setConnecting() { connecting_ = true; updateStatusIcon(); } void QtSystemTray::handleIconActivated(QSystemTrayIcon::ActivationReason reason) { if (reason == QSystemTrayIcon::Trigger) { emit clicked(); } } void QtSystemTray::setStatusType(StatusShow::Type type) { connecting_ = false; statusType_ = type; updateStatusIcon(); } void QtSystemTray::updateStatusIcon() { throbberMovie_.stop(); if (unreadMessages_) { trayIcon_->setIcon(newMessageIcon_); } else if (connecting_) { throbberMovie_.start(); } else { switch (statusType_) { case StatusShow::Online : trayIcon_->setIcon(onlineIcon_);break; case StatusShow::FFC : trayIcon_->setIcon(onlineIcon_);break; case StatusShow::Away : trayIcon_->setIcon(awayIcon_);break; case StatusShow::XA : trayIcon_->setIcon(awayIcon_);break; case StatusShow::DND : trayIcon_->setIcon(dndIcon_);break; case StatusShow::None : trayIcon_->setIcon(offlineIcon_);break; } } } } swift-im-2.0+dev6/Swift/QtUI/QtURIHandler.cpp0000644000175000017500000000126112227051774020547 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "QtURIHandler.h" #include #include #include #include "QtSwiftUtil.h" using namespace Swift; QtURIHandler::QtURIHandler() { qApp->installEventFilter(this); } bool QtURIHandler::eventFilter(QObject*, QEvent* event) { if (event->type() == QEvent::FileOpen) { QFileOpenEvent* fileOpenEvent = static_cast(event); if (fileOpenEvent->url().scheme() == "xmpp") { onURI(Q2PSTRING(fileOpenEvent->url().toString())); return true; } } return false; } swift-im-2.0+dev6/Swift/QtUI/QtNameWidget.cpp0000644000175000017500000000434512227051774020644 0ustar kismithkismith/* * Copyright (c) 2010-2012 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "QtNameWidget.h" #include #include #include #include #include #include #include namespace Swift { QtNameWidget::QtNameWidget(SettingsProvider* settings, QWidget *parent) : QWidget(parent), settings(settings) { QHBoxLayout* mainLayout = new QHBoxLayout(this); mainLayout->setSpacing(0); mainLayout->setContentsMargins(0,0,0,0); mode = settings->getSetting(QtUISettingConstants::SHOW_NICK_IN_ROSTER_HEADER) ? ShowNick : ShowJID; textLabel = new QtElidingLabel(this); QFont font = textLabel->font(); font.setBold(true); textLabel->setFont(font); mainLayout->addWidget(textLabel); } void QtNameWidget::setNick(const QString& nick) { this->nick = nick; updateText(); } void QtNameWidget::setJID(const QString& jid) { this->jid = jid; updateText(); } void QtNameWidget::mousePressEvent(QMouseEvent* event) { QMenu menu; bool hasNick = !nick.isEmpty(); QAction* showAsNick = new QAction(hasNick ? tr("Show Nickname") : tr("(No Nickname Set)"), this); showAsNick->setCheckable(true); showAsNick->setEnabled(hasNick); if (mode == ShowNick && hasNick) { showAsNick->setChecked(true); } menu.addAction(showAsNick); QAction* showAsJID = new QAction(tr("Show Address"), this); showAsJID->setCheckable(true); if (mode == ShowJID || !hasNick) { showAsJID->setChecked(true); } menu.addAction(showAsJID); QAction* editProfile = new QAction(tr("Edit Profile"), this); menu.addAction(editProfile); QAction* result = menu.exec(event->globalPos()); if (result == showAsJID) { mode = ShowJID; } else if (result == showAsNick) { mode = ShowNick; } else if (result == editProfile) { emit onChangeNickRequest(); } settings->storeSetting(QtUISettingConstants::SHOW_NICK_IN_ROSTER_HEADER, mode == ShowNick); updateText(); } void QtNameWidget::updateText() { QString text; if (mode == ShowNick && !nick.isEmpty()) { text = nick; } else { text = jid; } text.replace("<","<"); textLabel->setText(text); } } swift-im-2.0+dev6/Swift/QtUI/QtDBUSURIHandler.cpp0000644000175000017500000000170712227051774021232 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "QtDBUSURIHandler.h" #include #include #include "QtSwiftUtil.h" using namespace Swift; namespace { class DBUSAdaptor: public QDBusAbstractAdaptor { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "im.swift.Swift.URIHandler"); public: DBUSAdaptor(QtDBUSURIHandler* uriHandler) : QDBusAbstractAdaptor(uriHandler), uriHandler(uriHandler) { } public slots: void openURI(const QString& uri) { uriHandler->onURI(Q2PSTRING(uri)); } private: QtDBUSURIHandler* uriHandler; }; } QtDBUSURIHandler::QtDBUSURIHandler() { new DBUSAdaptor(this); QDBusConnection connection = QDBusConnection::sessionBus(); connection.registerService("im.swift.Swift.URIHandler"); connection.registerObject("/", this); } #include "QtDBUSURIHandler.moc" swift-im-2.0+dev6/Swift/QtUI/QtURLValidator.h0000644000175000017500000000057112227051774020572 0ustar kismithkismith/* * Copyright (c) 2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { class QtURLValidator : public QValidator { Q_OBJECT public: QtURLValidator(QObject* parent); virtual QValidator::State validate(QString& input, int& pos) const; }; } swift-im-2.0+dev6/Swift/QtUI/QtChatWindowFactory.h0000644000175000017500000000220612227051774021656 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include "Swift/Controllers/UIInterfaces/ChatWindowFactory.h" #include "Swiften/JID/JID.h" #include "QtSettingsProvider.h" #include #include namespace Swift { class QtChatTabs; class QtChatTheme; class UIEventStream; class QtUIPreferences; class QtChatWindowFactory : public QObject, public ChatWindowFactory { Q_OBJECT public: QtChatWindowFactory(QSplitter* splitter, SettingsProvider* settings, QtSettingsProvider* qtSettings, QtChatTabs* tabs, const QString& themePath, QMap emoticons); ~QtChatWindowFactory(); ChatWindow* createChatWindow(const JID &contact, UIEventStream* eventStream); signals: void changeSplitterState(QByteArray); private slots: void handleWindowGeometryChanged(); void handleSplitterMoved(); private: QString themePath_; SettingsProvider* settings_; QtSettingsProvider* qtOnlySettings_; QtChatTabs* tabs_; QtChatTheme* theme_; QMap emoticons_; }; } swift-im-2.0+dev6/Swift/QtUI/MessageSnippet.h0000644000175000017500000000130412227051774020677 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include "ChatSnippet.h" class QDateTime; namespace Swift { class MessageSnippet : public ChatSnippet { public: MessageSnippet(const QString& message, const QString& sender, const QDateTime& time, const QString& iconURI, bool isIncoming, bool appendToPrevious, QtChatTheme* theme, const QString& id); virtual ~MessageSnippet(); const QString& getContent() const { return content_; } QString getContinuationElementID() const { return "insert"; }; private: QString content_; }; } swift-im-2.0+dev6/Swift/QtUI/WinUIHelpers.cpp0000644000175000017500000000405212227051774020624 0ustar kismithkismith/* * Copyright (c) 2012 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include "WinUIHelpers.h" #include #include #include #pragma comment(lib, "cryptui.lib") #include #include namespace Swift { void WinUIHelpers::displayCertificateChainAsSheet(QWidget* parent, const std::vector& chain) { if (chain.empty()) { return; } // create certificate store to store the certificate chain in HCERTSTORE chainStore = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, NULL, CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, NULL); if (!chainStore) { return; } ByteArray certAsDER = chain[0]->toDER(); boost::shared_ptr certificate_chain; { PCCERT_CONTEXT certChain; BOOL ok = CertAddCertificateContextToStore(chainStore, CertCreateCertificateContext(X509_ASN_ENCODING, vecptr(certAsDER), certAsDER.size()), CERT_STORE_ADD_ALWAYS, &certChain); // maybe free the cert contex we created if (!ok || !certChain) { return; } certificate_chain.reset(certChain, CertFreeCertificateContext); } for (size_t i = 1; i < chain.size(); ++i) { ByteArray certAsDER = chain[i]->toDER(); CertAddCertificateContextToStore(chainStore, CertCreateCertificateContext(X509_ASN_ENCODING, vecptr(certAsDER), certAsDER.size()), CERT_STORE_ADD_ALWAYS, NULL); } CRYPTUI_VIEWCERTIFICATE_STRUCT viewDialogProperties = { 0 }; viewDialogProperties.dwSize = sizeof(viewDialogProperties); viewDialogProperties.hwndParent = parent->winId(); viewDialogProperties.dwFlags = CRYPTUI_DISABLE_EDITPROPERTIES | CRYPTUI_DISABLE_ADDTOSTORE | CRYPTUI_ENABLE_REVOCATION_CHECKING; viewDialogProperties.pCertContext = certificate_chain.get(); viewDialogProperties.cStores = 1; viewDialogProperties.rghStores = &chainStore; BOOL properties_changed; // blocking call that shows modal certificate dialog BOOL rv = ::CryptUIDlgViewCertificate(&viewDialogProperties, &properties_changed); } } swift-im-2.0+dev6/Swift/QtUI/QtLineEdit.h0000644000175000017500000000063612227051774017761 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include namespace Swift { class QtLineEdit : public QLineEdit { Q_OBJECT public: QtLineEdit(QWidget* parent = NULL); signals: void escapePressed(); protected: virtual void keyPressEvent(QKeyEvent* event); }; } swift-im-2.0+dev6/Swift/QtUI/QtTabbable.cpp0000644000175000017500000000305512227051774020311 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "QtTabbable.h" #include #include "QtChatTabs.h" namespace Swift { QtTabbable::~QtTabbable() { emit windowClosing(); } bool QtTabbable::isWidgetSelected() { /*isActiveWindow() shouldn't be necessary, but I don't trust it as far as I can throw it*/ if (!isActiveWindow()) { return false; } QtChatTabs* parent = qobject_cast(window()); return parent ? parent->getCurrentTab() == this : isAncestorOf(QApplication::focusWidget()); } void QtTabbable::keyPressEvent(QKeyEvent *event) { handleKeyPressEvent(event); } void QtTabbable::handleKeyPressEvent(QKeyEvent *event) { event->ignore(); int key = event->key(); Qt::KeyboardModifiers modifiers = event->modifiers(); if (key == Qt::Key_W && modifiers == Qt::ControlModifier) { close(); event->accept(); } else if ( (key == Qt::Key_PageUp && modifiers == Qt::ControlModifier) // || (key == Qt::Key_Left && modifiers == (Qt::ControlModifier & Qt::ShiftModifier)) ) { emit requestPreviousTab(); event->accept(); } else if ( (key == Qt::Key_PageDown && modifiers == Qt::ControlModifier) // || (key == Qt::Key_Right && modifiers == (Qt::ControlModifier & Qt::ShiftModifier) || (key == Qt::Key_Tab && modifiers == Qt::ControlModifier) ) { emit requestNextTab(); event->accept(); } else if ( (key == Qt::Key_A && modifiers == Qt::AltModifier) ) { emit requestActiveTab(); event->accept(); } } } swift-im-2.0+dev6/Swift/QtUI/CocoaUIHelpers.mm0000644000175000017500000000264212227051774020745 0ustar kismithkismith/* * Copyright (c) 2012 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include "CocoaUIHelpers.h" #include #include #include #include #include #include #pragma GCC diagnostic ignored "-Wold-style-cast" namespace Swift { void CocoaUIHelpers::displayCertificateChainAsSheet(QWidget* parent, const std::vector& chain) { NSWindow* parentWindow = [((NSView*)parent->winId()) window]; NSMutableArray* certificates = [[NSMutableArray alloc] init]; foreach(Certificate::ref cert, chain) { // convert chain to SecCertificateRef ByteArray certAsDER = cert->toDER(); boost::shared_ptr::type> certData(CFDataCreate(NULL, certAsDER.data(), certAsDER.size()), CFRelease); boost::shared_ptr macCert(SecCertificateCreateWithData(NULL, certData.get()), CFRelease); // add to NSMutable array [certificates addObject: (id)macCert.get()]; } SFCertificatePanel* panel = [[SFCertificatePanel alloc] init]; //[panel setPolicies:(id)policies.get()]; [panel beginSheetForWindow:parentWindow modalDelegate:nil didEndSelector:NULL contextInfo:NULL certificates:certificates showGroup:YES]; [certificates release]; } } swift-im-2.0+dev6/Swift/QtUI/QtFormResultItemModel.h0000644000175000017500000000173212227051774022164 0ustar kismithkismith/* * Copyright (c) 2012 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include namespace Swift { class QtFormResultItemModel : public QAbstractTableModel { Q_OBJECT public: QtFormResultItemModel(QObject* parent); QtFormResultItemModel(QObject* parent, Form::ref formResult); void setForm(Form::ref formResult); const Form::ref getForm() const; virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const; virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; virtual int columnCount(const QModelIndex &parent = QModelIndex()) const; virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; private: const std::string getFieldValue(const Form::FormItem& item, const int column) const; private: Form::ref formResult_; }; } swift-im-2.0+dev6/Swift/QtUI/QtStatusWidget.h0000644000175000017500000000325012227051774020706 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include "Swiften/Elements/StatusShow.h" #include #include #include class QComboBox; class QLabel; class QStackedWidget; class QListWidget; class QListWidgetItem; class QMovie; namespace Swift { class QtLineEdit; class QtElidingLabel; class QtStatusWidget : public QWidget { Q_OBJECT public: QtStatusWidget(QWidget *parent); ~QtStatusWidget(); StatusShow::Type getSelectedStatusShow(); void setStatusType(StatusShow::Type type); void setConnecting(); signals: void onChangeStatusRequest(StatusShow::Type showType, const QString& text); public slots: void setStatusText(const QString& text); private slots: void generateList(); void handleClicked(); void handleEditComplete(); void handleEditCancelled(); void handleApplicationFocusChanged(QWidget* old, QWidget* now); protected slots: virtual void mousePressEvent(QMouseEvent* event); void handleItemClicked(QListWidgetItem* item); static QString getNoMessage(); private: void viewMode(); void setNewToolTip(); //QComboBox *types_; QStackedWidget* stack_; QLabel* statusIcon_; QtElidingLabel* statusTextLabel_; QtLineEdit* statusEdit_; QString statusText_; QString newStatusText_; QMap icons_; StatusShow::Type selectedStatusType_; bool isClicking_; QListWidget* menu_; QCursor editCursor_; QCursor viewCursor_; bool editing_; QMovie* connectingMovie_; bool connecting_; static const QString NO_MESSAGE; }; } swift-im-2.0+dev6/Swift/QtUI/QtUISettingConstants.cpp0000644000175000017500000000172112227051774022363 0ustar kismithkismith/* * Copyright (c) 2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { const SettingsProvider::Setting QtUISettingConstants::COMPACT_ROSTER("compactRoster", false); const SettingsProvider::Setting QtUISettingConstants::CLICKTHROUGH_BANNER("clickthroughBanner", ""); const SettingsProvider::Setting QtUISettingConstants::CURRENT_ROSTER_TAB("currentRosterTab", 0); const SettingsProvider::Setting QtUISettingConstants::SHOW_NICK_IN_ROSTER_HEADER("showNickInRosterHeader", true); const SettingsProvider::Setting QtUISettingConstants::CHATWINDOW_FONT_SIZE("chatWindowFontSize", 0); const SettingsProvider::Setting QtUISettingConstants::HISTORYWINDOW_FONT_SIZE("historyWindowFontSize", 0); const SettingsProvider::Setting QtUISettingConstants::SHOW_EMOTICONS("showEmoticons", true); } swift-im-2.0+dev6/Swift/QtUI/QtAvatarWidget.h0000644000175000017500000000130212227051774020635 0ustar kismithkismith/* * 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 #include #include class QLabel; namespace Swift { class QtAvatarWidget : public QWidget { Q_OBJECT public: QtAvatarWidget(QWidget* parent); void setAvatar(const ByteArray& data, const std::string& type); const ByteArray& getAvatarData() const { return data; } const std::string& getAvatarType() const { return type; } void mousePressEvent(QMouseEvent* event); private: ByteArray data; std::string type; QLabel* label; }; } swift-im-2.0+dev6/Swift/QtUI/SystemMessageSnippet.h0000644000175000017500000000117712227051774022114 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include "ChatSnippet.h" class QDateTime; namespace Swift { class SystemMessageSnippet : public ChatSnippet { public: SystemMessageSnippet(const QString& message, const QDateTime& time, bool appendToPrevious, QtChatTheme* theme); virtual ~SystemMessageSnippet(); const QString& getContent() const {return content_;} /*QString getContinuationElementID() const { return "insert"; };*/ private: QString content_; }; } swift-im-2.0+dev6/Swift/QtUI/QtUtilities.cpp0000644000175000017500000000132312227051774020564 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "QtUtilities.h" #include #ifdef Q_WS_X11 #include #include #include #endif #include "Swift/Controllers/ApplicationInfo.h" namespace QtUtilities { void setX11Resource(QWidget* widget, const QString& c) { #ifdef Q_WS_X11 char res_class[] = SWIFT_APPLICATION_NAME; XClassHint hint; hint.res_name = (QString(SWIFT_APPLICATION_NAME) + "-" + c).toUtf8().data(); hint.res_class = res_class; XSetClassHint(widget->x11Info().display(), widget->winId(), &hint); #else (void) widget; (void) c; #endif } } swift-im-2.0+dev6/Swift/QtUI/FreeDesktopNotifier.cpp0000644000175000017500000000364612227051774022231 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma GCC diagnostic ignored "-Wredundant-decls" #include "Swift/QtUI/FreeDesktopNotifier.h" #include #include #include #include #include namespace Swift { FreeDesktopNotifier::FreeDesktopNotifier(const std::string& name) : applicationName(name) { } void FreeDesktopNotifier::showMessage(Type type, const std::string& subject, const std::string& description, const boost::filesystem::path& picture, boost::function) { QDBusConnection bus = QDBusConnection::sessionBus(); if (!bus.isConnected()) { return; } std::vector defaultTypes = getDefaultTypes(); if (std::find(defaultTypes.begin(), defaultTypes.end(), type) == defaultTypes.end()) { return; } QString body = description.c_str(); body = body.replace("&", "&"); body = body.replace("<", "<"); body = body.replace(">", ">"); int timeout = (type == IncomingMessage || type == SystemMessage) ? DEFAULT_MESSAGE_NOTIFICATION_TIMEOUT_SECONDS : DEFAULT_STATUS_NOTIFICATION_TIMEOUT_SECONDS; QDBusMessage msg = QDBusMessage::createMethodCall("org.freedesktop.Notifications", "/org/freedesktop/Notifications", "", "Notify"); QStringList actions; QMap hints; hints["x-canonical-append"] = QString("allowed"); msg << applicationName.c_str(); msg << quint32(0); // ID of previous notification to replace msg << imageScaler.getScaledImage(picture, 48).string().c_str(); // Icon to display msg << subject.c_str(); // Summary / Header of the message to display msg << body; // Body of the message to display msg << actions; // Actions from which the user may choose msg << hints; // Hints to the server displaying the message msg << qint32(timeout*1000); // Timeout in milliseconds bus.asyncCall(msg); } } swift-im-2.0+dev6/Swift/QtUI/QtClickableLabel.h0000644000175000017500000000061412227051774021071 0ustar kismithkismith/* * 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 namespace Swift { class QtClickableLabel : public QLabel { Q_OBJECT public: QtClickableLabel(QWidget* parent); void mousePressEvent(QMouseEvent* event); signals: void clicked(); }; } swift-im-2.0+dev6/Swift/QtUI/QtUtilities.h0000644000175000017500000000044112227051774020231 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once class QWidget; class QString; namespace QtUtilities { void setX11Resource(QWidget* widget, const QString& c); }; swift-im-2.0+dev6/Swift/QtUI/QtHistoryWindow.h0000644000175000017500000000355312227051774021116 0ustar kismithkismith/* * Copyright (c) 2012 Catalin Badea * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include #include #include #include #include #include namespace Swift { class QtHistoryWindow : public QtTabbable, public HistoryWindow { Q_OBJECT public: QtHistoryWindow(SettingsProvider*, UIEventStream*); ~QtHistoryWindow(); void activate(); void setRosterModel(Roster*); void addMessage(const std::string& message, const std::string& senderName, bool senderIsSelf, const std::string& avatarPath, const boost::posix_time::ptime& time, bool addAtTheTop); void resetConversationView(); void resetConversationViewTopInsertPoint(); void setDate(const boost::gregorian::date& date); void closeEvent(QCloseEvent* event); void showEvent(QShowEvent* event); std::string getSearchBoxText(); boost::gregorian::date getLastVisibleDate(); signals: void fontResized(int); public slots: void handleFontResized(int fontSizeSteps); protected slots: void handleScrollRequested(int pos); void handleScrollReachedTop(); void handleScrollReachedBottom(); void handleReturnPressed(); void handleCalendarClicked(const QDate& date); void handlePreviousButtonClicked(); void handleNextButtonClicked(); private: void handleSomethingSelectedChanged(RosterItem* item); Ui::QtHistoryWindow ui_; QtChatTheme* theme_; QtChatView* conversation_; QtTreeWidget* conversationRoster_; std::set dates_; int idCounter_; bool previousTopMessageWasSelf_; QString previousTopSenderName_; bool previousBottomMessageWasSelf_; QString previousBottomSenderName_; }; } swift-im-2.0+dev6/Swift/QtUI/CocoaApplicationActivateHelper.mm0000644000175000017500000000334212227051774024167 0ustar kismithkismith/* * Copyright (c) 2012 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include "CocoaApplicationActivateHelper.h" #include #include @interface CocoaApplicationActivateHelperDelegate : NSObject { } - (void) handleActivate: (NSAppleEventDescriptor*) event withReply: (NSAppleEventDescriptor*) reply; @end @implementation CocoaApplicationActivateHelperDelegate - (void) handleActivate: (NSAppleEventDescriptor*) event withReply: (NSAppleEventDescriptor*) reply { (void) event; (void) reply; QApplication::postEvent(qApp, new QEvent(QEvent::ApplicationActivate)); } @end namespace Swift { struct CocoaApplicationActivateHelper::Private { CocoaApplicationActivateHelperDelegate* delegate; bool initialized; }; CocoaApplicationActivateHelper::CocoaApplicationActivateHelper() { p = new Private(); p->delegate = [[CocoaApplicationActivateHelperDelegate alloc] init]; p->initialized = false; qApp->installEventFilter(this); } CocoaApplicationActivateHelper::~CocoaApplicationActivateHelper() { [[NSAppleEventManager sharedAppleEventManager] removeEventHandlerForEventClass:kCoreEventClass andEventID:kAEReopenApplication]; [p->delegate release]; delete p; } bool CocoaApplicationActivateHelper::eventFilter(QObject* object, QEvent* event) { if (object == qApp && event->type() == QEvent::ApplicationActivate && !p->initialized) { [[NSAppleEventManager sharedAppleEventManager] setEventHandler:p->delegate andSelector:@selector(handleActivate:withReply:) forEventClass:kCoreEventClass andEventID:kAEReopenApplication]; p->initialized = true; } return QObject::eventFilter(object, event); } } swift-im-2.0+dev6/Swift/QtUI/QtTextEdit.cpp0000644000175000017500000000436012227051774020347 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include namespace Swift { QtTextEdit::QtTextEdit(QWidget* parent) : QTextEdit(parent){ connect(this, SIGNAL(textChanged()), this, SLOT(handleTextChanged())); handleTextChanged(); }; void QtTextEdit::keyPressEvent(QKeyEvent* event) { int key = event->key(); Qt::KeyboardModifiers modifiers = event->modifiers(); if ((key == Qt::Key_Enter || key == Qt::Key_Return) && (modifiers == Qt::NoModifier || modifiers == Qt::KeypadModifier)) { emit returnPressed(); } else if (((key == Qt::Key_PageUp || key == Qt::Key_PageDown) && modifiers == Qt::ShiftModifier) || (key == Qt::Key_C && modifiers == Qt::ControlModifier && textCursor().selectedText().isEmpty()) || (key == Qt::Key_W && modifiers == Qt::ControlModifier) || (key == Qt::Key_PageUp && modifiers == Qt::ControlModifier) || (key == Qt::Key_PageDown && modifiers == Qt::ControlModifier) || (key == Qt::Key_Tab && modifiers == Qt::ControlModifier) || (key == Qt::Key_A && modifiers == Qt::AltModifier) || (key == Qt::Key_Tab) ) { emit unhandledKeyPressEvent(event); } else if ((key == Qt::Key_Up) || (key == Qt::Key_Down) ){ emit unhandledKeyPressEvent(event); QTextEdit::keyPressEvent(event); } else { QTextEdit::keyPressEvent(event); } } void QtTextEdit::handleTextChanged() { QSize previous(maximumSize()); setMaximumSize(QSize(maximumWidth(), sizeHint().height())); if (previous != maximumSize()) { updateGeometry(); } } QSize QtTextEdit::sizeHint() const { QFontMetrics inputMetrics(currentFont()); QRect horizontalBounds = contentsRect().adjusted(0,0,0,9999); QRect boundingRect = inputMetrics.boundingRect(horizontalBounds, Qt::TextWordWrap, toPlainText() + "A"); int left, top, right, bottom; getContentsMargins(&left, &top, &right, &bottom); int height = boundingRect.height() + top + bottom + inputMetrics.height(); return QSize(width(), height); //int numberOfLines = 1; //int lineHeight = inputMetrics.lineSpacing(); //return QSize(QTextEdit::sizeHint().width(), lineHeight * numberOfLines); } } swift-im-2.0+dev6/Swift/QtUI/NotifierTest/0000755000175000017500000000000012227051774020220 5ustar kismithkismithswift-im-2.0+dev6/Swift/QtUI/NotifierTest/NotifierTest.cpp0000644000175000017500000000141412227051774023343 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include "Swiften/Base/ByteArray.h" #include "Swiften/Notifier/GrowlNotifier.h" #include using namespace Swift; void notificationClicked(const std::string& message) { std::cout << "Notification clicked: " << message << std::endl; } int main(int argc, char* argv[]) { QApplication app(argc, argv); GrowlNotifier notifier("Swift-NotifierTest"); notifier.showMessage(Notifier::ContactAvailable, "Contact is available", "The contact has become available", ByteArray(), boost::bind(¬ificationClicked, "Message 1")); return app.exec(); } swift-im-2.0+dev6/Swift/QtUI/NotifierTest/NotifierTest.pro0000644000175000017500000000032412227051774023360 0ustar kismithkismithTEMPLATE = app TARGET = DEPENDPATH += . INCLUDEPATH += . INCLUDEPATH += . ../../../Swiften/3rdParty/Boost INCLUDEPATH += . ../../.. SOURCES += NotifierTest.cpp LIBS += ../../../Swiften/Swift.a -framework Growl swift-im-2.0+dev6/Swift/QtUI/WinUIHelpers.h0000644000175000017500000000061312227051774020270 0ustar kismithkismith/* * Copyright (c) 2012 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include namespace Swift { class WinUIHelpers { public: static void displayCertificateChainAsSheet(QWidget* parent, const std::vector& chain); }; } swift-im-2.0+dev6/Swift/QtUI/CAPICertificateSelector.cpp0000644000175000017500000000711112227051774022665 0ustar kismithkismith/* * Copyright (c) 2012 Isode Limited, London, England. * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include #include #define SECURITY_WIN32 #include #include #include #include #include #include #include #include namespace Swift { /////Hmm, maybe we should not exlude the "location" column #define exclude_columns CRYPTUI_SELECT_LOCATION_COLUMN | CRYPTUI_SELECT_INTENDEDUSE_COLUMN #define SHA1_HASH_LENGTH 20 static std::string getCertUri(PCCERT_CONTEXT cert, const char * cert_store_name) { DWORD cbHash = SHA1_HASH_LENGTH; BYTE aHash[SHA1_HASH_LENGTH]; std::string result("certstore:"); result += cert_store_name; result += ":sha1:"; if (CertGetCertificateContextProperty(cert, CERT_HASH_PROP_ID, aHash, &cbHash) == FALSE ) { return ""; } ByteArray byteArray = createByteArray((char *)(&aHash[0]), cbHash); result += Hexify::hexify(byteArray); return result; } std::string selectCAPICertificate() { const char* certStoreName = "MY"; DWORD storeFlags = CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG | CERT_SYSTEM_STORE_CURRENT_USER; HCERTSTORE hstore = CertOpenStore(CERT_STORE_PROV_SYSTEM_A, 0, 0, storeFlags, certStoreName); if (!hstore) { return ""; } HWND hwnd = GetForegroundWindow(); if (!hwnd) { hwnd = GetActiveWindow(); } std::string certificateDialogTitle = QT_TRANSLATE_NOOP("", "TLS Client Certificate Selection"); std::string certificateDialogPrompt = QT_TRANSLATE_NOOP("", "Select a certificate to use for authentication"); int titleLength = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, certificateDialogTitle.c_str(), -1, NULL, 0); int promptLength = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, certificateDialogPrompt.c_str(), -1, NULL, 0); wchar_t* titleChars = new wchar_t[titleLength]; wchar_t* promptChars = new wchar_t[promptLength]; //titleChars[titleLength] = '\0'; //promptChars[promptLength] = '\0'; titleLength = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, certificateDialogTitle.c_str(), -1, titleChars, titleLength); promptLength = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, certificateDialogPrompt.c_str(), -1, promptChars, promptLength); if (titleLength == 0 || promptLength == 0) { int error = GetLastError(); switch (error) { case ERROR_INSUFFICIENT_BUFFER: SWIFT_LOG(error) << "Insufficient buffer for rendering cert dialog" << std::endl;break; case ERROR_INVALID_FLAGS: SWIFT_LOG(error) << "Invalid flags for rendering cert dialog" << std::endl;break; case ERROR_INVALID_PARAMETER: SWIFT_LOG(error) << "Invalid parameter for rendering cert dialog" << std::endl;break; case ERROR_NO_UNICODE_TRANSLATION: SWIFT_LOG(error) << "Invalid unicode for rendering cert dialog" << std::endl;break; default: SWIFT_LOG(error) << "Unexpected multibyte conversion errorcode" << std::endl; } } /* Call Windows dialog to select a suitable certificate */ PCCERT_CONTEXT cert = CryptUIDlgSelectCertificateFromStore(hstore, hwnd, titleChars, promptChars, exclude_columns, 0, NULL); delete[] titleChars; delete[] promptChars; if (hstore) { CertCloseStore(hstore, 0); } std::string result; if (cert) { result = getCertUri(cert, certStoreName); CertFreeCertificateContext(cert); } return result; } bool isCAPIURI(std::string uri) { return (boost::iequals(uri.substr(0, 10), "certstore:")); } } swift-im-2.0+dev6/Swift/QtUI/QtWebView.cpp0000644000175000017500000000435412227051774020170 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "QtWebView.h" #include #include #include namespace Swift { QtWebView::QtWebView(QWidget* parent) : QWebView(parent), fontSizeIsMinimal(false) { setRenderHint(QPainter::SmoothPixmapTransform); filteredActions.push_back(QWebPage::CopyLinkToClipboard); filteredActions.push_back(QWebPage::CopyImageToClipboard); filteredActions.push_back(QWebPage::Copy); } void QtWebView::keyPressEvent(QKeyEvent* event) { Qt::KeyboardModifiers modifiers = event->modifiers(); int key = event->key(); if (modifiers == Qt::ShiftModifier && (key == Qt::Key_PageUp || key == Qt::Key_PageDown)) { modifiers = Qt::NoModifier; } QKeyEvent* translatedEvent = new QKeyEvent(QEvent::KeyPress, key, modifiers, event->text(), event->isAutoRepeat(), event->count()); QWebView::keyPressEvent(translatedEvent); delete translatedEvent; } void QtWebView::dragEnterEvent(QDragEnterEvent*) { } void QtWebView::setFontSizeIsMinimal(bool minimum) { fontSizeIsMinimal = minimum; } void QtWebView::contextMenuEvent(QContextMenuEvent* ev) { // Filter out the relevant actions from the standard actions QMenu* menu = page()->createStandardContextMenu(); QList actions(menu->actions()); for (int i = 0; i < actions.size(); ++i) { QAction* action = actions.at(i); bool removeAction = true; for(size_t j = 0; j < filteredActions.size(); ++j) { if (action == pageAction(filteredActions[j])) { removeAction = false; break; } } if (removeAction) { menu->removeAction(action); } } // Add our own custom actions menu->addAction(tr("Clear"), this, SIGNAL(clearRequested())); menu->addAction(tr("Increase font size"), this, SIGNAL(fontGrowRequested())); QAction* shrink = new QAction(tr("Decrease font size"), this); shrink->setEnabled(!fontSizeIsMinimal); connect(shrink, SIGNAL(triggered()), this, SIGNAL(fontShrinkRequested())); menu->addAction(shrink); menu->exec(ev->globalPos()); delete menu; } void QtWebView::focusInEvent(QFocusEvent* event) { QWebView::focusInEvent(event); emit gotFocus(); } } swift-im-2.0+dev6/Swift/QtUI/QtAdHocCommandWindow.cpp0000644000175000017500000001052112227051774022256 0ustar kismithkismith/* * Copyright (c) 2010-2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include const int FormLayoutIndex = 1; namespace Swift { QtAdHocCommandWindow::QtAdHocCommandWindow(boost::shared_ptr command) : command_(command) { formWidget_ = NULL; setAttribute(Qt::WA_DeleteOnClose); command->onNextStageReceived.connect(boost::bind(&QtAdHocCommandWindow::handleNextStageReceived, this, _1)); command->onError.connect(boost::bind(&QtAdHocCommandWindow::handleError, this, _1)); command->start(); layout_ = new QBoxLayout(QBoxLayout::TopToBottom, this); layout_->setContentsMargins(0,0,0,0); layout_->setSpacing(2); label_ = new QLabel(this); label_->setTextFormat(Qt::PlainText); layout_->addWidget(label_); QWidget* buttonsWidget = new QWidget(this); layout_->addWidget(buttonsWidget); QBoxLayout* buttonsLayout = new QBoxLayout(QBoxLayout::LeftToRight, buttonsWidget); cancelButton_ = new QPushButton(tr("Cancel"), buttonsWidget); buttonsLayout->addWidget(cancelButton_); connect(cancelButton_, SIGNAL(clicked()), this, SLOT(handleCancelClicked())); backButton_ = new QPushButton(tr("Back"), buttonsWidget); buttonsLayout->addWidget(backButton_); connect(backButton_, SIGNAL(clicked()), this, SLOT(handlePrevClicked())); nextButton_ = new QPushButton(tr("Next"), buttonsWidget); buttonsLayout->addWidget(nextButton_); connect(nextButton_, SIGNAL(clicked()), this, SLOT(handleNextClicked())); completeButton_ = new QPushButton(tr("Complete"), buttonsWidget); buttonsLayout->addWidget(completeButton_); connect(completeButton_, SIGNAL(clicked()), this, SLOT(handleCompleteClicked())); nextButton_->setEnabled(false); backButton_->setEnabled(false); completeButton_->setEnabled(false); actions_[Command::Next] = nextButton_; actions_[Command::Prev] = backButton_; actions_[Command::Complete] = completeButton_; actions_[Command::Cancel] = cancelButton_; } QtAdHocCommandWindow::~QtAdHocCommandWindow() { } void QtAdHocCommandWindow::handleCancelClicked() { command_->cancel(); close(); } void QtAdHocCommandWindow::handlePrevClicked() { command_->goBack(); } void QtAdHocCommandWindow::handleNextClicked() { command_->goNext(formWidget_ ? formWidget_->getCompletedForm() : Form::ref()); } void QtAdHocCommandWindow::handleCompleteClicked() { command_->complete(formWidget_ ? formWidget_->getCompletedForm() : Form::ref()); } void QtAdHocCommandWindow::handleNextStageReceived(Command::ref command) { QString notes; foreach (Command::Note note, command->getNotes()) { if (!notes.isEmpty()) { notes += "\n"; } QString qNote(P2QSTRING(note.note)); switch (note.type) { case Command::Note::Error: notes += tr("Error: %1").arg(qNote); break; case Command::Note::Warn: notes += tr("Warning: %1").arg(qNote); break; case Command::Note::Info: notes += qNote; break; } } label_->setText(notes); if (command->getForm()) { setForm(command->getForm()); } else { setNoForm(notes.isEmpty()); } setAvailableActions(command); } void QtAdHocCommandWindow::handleError(ErrorPayload::ref /*error*/) { nextButton_->setEnabled(false); backButton_->setEnabled(false); completeButton_->setEnabled(false); label_->setText(tr("Error executing command")); } void QtAdHocCommandWindow::setForm(Form::ref form) { form_ = form; delete formWidget_; formWidget_ = new QtFormWidget(form, this); layout_->insertWidget(FormLayoutIndex, formWidget_); show(); } void QtAdHocCommandWindow::setNoForm(bool andHide) { form_.reset(); delete formWidget_; formWidget_ = NULL; resize(minimumSize()); setVisible(!andHide); } typedef std::pair ActionButton; void QtAdHocCommandWindow::setAvailableActions(Command::ref /*commandResult*/) { foreach (ActionButton pair, actions_) { OutgoingAdHocCommandSession::ActionState state = command_->getActionState(pair.first); if (state & OutgoingAdHocCommandSession::Present) { pair.second->show(); } else { pair.second->hide(); } if (state & OutgoingAdHocCommandSession::Enabled) { pair.second->setEnabled(true); } else { pair.second->setEnabled(false); } } } } swift-im-2.0+dev6/Swift/QtUI/QtLoginWindow.h0000644000175000017500000000643212227051774020524 0ustar kismithkismith/* * Copyright (c) 2010-2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include #include #include #include #include #include #include #include class QLabel; class QToolButton; class QComboBox; namespace Swift { class SettingsProvider; class TimerFactory; class QtLoginWindow : public QMainWindow, public LoginWindow { Q_OBJECT public: struct QtMenus { QtMenus(QMenu* swiftMenu, QMenu* generalMenu) : swiftMenu(swiftMenu), generalMenu(generalMenu) {} QMenu* swiftMenu; QMenu* generalMenu; }; public: QtLoginWindow(UIEventStream* uiEventStream, SettingsProvider* settings, TimerFactory* timerFactory); void morphInto(MainWindow *mainWindow); virtual void loggedOut(); virtual void setShowNotificationToggle(bool); virtual void setMessage(const std::string& message); virtual void addAvailableAccount(const std::string& defaultJID, const std::string& defaultPassword, const std::string& defaultCertificate, const ClientOptions& options); virtual void removeAvailableAccount(const std::string& jid); virtual void setLoginAutomatically(bool loginAutomatically); virtual void setIsLoggingIn(bool loggingIn); void selectUser(const std::string& user); bool askUserToTrustCertificatePermanently(const std::string& message, const std::vector& certificate); void hide(); QtMenus getMenus() const; virtual void quit(); signals: void geometryChanged(); private slots: void loginClicked(); void handleCertficateChecked(bool); void handleQuit(); void handleShowXMLConsole(); void handleShowFileTransferOverview(); void handleToggleSounds(bool enabled); void handleToggleNotifications(bool enabled); void handleAbout(); void bringToFront(); void toggleBringToFront(); void handleUsernameTextChanged(); void resizeEvent(QResizeEvent* event); void moveEvent(QMoveEvent* event); void handleSettingChanged(const std::string& settingPath); void handleOpenConnectionOptions(); protected: bool eventFilter(QObject *obj, QEvent *event); private: void setInitialMenus(); QWidget* loginWidgetWrapper_; QStringList usernames_; QStringList passwords_; QStringList certificateFiles_; std::vector options_; QComboBox* username_; QLineEdit* password_; QPushButton* loginButton_; /* If you add a widget here, change setLoggingIn as well.*/ QCheckBox* remember_; QCheckBox* loginAutomatically_; QStackedWidget* stack_; QLabel* message_; QString certificateFile_; QToolButton* certificateButton_; QMenuBar* menuBar_; QMenu* swiftMenu_; QMenu* generalMenu_; QAction* toggleSoundsAction_; QAction* toggleNotificationsAction_; UIEventStream* uiEventStream_; QPointer aboutDialog_; SettingsProvider* settings_; QAction* xmlConsoleAction_; QAction* fileTransferOverviewAction_; TimerFactory* timerFactory_; ClientOptions currentOptions_; }; } swift-im-2.0+dev6/Swift/QtUI/QtBookmarkDetailWindow.h0000644000175000017500000000113712227051774022341 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include "ui_QtBookmarkDetailWindow.h" #include #include #include "Swiften/MUC/MUCBookmark.h" namespace Swift { class QtBookmarkDetailWindow : public QDialog, protected Ui::QtBookmarkDetailWindow { Q_OBJECT public: QtBookmarkDetailWindow(QWidget* parent = NULL); virtual bool commit() = 0; boost::optional createBookmarkFromForm(); public slots: void accept(); }; } swift-im-2.0+dev6/Swift/QtUI/QtEditBookmarkWindow.cpp0000644000175000017500000000172212227051774022357 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "QtEditBookmarkWindow.h" #include "QtSwiftUtil.h" namespace Swift { QtEditBookmarkWindow::QtEditBookmarkWindow(UIEventStream* eventStream, const MUCBookmark& bookmark) : eventStream_(eventStream), bookmark_(bookmark) { name_->setText(P2QSTRING(bookmark.getName())); room_->setText(P2QSTRING(bookmark.getRoom().toString())); autojoin_->setChecked(bookmark.getAutojoin()); nick_->setText(bookmark.getNick() ? P2QSTRING(bookmark.getNick().get()) : ""); password_->setText(bookmark.getPassword() ? P2QSTRING(bookmark.getPassword().get()) : ""); } bool QtEditBookmarkWindow::commit() { boost::optional bookmark = createBookmarkFromForm(); if (!bookmark) { return false; } eventStream_->send(boost::shared_ptr(new EditMUCBookmarkUIEvent(bookmark_, *bookmark))); return true; } } swift-im-2.0+dev6/Swift/QtUI/QtFormResultItemModel.cpp0000644000175000017500000000523512227051774022521 0ustar kismithkismith/* * Copyright (c) 2012 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include "QtFormResultItemModel.h" #include #include #include namespace Swift { QtFormResultItemModel::QtFormResultItemModel(QObject *parent) : QAbstractTableModel(parent) { } QtFormResultItemModel::QtFormResultItemModel(QObject* parent, Form::ref formResult) : QAbstractTableModel(parent), formResult_(formResult) { } void QtFormResultItemModel::setForm(Form::ref formResult) { emit layoutAboutToBeChanged(); formResult_ = formResult; emit layoutChanged(); } const Form::ref QtFormResultItemModel::getForm() const { return formResult_; } QVariant QtFormResultItemModel::headerData(int section, Qt::Orientation /*orientation*/, int role) const { if (!formResult_) return QVariant(); if (role != Qt::DisplayRole) return QVariant(); if (static_cast(section) >= formResult_->getReportedFields().size()) return QVariant(); return QVariant(QString::fromStdString(formResult_->getReportedFields().at(section)->getLabel())); } int QtFormResultItemModel::rowCount(const QModelIndex &/*parent*/) const { if (!formResult_) return 0; return formResult_->getItems().size(); } int QtFormResultItemModel::columnCount(const QModelIndex &/*parent*/) const { if (!formResult_) return 0; return formResult_->getReportedFields().size(); } QVariant QtFormResultItemModel::data(const QModelIndex &index, int role) const { if (!formResult_) return QVariant(); if (role != Qt::DisplayRole || !index.isValid()) { return QVariant(); } if (static_cast(index.row()) >= formResult_->getItems().size()) return QVariant(); if (static_cast(index.column()) >= formResult_->getReportedFields().size()) return QVariant(); Form::FormItem item = formResult_->getItems().at(index.row()); return QVariant(P2QSTRING(getFieldValue(item, index.column()))); } const std::string QtFormResultItemModel::getFieldValue(const Form::FormItem& item, const int column) const { // determine field name std::string name = formResult_->getReportedFields().at(column)->getName(); foreach(FormField::ref field, item) { if (field->getName() == name) { std::string delimiter = ""; if (boost::dynamic_pointer_cast(field)) { delimiter = "\n"; } else if (boost::dynamic_pointer_cast(field)) { delimiter = ", "; } else if (boost::dynamic_pointer_cast(field)) { delimiter = ", "; } return boost::algorithm::join(field->getRawValues(), delimiter); } } return ""; } } swift-im-2.0+dev6/Swift/QtUI/CocoaUIHelpers.h0000644000175000017500000000061512227051774020561 0ustar kismithkismith/* * Copyright (c) 2012 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include namespace Swift { class CocoaUIHelpers { public: static void displayCertificateChainAsSheet(QWidget* parent, const std::vector& chain); }; } swift-im-2.0+dev6/Swift/QtUI/QtSubscriptionRequestWindow.h0000644000175000017500000000175312227051774023512 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include "Swift/Controllers/XMPPEvents/SubscriptionRequestEvent.h" namespace Swift { class QtSubscriptionRequestWindow : public QDialog { Q_OBJECT public: static QtSubscriptionRequestWindow* getWindow(boost::shared_ptr event, QWidget* parent = 0); ~QtSubscriptionRequestWindow(); boost::shared_ptr getEvent(); private slots: void handleYes(); void handleNo(); void handleDefer(); private: QtSubscriptionRequestWindow(boost::shared_ptr event, QWidget* parent = 0); static QList windows_; boost::shared_ptr event_; /*QPushButton* yesButton_; QPushButton* noButton_; QPushButton* deferButton_;*/ }; } swift-im-2.0+dev6/Swift/QtUI/QtChatView.cpp0000644000175000017500000004105412227051774020330 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "QtChatView.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "QtWebView.h" #include "QtChatTheme.h" #include "QtChatWindow.h" #include "QtSwiftUtil.h" namespace Swift { QtChatView::QtChatView(QtChatTheme* theme, QWidget* parent, bool disableAutoScroll) : QWidget(parent), fontSizeSteps_(0), disableAutoScroll_(disableAutoScroll) { theme_ = theme; QVBoxLayout* mainLayout = new QVBoxLayout(this); mainLayout->setSpacing(0); mainLayout->setContentsMargins(0,0,0,0); webView_ = new QtWebView(this); connect(webView_, SIGNAL(linkClicked(const QUrl&)), SLOT(handleLinkClicked(const QUrl&))); connect(webView_, SIGNAL(loadFinished(bool)), SLOT(handleViewLoadFinished(bool))); connect(webView_, SIGNAL(gotFocus()), SIGNAL(gotFocus())); connect(webView_, SIGNAL(clearRequested()), SLOT(handleClearRequested())); connect(webView_, SIGNAL(fontGrowRequested()), SLOT(increaseFontSize())); connect(webView_, SIGNAL(fontShrinkRequested()), SLOT(decreaseFontSize())); #ifdef Q_WS_X11 /* To give a border on Linux, where it looks bad without */ QStackedWidget* stack = new QStackedWidget(this); stack->addWidget(webView_); stack->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); stack->setLineWidth(2); mainLayout->addWidget(stack); #else mainLayout->addWidget(webView_); #endif #ifdef SWIFT_EXPERIMENTAL_FT setAcceptDrops(true); #endif webPage_ = new QWebPage(this); webPage_->setLinkDelegationPolicy(QWebPage::DelegateAllLinks); //webPage_->settings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true); webView_->setPage(webPage_); connect(webPage_, SIGNAL(selectionChanged()), SLOT(copySelectionToClipboard())); connect(webPage_, SIGNAL(scrollRequested(int, int, const QRect&)), SLOT(handleScrollRequested(int, int, const QRect&))); viewReady_ = false; isAtBottom_ = true; resetView(); } void QtChatView::handleClearRequested() { QMessageBox messageBox(this); messageBox.setWindowTitle(tr("Clear log")); messageBox.setText(tr("You are about to clear the contents of your chat log.")); messageBox.setInformativeText(tr("Are you sure?")); messageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); messageBox.setDefaultButton(QMessageBox::Yes); int button = messageBox.exec(); if (button == QMessageBox::Yes) { logCleared(); resetView(); } } void QtChatView::handleKeyPressEvent(QKeyEvent* event) { webView_->keyPressEvent(event); } void QtChatView::addMessageBottom(boost::shared_ptr snippet) { if (viewReady_) { addToDOM(snippet); } else { /* If this asserts, the previous queuing code was necessary and should be reinstated */ assert(false); } } void QtChatView::addMessageTop(boost::shared_ptr snippet) { // save scrollbar maximum value if (!topMessageAdded_) { scrollBarMaximum_ = webPage_->mainFrame()->scrollBarMaximum(Qt::Vertical); } topMessageAdded_ = true; QWebElement continuationElement = firstElement_.findFirst("#insert"); bool insert = snippet->getAppendToPrevious(); bool fallback = continuationElement.isNull(); boost::shared_ptr newSnippet = (insert && fallback) ? snippet->getContinuationFallbackSnippet() : snippet; QWebElement newElement = snippetToDOM(newSnippet); if (insert && !fallback) { Q_ASSERT(!continuationElement.isNull()); continuationElement.replace(newElement); } else { continuationElement.removeFromDocument(); topInsertPoint_.prependOutside(newElement); } firstElement_ = newElement; if (lastElement_.isNull()) { lastElement_ = firstElement_; } if (fontSizeSteps_ != 0) { double size = 1.0 + 0.2 * fontSizeSteps_; QString sizeString(QString().setNum(size, 'g', 3) + "em"); const QWebElementCollection spans = firstElement_.findAll("span.swift_resizable"); foreach (QWebElement span, spans) { span.setStyleProperty("font-size", sizeString); } } } QWebElement QtChatView::snippetToDOM(boost::shared_ptr snippet) { QWebElement newElement = newInsertPoint_.clone(); newElement.setInnerXml(snippet->getContent()); Q_ASSERT(!newElement.isNull()); return newElement; } void QtChatView::addToDOM(boost::shared_ptr snippet) { rememberScrolledToBottom(); bool insert = snippet->getAppendToPrevious(); QWebElement continuationElement = lastElement_.findFirst("#insert"); bool fallback = insert && continuationElement.isNull(); boost::shared_ptr newSnippet = (insert && fallback) ? snippet->getContinuationFallbackSnippet() : snippet; QWebElement newElement = snippetToDOM(newSnippet); if (insert && !fallback) { Q_ASSERT(!continuationElement.isNull()); continuationElement.replace(newElement); } else { continuationElement.removeFromDocument(); newInsertPoint_.prependOutside(newElement); } lastElement_ = newElement; if (fontSizeSteps_ != 0) { double size = 1.0 + 0.2 * fontSizeSteps_; QString sizeString(QString().setNum(size, 'g', 3) + "em"); const QWebElementCollection spans = lastElement_.findAll("span.swift_resizable"); foreach (QWebElement span, spans) { span.setStyleProperty("font-size", sizeString); } } } void QtChatView::addLastSeenLine() { if (lineSeparator_.isNull()) { lineSeparator_ = newInsertPoint_.clone(); lineSeparator_.setInnerXml(QString("
")); newInsertPoint_.prependOutside(lineSeparator_); } else { QWebElement lineSeparatorC = lineSeparator_.clone(); lineSeparatorC.removeFromDocument(); } newInsertPoint_.prependOutside(lineSeparator_); } void QtChatView::replaceLastMessage(const QString& newMessage) { assert(viewReady_); rememberScrolledToBottom(); assert(!lastElement_.isNull()); QWebElement replace = lastElement_.findFirst("span.swift_message"); assert(!replace.isNull()); QString old = lastElement_.toOuterXml(); replace.setInnerXml(ChatSnippet::escape(newMessage)); } void QtChatView::replaceLastMessage(const QString& newMessage, const QString& note) { rememberScrolledToBottom(); replaceLastMessage(newMessage); QWebElement replace = lastElement_.findFirst("span.swift_time"); assert(!replace.isNull()); replace.setInnerXml(ChatSnippet::escape(note)); } QString QtChatView::getLastSentMessage() { return lastElement_.toPlainText(); } void QtChatView::addToJSEnvironment(const QString& name, QObject* obj) { webView_->page()->currentFrame()->addToJavaScriptWindowObject(name, obj); } void QtChatView::replaceMessage(const QString& newMessage, const QString& id, const QDateTime& editTime) { rememberScrolledToBottom(); QWebElement message = document_.findFirst("#" + id); if (!message.isNull()) { QWebElement replaceContent = message.findFirst("span.swift_inner_message"); assert(!replaceContent.isNull()); QString old = replaceContent.toOuterXml(); replaceContent.setInnerXml(ChatSnippet::escape(newMessage)); QWebElement replaceTime = message.findFirst("span.swift_time"); assert(!replaceTime.isNull()); old = replaceTime.toOuterXml(); replaceTime.setInnerXml(ChatSnippet::escape(tr("%1 edited").arg(ChatSnippet::timeToEscapedString(editTime)))); } else { qWarning() << "Trying to replace element with id " << id << " but it's not there."; } } void QtChatView::showEmoticons(bool show) { { const QWebElementCollection spans = document_.findAll("span.swift_emoticon_image"); foreach (QWebElement span, spans) { span.setStyleProperty("display", show ? "inline" : "none"); } } { const QWebElementCollection spans = document_.findAll("span.swift_emoticon_text"); foreach (QWebElement span, spans) { span.setStyleProperty("display", show ? "none" : "inline"); } } } void QtChatView::copySelectionToClipboard() { if (!webPage_->selectedText().isEmpty()) { webPage_->triggerAction(QWebPage::Copy); } } void QtChatView::setAckXML(const QString& id, const QString& xml) { QWebElement message = document_.findFirst("#" + id); /* Deliberately not asserting here, so that when we start expiring old messages it won't hit us */ if (message.isNull()) return; QWebElement ackElement = message.findFirst("span.swift_ack"); assert(!ackElement.isNull()); ackElement.setInnerXml(xml); } void QtChatView::setReceiptXML(const QString& id, const QString& xml) { QWebElement message = document_.findFirst("#" + id); if (message.isNull()) return; QWebElement receiptElement = message.findFirst("span.swift_receipt"); assert(!receiptElement.isNull()); receiptElement.setInnerXml(xml); } void QtChatView::displayReceiptInfo(const QString& id, bool showIt) { QWebElement message = document_.findFirst("#" + id); if (message.isNull()) return; QWebElement receiptElement = message.findFirst("span.swift_receipt"); assert(!receiptElement.isNull()); receiptElement.setStyleProperty("display", showIt ? "inline" : "none"); } void QtChatView::rememberScrolledToBottom() { isAtBottom_ = webPage_->mainFrame()->scrollBarValue(Qt::Vertical) >= (webPage_->mainFrame()->scrollBarMaximum(Qt::Vertical) - 1); } void QtChatView::scrollToBottom() { isAtBottom_ = true; webPage_->mainFrame()->setScrollBarValue(Qt::Vertical, webPage_->mainFrame()->scrollBarMaximum(Qt::Vertical)); webView_->update(); /* Work around redraw bug in some versions of Qt. */ } void QtChatView::handleFrameSizeChanged() { if (topMessageAdded_) { // adjust new scrollbar position int newMaximum = webPage_->mainFrame()->scrollBarMaximum(Qt::Vertical); webPage_->mainFrame()->setScrollBarValue(Qt::Vertical, newMaximum - scrollBarMaximum_); topMessageAdded_ = false; } if (isAtBottom_ && !disableAutoScroll_) { scrollToBottom(); } } void QtChatView::handleLinkClicked(const QUrl& url) { QDesktopServices::openUrl(url); } void QtChatView::handleViewLoadFinished(bool ok) { Q_ASSERT(ok); viewReady_ = true; } void QtChatView::increaseFontSize(int numSteps) { //qDebug() << "Increasing"; fontSizeSteps_ += numSteps; emit fontResized(fontSizeSteps_); } void QtChatView::decreaseFontSize() { fontSizeSteps_--; if (fontSizeSteps_ < 0) { fontSizeSteps_ = 0; } emit fontResized(fontSizeSteps_); } void QtChatView::resizeFont(int fontSizeSteps) { fontSizeSteps_ = fontSizeSteps; double size = 1.0 + 0.2 * fontSizeSteps_; QString sizeString(QString().setNum(size, 'g', 3) + "em"); //qDebug() << "Setting to " << sizeString; const QWebElementCollection spans = document_.findAll("span.swift_resizable"); foreach (QWebElement span, spans) { span.setStyleProperty("font-size", sizeString); } webView_->setFontSizeIsMinimal(size == 1.0); } void QtChatView::resetView() { lastElement_ = QWebElement(); firstElement_ = lastElement_; topMessageAdded_ = false; scrollBarMaximum_ = 0; QString pageHTML = theme_->getTemplate(); pageHTML.replace("==bodyBackground==", "background-color:#e3e3e3"); pageHTML.replace(pageHTML.indexOf("%@"), 2, theme_->getBase()); if (pageHTML.count("%@") > 3) { pageHTML.replace(pageHTML.indexOf("%@"), 2, theme_->getMainCSS()); } pageHTML.replace(pageHTML.indexOf("%@"), 2, "Variants/Blue on Green.css"); pageHTML.replace(pageHTML.indexOf("%@"), 2, ""/*headerSnippet.getContent()*/); pageHTML.replace(pageHTML.indexOf("%@"), 2, ""/*footerSnippet.getContent()*/); QEventLoop syncLoop; connect(webView_, SIGNAL(loadFinished(bool)), &syncLoop, SLOT(quit())); webPage_->mainFrame()->setHtml(pageHTML); while (!viewReady_) { QTimer t; t.setSingleShot(true); connect(&t, SIGNAL(timeout()), &syncLoop, SLOT(quit())); t.start(50); syncLoop.exec(); } document_ = webPage_->mainFrame()->documentElement(); resetTopInsertPoint(); QWebElement chatElement = document_.findFirst("#Chat"); newInsertPoint_ = chatElement.clone(); newInsertPoint_.setOuterXml("
"); chatElement.appendInside(newInsertPoint_); Q_ASSERT(!newInsertPoint_.isNull()); scrollToBottom(); connect(webPage_->mainFrame(), SIGNAL(contentsSizeChanged(const QSize&)), this, SLOT(handleFrameSizeChanged()), Qt::UniqueConnection); } QWebElement findElementWithID(QWebElement document, QString elementName, QString id) { QWebElementCollection elements = document.findAll(elementName); foreach(QWebElement element, elements) { if (element.attribute("id") == id) { return element; } } return QWebElement(); } void QtChatView::setFileTransferProgress(QString id, const int percentageDone) { QWebElement ftElement = findElementWithID(document_, "div", id); if (ftElement.isNull()) { SWIFT_LOG(debug) << "Tried to access FT UI via invalid id!" << std::endl; return; } QWebElement progressBar = ftElement.findFirst("div.progressbar"); progressBar.setStyleProperty("width", QString::number(percentageDone) + "%"); QWebElement progressBarValue = ftElement.findFirst("div.progressbar-value"); progressBarValue.setInnerXml(QString::number(percentageDone) + " %"); } void QtChatView::setFileTransferStatus(QString id, const ChatWindow::FileTransferState state, const QString& /* msg */) { QWebElement ftElement = findElementWithID(document_, "div", id); if (ftElement.isNull()) { SWIFT_LOG(debug) << "Tried to access FT UI via invalid id! id = " << Q2PSTRING(id) << std::endl; return; } QString newInnerHTML = ""; if (state == ChatWindow::WaitingForAccept) { newInnerHTML = tr("Waiting for other side to accept the transfer.") + "
" + QtChatWindow::buildChatWindowButton(tr("Cancel"), QtChatWindow::ButtonFileTransferCancel, id); } if (state == ChatWindow::Negotiating) { // replace with text "Negotiaging" + Cancel button newInnerHTML = tr("Negotiating...") + "
" + QtChatWindow::buildChatWindowButton(tr("Cancel"), QtChatWindow::ButtonFileTransferCancel, id); } else if (state == ChatWindow::Transferring) { // progress bar + Cancel Button newInnerHTML = "
" "
" "
" "0%" "
" "
" "
" + QtChatWindow::buildChatWindowButton(tr("Cancel"), QtChatWindow::ButtonFileTransferCancel, id); } else if (state == ChatWindow::Canceled) { newInnerHTML = tr("Transfer has been canceled!"); } else if (state == ChatWindow::Finished) { // text "Successful transfer" newInnerHTML = tr("Transfer completed successfully."); } else if (state == ChatWindow::FTFailed) { newInnerHTML = tr("Transfer failed."); } ftElement.setInnerXml(newInnerHTML); } void QtChatView::setWhiteboardSessionStatus(QString id, const ChatWindow::WhiteboardSessionState state) { QWebElement divElement = findElementWithID(document_, "div", id); QString newInnerHTML; if (state == ChatWindow::WhiteboardAccepted) { newInnerHTML = tr("Started whiteboard chat") + "
" + QtChatWindow::buildChatWindowButton(tr("Show whiteboard"), QtChatWindow::ButtonWhiteboardShowWindow, id); } else if (state == ChatWindow::WhiteboardTerminated) { newInnerHTML = tr("Whiteboard chat has been canceled"); } else if (state == ChatWindow::WhiteboardRejected) { newInnerHTML = tr("Whiteboard chat request has been rejected"); } divElement.setInnerXml(newInnerHTML); } void QtChatView::setMUCInvitationJoined(QString id) { QWebElement divElement = findElementWithID(document_, "div", id); QWebElement buttonElement = findElementWithID(divElement, "input", "mucinvite"); if (!buttonElement.isNull()) { buttonElement.setAttribute("value", tr("Return to room")); } } void QtChatView::handleScrollRequested(int, int dy, const QRect&) { rememberScrolledToBottom(); int pos = webPage_->mainFrame()->scrollBarValue(Qt::Vertical) - dy; emit scrollRequested(pos); if (pos == 0) { emit scrollReachedTop(); } else if (pos == webPage_->mainFrame()->scrollBarMaximum(Qt::Vertical)) { emit scrollReachedBottom(); } } int QtChatView::getSnippetPositionByDate(const QDate& date) { QWebElement message = webPage_->mainFrame()->documentElement().findFirst(".date" + date.toString(Qt::ISODate)); return message.geometry().top(); } void QtChatView::resetTopInsertPoint() { QWebElement continuationElement = firstElement_.findFirst("#insert"); continuationElement.removeFromDocument(); firstElement_ = QWebElement(); topInsertPoint_.removeFromDocument(); QWebElement chatElement = document_.findFirst("#Chat"); topInsertPoint_ = chatElement.clone(); topInsertPoint_.setOuterXml("
"); chatElement.prependInside(topInsertPoint_); } } swift-im-2.0+dev6/Swift/QtUI/QtURLValidator.cpp0000644000175000017500000000113712227051774021124 0ustar kismithkismith/* * Copyright (c) 2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include namespace Swift { QtURLValidator::QtURLValidator(QObject* parent) { } QValidator::State QtURLValidator::validate(QString& input, int& pos) const { URL url = URL::fromString(Q2PSTRING(input)); bool valid = !url.isEmpty(); valid &= (url.getScheme() == "http" || url.getScheme() == "https"); return valid ? Acceptable : Intermediate; } } swift-im-2.0+dev6/Swift/QtUI/QtTabWidget.cpp0000644000175000017500000000056512227051774020472 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "QtTabWidget.h" namespace Swift { QtTabWidget::QtTabWidget(QWidget* parent) : QTabWidget(parent) { } QtTabWidget::~QtTabWidget() { } QTabBar* QtTabWidget::tabBar() { return QTabWidget::tabBar(); } } swift-im-2.0+dev6/Swift/QtUI/QtNameWidget.h0000644000175000017500000000137612227051774020312 0ustar kismithkismith/* * 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 namespace Swift { class QtElidingLabel; class SettingsProvider; class QtNameWidget : public QWidget { Q_OBJECT public: QtNameWidget(SettingsProvider* settings, QWidget *parent); void setNick(const QString& text); void setJID(const QString& jid); signals: void onChangeNickRequest(); private: void updateText(); virtual void mousePressEvent(QMouseEvent* event); private: enum Mode { ShowNick, ShowJID, }; SettingsProvider* settings; Mode mode; QtElidingLabel* textLabel; QString jid; QString nick; }; } swift-im-2.0+dev6/Swift/QtUI/swift-open-uri.cpp0000644000175000017500000000127112227051774021176 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include int main(int argc, char* argv[]) { QCoreApplication app(argc, argv); QDBusConnection bus = QDBusConnection::sessionBus(); if (!bus.isConnected()) { return -1; } if (argc != 2) { std::cerr << "Usage: " << argv[0] << " uri" << std::endl; return -1; } QDBusMessage msg = QDBusMessage::createMethodCall("im.swift.Swift.URIHandler", "/", "im.swift.Swift.URIHandler", "openURI"); msg << argv[1]; bus.call(msg); return 0; } swift-im-2.0+dev6/Swift/QtUI/QtTabbable.h0000644000175000017500000000161612227051774017757 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include namespace Swift { class QtTabbable : public QWidget { Q_OBJECT public: enum AlertType {NoActivity, WaitingActivity, ImpendingActivity}; ~QtTabbable(); bool isWidgetSelected(); virtual AlertType getWidgetAlertState() {return NoActivity;}; virtual int getCount() {return 0;} protected: QtTabbable() : QWidget() {}; void keyPressEvent(QKeyEvent* event); protected slots: void handleKeyPressEvent(QKeyEvent* event); signals: void titleUpdated(); void countUpdated(); void windowClosing(); void windowOpening(); void wantsToActivate(); void requestPreviousTab(); void requestNextTab(); void requestActiveTab(); void requestFlash(); }; } swift-im-2.0+dev6/Swift/QtUI/QtBookmarkDetailWindow.cpp0000644000175000017500000000263212227051774022675 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "QtBookmarkDetailWindow.h" #include "QtSwiftUtil.h" #include namespace Swift { QtBookmarkDetailWindow::QtBookmarkDetailWindow(QWidget* parent) : QDialog(parent) { setupUi(this); setAttribute(Qt::WA_DeleteOnClose, true); //connect(buttons_, SIGNAL(accepted()), SLOT(accept())); //connect(buttons_, SIGNAL(rejected()), SLOT(reject())); } void QtBookmarkDetailWindow::accept() { if (commit()) { QDialog::accept(); } } boost::optional QtBookmarkDetailWindow::createBookmarkFromForm() { //check room //check bookmarkName JID room(Q2PSTRING(room_->text())); if (!room.isValid() || room.getNode().empty() || !room.getResource().empty()) { QMessageBox::warning(this, tr("Bookmark not valid"), tr("You must specify a valid room address (e.g. someroom@rooms.example.com).")); return boost::optional(); } std::string name(Q2PSTRING(name_->text())); if (name.empty()) { name = room.toString(); } MUCBookmark bookmark(room, name); std::string nick(Q2PSTRING(nick_->text())); std::string password(Q2PSTRING(password_->text())); bookmark.setAutojoin(autojoin_->isChecked()); if (!nick.empty()) { bookmark.setNick(nick); } if (!password.empty()) { bookmark.setPassword(password); } return bookmark; } } swift-im-2.0+dev6/Swift/QtUI/QtSystemTray.h0000644000175000017500000000206312227051774020404 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include "Swift/Controllers/SystemTray.h" #include #include class QIcon; class QMenu; namespace Swift { class QtSystemTray : public QObject, public SystemTray { Q_OBJECT public: QtSystemTray(); ~QtSystemTray(); void setUnreadMessages(bool some); void setStatusType(StatusShow::Type type); void setConnecting(); QSystemTrayIcon* getQSystemTrayIcon() { return trayIcon_; } signals: void clicked(); private slots: void handleIconActivated(QSystemTrayIcon::ActivationReason reason); void handleThrobberFrameChanged(int); private: void updateStatusIcon(); StatusShow::Type statusType_; QSystemTrayIcon* trayIcon_; QMenu* trayMenu_; QIcon onlineIcon_; QIcon awayIcon_; QIcon dndIcon_; QIcon offlineIcon_; QIcon newMessageIcon_; QMovie throbberMovie_; bool unreadMessages_; bool connecting_; }; } swift-im-2.0+dev6/Swift/QtUI/QtWebView.h0000644000175000017500000000140412227051774017626 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include namespace Swift { class QtWebView : public QWebView { Q_OBJECT public: QtWebView(QWidget* parent); void keyPressEvent(QKeyEvent* event); void dragEnterEvent(QDragEnterEvent *event); void contextMenuEvent(QContextMenuEvent* ev); void setFontSizeIsMinimal(bool minimum); signals: void gotFocus(); void clearRequested(); void fontGrowRequested(); void fontShrinkRequested(); protected: void focusInEvent(QFocusEvent* event); private: std::vector filteredActions; bool fontSizeIsMinimal; }; } swift-im-2.0+dev6/Swift/QtUI/QtFileTransferListWidget.h0000644000175000017500000000161012227051774022641 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include "Swift/Controllers/UIInterfaces/FileTransferListWidget.h" #include "QtTabbable.h" #include #include #include namespace Swift { class FileTransferOverview; class QtFileTransferListItemModel; class QtFileTransferListWidget : public QtTabbable, public FileTransferListWidget { Q_OBJECT public: QtFileTransferListWidget(); virtual ~QtFileTransferListWidget(); void show(); void activate(); void setFileTransferOverview(FileTransferOverview *); private: virtual void closeEvent(QCloseEvent* event); virtual void showEvent(QShowEvent* event); private: QTreeView* treeView; QtFileTransferListItemModel* itemModel; FileTransferOverview* fileTransferOverview; }; } swift-im-2.0+dev6/Swift/QtUI/QtConnectionSettings.ui0000644000175000017500000003667112227051774022302 0ustar kismithkismith QtConnectionSettings 0 0 406 454 Connection Options Connection Method: 1 0 Automatic Manual BOSH Qt::Horizontal 238 20 Qt::Horizontal 0 Qt::Horizontal 40 20 Secure connection: Never Encrypt when possible Always encrypt Allow Compression Allow sending password over insecure connection Qt::Vertical QSizePolicy::Fixed 20 4 Manually select server Qt::Horizontal QSizePolicy::Fixed 18 20 false Hostname: false 4 0 false Port: false 1 0 Connection Proxy Proxy type: 1 None Use system-configured proxy SOCKS5 HTTP Connect Qt::Horizontal 40 20 Override system-configured proxy Qt::Horizontal QSizePolicy::Fixed 18 20 false Hostname: false 4 0 false Port: false 1 0 Qt::Vertical 20 88 BOSH URI: 4 0 Manually select HTTP proxy Qt::Horizontal QSizePolicy::Fixed 18 20 false Hostname: false 4 0 false Port: false 1 0 Qt::Vertical 20 80 Connection options will be saved when next connecting to this account. true Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox rejected() QtConnectionSettings reject() 316 260 286 274 swift-im-2.0+dev6/Swift/QtUI/QtRosterHeader.h0000644000175000017500000000255712227051774020657 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include #include #include #include "Swiften/Elements/StatusShow.h" #include "QtTextEdit.h" class QHBoxLayout; namespace Swift { class QtClickableLabel; class QtStatusWidget; class QtNameWidget; class SettingsProvider; class QtRosterHeader : public QWidget { Q_OBJECT public: QtRosterHeader(SettingsProvider* settings, QWidget* parent = NULL); void setAvatar(const QString& path); void setJID(const QString& jid); void setNick(const QString& nick); void setStatusText(const QString& statusMessage); void setStatusType(StatusShow::Type type); void setConnecting(); void setStreamEncryptionStatus(bool tlsInPlace); signals: void onChangeStatusRequest(StatusShow::Type showType, const QString &statusMessage); void onEditProfileRequest(); void onShowCertificateInfo(); private slots: void handleChangeStatusRequest(StatusShow::Type type, const QString &statusMessage); private: QString name_; QtClickableLabel* avatarLabel_; QtNameWidget* nameWidget_; QtTextEdit* statusEdit_; QtStatusWidget* statusWidget_; QToolButton* securityInfoButton_; static const int avatarSize_; }; } swift-im-2.0+dev6/Swift/QtUI/QtInviteToChatWindow.h0000644000175000017500000000200412227051774022004 0ustar kismithkismith/* * Copyright (c) 2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include class QLineEdit; class QBoxLayout; class QDialogButtonBox; namespace Swift { class QtInviteToChatWindow : public QDialog, public InviteToChatWindow { Q_OBJECT public: QtInviteToChatWindow(QWidget* parent = NULL); virtual ~QtInviteToChatWindow(); virtual std::string getReason() const; virtual std::vector getJIDs() const; virtual void setAutoCompletions(std::vector > completions); private: void addJIDLine(); private slots: void handleJIDTextChanged(); void handleAccepting(); void handleRejecting(); private: QStringListModel completions_; QLineEdit* reason_; QBoxLayout* jidsLayout_; std::vector jids_; QDialogButtonBox* buttonBox_; }; } swift-im-2.0+dev6/Swift/QtUI/QtChatWindowJSBridge.cpp0000644000175000017500000000050412227051774022232 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include "QtChatWindowJSBridge.h" namespace Swift { QtChatWindowJSBridge::QtChatWindowJSBridge() { } QtChatWindowJSBridge::~QtChatWindowJSBridge() { } } swift-im-2.0+dev6/Swift/QtUI/QtCertificateViewerDialog.h0000644000175000017500000000170412227051774023005 0ustar kismithkismith/* * Copyright (c) 2012 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include #include #include #include namespace Ui { class QtCertificateViewerDialog; } namespace Swift { class QtCertificateViewerDialog : public QDialog { Q_OBJECT public: explicit QtCertificateViewerDialog(QWidget* parent = 0); ~QtCertificateViewerDialog(); void setCertificateChain(const std::vector& chain); static void displayCertificateChainAsSheet(QWidget* parent, const std::vector& chain); private slots: void currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*); private: void setCertificateDetails(const QSslCertificate& cert); private: Ui::QtCertificateViewerDialog *ui; QList currentChain; }; } swift-im-2.0+dev6/Swift/QtUI/ChatList/0000755000175000017500000000000012227051774017314 5ustar kismithkismithswift-im-2.0+dev6/Swift/QtUI/ChatList/ChatListRecentItem.cpp0000644000175000017500000000301412227051774023511 0ustar kismithkismith/* * Copyright (c) 2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include namespace Swift { ChatListRecentItem::ChatListRecentItem(const ChatListWindow::Chat& chat, ChatListGroupItem* parent) : ChatListItem(parent), chat_(chat) { } const ChatListWindow::Chat& ChatListRecentItem::getChat() const { return chat_; } QVariant ChatListRecentItem::data(int role) const { switch (role) { case Qt::DisplayRole: return P2QSTRING(chat_.chatName); case DetailTextRole: return P2QSTRING(chat_.activity); /*case Qt::TextColorRole: return textColor_; case Qt::BackgroundColorRole: return backgroundColor_; case Qt::ToolTipRole: return isContact() ? toolTipString() : QVariant(); case StatusTextRole: return statusText_;*/ case AvatarRole: return QVariant(QString(chat_.avatarPath.string().c_str())); case PresenceIconRole: return getPresenceIcon(); default: return QVariant(); } } QIcon ChatListRecentItem::getPresenceIcon() const { QString iconString; switch (chat_.statusType) { case StatusShow::Online: iconString = "online";break; case StatusShow::Away: iconString = "away";break; case StatusShow::XA: iconString = "away";break; case StatusShow::FFC: iconString = "online";break; case StatusShow::DND: iconString = "dnd";break; case StatusShow::None: iconString = "offline";break; } return QIcon(":/icons/" + iconString + ".png"); } } swift-im-2.0+dev6/Swift/QtUI/ChatList/ChatListWhiteboardItem.h0000644000175000017500000000161212227051774024030 0ustar kismithkismith/* * Copyright (c) 2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include #include #include namespace Swift { class ChatListWhiteboardItem : public ChatListItem { public: enum RecentItemRoles { DetailTextRole = Qt::UserRole, AvatarRole = Qt::UserRole + 1, PresenceIconRole = Qt::UserRole + 2/*, StatusShowTypeRole = Qt::UserRole + 3*/ }; ChatListWhiteboardItem(const ChatListWindow::Chat& chat, ChatListGroupItem* parent); const ChatListWindow::Chat& getChat() const; QVariant data(int role) const; private: QIcon getPresenceIcon() const; ChatListWindow::Chat chat_; }; } swift-im-2.0+dev6/Swift/QtUI/ChatList/ChatListMUCItem.h0000644000175000017500000000143412227051774022366 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include "Swiften/MUC/MUCBookmark.h" #include "Swift/QtUI/ChatList/ChatListItem.h" namespace Swift { class ChatListMUCItem : public ChatListItem { public: enum MUCItemRoles { DetailTextRole = Qt::UserRole/*, AvatarRole = Qt::UserRole + 1, PresenceIconRole = Qt::UserRole + 2, StatusShowTypeRole = Qt::UserRole + 3*/ }; ChatListMUCItem(const MUCBookmark& bookmark, ChatListGroupItem* parent); const MUCBookmark& getBookmark() const; QVariant data(int role) const; private: MUCBookmark bookmark_; QList items_; }; } swift-im-2.0+dev6/Swift/QtUI/ChatList/ChatListRecentItem.h0000644000175000017500000000160212227051774023157 0ustar kismithkismith/* * Copyright (c) 2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include #include #include namespace Swift { class ChatListRecentItem : public ChatListItem { public: enum RecentItemRoles { DetailTextRole = Qt::UserRole, AvatarRole = Qt::UserRole + 1, PresenceIconRole = Qt::UserRole + 2/*, StatusShowTypeRole = Qt::UserRole + 3*/ }; ChatListRecentItem(const ChatListWindow::Chat& chat, ChatListGroupItem* parent); const ChatListWindow::Chat& getChat() const; QVariant data(int role) const; private: QIcon getPresenceIcon() const; ChatListWindow::Chat chat_; }; } swift-im-2.0+dev6/Swift/QtUI/ChatList/ChatListModel.h0000644000175000017500000000260312227051774022162 0ustar kismithkismith/* * Copyright (c) 2010-2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include #include #include namespace Swift { class ChatListModel : public QAbstractItemModel { Q_OBJECT public: ChatListModel(); void addMUCBookmark(const MUCBookmark& bookmark); void removeMUCBookmark(const MUCBookmark& bookmark); void addWhiteboardSession(const ChatListWindow::Chat& chat); void removeWhiteboardSession(const JID& jid); int columnCount(const QModelIndex& parent = QModelIndex()) const; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const; QModelIndex parent(const QModelIndex& index) const; int rowCount(const QModelIndex& parent = QModelIndex()) const; ChatListItem* getItemForIndex(const QModelIndex& index) const; void clearBookmarks(); void setRecents(const std::list& recents); private: ChatListGroupItem* mucBookmarks_; ChatListGroupItem* recents_; ChatListGroupItem* whiteboards_; ChatListGroupItem* root_; }; } swift-im-2.0+dev6/Swift/QtUI/ChatList/ChatListMUCItem.cpp0000644000175000017500000000201012227051774022710 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Swift/QtUI/ChatList/ChatListMUCItem.h" #include "Swift/QtUI/QtSwiftUtil.h" namespace Swift { ChatListMUCItem::ChatListMUCItem(const MUCBookmark& bookmark, ChatListGroupItem* parent) : ChatListItem(parent), bookmark_(bookmark) { } const MUCBookmark& ChatListMUCItem::getBookmark() const { return bookmark_; } QVariant ChatListMUCItem::data(int role) const { switch (role) { case Qt::DisplayRole: return P2QSTRING(bookmark_.getName()); case DetailTextRole: return P2QSTRING(bookmark_.getRoom().toString()); /*case Qt::TextColorRole: return textColor_; case Qt::BackgroundColorRole: return backgroundColor_; case Qt::ToolTipRole: return isContact() ? toolTipString() : QVariant(); case StatusTextRole: return statusText_; case AvatarRole: return avatar_; case PresenceIconRole: return getPresenceIcon();*/ default: return QVariant(); } } } swift-im-2.0+dev6/Swift/QtUI/ChatList/QtChatListWindow.cpp0000644000175000017500000001347112227051774023236 0ustar kismithkismith/* * Copyright (c) 2010-2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Swift/QtUI/ChatList/QtChatListWindow.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Swift { QtChatListWindow::QtChatListWindow(UIEventStream *uiEventStream, SettingsProvider* settings, QWidget* parent) : QTreeView(parent) { eventStream_ = uiEventStream; settings_ = settings;; bookmarksEnabled_ = false; model_ = new ChatListModel(); setModel(model_); delegate_ = new ChatListDelegate(settings_->getSetting(QtUISettingConstants::COMPACT_ROSTER)); setItemDelegate(delegate_); setHeaderHidden(true); #ifdef SWIFT_PLATFORM_MACOSX setAlternatingRowColors(true); #endif expandAll(); setAnimated(true); setIndentation(0); setRootIsDecorated(true); setupContextMenus(); connect(this, SIGNAL(activated(const QModelIndex&)), this, SLOT(handleItemActivated(const QModelIndex&))); connect(this, SIGNAL(clicked(const QModelIndex&)), this, SLOT(handleClicked(const QModelIndex&))); settings_->onSettingChanged.connect(boost::bind(&QtChatListWindow::handleSettingChanged, this, _1)); } QtChatListWindow::~QtChatListWindow() { settings_->onSettingChanged.disconnect(boost::bind(&QtChatListWindow::handleSettingChanged, this, _1)); delete model_; delete delegate_; delete mucMenu_; delete emptyMenu_; } void QtChatListWindow::handleSettingChanged(const std::string& setting) { if (setting == QtUISettingConstants::COMPACT_ROSTER.getKey()) { delegate_->setCompact(settings_->getSetting(QtUISettingConstants::COMPACT_ROSTER)); repaint(); } } void QtChatListWindow::setBookmarksEnabled(bool enabled) { bookmarksEnabled_ = enabled; } void QtChatListWindow::handleClicked(const QModelIndex& index) { ChatListGroupItem* item = dynamic_cast(static_cast(index.internalPointer())); if (item) { setExpanded(index, !isExpanded(index)); } } void QtChatListWindow::setupContextMenus() { mucMenu_ = new QMenu(); mucMenu_->addAction(tr("Add New Bookmark"), this, SLOT(handleAddBookmark())); mucMenu_->addAction(tr("Edit Bookmark"), this, SLOT(handleEditBookmark())); mucMenu_->addAction(tr("Remove Bookmark"), this, SLOT(handleRemoveBookmark())); emptyMenu_ = new QMenu(); emptyMenu_->addAction(tr("Add New Bookmark"), this, SLOT(handleAddBookmark())); } void QtChatListWindow::handleItemActivated(const QModelIndex& index) { ChatListItem* item = model_->getItemForIndex(index); if (ChatListMUCItem* mucItem = dynamic_cast(item)) { if (bookmarksEnabled_) { onMUCBookmarkActivated(mucItem->getBookmark()); } } else if (ChatListRecentItem* recentItem = dynamic_cast(item)) { if (!recentItem->getChat().isMUC || bookmarksEnabled_) { onRecentActivated(recentItem->getChat()); } } else if (ChatListWhiteboardItem* whiteboardItem = dynamic_cast(item)) { if (!whiteboardItem->getChat().isMUC || bookmarksEnabled_) { eventStream_->send(boost::make_shared(whiteboardItem->getChat().jid)); } } } void QtChatListWindow::clearBookmarks() { model_->clearBookmarks(); } void QtChatListWindow::addMUCBookmark(const MUCBookmark& bookmark) { model_->addMUCBookmark(bookmark); } void QtChatListWindow::removeMUCBookmark(const MUCBookmark& bookmark) { model_->removeMUCBookmark(bookmark); } void QtChatListWindow::addWhiteboardSession(const ChatListWindow::Chat& chat) { model_->addWhiteboardSession(chat); } void QtChatListWindow::removeWhiteboardSession(const JID& jid) { model_->removeWhiteboardSession(jid); } void QtChatListWindow::setRecents(const std::list& recents) { model_->setRecents(recents); } void QtChatListWindow::setUnreadCount(int unread) { emit onCountUpdated(unread); } void QtChatListWindow::handleRemoveBookmark() { ChatListMUCItem* mucItem = dynamic_cast(contextMenuItem_); if (!mucItem) return; eventStream_->send(boost::shared_ptr(new RemoveMUCBookmarkUIEvent(mucItem->getBookmark()))); } void QtChatListWindow::handleAddBookmark() { (new QtAddBookmarkWindow(eventStream_))->show(); } void QtChatListWindow::handleEditBookmark() { ChatListMUCItem* mucItem = dynamic_cast(contextMenuItem_); if (!mucItem) return; QtEditBookmarkWindow* window = new QtEditBookmarkWindow(eventStream_, mucItem->getBookmark()); window->show(); } void QtChatListWindow::contextMenuEvent(QContextMenuEvent* event) { QModelIndex index = indexAt(event->pos()); ChatListItem* baseItem = index.isValid() ? static_cast(index.internalPointer()) : NULL; contextMenuItem_ = baseItem; if (!baseItem) { emptyMenu_->exec(QCursor::pos()); return; } ChatListMUCItem* mucItem = dynamic_cast(baseItem); if (mucItem) { if (!bookmarksEnabled_) { return; } mucMenu_->exec(QCursor::pos()); } else { QMenu menu; QAction* clearRecents = menu.addAction(tr("Clear recents")); menu.addAction(clearRecents); QAction* result = menu.exec(event->globalPos()); if (result == clearRecents) { onClearRecentsRequested(); } } } } swift-im-2.0+dev6/Swift/QtUI/ChatList/ChatListItem.h0000644000175000017500000000102012227051774022010 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include namespace Swift { class ChatListGroupItem; class ChatListItem { public: ChatListItem(ChatListGroupItem* parent) {parent_ = parent;}; virtual ~ChatListItem() {} ChatListGroupItem* parent() {return parent_;}; virtual QVariant data(int role) const = 0; private: ChatListGroupItem* parent_; }; } swift-im-2.0+dev6/Swift/QtUI/ChatList/ChatListDelegate.h0000644000175000017500000000251012227051774022631 0ustar kismithkismith/* * Copyright (c) 2010-2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include "Swift/QtUI/Roster/GroupItemDelegate.h" namespace Swift { class ChatListMUCItem; class ChatListRecentItem; class ChatListWhiteboardItem; class ChatListDelegate : public QStyledItemDelegate { public: ChatListDelegate(bool compact); ~ChatListDelegate(); QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const; void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; public slots: void setCompact(bool compact); private: void paintMUC(QPainter* painter, const QStyleOptionViewItem& option, ChatListMUCItem* item) const; void paintRecent(QPainter* painter, const QStyleOptionViewItem& option, ChatListRecentItem* item) const; void paintWhiteboard(QPainter* painter, const QStyleOptionViewItem& option, ChatListWhiteboardItem* item) const; QSize mucSizeHint(const QStyleOptionViewItem& /*option*/, const QModelIndex& /*index*/ ) const; QSize recentSizeHint(const QStyleOptionViewItem& /*option*/, const QModelIndex& /*index*/ ) const; bool compact_; DelegateCommons common_; GroupItemDelegate* groupDelegate_; }; } swift-im-2.0+dev6/Swift/QtUI/ChatList/ChatListModel.cpp0000644000175000017500000000773612227051774022531 0ustar kismithkismith/* * Copyright (c) 2010-2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include namespace Swift { ChatListModel::ChatListModel() : whiteboards_(NULL) { root_ = new ChatListGroupItem("", NULL, false); mucBookmarks_ = new ChatListGroupItem(tr("Bookmarked Rooms"), root_); recents_ = new ChatListGroupItem(tr("Recent Chats"), root_, false); #ifdef SWIFT_EXPERIMENTAL_WB whiteboards_ = new ChatListGroupItem(tr("Opened Whiteboards"), root_, false); root_->addItem(whiteboards_); #endif root_->addItem(recents_); root_->addItem(mucBookmarks_); } void ChatListModel::clearBookmarks() { emit layoutAboutToBeChanged(); mucBookmarks_->clear(); emit layoutChanged(); } void ChatListModel::addMUCBookmark(const Swift::MUCBookmark& bookmark) { emit layoutAboutToBeChanged(); mucBookmarks_->addItem(new ChatListMUCItem(bookmark, mucBookmarks_)); emit layoutChanged(); //QModelIndex index = createIndex(mucBookmarks_->rowCount() - 1, 0, mucBookmarks_); //emit dataChanged(index, index); //emit dataChanged(parent(index), parent(index)); } void ChatListModel::removeMUCBookmark(const Swift::MUCBookmark& bookmark) { for (int i = 0; i < mucBookmarks_->rowCount(); i++) { ChatListMUCItem* item = dynamic_cast(mucBookmarks_->item(i)); if (item->getBookmark() == bookmark) { emit layoutAboutToBeChanged(); mucBookmarks_->remove(i); emit layoutChanged(); break; } } } void ChatListModel::addWhiteboardSession(const ChatListWindow::Chat& chat) { emit layoutAboutToBeChanged(); whiteboards_->addItem(new ChatListWhiteboardItem(chat, whiteboards_)); emit layoutChanged(); } void ChatListModel::removeWhiteboardSession(const JID& jid) { for (int i = 0; i < whiteboards_->rowCount(); i++) { ChatListWhiteboardItem* item = dynamic_cast(whiteboards_->item(i)); if (item->getChat().jid == jid) { emit layoutAboutToBeChanged(); whiteboards_->remove(i); emit layoutChanged(); break; } } } void ChatListModel::setRecents(const std::list& recents) { emit layoutAboutToBeChanged(); recents_->clear(); foreach (const ChatListWindow::Chat chat, recents) { recents_->addItem(new ChatListRecentItem(chat, recents_)); //whiteboards_->addItem(new ChatListRecentItem(chat, whiteboards_)); } emit layoutChanged(); } int ChatListModel::columnCount(const QModelIndex& /*parent*/) const { return 1; } ChatListItem* ChatListModel::getItemForIndex(const QModelIndex& index) const { return index.isValid() ? static_cast(index.internalPointer()) : NULL; } QVariant ChatListModel::data(const QModelIndex& index, int role) const { ChatListItem* item = getItemForIndex(index); return item ? item->data(role) : QVariant(); } QModelIndex ChatListModel::index(int row, int column, const QModelIndex & parent) const { if (!hasIndex(row, column, parent)) { return QModelIndex(); } ChatListGroupItem *parentItem = parent.isValid() ? static_cast(parent.internalPointer()) : root_; return row < parentItem->rowCount() ? createIndex(row, column, parentItem->item(row)) : QModelIndex(); } QModelIndex ChatListModel::parent(const QModelIndex& index) const { if (!index.isValid()) { return QModelIndex(); } ChatListGroupItem* parent = static_cast(index.internalPointer())->parent(); return (parent == root_) ? QModelIndex() : createIndex(parent->parent()->row(parent), 0, parent); } int ChatListModel::rowCount(const QModelIndex& parentIndex) const { ChatListGroupItem* parent = NULL; if (parentIndex.isValid()) { parent = dynamic_cast(static_cast(parentIndex.internalPointer())); } else { parent = root_; } int count = (parent ? parent->rowCount() : 0); return count; } } swift-im-2.0+dev6/Swift/QtUI/ChatList/ChatListGroupItem.h0000644000175000017500000000242612227051774023040 0ustar kismithkismith/* * Copyright (c) 2010-2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include "Swift/QtUI/ChatList/ChatListItem.h" namespace Swift { class ChatListGroupItem : public ChatListItem { public: ChatListGroupItem(const QString& name, ChatListGroupItem* parent, bool sorted = true) : ChatListItem(parent), name_(name), sorted_(sorted) {}; void addItem(ChatListItem* item) {items_.push_back(item); if (sorted_) {qStableSort(items_.begin(), items_.end(), pointerItemLessThan);}}; void remove(int index) {items_.removeAt(index);}; int rowCount() {return items_.size();}; ChatListItem* item(int i) {return items_[i];}; int row(ChatListItem* item) {return items_.indexOf(item);}; QVariant data(int role) const {return (role == Qt::DisplayRole) ? name_ : QVariant();}; void clear() {items_.clear();}; private: static bool pointerItemLessThan(const ChatListItem* first, const ChatListItem* second) { QString myName = first->data(Qt::DisplayRole).toString().toLower(); QString theirName = second->data(Qt::DisplayRole).toString().toLower(); return myName < theirName; } QString name_; QList items_; bool sorted_; }; } swift-im-2.0+dev6/Swift/QtUI/ChatList/QtChatListWindow.h0000644000175000017500000000327212227051774022701 0ustar kismithkismith/* * Copyright (c) 2010-2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include "Swift/Controllers/UIInterfaces/ChatListWindow.h" #include "Swift/Controllers/UIEvents/UIEventStream.h" #include "Swift/QtUI/ChatList/ChatListModel.h" #include "Swift/QtUI/ChatList/ChatListDelegate.h" namespace Swift { class SettingsProvider; class QtChatListWindow : public QTreeView, public ChatListWindow { Q_OBJECT public: QtChatListWindow(UIEventStream *uiEventStream, SettingsProvider* settings, QWidget* parent = NULL); virtual ~QtChatListWindow(); void addMUCBookmark(const MUCBookmark& bookmark); void removeMUCBookmark(const MUCBookmark& bookmark); void addWhiteboardSession(const ChatListWindow::Chat& chat); void removeWhiteboardSession(const JID& jid); void setBookmarksEnabled(bool enabled); void setRecents(const std::list& recents); void setUnreadCount(int unread); void clearBookmarks(); signals: void onCountUpdated(int count); private slots: void handleItemActivated(const QModelIndex&); void handleAddBookmark(); void handleEditBookmark(); void handleRemoveBookmark(); void handleClicked(const QModelIndex& index); void handleSettingChanged(const std::string& setting); protected: void contextMenuEvent(QContextMenuEvent* event); private: void setupContextMenus(); bool bookmarksEnabled_; UIEventStream* eventStream_; ChatListModel* model_; ChatListDelegate* delegate_; QMenu* mucMenu_; QMenu* emptyMenu_; ChatListItem* contextMenuItem_; SettingsProvider* settings_; }; } swift-im-2.0+dev6/Swift/QtUI/ChatList/ChatListWhiteboardItem.cpp0000644000175000017500000000310212227051774024357 0ustar kismithkismith/* * Copyright (c) 2012 Mateusz Piękos * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include #include namespace Swift { ChatListWhiteboardItem::ChatListWhiteboardItem(const ChatListWindow::Chat& chat, ChatListGroupItem* parent) : ChatListItem(parent), chat_(chat) { } const ChatListWindow::Chat& ChatListWhiteboardItem::getChat() const { return chat_; } QVariant ChatListWhiteboardItem::data(int role) const { switch (role) { case Qt::DisplayRole: return P2QSTRING(chat_.chatName); case DetailTextRole: return P2QSTRING(chat_.activity); /*case Qt::TextColorRole: return textColor_; case Qt::BackgroundColorRole: return backgroundColor_; case Qt::ToolTipRole: return isContact() ? toolTipString() : QVariant(); case StatusTextRole: return statusText_;*/ case AvatarRole: return QVariant(QString(chat_.avatarPath.string().c_str())); case PresenceIconRole: return getPresenceIcon(); default: return QVariant(); } } QIcon ChatListWhiteboardItem::getPresenceIcon() const { QString iconString; switch (chat_.statusType) { case StatusShow::Online: iconString = "online";break; case StatusShow::Away: iconString = "away";break; case StatusShow::XA: iconString = "away";break; case StatusShow::FFC: iconString = "online";break; case StatusShow::DND: iconString = "dnd";break; case StatusShow::None: iconString = "offline";break; } return QIcon(":/icons/" + iconString + ".png"); } } swift-im-2.0+dev6/Swift/QtUI/ChatList/ChatListDelegate.cpp0000644000175000017500000001420512227051774023170 0ustar kismithkismith/* * Copyright (c) 2010-2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include "Swift/QtUI/ChatList/ChatListDelegate.h" #include "Swift/QtUI/Roster/GroupItemDelegate.h" #include "Swift/QtUI/ChatList/ChatListItem.h" #include "Swift/QtUI/ChatList/ChatListMUCItem.h" #include "Swift/QtUI/ChatList/ChatListRecentItem.h" #include "Swift/QtUI/ChatList/ChatListWhiteboardItem.h" #include "Swift/QtUI/ChatList/ChatListGroupItem.h" namespace Swift { ChatListDelegate::ChatListDelegate(bool compact) : compact_(compact) { groupDelegate_ = new GroupItemDelegate(); } ChatListDelegate::~ChatListDelegate() { delete groupDelegate_; } void ChatListDelegate::setCompact(bool compact) { compact_ = compact; } QSize ChatListDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index ) const { ChatListItem* item = static_cast(index.internalPointer()); if (item && dynamic_cast(item)) { return mucSizeHint(option, index); } else if (item && dynamic_cast(item)) { return common_.contactSizeHint(option, index, compact_); } else if (item && dynamic_cast(item)) { return groupDelegate_->sizeHint(option, index); } else if (item && dynamic_cast(item)) { return common_.contactSizeHint(option, index, compact_); } return QStyledItemDelegate::sizeHint(option, index); } QSize ChatListDelegate::mucSizeHint(const QStyleOptionViewItem& /*option*/, const QModelIndex& /*index*/ ) const { QFontMetrics nameMetrics(common_.nameFont); QFontMetrics statusMetrics(common_.detailFont); int sizeByText = 2 * common_.verticalMargin + nameMetrics.height() + statusMetrics.height(); return QSize(150, sizeByText); } QSize ChatListDelegate::recentSizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const { return mucSizeHint(option, index); } void ChatListDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { ChatListItem* item = static_cast(index.internalPointer()); if (item && dynamic_cast(item)) { paintMUC(painter, option, dynamic_cast(item)); } else if (item && dynamic_cast(item)) { paintRecent(painter, option, dynamic_cast(item)); } else if (item && dynamic_cast(item)) { ChatListGroupItem* group = dynamic_cast(item); groupDelegate_->paint(painter, option, group->data(Qt::DisplayRole).toString(), group->rowCount(), option.state & QStyle::State_Open); } else if (item && dynamic_cast(item)) { paintWhiteboard(painter, option, dynamic_cast(item)); } else { QStyledItemDelegate::paint(painter, option, index); } } void ChatListDelegate::paintMUC(QPainter* painter, const QStyleOptionViewItem& option, ChatListMUCItem* item) const { painter->save(); QRect fullRegion(option.rect); if ( option.state & QStyle::State_Selected ) { painter->fillRect(fullRegion, option.palette.highlight()); painter->setPen(option.palette.highlightedText().color()); } else { QColor nameColor = item->data(Qt::TextColorRole).value(); painter->setPen(QPen(nameColor)); } QFontMetrics nameMetrics(common_.nameFont); painter->setFont(common_.nameFont); int extraFontWidth = nameMetrics.width("H"); int leftOffset = common_.horizontalMargin * 2 + extraFontWidth / 2; QRect textRegion(fullRegion.adjusted(leftOffset, 0, 0, 0)); int nameHeight = nameMetrics.height() + common_.verticalMargin; QRect nameRegion(textRegion.adjusted(0, common_.verticalMargin, 0, 0)); DelegateCommons::drawElidedText(painter, nameRegion, item->data(Qt::DisplayRole).toString()); painter->setFont(common_.detailFont); painter->setPen(QPen(QColor(160,160,160))); QRect detailRegion(textRegion.adjusted(0, nameHeight, 0, 0)); DelegateCommons::drawElidedText(painter, detailRegion, item->data(ChatListMUCItem::DetailTextRole).toString()); painter->restore(); } void ChatListDelegate::paintRecent(QPainter* painter, const QStyleOptionViewItem& option, ChatListRecentItem* item) const { QColor nameColor = item->data(Qt::TextColorRole).value(); QString avatarPath; if (item->data(ChatListRecentItem::AvatarRole).isValid() && !item->data(ChatListRecentItem::AvatarRole).value().isNull()) { avatarPath = item->data(ChatListRecentItem::AvatarRole).value(); } QIcon presenceIcon = item->data(ChatListRecentItem::PresenceIconRole).isValid() && !item->data(ChatListRecentItem::PresenceIconRole).value().isNull() ? item->data(ChatListRecentItem::PresenceIconRole).value() : QIcon(":/icons/offline.png"); QString name = item->data(Qt::DisplayRole).toString(); //qDebug() << "Avatar for " << name << " = " << avatarPath; QString statusText = item->data(ChatListRecentItem::DetailTextRole).toString(); common_.paintContact(painter, option, nameColor, avatarPath, presenceIcon, name, statusText, item->getChat().unreadCount, compact_); } void ChatListDelegate::paintWhiteboard(QPainter* painter, const QStyleOptionViewItem& option, ChatListWhiteboardItem* item) const { QColor nameColor = item->data(Qt::TextColorRole).value(); QString avatarPath; if (item->data(ChatListWhiteboardItem::AvatarRole).isValid() && !item->data(ChatListWhiteboardItem::AvatarRole).value().isNull()) { avatarPath = item->data(ChatListWhiteboardItem::AvatarRole).value(); } QIcon presenceIcon;/* = item->data(ChatListWhiteboardItem::PresenceIconRole).isValid() && !item->data(ChatListWhiteboardItem::PresenceIconRole).value().isNull() ? item->data(ChatListWhiteboardItem::PresenceIconRole).value() : QIcon(":/icons/offline.png");*/ QString name = item->data(Qt::DisplayRole).toString(); //qDebug() << "Avatar for " << name << " = " << avatarPath; QString statusText = item->data(ChatListWhiteboardItem::DetailTextRole).toString(); common_.paintContact(painter, option, nameColor, avatarPath, presenceIcon, name, statusText, item->getChat().unreadCount, compact_); } } swift-im-2.0+dev6/Swift/QtUI/QtSwiftUtil.h0000644000175000017500000000056012227051774020212 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #define P2QSTRING(a) QString::fromUtf8(a.c_str()) #define Q2PSTRING(a) std::string(a.toUtf8()) #define B2QDATE(a) QDateTime::fromTime_t((a - boost::posix_time::from_time_t(0)).total_seconds()) swift-im-2.0+dev6/Swift/QtUI/QtSoundPlayer.cpp0000644000175000017500000000165612227051774021067 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "QtSoundPlayer.h" #include #include #include "SwifTools/Application/ApplicationPathProvider.h" namespace Swift { QtSoundPlayer::QtSoundPlayer(ApplicationPathProvider* applicationPathProvider) : applicationPathProvider(applicationPathProvider) { } void QtSoundPlayer::playSound(SoundEffect sound) { switch (sound) { case MessageReceived: playSound("/sounds/message-received.wav"); break; } } void QtSoundPlayer::playSound(const std::string& soundResource) { boost::filesystem::path resourcePath = applicationPathProvider->getResourcePath(soundResource); if (boost::filesystem::exists(resourcePath)) { QSound::play(resourcePath.string().c_str()); } else { std::cerr << "Unable to find sound: " << soundResource << std::endl; } } } swift-im-2.0+dev6/Swift/QtUI/QtEditBookmarkWindow.h0000644000175000017500000000113312227051774022020 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include "QtBookmarkDetailWindow.h" #include "Swift/Controllers/UIEvents/UIEventStream.h" #include "Swift/Controllers/UIEvents/EditMUCBookmarkUIEvent.h" namespace Swift { class QtEditBookmarkWindow : public QtBookmarkDetailWindow { Q_OBJECT public: QtEditBookmarkWindow(UIEventStream* eventStream, const MUCBookmark& bookmark); bool commit(); private: UIEventStream* eventStream_; MUCBookmark bookmark_; }; } swift-im-2.0+dev6/Swift/QtUI/QtSettingsProvider.cpp0000644000175000017500000000666612227051774022143 0ustar kismithkismith/* * Copyright (c) 2010-2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include namespace Swift { QtSettingsProvider::QtSettingsProvider() { } QtSettingsProvider::~QtSettingsProvider() { } bool QtSettingsProvider::hasSetting(const std::string& key) { return !settings_.value(key.c_str()).isNull(); } std::string QtSettingsProvider::getSetting(const Setting& setting) { QVariant variant = settings_.value(setting.getKey().c_str()); return variant.isNull() ? setting.getDefaultValue() : std::string(variant.toString().toUtf8()); } void QtSettingsProvider::storeSetting(const Setting& setting, const std::string& settingValue) { bool changed = false; if (getSetting(setting) != settingValue) { changed = true; } settings_.setValue(setting.getKey().c_str(), settingValue.c_str()); if (changed) { onSettingChanged(setting.getKey()); } updatePermissions(); } bool QtSettingsProvider::getSetting(const Setting& setting) { QVariant variant = settings_.value(setting.getKey().c_str()); return variant.isNull() ? setting.getDefaultValue() : variant.toBool(); } void QtSettingsProvider::storeSetting(const Setting& setting, const bool& settingValue) { bool changed = false; if (getSetting(setting) != settingValue) { changed = true; } settings_.setValue(setting.getKey().c_str(), settingValue); if (changed) { onSettingChanged(setting.getKey()); } updatePermissions(); } int QtSettingsProvider::getSetting(const Setting& setting) { QVariant variant = settings_.value(setting.getKey().c_str()); return variant.isNull() ? setting.getDefaultValue() : variant.toInt(); } void QtSettingsProvider::storeSetting(const Setting& setting, const int& settingValue) { bool changed = false; if (getSetting(setting) != settingValue) { changed = true; } settings_.setValue(setting.getKey().c_str(), settingValue); if (changed) { onSettingChanged(setting.getKey()); } updatePermissions(); } std::vector QtSettingsProvider::getAvailableProfiles() { std::vector profiles; QVariant profilesVariant = settings_.value("profileList"); foreach(QString profileQString, profilesVariant.toStringList()) { profiles.push_back(std::string(profileQString.toUtf8())); } return profiles; } void QtSettingsProvider::createProfile(const std::string& profile) { QStringList stringList = settings_.value("profileList").toStringList(); stringList.append(profile.c_str()); settings_.setValue("profileList", stringList); updatePermissions(); } void QtSettingsProvider::removeProfile(const std::string& profile) { QString profileStart(QString(profile.c_str()) + ":"); foreach (QString key, settings_.allKeys()) { if (key.startsWith(profileStart)) { settings_.remove(key); } } QStringList stringList = settings_.value("profileList").toStringList(); stringList.removeAll(profile.c_str()); settings_.setValue("profileList", stringList); updatePermissions(); } QSettings* QtSettingsProvider::getQSettings() { return &settings_; } void QtSettingsProvider::updatePermissions() { #if !defined(Q_WS_WIN) && !defined(Q_WS_MAC) QFile file(settings_.fileName()); if (file.exists()) { file.setPermissions(QFile::ReadOwner|QFile::WriteOwner); } #endif } bool QtSettingsProvider::getIsSettingFinal(const std::string& /*settingPath*/) { return false; } } swift-im-2.0+dev6/Swift/QtUI/WindowsNotifier.cpp0000644000175000017500000000335412227051774021444 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "WindowsNotifier.h" #include #include #include #include #include "QtWin32NotifierWindow.h" #include "QtSwiftUtil.h" namespace Swift { WindowsNotifier::WindowsNotifier(const std::string& name, const boost::filesystem::path& icon, QSystemTrayIcon* tray) : tray(tray) { notifierWindow = new QtWin32NotifierWindow(); snarlNotifier = new SnarlNotifier(name, notifierWindow, icon); connect(tray, SIGNAL(messageClicked()), SLOT(handleMessageClicked())); } WindowsNotifier::~WindowsNotifier() { delete snarlNotifier; delete notifierWindow; } void WindowsNotifier::showMessage(Type type, const std::string& subject, const std::string& description, const boost::filesystem::path& picture, boost::function callback) { if (snarlNotifier->isAvailable()) { snarlNotifier->showMessage(type, subject, description, picture, callback); return; } std::vector defaultTypes = getDefaultTypes(); if (std::find(defaultTypes.begin(), defaultTypes.end(), type) == defaultTypes.end()) { return; } lastCallback = callback; int timeout = (type == IncomingMessage || type == SystemMessage) ? DEFAULT_MESSAGE_NOTIFICATION_TIMEOUT_SECONDS : DEFAULT_STATUS_NOTIFICATION_TIMEOUT_SECONDS; tray->showMessage(P2QSTRING(subject), P2QSTRING(description), type == SystemMessage ? QSystemTrayIcon::Information : QSystemTrayIcon::NoIcon, timeout * 1000); } void WindowsNotifier::handleMessageClicked() { if (lastCallback) { lastCallback(); } } void WindowsNotifier::purgeCallbacks() { lastCallback = boost::function(); } } swift-im-2.0+dev6/Swift/QtUI/QtAddBookmarkWindow.h0000644000175000017500000000104012227051774021620 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include "QtBookmarkDetailWindow.h" #include "Swift/Controllers/UIEvents/UIEventStream.h" #include "Swift/Controllers/UIEvents/AddMUCBookmarkUIEvent.h" namespace Swift { class QtAddBookmarkWindow : public QtBookmarkDetailWindow { Q_OBJECT public: QtAddBookmarkWindow(UIEventStream* eventStream); bool commit(); private: UIEventStream* eventStream_; }; } swift-im-2.0+dev6/Swift/QtUI/main.cpp0000644000175000017500000000632112227051774017233 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "QtSwift.h" #include "QtTranslator.h" #include "QtSwiftUtil.h" int main(int argc, char* argv[]) { QApplication app(argc, argv); Swift::PlatformApplicationPathProvider applicationPathProvider(SWIFT_APPLICATION_NAME); Swift::CrashReporter crashReporter(applicationPathProvider.getDataDir() / "crashes"); QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8")); // Parse program options boost::program_options::options_description desc = Swift::QtSwift::getOptionsDescription(); boost::program_options::variables_map vm; try { boost::program_options::store(boost::program_options::parse_command_line(argc, argv, desc), vm); } catch (const boost::program_options::unknown_option& option) { #if BOOST_VERSION >= 104200 std::cout << "Ignoring unknown option " << option.get_option_name() << " but continuing." << std::endl; #else std::cout << "Error: " << option.what() << " (continuing)" << std::endl; #endif } boost::program_options::notify(vm); if (vm.count("help") > 0) { std::cout << SWIFT_APPLICATION_NAME << " is an instant messaging client for the XMPP network." << std::endl; std::cout << std::endl; std::cout << "Usage: " << argv[0] << " [OPTIONS]..." << std::endl; std::cout << std::endl; std::cout << desc << std::endl; return 1; } if (vm.count("version") > 0) { std::cout << SWIFT_APPLICATION_NAME << " " << buildVersion << std::endl; return 0; } // Translation QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8")); boost::filesystem::path someTranslationPath = applicationPathProvider.getResourcePath("/translations/swift_en.qm"); QTranslator qtTranslator; if (!someTranslationPath.empty()) { #if QT_VERSION >= 0x040800 if (vm.count("language") > 0) { qtTranslator.load(QString(SWIFT_APPLICATION_NAME).toLower() + "_" + P2QSTRING(vm["language"].as()), someTranslationPath.parent_path().string().c_str()); } else { qtTranslator.load(QLocale::system(), QString(SWIFT_APPLICATION_NAME).toLower(), "_", someTranslationPath.parent_path().string().c_str()); } #else //std::cout << "Loading " << std::string(QLocale::system().name().toUtf8()) << std::endl; qtTranslator.load(QString(SWIFT_APPLICATION_NAME).toLower() + "_" + QLocale::system().name(), someTranslationPath.parent_path().string().c_str()); #endif } app.installTranslator(&qtTranslator); QtTranslator swiftTranslator; Swift::Translator::setInstance(&swiftTranslator); Swift::QtSwift swift(vm); int result = app.exec(); Swift::Translator::setInstance(NULL); return result; } swift-im-2.0+dev6/Swift/QtUI/QtBookmarkDetailWindow.ui0000644000175000017500000000742712227051774022537 0ustar kismithkismith QtBookmarkDetailWindow 0 0 396 282 0 0 Edit Bookmark Details false 10 20 371 241 Bookmark Name: Room Address: Your Nickname: Room password: Qt::Horizontal 40 20 Enter automatically true Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttons_ accepted() QtBookmarkDetailWindow accept() 248 254 157 274 buttons_ rejected() QtBookmarkDetailWindow reject() 316 260 286 274 swift-im-2.0+dev6/Swift/QtUI/QtMainWindow.h0000644000175000017500000000617412227051774020343 0ustar kismithkismith/* * Copyright (c) 2010-2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include "Swift/Controllers/UIInterfaces/MainWindow.h" #include "Swift/QtUI/QtRosterHeader.h" #include "Swift/QtUI/EventViewer/QtEventWindow.h" #include "Swift/QtUI/ChatList/QtChatListWindow.h" #include "Swift/QtUI/QtLoginWindow.h" #include class QComboBox; class QLineEdit; class QPushButton; class QToolBar; class QAction; class QMenu; class QTabWidget; namespace Swift { class QtRosterWidget; class TreeWidget; class UIEventStream; class QtTabWidget; class SettingsProvider; class QtUIPreferences; class QtMainWindow : public QWidget, public MainWindow { Q_OBJECT public: QtMainWindow(SettingsProvider*, UIEventStream* eventStream, QtLoginWindow::QtMenus loginMenus, bool emoticonsExist); virtual ~QtMainWindow(); std::vector getMenus() {return menus_;} void setMyNick(const std::string& name); void setMyJID(const JID& jid); void setMyAvatarPath(const std::string& path); void setMyStatusText(const std::string& status); void setMyStatusType(StatusShow::Type type); void setConnecting(); void setStreamEncryptionStatus(bool tlsInPlaceAndValid); void openCertificateDialog(const std::vector& chain); static void openCertificateDialog(const std::vector& chain, QWidget* parent); QtEventWindow* getEventWindow(); QtChatListWindow* getChatListWindow(); void setRosterModel(Roster* roster); void setAvailableAdHocCommands(const std::vector& commands); private slots: void handleStatusChanged(StatusShow::Type showType, const QString &statusMessage); void handleSettingChanged(const std::string& settingPath); void handleShowOfflineToggled(bool); void handleShowEmoticonsToggled(bool); void handleJoinMUCAction(); void handleViewLogsAction(); void handleSignOutAction(); void handleEditProfileAction(); void handleAddUserActionTriggered(bool checked); void handleChatUserActionTriggered(bool checked); void handleAdHocActionTriggered(bool checked); void handleEventCountUpdated(int count); void handleChatCountUpdated(int count); void handleEditProfileRequest(); void handleTabChanged(int index); void handleToggleRequestDeliveryReceipts(bool enabled); void handleShowCertificateInfo(); private: SettingsProvider* settings_; QtLoginWindow::QtMenus loginMenus_; std::vector menus_; QtRosterWidget* treeWidget_; QtRosterHeader* meView_; QAction* addUserAction_; QAction* editUserAction_; QAction* chatUserAction_; QAction* showOfflineAction_; QAction* showEmoticonsAction_; QAction* toggleRequestDeliveryReceipts_; QMenu* serverAdHocMenu_; QtTabWidget* tabs_; QWidget* contactsTabWidget_; QWidget* eventsTabWidget_; QtEventWindow* eventWindow_; QtChatListWindow* chatListWindow_; UIEventStream* uiEventStream_; std::vector serverAdHocCommands_; QList serverAdHocCommandActions_; }; } swift-im-2.0+dev6/Swift/QtUI/ChatSnippet.h0000644000175000017500000000306312227051774020176 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include "QtChatTheme.h" namespace Swift { class ChatSnippet { public: ChatSnippet(bool appendToPrevious); virtual ~ChatSnippet(); virtual const QString& getContent() const = 0; virtual QString getContinuationElementID() const { return ""; } boost::shared_ptr getContinuationFallbackSnippet() const {return continuationFallback_;} bool getAppendToPrevious() const { return appendToPrevious_; } static QString escape(const QString& original) { QString result(original); result.replace("%message%", "%message%"); result.replace("%sender%", "%sender%"); result.replace("%wrapped_sender%", "%wrapped_sender%"); result.replace("%time%", "%%time%"); result.replace("%shortTime%", "%%shortTime%"); result.replace("%userIconPath%", "%userIconPath%"); result.replace("\t", " "); result.replace(" ", "  "); return result; } static QString timeToEscapedString(const QDateTime& time); protected: QString wrapResizable(const QString& text); void setContinuationFallbackSnippet(boost::shared_ptr continuationFallback) { continuationFallback_ = continuationFallback; } private: bool appendToPrevious_; boost::shared_ptr continuationFallback_; }; } swift-im-2.0+dev6/Swift/QtUI/QtSettingsProvider.h0000644000175000017500000000233012227051774021570 0ustar kismithkismith/* * Copyright (c) 2010-2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include namespace Swift { class QtSettingsProvider : public SettingsProvider { public: QtSettingsProvider(); virtual ~QtSettingsProvider(); virtual std::string getSetting(const Setting& setting); virtual void storeSetting(const Setting& setting, const std::string& value); virtual bool getSetting(const Setting& setting); virtual void storeSetting(const Setting& setting, const bool& value); virtual int getSetting(const Setting& setting); virtual void storeSetting(const Setting& setting, const int& value); virtual std::vector getAvailableProfiles(); virtual void createProfile(const std::string& profile); virtual void removeProfile(const std::string& profile); virtual bool hasSetting(const std::string& key); QSettings* getQSettings(); protected: virtual bool getIsSettingFinal(const std::string& settingPath); private: void updatePermissions(); private: QSettings settings_; }; } swift-im-2.0+dev6/Swift/QtUI/MUCSearch/0000755000175000017500000000000012227051774017353 5ustar kismithkismithswift-im-2.0+dev6/Swift/QtUI/MUCSearch/MUCSearchModel.h0000644000175000017500000000203012227051774022252 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include "Swift/QtUI/MUCSearch/MUCSearchServiceItem.h" namespace Swift { class MUCSearchModel : public QAbstractItemModel { Q_OBJECT public: MUCSearchModel(); void clear(); void addService(MUCSearchServiceItem* service); int columnCount(const QModelIndex& parent = QModelIndex()) const; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const; QModelIndex parent(const QModelIndex& index) const; int rowCount(const QModelIndex& parent = QModelIndex()) const; // ChatListItem* getItemForIndex(const QModelIndex& index) const; private: // ChatListGroupItem* mucBookmarks_; // ChatListGroupItem* root_; QList services_; }; } swift-im-2.0+dev6/Swift/QtUI/MUCSearch/QtMUCSearchWindow.ui0000644000175000017500000000517612227051774023172 0ustar kismithkismith QtMUCSearchWindow 0 0 523 368 Search Room Service: 2 0 true 1 0 true Qt::Horizontal 40 20 Cancel false OK false List rooms swift-im-2.0+dev6/Swift/QtUI/MUCSearch/MUCSearchModel.cpp0000644000175000017500000000453412227051774022620 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Swift/QtUI/MUCSearch/MUCSearchModel.h" #include "Swift/QtUI/MUCSearch/MUCSearchEmptyItem.h" namespace Swift { MUCSearchModel::MUCSearchModel() { } void MUCSearchModel::clear() { emit layoutAboutToBeChanged(); services_.clear(); emit layoutChanged(); } void MUCSearchModel::addService(MUCSearchServiceItem* service) { emit layoutAboutToBeChanged(); services_.push_back(service); emit layoutChanged(); } int MUCSearchModel::columnCount(const QModelIndex& /*parent*/) const { return 1; } QVariant MUCSearchModel::data(const QModelIndex& index, int role) const { return index.isValid() ? static_cast(index.internalPointer())->data(role) : QVariant(); } QModelIndex MUCSearchModel::index(int row, int column, const QModelIndex & parent) const { if (!hasIndex(row, column, parent)) { return QModelIndex(); } if (parent.isValid()) { MUCSearchServiceItem* parentItem = static_cast(parent.internalPointer()); return row < parentItem->rowCount() ? createIndex(row, column, parentItem->getItem(row)) : QModelIndex(); } else { return row < services_.size() ? createIndex(row, column, services_[row]) : QModelIndex(); } } QModelIndex MUCSearchModel::parent(const QModelIndex& index) const { if (!index.isValid()) { return QModelIndex(); } MUCSearchItem* item = static_cast(index.internalPointer()); if (!item) { return QModelIndex(); } else if (dynamic_cast(item)) { return QModelIndex(); } MUCSearchServiceItem* parent = NULL; if (MUCSearchRoomItem* roomItem = dynamic_cast(item)) { parent = roomItem->getParent(); } else if (MUCSearchEmptyItem* emptyItem = dynamic_cast(item)) { parent = emptyItem->getParent(); } if (parent) { int row = services_.indexOf(parent); return createIndex(row, 1, parent); } else { return QModelIndex(); } } int MUCSearchModel::rowCount(const QModelIndex& parentIndex) const { if (!parentIndex.isValid()) { return services_.size(); } if (dynamic_cast(static_cast(parentIndex.internalPointer()))) { return services_[parentIndex.row()]->rowCount(); } else { return 0; } } } swift-im-2.0+dev6/Swift/QtUI/MUCSearch/QtMUCSearchWindow.cpp0000644000175000017500000001467712227051774023345 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Swift/QtUI/MUCSearch/QtMUCSearchWindow.h" #include #include #include #include #include #include "Swift/Controllers/UIEvents/JoinMUCUIEvent.h" #include "Swift/Controllers/UIEvents/AddMUCBookmarkUIEvent.h" #include "Swift/QtUI/MUCSearch/MUCSearchModel.h" #include "Swift/QtUI/MUCSearch/MUCSearchDelegate.h" #include "Swift/QtUI/MUCSearch/MUCSearchEmptyItem.h" #include "Swift/QtUI/QtSwiftUtil.h" namespace Swift { QtMUCSearchWindow::QtMUCSearchWindow() { ui_.setupUi(this); #ifndef Q_WS_MAC setWindowIcon(QIcon(":/logo-icon-16.png")); #endif setModal(true); ui_.filter_->hide(); model_ = new MUCSearchModel(); delegate_ = new MUCSearchDelegate(); ui_.results_->setModel(model_); ui_.results_->setItemDelegate(delegate_); ui_.results_->setHeaderHidden(true); ui_.results_->setRootIsDecorated(true); ui_.results_->setAnimated(true); ui_.results_->setAlternatingRowColors(true); connect(ui_.searchButton, SIGNAL(clicked()), this, SLOT(handleSearch())); connect(ui_.service_, SIGNAL(activated(const QString&)), this, SLOT(handleSearch(const QString&))); connect(ui_.results_->selectionModel(), SIGNAL(selectionChanged (const QItemSelection&, const QItemSelection&)), this, SLOT(handleSelectionChanged (const QItemSelection&, const QItemSelection&))); connect(ui_.results_, SIGNAL(activated(const QModelIndex&)), this, SLOT(handleActivated(const QModelIndex&))); connect(ui_.results_, SIGNAL(activated(const QModelIndex&)), this, SLOT(handleActivated(const QModelIndex&))); // Not using a button box, because i can't seem to be able to make the ok button non-default (on mac) connect(ui_.okButton, SIGNAL(clicked()), this, SLOT(accept())); ui_.okButton->setEnabled(false); connect(ui_.cancelButton, SIGNAL(clicked()), this, SLOT(reject())); throbber_ = new QLabel(tr("Searching"), ui_.results_); throbber_->setMovie(new QMovie(":/icons/throbber.gif", QByteArray(), throbber_)); throbber_->setToolTip(tr("Searching")); hasHadScrollBars_ = false; updateThrobberPosition(); setSearchInProgress(false); } QtMUCSearchWindow::~QtMUCSearchWindow() { } void QtMUCSearchWindow::resizeEvent(QResizeEvent* /*event*/) { updateThrobberPosition(); } void QtMUCSearchWindow::updateThrobberPosition() { bool isShown = throbber_->isVisible(); int resultWidth = ui_.results_->width(); int resultHeight = ui_.results_->height(); //throbberWidth = throbber_->movie()->scaledSize().width(); //throbberHeight = throbber_->movie()->scaledSize().height(); int throbberWidth = 16; /* This is nasty, but the above doesn't work! */ int throbberHeight = 16; /* It's difficult (or I spent a while trying) to work out whether the scrollbars are currently shown and their appropriate size, * because if you listen for the expanded/collapsed signals, you seem to get them before the scrollbars are updated. * This seems an acceptable workaround. */ hasHadScrollBars_ |= ui_.results_->verticalScrollBar()->isVisible(); int hMargin = hasHadScrollBars_ ? ui_.results_->verticalScrollBar()->width() + 2 : 2; int vMargin = 2; /* We don't get horizontal scrollbars */ throbber_->setGeometry(QRect(resultWidth - throbberWidth - hMargin, resultHeight - throbberHeight - vMargin, throbberWidth, throbberHeight)); /* include margins */ throbber_->setVisible(isShown); } void QtMUCSearchWindow::addSavedServices(const std::list& services) { ui_.service_->clear(); foreach (const JID& jid, services) { ui_.service_->addItem(P2QSTRING(jid.toString())); } if (!services.empty()) { ui_.service_->setEditText(P2QSTRING(services.begin()->toString())); } else { ui_.service_->clearEditText(); } } void QtMUCSearchWindow::handleActivated(const QModelIndex& index) { if (!index.isValid()) { return; } if (dynamic_cast(static_cast(index.internalPointer()))) { accept(); } } void QtMUCSearchWindow::handleSearch() { handleSearch(ui_.service_->currentText()); } void QtMUCSearchWindow::handleSearch(const QString& service) { if (!service.isEmpty()) { onSearchService(JID(Q2PSTRING(service))); } } void QtMUCSearchWindow::show() { QWidget::show(); QWidget::activateWindow(); } void QtMUCSearchWindow::clearList() { model_->clear(); } void QtMUCSearchWindow::addService(const MUCService& service) { updateThrobberPosition(); MUCSearchServiceItem* serviceItem = new MUCSearchServiceItem(P2QSTRING(service.getJID().toString())); if (service.getRooms().size() > 0) { foreach (MUCService::MUCRoom room, service.getRooms()) { new MUCSearchRoomItem(P2QSTRING(room.getNode()), serviceItem); } } else { new MUCSearchEmptyItem(serviceItem); } model_->addService(serviceItem); ui_.results_->expandAll(); } void QtMUCSearchWindow::setSearchInProgress(bool searching) { if (searching) { throbber_->movie()->start(); } else { throbber_->movie()->stop(); } throbber_->setVisible(searching); } void QtMUCSearchWindow::accept() { MUCSearchRoomItem* room = getSelectedRoom(); if (room) { onFinished(JID(Q2PSTRING(room->getNode()), Q2PSTRING(room->getParent()->getHost()))); } else { onFinished(boost::optional()); } QDialog::accept(); } void QtMUCSearchWindow::reject() { onFinished(boost::optional()); QDialog::reject(); } void QtMUCSearchWindow::handleSelectionChanged(const QItemSelection&, const QItemSelection&) { ui_.okButton->setEnabled(getSelectedRoom()); } MUCSearchRoomItem* QtMUCSearchWindow::getSelectedRoom() const { // Not using selectedIndexes(), because this seems to cause a crash in Qt (4.7.0) in the // QModelIndexList destructor. // This is a workaround posted in http://www.qtcentre.org/threads/16933 (although this case // was resolved by linking against the debug libs, ours isn't, and we're not alone) QItemSelection ranges = ui_.results_->selectionModel()->selection(); QModelIndexList lstIndex; for (int i = 0; i < ranges.count(); ++i) { QModelIndex parent = ranges.at(i).parent(); int right = ranges.at(i).model()->columnCount(parent) - 1; if (ranges.at(i).left() == 0 && ranges.at(i).right() == right) { for (int r = ranges.at(i).top(); r <= ranges.at(i).bottom(); ++r) { lstIndex.append(ranges.at(i).model()->index(r, 0, parent)); } } } if (lstIndex.isEmpty()) { return NULL; } else { return dynamic_cast(static_cast(lstIndex.first().internalPointer())); } } } swift-im-2.0+dev6/Swift/QtUI/MUCSearch/MUCSearchEmptyItem.cpp0000644000175000017500000000147612227051774023477 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include namespace Swift { MUCSearchEmptyItem::MUCSearchEmptyItem(MUCSearchServiceItem* parent) : parent(parent) { parent->addRoom(this); } MUCSearchServiceItem* MUCSearchEmptyItem::getParent() { return parent; } QVariant MUCSearchEmptyItem::data(int role) { switch (role) { case Qt::DisplayRole: return QVariant(QObject::tr("No rooms found")); case Qt::FontRole: { QFont font; font.setItalic(true); return font; } case Qt::ForegroundRole: return QColor(Qt::gray); default: return QVariant(); } } } swift-im-2.0+dev6/Swift/QtUI/MUCSearch/MUCSearchServiceItem.h0000644000175000017500000000152112227051774023435 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include "Swift/QtUI/MUCSearch/MUCSearchRoomItem.h" namespace Swift { class MUCSearchServiceItem : public MUCSearchItem { public: MUCSearchServiceItem(const QString& jidString) : jidString_(jidString) {} void addRoom(MUCSearchItem* room) {rooms_.push_back(room);} int rowCount() {return rooms_.count();} MUCSearchItem* getItem(int i) {return rooms_[i];} QVariant data(int role) { switch (role) { case Qt::DisplayRole: return QVariant(jidString_); default: return QVariant(); } } QString getHost() const {return jidString_;} private: QList rooms_; QString jidString_; }; } swift-im-2.0+dev6/Swift/QtUI/MUCSearch/MUCSearchEmptyItem.h0000644000175000017500000000077012227051774023140 0ustar kismithkismith/* * 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 namespace Swift { class MUCSearchServiceItem; class MUCSearchEmptyItem : public MUCSearchItem { public: MUCSearchEmptyItem(MUCSearchServiceItem* parent); MUCSearchServiceItem* getParent(); QVariant data(int role); private: MUCSearchServiceItem* parent; }; } swift-im-2.0+dev6/Swift/QtUI/MUCSearch/MUCSearchRoomItem.h0000644000175000017500000000110212227051774022744 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include "Swift/QtUI/MUCSearch/MUCSearchItem.h" namespace Swift { class MUCSearchServiceItem; class MUCSearchRoomItem : public MUCSearchItem { public: MUCSearchRoomItem(const QString& node, MUCSearchServiceItem* parent); MUCSearchServiceItem* getParent(); QVariant data(int role); QString getNode() const {return node_;} private: MUCSearchServiceItem* parent_; QString node_; }; } swift-im-2.0+dev6/Swift/QtUI/MUCSearch/MUCSearchItem.h0000644000175000017500000000050012227051774022110 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include namespace Swift { class MUCSearchItem { public: virtual ~MUCSearchItem() {} virtual QVariant data(int role) = 0; }; } swift-im-2.0+dev6/Swift/QtUI/MUCSearch/MUCSearchDelegate.cpp0000644000175000017500000000647112227051774023274 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include "Swift/QtUI/MUCSearch/MUCSearchDelegate.h" #include "Swift/QtUI/Roster/GroupItemDelegate.h" #include "Swift/QtUI/MUCSearch/MUCSearchItem.h" #include "Swift/QtUI/MUCSearch/MUCSearchRoomItem.h" #include "Swift/QtUI/MUCSearch/MUCSearchServiceItem.h" namespace Swift { MUCSearchDelegate::MUCSearchDelegate() { } MUCSearchDelegate::~MUCSearchDelegate() { } // QSize MUCSearchDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index ) const { // // MUCSearchItem* item = static_cast(index.internalPointer()); // // if (item && dynamic_cast(item)) { // // return mucSizeHint(option, index); // // } else if (item && dynamic_cast(item)) { // // return groupDelegate_->sizeHint(option, index); // // } // return QStyledItemDelegate::sizeHint(option, index); // } // QSize MUCSearchDelegate::mucSizeHint(const QStyleOptionViewItem& /*option*/, const QModelIndex& /*index*/ ) const { // QFontMetrics nameMetrics(common_.nameFont); // QFontMetrics statusMetrics(common_.detailFont); // int sizeByText = 2 * common_.verticalMargin + nameMetrics.height() + statusMetrics.height(); // return QSize(150, sizeByText); // } // void MUCSearchDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { // MUCSearchItem* item = static_cast(index.internalPointer()); // if (item && dynamic_cast(item)) { // paintMUC(painter, option, dynamic_cast(item)); // } else if (item && dynamic_cast(item)) { // MUCSearchGroupItem* group = dynamic_cast(item); // groupDelegate_->paint(painter, option, group->data(Qt::DisplayRole).toString(), group->rowCount(), option.state & QStyle::State_Open); // } else { // QStyledItemDelegate::paint(painter, option, index); // } // } // void MUCSearchDelegate::paintMUC(QPainter* painter, const QStyleOptionViewItem& option, MUCSearchMUCItem* item) const { // painter->save(); // QRect fullRegion(option.rect); // if ( option.state & QStyle::State_Selected ) { // painter->fillRect(fullRegion, option.palette.highlight()); // painter->setPen(option.palette.highlightedText().color()); // } else { // QColor nameColor = item->data(Qt::TextColorRole).value(); // painter->setPen(QPen(nameColor)); // } // QFontMetrics nameMetrics(common_.nameFont); // painter->setFont(common_.nameFont); // int extraFontWidth = nameMetrics.width("H"); // int leftOffset = common_.horizontalMargin * 2 + extraFontWidth / 2; // QRect textRegion(fullRegion.adjusted(leftOffset, 0, 0, 0)); // int nameHeight = nameMetrics.height() + common_.verticalMargin; // QRect nameRegion(textRegion.adjusted(0, common_.verticalMargin, 0, 0)); // painter->drawText(nameRegion, Qt::AlignTop, item->data(Qt::DisplayRole).toString()); // painter->setFont(common_.detailFont); // painter->setPen(QPen(QColor(160,160,160))); // QRect detailRegion(textRegion.adjusted(0, nameHeight, 0, 0)); // painter->drawText(detailRegion, Qt::AlignTop, item->data(DetailTextRole).toString()); // painter->restore(); // } } swift-im-2.0+dev6/Swift/QtUI/MUCSearch/QtMUCSearchWindow.h0000644000175000017500000000245112227051774022775 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include "Swift/QtUI/MUCSearch/ui_QtMUCSearchWindow.h" #include "Swift/Controllers/UIInterfaces/MUCSearchWindow.h" namespace Swift { class MUCSearchModel; class MUCSearchDelegate; class MUCSearchRoomItem; class QtMUCSearchWindow : public QDialog, public MUCSearchWindow { Q_OBJECT public: QtMUCSearchWindow(); virtual ~QtMUCSearchWindow(); virtual void clearList(); virtual void addService(const MUCService& service); virtual void addSavedServices(const std::list& services); virtual void setSearchInProgress(bool searching); virtual void show(); virtual void accept(); virtual void reject(); protected: virtual void resizeEvent(QResizeEvent* event); private slots: void handleSearch(); void handleSearch(const QString&); void handleActivated(const QModelIndex& index); void updateThrobberPosition(); void handleSelectionChanged (const QItemSelection&, const QItemSelection&); MUCSearchRoomItem* getSelectedRoom() const; private: Ui::QtMUCSearchWindow ui_; MUCSearchModel* model_; MUCSearchDelegate* delegate_; QLabel* throbber_; bool hasHadScrollBars_; }; } swift-im-2.0+dev6/Swift/QtUI/MUCSearch/MUCSearchRoomItem.cpp0000644000175000017500000000122012227051774023300 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Swift/QtUI/MUCSearch/MUCSearchRoomItem.h" #include "Swift/QtUI/MUCSearch/MUCSearchServiceItem.h" namespace Swift { MUCSearchRoomItem::MUCSearchRoomItem(const QString& node, MUCSearchServiceItem* parent) : parent_(parent), node_(node) { parent_->addRoom(this); } MUCSearchServiceItem* MUCSearchRoomItem::getParent() { return parent_; } QVariant MUCSearchRoomItem::data(int role) { switch (role) { case Qt::DisplayRole: return QVariant(node_); default: return QVariant(); } } } swift-im-2.0+dev6/Swift/QtUI/MUCSearch/MUCSearchDelegate.h0000644000175000017500000000150712227051774022734 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include "Swift/QtUI/Roster/DelegateCommons.h" namespace Swift { class MUCSearchDelegate : public QStyledItemDelegate { public: MUCSearchDelegate(); ~MUCSearchDelegate(); /* QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const; */ /* void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; */ private: // void paintMUC(QPainter* painter, const QStyleOptionViewItem& option, MUCSearchMUCItem* item) const; // QSize mucSizeHint(const QStyleOptionViewItem& /*option*/, const QModelIndex& /*index*/ ) const; DelegateCommons common_; }; } swift-im-2.0+dev6/Swift/QtUI/QtCachedImageScaler.cpp0000644000175000017500000000204612227051774022060 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "QtCachedImageScaler.h" #include #include namespace Swift { QtCachedImageScaler::QtCachedImageScaler() { } boost::filesystem::path QtCachedImageScaler::getScaledImage(const boost::filesystem::path& imagePath, int size) { boost::filesystem::path scaledImagePath(imagePath.string() + "." + boost::lexical_cast(size)); if (!boost::filesystem::exists(scaledImagePath)) { QImage image(imagePath.string().c_str()); if (!image.isNull()) { if (image.width() > size || image.height() > size) { QImage scaledImage = image.scaled(size, size, Qt::KeepAspectRatio, Qt::SmoothTransformation); scaledImage.save(QString::fromUtf8(scaledImagePath.string().c_str()), "PNG"); } else { image.save(QString::fromUtf8(scaledImagePath.string().c_str()), "PNG"); } } else { return imagePath; } } return scaledImagePath; } } swift-im-2.0+dev6/Swift/QtUI/QtFormWidget.h0000644000175000017500000000144712227051774020334 0ustar kismithkismith/* * Copyright (c) 2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include class QListWidget; namespace Swift { class QtFormWidget : public QWidget { Q_OBJECT public: QtFormWidget(Form::ref form, QWidget* parent = NULL); virtual ~QtFormWidget(); Form::ref getCompletedForm(); void setEditable(bool editable); private: QWidget* createWidget(FormField::ref field); QListWidget* createList(FormField::ref field); template void setEnabled(QWidget* rawWidget, bool editable); template void setEditable(QWidget* rawWidget, bool editable); std::map fields_; Form::ref form_; }; } swift-im-2.0+dev6/Swift/QtUI/UserSearch/0000755000175000017500000000000012227051774017645 5ustar kismithkismithswift-im-2.0+dev6/Swift/QtUI/UserSearch/QtUserSearchDetailsPage.cpp0000644000175000017500000000257612227051774025037 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Swift/QtUI/UserSearch/QtUserSearchDetailsPage.h" #include #include #include #include #include namespace Swift { QtUserSearchDetailsPage::QtUserSearchDetailsPage(const std::set& groups) { QVBoxLayout* layout = new QVBoxLayout(this); layout->addWidget(new QLabel(tr("Please choose a name for the contact, and select the groups you want to add the contact to."))); editWidget = new QtContactEditWidget(groups, this); layout->addWidget(editWidget); } QtUserSearchDetailsPage::~QtUserSearchDetailsPage() { } void QtUserSearchDetailsPage::setJID(const JID& jid) { contactJID = jid; } void QtUserSearchDetailsPage::setNameSuggestions(const std::vector& nameSuggestions) { editWidget->setNameSuggestions(nameSuggestions); } void QtUserSearchDetailsPage::setName(const std::string& name) { editWidget->setName(name); } std::set QtUserSearchDetailsPage::getSelectedGroups() { return editWidget->getSelectedGroups(); } std::string QtUserSearchDetailsPage::getName() { return editWidget->getName(); } void QtUserSearchDetailsPage::clear() { editWidget->clear(); } } swift-im-2.0+dev6/Swift/QtUI/UserSearch/QtUserSearchFirstPage.h0000644000175000017500000000130512227051774024173 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include namespace Swift { class UserSearchModel; class UserSearchDelegate; class UserSearchResult; class UIEventStream; class QtUserSearchFirstPage : public QWizardPage, public Ui::QtUserSearchFirstPage { Q_OBJECT public: QtUserSearchFirstPage(UserSearchWindow::Type type, const QString& title); virtual bool isComplete() const; public slots: void emitCompletenessCheck(); }; } swift-im-2.0+dev6/Swift/QtUI/UserSearch/QtUserSearchResultsPage.cpp0000644000175000017500000000216412227051774025104 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Swift/QtUI/UserSearch/QtUserSearchResultsPage.h" namespace Swift { QtUserSearchResultsPage::QtUserSearchResultsPage() { setupUi(this); connect(results_, SIGNAL(activated(const QModelIndex&)), this, SLOT(emitCompletenessCheck())); connect(results_, SIGNAL(activated(const QModelIndex&)), this, SIGNAL(onUserTriggersContinue())); connect(results_, SIGNAL(clicked(const QModelIndex&)), this, SLOT(emitCompletenessCheck())); connect(results_, SIGNAL(entered(const QModelIndex&)), this, SLOT(emitCompletenessCheck())); results_->setExpandsOnDoubleClick(false); setNoResults(false); } bool QtUserSearchResultsPage::isComplete() const { return results_->currentIndex().isValid(); } void QtUserSearchResultsPage::setNoResults(bool noResults) { if (noResults) { results_->setEnabled(false); noResults_->show(); } else { results_->setEnabled(true); noResults_->hide(); } } void QtUserSearchResultsPage::emitCompletenessCheck() { emit completeChanged(); } } swift-im-2.0+dev6/Swift/QtUI/UserSearch/UserSearchDelegate.h0000644000175000017500000000124412227051774023516 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include namespace Swift { class UserSearchDelegate : public QStyledItemDelegate { public: UserSearchDelegate(); ~UserSearchDelegate(); void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index ) const; private: DelegateCommons common_; }; } swift-im-2.0+dev6/Swift/QtUI/UserSearch/QtUserSearchFieldsPage.ui0000644000175000017500000000520612227051774024504 0ustar kismithkismith QtUserSearchFieldsPage 0 0 400 300 Nickname: First name: Last name: E-Mail: Fetching search fields 0 Qt::Vertical 20 40 swift-im-2.0+dev6/Swift/QtUI/UserSearch/UserSearchModel.h0000644000175000017500000000240112227051774023040 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include "Swift/Controllers/Chat/UserSearchController.h" namespace Swift { class UserSearchModel : public QAbstractItemModel { Q_OBJECT public: enum UserItemRoles { DetailTextRole = Qt::UserRole/*, AvatarRole = Qt::UserRole + 1, PresenceIconRole = Qt::UserRole + 2, StatusShowTypeRole = Qt::UserRole + 3*/ }; UserSearchModel(); void clear(); void setResults(const std::vector& results); int columnCount(const QModelIndex& parent = QModelIndex()) const; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; static QVariant data(UserSearchResult* item, int role); QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const; QModelIndex parent(const QModelIndex& index) const; int rowCount(const QModelIndex& parent = QModelIndex()) const; private: static QString nameLine(UserSearchResult* item); static QString detailLine(UserSearchResult* item); std::vector results_; }; } swift-im-2.0+dev6/Swift/QtUI/UserSearch/QtUserSearchResultsPage.ui0000644000175000017500000000146612227051774024743 0ustar kismithkismith QtUserSearchResultsPage 0 0 400 300 false No results. swift-im-2.0+dev6/Swift/QtUI/UserSearch/UserSearchModel.cpp0000644000175000017500000000456412227051774023407 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Swift/QtUI/UserSearch/UserSearchModel.h" #include "Swift/QtUI/QtSwiftUtil.h" namespace Swift { UserSearchModel::UserSearchModel() { } void UserSearchModel::clear() { emit layoutAboutToBeChanged(); results_.clear(); emit layoutChanged(); } void UserSearchModel::setResults(const std::vector& results) { clear(); emit layoutAboutToBeChanged(); results_ = results; emit layoutChanged(); } int UserSearchModel::columnCount(const QModelIndex& /*parent*/) const { return 1; } QVariant UserSearchModel::data(const QModelIndex& index, int role) const { if (!index.isValid()) return QVariant(); UserSearchResult* result = static_cast(index.internalPointer()); return data(result, role); } QVariant UserSearchModel::data(UserSearchResult* item, int role) { switch (role) { case Qt::DisplayRole: return QVariant(nameLine(item)); case DetailTextRole: return QVariant(detailLine(item)); default: return QVariant(); } } QString UserSearchModel::nameLine(UserSearchResult* item) { QString result; const std::map fields = item->getFields(); std::map::const_iterator first = fields.find("first"); if (first != fields.end()) { result += P2QSTRING((*first).second); } std::map::const_iterator last = fields.find("last"); if (last != fields.end()) { if (!result.isEmpty()) { result += " "; } result += P2QSTRING((*last).second); } if (result.isEmpty()) { result = P2QSTRING(item->getJID().toString()); } return result; } QString UserSearchModel::detailLine(UserSearchResult* item) { return P2QSTRING(item->getJID().toString()); } QModelIndex UserSearchModel::index(int row, int column, const QModelIndex & parent) const { if (!hasIndex(row, column, parent)) { return QModelIndex(); } return row < static_cast(results_.size()) ? createIndex(row, column, reinterpret_cast(const_cast(&(results_[row])))) : QModelIndex(); } QModelIndex UserSearchModel::parent(const QModelIndex& /*index*/) const { return QModelIndex(); } int UserSearchModel::rowCount(const QModelIndex& parentIndex) const { if (!parentIndex.isValid()) { return results_.size(); } return 0; } } swift-im-2.0+dev6/Swift/QtUI/UserSearch/QtUserSearchFirstPage.cpp0000644000175000017500000000240612227051774024531 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Swift/QtUI/UserSearch/QtUserSearchFirstPage.h" #include "Swift/QtUI/QtSwiftUtil.h" namespace Swift { QtUserSearchFirstPage::QtUserSearchFirstPage(UserSearchWindow::Type type, const QString& title) { setupUi(this); setTitle(title); setSubTitle(QString(tr("%1. If you know their address you can enter it directly, or you can search for them.")).arg(type == UserSearchWindow::AddContact ? tr("Add another user to your contact list") : tr("Chat to another user"))); connect(jid_, SIGNAL(textChanged(const QString&)), this, SLOT(emitCompletenessCheck())); connect(service_->lineEdit(), SIGNAL(textChanged(const QString&)), this, SLOT(emitCompletenessCheck())); } bool QtUserSearchFirstPage::isComplete() const { bool complete = false; if (byJID_->isChecked()) { complete = JID(Q2PSTRING(jid_->text().trimmed())).isValid(); } else if (byLocalSearch_->isChecked()) { complete = true; } else if (byRemoteSearch_->isChecked()) { complete = JID(Q2PSTRING(service_->currentText().trimmed())).isValid(); } return complete; } void QtUserSearchFirstPage::emitCompletenessCheck() { emit completeChanged(); } } swift-im-2.0+dev6/Swift/QtUI/UserSearch/QtUserSearchWindow.h0000644000175000017500000000420012227051774023553 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include namespace Swift { class UserSearchModel; class UserSearchDelegate; class UserSearchResult; class UIEventStream; class QtUserSearchFirstPage; class QtUserSearchFieldsPage; class QtUserSearchResultsPage; class QtUserSearchDetailsPage; class QtFormResultItemModel; class QtUserSearchWindow : public QWizard, public UserSearchWindow, private Ui::QtUserSearchWizard { Q_OBJECT public: QtUserSearchWindow(UIEventStream* eventStream, UserSearchWindow::Type type, const std::set& groups); virtual ~QtUserSearchWindow(); virtual void addSavedServices(const std::vector& services); virtual void clear(); virtual void show(); virtual void setResults(const std::vector& results); virtual void setResultsForm(Form::ref results); virtual void setSelectedService(const JID& jid); virtual void setServerSupportsSearch(bool error); virtual void setSearchError(bool error); virtual void setSearchFields(boost::shared_ptr fields); virtual void setNameSuggestions(const std::vector& suggestions); virtual void prepopulateJIDAndName(const JID& jid, const std::string& name); protected: virtual int nextId() const; private slots: void handleFirstPageRadioChange(); virtual void handleCurrentChanged(int); virtual void handleAccepted(); private: void clearForm(); void setError(const QString& error); JID getServerToSearch(); void handleSearch(); JID getContactJID() const; private: UIEventStream* eventStream_; UserSearchWindow::Type type_; QAbstractItemModel* model_; UserSearchDelegate* delegate_; QtUserSearchFirstPage* firstPage_; QtUserSearchFieldsPage* fieldsPage_; QtUserSearchResultsPage* resultsPage_; QtUserSearchDetailsPage* detailsPage_; JID myServer_; int lastPage_; }; } swift-im-2.0+dev6/Swift/QtUI/UserSearch/QtUserSearchWizard.ui0000644000175000017500000000062612227051774023742 0ustar kismithkismith QtUserSearchWizard 0 0 711 524 Find User swift-im-2.0+dev6/Swift/QtUI/UserSearch/QtUserSearchWindow.cpp0000644000175000017500000002532312227051774024117 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Swift/QtUI/UserSearch/QtUserSearchWindow.h" #include #include #include #include #include #include #include "Swift/Controllers/UIEvents/UIEventStream.h" #include "Swift/Controllers/UIEvents/RequestChatUIEvent.h" #include "Swift/Controllers/UIEvents/AddContactUIEvent.h" #include "Swift/QtUI/UserSearch/UserSearchModel.h" #include "Swift/QtUI/UserSearch/UserSearchDelegate.h" #include "Swift/QtUI/QtSwiftUtil.h" #include "Swift/QtUI/QtFormResultItemModel.h" #include "QtUserSearchFirstPage.h" #include "QtUserSearchFieldsPage.h" #include "QtUserSearchResultsPage.h" #include "QtUserSearchDetailsPage.h" namespace Swift { QtUserSearchWindow::QtUserSearchWindow(UIEventStream* eventStream, UserSearchWindow::Type type, const std::set& groups) : eventStream_(eventStream), type_(type), model_(NULL) { setupUi(this); #ifndef Q_WS_MAC setWindowIcon(QIcon(":/logo-icon-16.png")); #endif QString title(type == UserSearchWindow::AddContact ? tr("Add Contact") : tr("Chat to User")); setWindowTitle(title); delegate_ = new UserSearchDelegate(); firstPage_ = new QtUserSearchFirstPage(type, title); connect(firstPage_->byJID_, SIGNAL(toggled(bool)), this, SLOT(handleFirstPageRadioChange())); connect(firstPage_->byLocalSearch_, SIGNAL(toggled(bool)), this, SLOT(handleFirstPageRadioChange())); connect(firstPage_->byRemoteSearch_, SIGNAL(toggled(bool)), this, SLOT(handleFirstPageRadioChange())); #if QT_VERSION >= 0x040700 firstPage_->jid_->setPlaceholderText(tr("alice@wonderland.lit")); #endif firstPage_->service_->setEnabled(false); setPage(1, firstPage_); fieldsPage_ = new QtUserSearchFieldsPage(); fieldsPage_->fetchingThrobber_->setMovie(new QMovie(":/icons/throbber.gif", QByteArray(), this)); fieldsPage_->fetchingThrobber_->movie()->stop(); setPage(2, fieldsPage_); resultsPage_ = new QtUserSearchResultsPage(); #ifdef SWIFT_PLATFORM_MACOSX resultsPage_->results_->setAlternatingRowColors(true); #endif if (type == AddContact) { connect(resultsPage_, SIGNAL(onUserTriggersContinue()), this, SLOT(next())); } else { connect(resultsPage_, SIGNAL(onUserTriggersContinue()), this, SLOT(accept())); } setPage(3, resultsPage_); detailsPage_ = new QtUserSearchDetailsPage(groups); setPage(4, detailsPage_); connect(this, SIGNAL(currentIdChanged(int)), this, SLOT(handleCurrentChanged(int))); connect(this, SIGNAL(accepted()), this, SLOT(handleAccepted())); clear(); } QtUserSearchWindow::~QtUserSearchWindow() { delete model_; } void QtUserSearchWindow::handleCurrentChanged(int page) { resultsPage_->emitCompletenessCheck(); if (page == 2 && lastPage_ == 1) { setError(""); /* next won't be called if JID is selected */ JID server = getServerToSearch(); clearForm(); onFormRequested(server); } else if (page == 3 && lastPage_ == 2) { handleSearch(); } else if (page == 4) { detailsPage_->clear(); detailsPage_->setJID(getContactJID()); onNameSuggestionRequested(getContactJID()); } lastPage_ = page; } JID QtUserSearchWindow::getServerToSearch() { return firstPage_->byRemoteSearch_->isChecked() ? JID(Q2PSTRING(firstPage_->service_->currentText().trimmed())) : myServer_; } void QtUserSearchWindow::handleAccepted() { JID jid = getContactJID(); if (type_ == AddContact) { eventStream_->send(boost::make_shared(jid, detailsPage_->getName(), detailsPage_->getSelectedGroups())); } else { boost::shared_ptr event(new RequestChatUIEvent(jid)); eventStream_->send(event); } } int QtUserSearchWindow::nextId() const { switch (currentId()) { case 1: return firstPage_->byJID_->isChecked() ? (type_ == AddContact ? 4 : -1) : 2; case 2: return 3; case 3: return type_ == AddContact ? 4 : -1; case 4: return -1; default: return -1; } } void QtUserSearchWindow::handleFirstPageRadioChange() { if (firstPage_->byJID_->isChecked()) { firstPage_->jid_->setText(""); firstPage_->jid_->setEnabled(true); firstPage_->service_->setEnabled(false); restart(); } else if (firstPage_->byRemoteSearch_->isChecked()) { firstPage_->service_->setEditText(""); firstPage_->jid_->setEnabled(false); firstPage_->service_->setEnabled(true); //firstPage_->jid_->setText(""); restart(); } else { firstPage_->jid_->setEnabled(false); firstPage_->service_->setEnabled(false); restart(); } } void QtUserSearchWindow::handleSearch() { boost::shared_ptr search(new SearchPayload()); if (fieldsPage_->getFormWidget()) { search->setForm(fieldsPage_->getFormWidget()->getCompletedForm()); } else { if (fieldsPage_->nickInput_->isEnabled()) { search->setNick(Q2PSTRING(fieldsPage_->nickInput_->text())); } if (fieldsPage_->firstInput_->isEnabled()) { search->setFirst(Q2PSTRING(fieldsPage_->firstInput_->text())); } if (fieldsPage_->lastInput_->isEnabled()) { search->setLast(Q2PSTRING(fieldsPage_->lastInput_->text())); } if (fieldsPage_->emailInput_->isEnabled()) { search->setEMail(Q2PSTRING(fieldsPage_->emailInput_->text())); } } onSearchRequested(search, getServerToSearch()); } JID QtUserSearchWindow::getContactJID() const { JID jid; if (!firstPage_->byJID_->isChecked()) { if (dynamic_cast(model_)) { UserSearchResult* userItem = static_cast(resultsPage_->results_->currentIndex().internalPointer()); if (userItem) { /* Remember to leave this if we change to dynamic cast */ jid = userItem->getJID(); } } else { int row = resultsPage_->results_->currentIndex().row(); Form::FormItem item = dynamic_cast(model_)->getForm()->getItems().at(row); JID fallbackJid; foreach(FormField::ref field, item) { if (boost::dynamic_pointer_cast(field)) { jid = JID(field->getRawValues().at(0)); break; } if (field->getName() == "jid") { fallbackJid = field->getRawValues().at(0); } } if (!jid.isValid()) { jid = fallbackJid; } } } else { jid = JID(Q2PSTRING(firstPage_->jid_->text().trimmed())); } return jid; } void QtUserSearchWindow::show() { clear(); QWidget::show(); } void QtUserSearchWindow::addSavedServices(const std::vector& services) { firstPage_->service_->clear(); foreach (JID jid, services) { firstPage_->service_->addItem(P2QSTRING(jid.toString())); } firstPage_->service_->clearEditText(); } void QtUserSearchWindow::setSearchFields(boost::shared_ptr fields) { fieldsPage_->fetchingThrobber_->hide(); fieldsPage_->fetchingThrobber_->movie()->stop(); fieldsPage_->fetchingLabel_->hide(); fieldsPage_->instructionsLabel_->setText(fields->getInstructions() ? P2QSTRING(fields->getInstructions().get()) : "Enter search terms"); if (fields->getForm()) { fieldsPage_->setFormWidget(new QtFormWidget(fields->getForm(), fieldsPage_)); } else { fieldsPage_->setFormWidget(NULL); bool enabled[8] = {fields->getNick(), fields->getNick(), fields->getFirst(), fields->getFirst(), fields->getLast(), fields->getLast(), fields->getEMail(), fields->getEMail()}; QWidget* legacySearchWidgets[8] = {fieldsPage_->nickInputLabel_, fieldsPage_->nickInput_, fieldsPage_->firstInputLabel_, fieldsPage_->firstInput_, fieldsPage_->lastInputLabel_, fieldsPage_->lastInput_, fieldsPage_->emailInputLabel_, fieldsPage_->emailInput_}; for (int i = 0; i < 8; i++) { legacySearchWidgets[i]->setVisible(enabled[i]); legacySearchWidgets[i]->setEnabled(enabled[i]); } } fieldsPage_->emitCompletenessCheck(); } void QtUserSearchWindow::setNameSuggestions(const std::vector& suggestions) { if (detailsPage_) { detailsPage_->setNameSuggestions(suggestions); } } void QtUserSearchWindow::prepopulateJIDAndName(const JID& jid, const std::string& name) { firstPage_->jid_->setText(P2QSTRING(jid.toBare().toString())); detailsPage_->setJID(jid); lastPage_ = 1; restart(); next(); detailsPage_->setName(name); } void QtUserSearchWindow::setResults(const std::vector& results) { UserSearchModel *newModel = new UserSearchModel(); newModel->setResults(results); resultsPage_->results_->setModel(newModel); resultsPage_->results_->setItemDelegate(delegate_); resultsPage_->results_->setHeaderHidden(true); delete model_; model_ = newModel; resultsPage_->setNoResults(model_->rowCount() == 0); resultsPage_->emitCompletenessCheck(); } void QtUserSearchWindow::setResultsForm(Form::ref results) { QtFormResultItemModel *newModel = new QtFormResultItemModel(this); newModel->setForm(results); resultsPage_->results_->setModel(newModel); resultsPage_->results_->setItemDelegate(new QItemDelegate()); resultsPage_->results_->setHeaderHidden(false); resultsPage_->results_->header()->setResizeMode(QHeaderView::ResizeToContents); delete model_; model_ = newModel; resultsPage_->setNoResults(model_->rowCount() == 0); resultsPage_->emitCompletenessCheck(); } void QtUserSearchWindow::setSelectedService(const JID& jid) { myServer_ = jid; } void QtUserSearchWindow::clearForm() { fieldsPage_->fetchingThrobber_->show(); fieldsPage_->fetchingThrobber_->movie()->start(); fieldsPage_->fetchingLabel_->show(); QWidget* legacySearchWidgets[8] = {fieldsPage_->nickInputLabel_, fieldsPage_->nickInput_, fieldsPage_->firstInputLabel_, fieldsPage_->firstInput_, fieldsPage_->lastInputLabel_, fieldsPage_->lastInput_, fieldsPage_->emailInputLabel_, fieldsPage_->emailInput_}; for (int i = 0; i < 8; i++) { legacySearchWidgets[i]->hide(); if (QLineEdit* edit = qobject_cast(legacySearchWidgets[i])) { edit->clear(); } } fieldsPage_->emitCompletenessCheck(); } void QtUserSearchWindow::clear() { firstPage_->errorLabel_->setVisible(false); QString howText; if (type_ == AddContact) { howText = QString(tr("How would you like to find the user to add?")); } else { howText = QString(tr("How would you like to find the user to chat to?")); } firstPage_->howLabel_->setText(howText); firstPage_->byJID_->setChecked(true); clearForm(); resultsPage_->results_->setModel(NULL); delete model_; model_ = NULL; handleFirstPageRadioChange(); restart(); lastPage_ = 1; } void QtUserSearchWindow::setError(const QString& error) { if (error.isEmpty()) { firstPage_->errorLabel_->hide(); } else { firstPage_->errorLabel_->setText(QString("%1").arg(error)); firstPage_->errorLabel_->show(); restart(); lastPage_ = 1; } } void QtUserSearchWindow::setSearchError(bool error) { if (error) { setError(tr("Error while searching")); } } void QtUserSearchWindow::setServerSupportsSearch(bool support) { if (!support) { setError(tr("This server doesn't support searching for users.")); } } } swift-im-2.0+dev6/Swift/QtUI/UserSearch/UserSearchDelegate.cpp0000644000175000017500000000460212227051774024052 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include "Swift/QtUI/UserSearch/UserSearchDelegate.h" //#include "Swift/QtUI/Roster/GroupItemDelegate.h" #include #include //#include "Swift/QtUI/MUCSearch/MUCSearchServiceItem.h" namespace Swift { UserSearchDelegate::UserSearchDelegate() { } UserSearchDelegate::~UserSearchDelegate() { } QSize UserSearchDelegate::sizeHint(const QStyleOptionViewItem& /*option*/, const QModelIndex& /*index*/ ) const { //UserSearchItem* item = static_cast(index.internalPointer()); QFontMetrics nameMetrics(common_.nameFont); QFontMetrics statusMetrics(common_.detailFont); int sizeByText = 2 * common_.verticalMargin + nameMetrics.height() + statusMetrics.height(); return QSize(150, sizeByText); } void UserSearchDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { UserSearchResult* item = static_cast(index.internalPointer()); painter->save(); QRect fullRegion(option.rect); if (option.state & QStyle::State_Selected) { painter->fillRect(fullRegion, option.palette.highlight()); painter->setPen(option.palette.highlightedText().color()); } else { QColor nameColor = UserSearchModel::data(item, Qt::TextColorRole).value (); painter->setPen(QPen(nameColor)); } QFontMetrics nameMetrics(common_.nameFont); painter->setFont(common_.nameFont); int extraFontWidth = nameMetrics.width("H"); int leftOffset = common_.horizontalMargin * 2 + extraFontWidth / 2; QRect textRegion(fullRegion.adjusted(leftOffset, 0, 0, 0)); int nameHeight = nameMetrics.height() + common_.verticalMargin; QRect nameRegion(textRegion.adjusted(0, common_.verticalMargin, 0, 0)); painter->drawText(nameRegion, Qt::AlignTop, UserSearchModel::data(item, Qt::DisplayRole).toString()); painter->setFont(common_.detailFont); painter->setPen(QPen(QColor(160, 160, 160))); QRect detailRegion(textRegion.adjusted(0, nameHeight, 0, 0)); painter->drawText(detailRegion, Qt::AlignTop, UserSearchModel::data(item, UserSearchModel::DetailTextRole).toString()); painter->restore(); } } swift-im-2.0+dev6/Swift/QtUI/UserSearch/QtUserSearchFieldsPage.h0000644000175000017500000000123112227051774024310 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include namespace Swift { class QtUserSearchFieldsPage : public QWizardPage, public Ui::QtUserSearchFieldsPage { Q_OBJECT public: QtUserSearchFieldsPage(); virtual bool isComplete() const; QtFormWidget* getFormWidget(); void setFormWidget(QtFormWidget *widget); public slots: void emitCompletenessCheck(); private: QtFormWidget *formWidget_; }; } swift-im-2.0+dev6/Swift/QtUI/UserSearch/QtUserSearchDetailsPage.h0000644000175000017500000000171512227051774024476 0ustar kismithkismith/* * 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 #include #include #include #include #include #include namespace Swift { class QtContactEditWidget; class QtUserSearchDetailsPage : public QWizardPage { Q_OBJECT public: QtUserSearchDetailsPage(const std::set& availableGroups); virtual ~QtUserSearchDetailsPage(); void setJID(const JID& jid); void setNameSuggestions(const std::vector& nameSuggestions); void setName(const std::string& name); std::set getSelectedGroups(); std::string getName(); void clear(); signals: void onUserTriggersFinish(); private: QtContactEditWidget* editWidget; JID contactJID; }; } swift-im-2.0+dev6/Swift/QtUI/UserSearch/QtUserSearchFirstPage.ui0000644000175000017500000000637412227051774024374 0ustar kismithkismith QtUserSearchFirstPage 0 0 422 315 Add a user Add another user to your contact list. If you know their address you can add them directly, or you can search for them. I know their address: I'd like to search my server Qt::Horizontal 40 20 I'd like to search another server: 0 0 true Qt::Vertical 20 77 Qt::AlignCenter Qt::Vertical 20 40 swift-im-2.0+dev6/Swift/QtUI/UserSearch/QtUserSearchResultsPage.h0000644000175000017500000000112012227051774024540 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include namespace Swift { class QtUserSearchResultsPage : public QWizardPage, public Ui::QtUserSearchResultsPage { Q_OBJECT public: QtUserSearchResultsPage(); virtual bool isComplete() const; void setNoResults(bool noResults); signals: void onUserTriggersContinue(); public slots: void emitCompletenessCheck(); }; } swift-im-2.0+dev6/Swift/QtUI/UserSearch/QtUserSearchFieldsPage.cpp0000644000175000017500000000166612227051774024657 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Swift/QtUI/UserSearch/QtUserSearchFieldsPage.h" namespace Swift { QtUserSearchFieldsPage::QtUserSearchFieldsPage() : formWidget_(0) { setupUi(this); } bool QtUserSearchFieldsPage::isComplete() const { if (formWidget_) { return formWidget_->isEnabled(); } else { return nickInput_->isEnabled() || firstInput_->isEnabled() || lastInput_->isEnabled() || emailInput_->isEnabled(); } } QtFormWidget* QtUserSearchFieldsPage::getFormWidget() { return formWidget_; } void QtUserSearchFieldsPage::setFormWidget(QtFormWidget *widget) { if (formWidget_) { delete formWidget_; formWidget_ = NULL; } if (widget) { formContainer_->layout()->addWidget(widget); } formWidget_ = widget; } void QtUserSearchFieldsPage::emitCompletenessCheck() { emit completeChanged(); } } swift-im-2.0+dev6/Swift/QtUI/Whiteboard/0000755000175000017500000000000012227051774017671 5ustar kismithkismithswift-im-2.0+dev6/Swift/QtUI/Whiteboard/TextDialog.h0000644000175000017500000000152712227051774022113 0ustar kismithkismith/* * Copyright (c) 2012 Mateusz Piękos * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include #include #include #include #include #include #include using namespace std; namespace Swift { class TextDialog : public QDialog { Q_OBJECT; public: TextDialog(QGraphicsTextItem* item, QWidget* parent = 0); private: QGraphicsTextItem* item; QLineEdit* editor; QDialogButtonBox* buttonBox; QVBoxLayout* layout; QHBoxLayout* hLayout; QSpinBox* fontSizeBox; signals: void accepted(QGraphicsTextItem* item); private slots: void accept(); void changeItemText(const QString &text); void changeItemFontSize(int i); }; } swift-im-2.0+dev6/Swift/QtUI/Whiteboard/ColorWidget.h0000644000175000017500000000104512227051774022264 0ustar kismithkismith/* * Copyright (c) 2012 Mateusz Piękos * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include namespace Swift { class ColorWidget : public QWidget { Q_OBJECT; public: ColorWidget(QWidget* parent = 0); QSize sizeHint() const; public slots: void setColor(QColor color); private: QColor color; protected: void paintEvent(QPaintEvent* /*event*/); void mouseReleaseEvent(QMouseEvent* event); signals: void clicked(); }; } swift-im-2.0+dev6/Swift/QtUI/Whiteboard/WhiteboardElementDrawingVisitor.h0000644000175000017500000001600012227051774026335 0ustar kismithkismith/* * Copyright (c) 2012 Mateusz Piękos * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include #include #include #include #include #include #include namespace Swift { class WhiteboardElementDrawingVisitor : public WhiteboardElementVisitor { public: WhiteboardElementDrawingVisitor(GView* graphicsView, int pos, GView::Type type) : graphicsView_(graphicsView), pos_(pos), type_(type) {} void visit(WhiteboardLineElement& element) { QGraphicsLineItem *item; if (type_ == GView::New) { item = new QGraphicsLineItem(element.x1(), element.y1(), element.x2(), element.y2()); graphicsView_->addItem(item, P2QSTRING(element.getID()), pos_); } else { item = qgraphicsitem_cast(graphicsView_->getItem(P2QSTRING(element.getID()))); QLineF line(element.x1(), element.y1(), element.x2(), element.y2()); item->setLine(line); item->setPos(0,0); graphicsView_->deselect(P2QSTRING(element.getID())); } if (item) { QPen pen; WhiteboardColor color = element.getColor(); pen.setColor(QColor(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha())); pen.setWidth(element.getPenWidth()); item->setPen(pen); QString id = P2QSTRING(element.getID()); item->setData(100, id); } } void visit(WhiteboardFreehandPathElement& element) { FreehandLineItem *item; if (type_ == GView::New) { item = new FreehandLineItem; } else { item = qgraphicsitem_cast(graphicsView_->getItem(P2QSTRING(element.getID()))); item->setPos(0,0); graphicsView_->deselect(P2QSTRING(element.getID())); } if (item) { QPen pen; WhiteboardColor color = element.getColor(); pen.setColor(QColor(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha())); pen.setWidth(element.getPenWidth()); item->setPen(pen); std::vector >::const_iterator it = element.getPoints().begin(); item->setStartPoint(QPointF(it->first, it->second)); for (++it; it != element.getPoints().end(); ++it) { item->lineTo(QPointF(it->first, it->second)); } QString id = P2QSTRING(element.getID()); item->setData(100, id); } if (type_ == GView::New) { graphicsView_->addItem(item, P2QSTRING(element.getID()), pos_); } } void visit(WhiteboardRectElement& element) { QGraphicsRectItem* item; if (type_ == GView::New) { item = new QGraphicsRectItem(element.getX(), element.getY(), element.getWidth(), element.getHeight()); graphicsView_->addItem(item, P2QSTRING(element.getID()), pos_); } else { item = qgraphicsitem_cast(graphicsView_->getItem(P2QSTRING(element.getID()))); QRectF rect(element.getX(), element.getY(), element.getWidth(), element.getHeight()); item->setRect(rect); item->setPos(0,0); graphicsView_->deselect(P2QSTRING(element.getID())); } if (item) { QPen pen; QBrush brush(Qt::SolidPattern); WhiteboardColor penColor = element.getPenColor(); WhiteboardColor brushColor = element.getBrushColor(); pen.setColor(QColor(penColor.getRed(), penColor.getGreen(), penColor.getBlue(), penColor.getAlpha())); pen.setWidth(element.getPenWidth()); brush.setColor(QColor(brushColor.getRed(), brushColor.getGreen(), brushColor.getBlue(), brushColor.getAlpha())); item->setPen(pen); item->setBrush(brush); QString id = P2QSTRING(element.getID()); item->setData(100, id); } } void visit(WhiteboardPolygonElement& element) { QGraphicsPolygonItem* item = qgraphicsitem_cast(graphicsView_->getItem(P2QSTRING(element.getID()))); if (item == 0 && type_ == GView::New) { item = new QGraphicsPolygonItem(); QString id = P2QSTRING(element.getID()); item->setData(100, id); graphicsView_->addItem(item, id, pos_); } graphicsView_->deselect(P2QSTRING(element.getID())); QPen pen; QBrush brush(Qt::SolidPattern); WhiteboardColor penColor = element.getPenColor(); WhiteboardColor brushColor = element.getBrushColor(); pen.setColor(QColor(penColor.getRed(), penColor.getGreen(), penColor.getBlue(), penColor.getAlpha())); pen.setWidth(element.getPenWidth()); brush.setColor(QColor(brushColor.getRed(), brushColor.getGreen(), brushColor.getBlue(), brushColor.getAlpha())); item->setPen(pen); item->setBrush(brush); item->setPos(0,0); QPolygonF polygon; std::vector >::const_iterator it = element.getPoints().begin(); for (; it != element.getPoints().end(); ++it) { polygon.append(QPointF(it->first, it->second)); } item->setPolygon(polygon); } void visit(WhiteboardTextElement& element) { QGraphicsTextItem* item; QString id = P2QSTRING(element.getID()); if (type_ == GView::New) { item = new QGraphicsTextItem; graphicsView_->addItem(item, id, pos_); } else { item = qgraphicsitem_cast(graphicsView_->getItem(id)); graphicsView_->deselect(P2QSTRING(element.getID())); } if (item) { item->setPlainText(P2QSTRING(element.getText())); item->setPos(QPointF(element.getX(), element.getY())); QFont font = item->font(); font.setPointSize(element.getSize()); item->setFont(font); WhiteboardColor color = element.getColor(); item->setDefaultTextColor(QColor(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha())); item->setData(100, id); } } void visit(WhiteboardEllipseElement& element) { QRectF rect; QGraphicsEllipseItem* item; QString id = P2QSTRING(element.getID()); rect.setTopLeft(QPointF(element.getCX()-element.getRX(), element.getCY()-element.getRY())); rect.setBottomRight(QPointF(element.getCX()+element.getRX(), element.getCY()+element.getRY())); if (type_ == GView::New) { item = new QGraphicsEllipseItem(rect); graphicsView_->addItem(item, id, pos_); } else { item = qgraphicsitem_cast(graphicsView_->getItem(id)); item->setRect(rect); item->setPos(0,0); graphicsView_->deselect(P2QSTRING(element.getID())); } QPen pen; QBrush brush(Qt::SolidPattern); WhiteboardColor penColor = element.getPenColor(); WhiteboardColor brushColor = element.getBrushColor(); pen.setColor(QColor(penColor.getRed(), penColor.getGreen(), penColor.getBlue(), penColor.getAlpha())); pen.setWidth(element.getPenWidth()); brush.setColor(QColor(brushColor.getRed(), brushColor.getGreen(), brushColor.getBlue(), brushColor.getAlpha())); item->setPen(pen); item->setBrush(brush); item->setData(100, id); } private: GView* graphicsView_; int pos_; GView::Type type_; }; } swift-im-2.0+dev6/Swift/QtUI/Whiteboard/TextDialog.cpp0000644000175000017500000000241212227051774022440 0ustar kismithkismith/* * Copyright (c) 2012 Mateusz Piękos * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include "TextDialog.h" namespace Swift { TextDialog::TextDialog(QGraphicsTextItem* item, QWidget* parent) : QDialog(parent) { this->item = item; layout = new QVBoxLayout(this); hLayout = new QHBoxLayout; editor = new QLineEdit(this); connect(editor, SIGNAL(textChanged(const QString&)), this, SLOT(changeItemText(const QString&))); fontSizeBox = new QSpinBox(this); fontSizeBox->setMinimum(1); connect(fontSizeBox, SIGNAL(valueChanged(int)), this, SLOT(changeItemFontSize(int))); fontSizeBox->setValue(13); buttonBox = new QDialogButtonBox(this); buttonBox->setStandardButtons(QDialogButtonBox::Ok); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); hLayout->addWidget(editor); hLayout->addWidget(fontSizeBox); layout->addLayout(hLayout); layout->addWidget(buttonBox); } void TextDialog::changeItemText(const QString &text) { item->setPlainText(text); } void TextDialog::changeItemFontSize(int i) { QFont font = item->font(); font.setPointSize(i); item->setFont(font); } void TextDialog::accept() { emit accepted(item); done(QDialog::Accepted); } } swift-im-2.0+dev6/Swift/QtUI/Whiteboard/QtWhiteboardWindow.cpp0000644000175000017500000003473212227051774024173 0ustar kismithkismith/* * Copyright (c) 2012 Mateusz Piękos * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include "QtWhiteboardWindow.h" #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Swift { QtWhiteboardWindow::QtWhiteboardWindow(WhiteboardSession::ref whiteboardSession) : QWidget() { #ifndef Q_WS_MAC setWindowIcon(QIcon(":/logo-icon-16.png")); #endif layout = new QVBoxLayout(this); hLayout = new QHBoxLayout; sidebarLayout = new QVBoxLayout; toolboxLayout = new QGridLayout; scene = new QGraphicsScene(this); scene->setSceneRect(0, 0, 400, 400); graphicsView = new GView(scene, this); graphicsView->setMode(GView::Line); connect(graphicsView, SIGNAL(lastItemChanged(QGraphicsItem*, int, GView::Type)), this, SLOT(handleLastItemChanged(QGraphicsItem*, int, GView::Type))); connect(graphicsView, SIGNAL(itemDeleted(QString, int)), this, SLOT(handleItemDeleted(QString, int))); widthBox = new QSpinBox(this); connect(widthBox, SIGNAL(valueChanged(int)), this, SLOT(changeLineWidth(int))); connect(graphicsView, SIGNAL(lineWidthChanged(int)), widthBox, SLOT(setValue(int))); widthBox->setValue(1); moveUpButton = new QPushButton("Move Up", this); connect(moveUpButton, SIGNAL(clicked()), graphicsView, SLOT(moveUpSelectedItem())); moveDownButton = new QPushButton("Move Down", this); connect(moveDownButton, SIGNAL(clicked()), graphicsView, SLOT(moveDownSelectedItem())); strokeLayout = new QHBoxLayout; strokeColor = new ColorWidget; strokeLayout->addWidget(new QLabel("Stroke:")); strokeLayout->addWidget(strokeColor); connect(strokeColor, SIGNAL(clicked()), this, SLOT(showColorDialog())); connect(graphicsView, SIGNAL(lineColorChanged(QColor)), strokeColor, SLOT(setColor(QColor))); fillLayout = new QHBoxLayout; fillColor = new ColorWidget; fillLayout->addWidget(new QLabel("Fill:")); fillLayout->addWidget(fillColor); connect(fillColor, SIGNAL(clicked()), this, SLOT(showBrushColorDialog())); connect(graphicsView, SIGNAL(brushColorChanged(QColor)), fillColor, SLOT(setColor(QColor))); rubberButton = new QToolButton(this); rubberButton->setIcon(QIcon(":/icons/eraser.png")); rubberButton->setCheckable(true); rubberButton->setAutoExclusive(true); connect(rubberButton, SIGNAL(clicked()), this, SLOT(setRubberMode())); lineButton = new QToolButton(this); lineButton->setIcon(QIcon(":/icons/line.png")); lineButton->setCheckable(true); lineButton->setAutoExclusive(true); lineButton->setChecked(true); connect(lineButton, SIGNAL(clicked()), this, SLOT(setLineMode())); rectButton = new QToolButton(this); rectButton->setIcon(QIcon(":/icons/rect.png")); rectButton->setCheckable(true); rectButton->setAutoExclusive(true); connect(rectButton, SIGNAL(clicked()), this, SLOT(setRectMode())); circleButton = new QToolButton(this); circleButton->setIcon(QIcon(":/icons/circle.png")); circleButton->setCheckable(true); circleButton->setAutoExclusive(true); connect(circleButton, SIGNAL(clicked()), this, SLOT(setCircleMode())); handLineButton = new QToolButton(this); handLineButton->setIcon(QIcon(":/icons/handline.png")); handLineButton->setCheckable(true); handLineButton->setAutoExclusive(true); connect(handLineButton, SIGNAL(clicked()), this, SLOT(setHandLineMode())); textButton = new QToolButton(this); textButton->setIcon(QIcon(":/icons/text.png")); textButton->setCheckable(true); textButton->setAutoExclusive(true); connect(textButton, SIGNAL(clicked()), this, SLOT(setTextMode())); polygonButton = new QToolButton(this); polygonButton->setIcon(QIcon(":/icons/polygon.png")); polygonButton->setCheckable(true); polygonButton->setAutoExclusive(true); connect(polygonButton, SIGNAL(clicked()), this, SLOT(setPolygonMode())); selectButton = new QToolButton(this); selectButton->setIcon(QIcon(":/icons/cursor.png")); selectButton->setCheckable(true); selectButton->setAutoExclusive(true); connect(selectButton, SIGNAL(clicked()), this, SLOT(setSelectMode())); toolboxLayout->addWidget(rubberButton, 0, 0); toolboxLayout->addWidget(selectButton, 0, 1); toolboxLayout->addWidget(lineButton, 0, 2); toolboxLayout->addWidget(circleButton, 1, 0); toolboxLayout->addWidget(handLineButton, 1, 1); toolboxLayout->addWidget(rectButton, 1, 2); toolboxLayout->addWidget(textButton, 2, 0); toolboxLayout->addWidget(polygonButton, 2, 1); sidebarLayout->addLayout(toolboxLayout); sidebarLayout->addSpacing(30); sidebarLayout->addWidget(moveUpButton); sidebarLayout->addWidget(moveDownButton); sidebarLayout->addSpacing(40); sidebarLayout->addWidget(widthBox); sidebarLayout->addLayout(strokeLayout); sidebarLayout->addLayout(fillLayout); sidebarLayout->addStretch(); hLayout->addWidget(graphicsView); hLayout->addLayout(sidebarLayout); layout->addLayout(hLayout); this->setLayout(layout); setSession(whiteboardSession); } void QtWhiteboardWindow::handleWhiteboardOperationReceive(const WhiteboardOperation::ref operation) { WhiteboardInsertOperation::ref insertOp = boost::dynamic_pointer_cast(operation); if (insertOp) { WhiteboardElementDrawingVisitor visitor(graphicsView, operation->getPos(), GView::New); insertOp->getElement()->accept(visitor); } WhiteboardUpdateOperation::ref updateOp = boost::dynamic_pointer_cast(operation); if (updateOp) { WhiteboardElementDrawingVisitor visitor(graphicsView, operation->getPos(), GView::Update); updateOp->getElement()->accept(visitor); if (updateOp->getPos() != updateOp->getNewPos()) { graphicsView->move(graphicsView->getItem(P2QSTRING(updateOp->getElement()->getID())), updateOp->getNewPos()); } } WhiteboardDeleteOperation::ref deleteOp = boost::dynamic_pointer_cast(operation); if (deleteOp) { graphicsView->deleteItem(P2QSTRING(deleteOp->getElementID())); } } void QtWhiteboardWindow::changeLineWidth(int i) { graphicsView->setLineWidth(i); } void QtWhiteboardWindow::showColorDialog() { QColor color = QColorDialog::getColor(graphicsView->getLineColor(), 0, "Select pen color", QColorDialog::ShowAlphaChannel); if(color.isValid()) graphicsView->setLineColor(color); } void QtWhiteboardWindow::showBrushColorDialog() { QColor color = QColorDialog::getColor(graphicsView->getBrushColor(), 0, "Select brush color", QColorDialog::ShowAlphaChannel); if(color.isValid()) graphicsView->setBrushColor(color); } void QtWhiteboardWindow::setRubberMode() { graphicsView->setMode(GView::Rubber); } void QtWhiteboardWindow::setLineMode() { graphicsView->setMode(GView::Line); } void QtWhiteboardWindow::setRectMode() { graphicsView->setMode(GView::Rect); } void QtWhiteboardWindow::setCircleMode() { graphicsView->setMode(GView::Circle); } void QtWhiteboardWindow::setHandLineMode() { graphicsView->setMode(GView::HandLine); } void QtWhiteboardWindow::setTextMode() { graphicsView->setMode(GView::Text); } void QtWhiteboardWindow::setPolygonMode() { graphicsView->setMode(GView::Polygon); } void QtWhiteboardWindow::setSelectMode() { graphicsView->setMode(GView::Select); } void QtWhiteboardWindow::show() { QWidget::show(); } void QtWhiteboardWindow::setSession(WhiteboardSession::ref session) { graphicsView->clear(); whiteboardSession_ = session; whiteboardSession_->onOperationReceived.connect(boost::bind(&QtWhiteboardWindow::handleWhiteboardOperationReceive, this, _1)); whiteboardSession_->onRequestAccepted.connect(boost::bind(&QWidget::show, this)); whiteboardSession_->onSessionTerminated.connect(boost::bind(&QtWhiteboardWindow::handleSessionTerminate, this)); } void QtWhiteboardWindow::activateWindow() { QWidget::activateWindow(); } void QtWhiteboardWindow::setName(const std::string& name) { setWindowTitle(P2QSTRING(name)); } void QtWhiteboardWindow::handleLastItemChanged(QGraphicsItem* item, int pos, GView::Type type) { WhiteboardElement::ref el; QGraphicsLineItem* lineItem = qgraphicsitem_cast(item); if (lineItem != 0) { QLine line = lineItem->line().toLine(); QColor color = lineItem->pen().color(); WhiteboardLineElement::ref element = boost::make_shared(line.x1()+lineItem->pos().x(), line.y1()+lineItem->pos().y(), line.x2()+lineItem->pos().x(), line.y2()+lineItem->pos().y()); element->setColor(WhiteboardColor(color.red(), color.green(), color.blue(), color.alpha())); element->setPenWidth(lineItem->pen().width()); element->setID(lineItem->data(100).toString().toStdString()); el = element; } FreehandLineItem* freehandLineItem = qgraphicsitem_cast(item); if (freehandLineItem != 0) { WhiteboardFreehandPathElement::ref element = boost::make_shared(); QColor color = freehandLineItem->pen().color(); std::vector > points; QVector::const_iterator it = freehandLineItem->points().constBegin(); for ( ; it != freehandLineItem->points().constEnd(); ++it) { points.push_back(std::pair(it->x()+item->pos().x(), it->y()+item->pos().y())); } element->setColor(WhiteboardColor(color.red(), color.green(), color.blue(), color.alpha())); element->setPenWidth(freehandLineItem->pen().width()); element->setPoints(points); element->setID(freehandLineItem->data(100).toString().toStdString()); el = element; } QGraphicsRectItem* rectItem = qgraphicsitem_cast(item); if (rectItem != 0) { QRectF rect = rectItem->rect(); WhiteboardRectElement::ref element = boost::make_shared(rect.x()+item->pos().x(), rect.y()+item->pos().y(), rect.width(), rect.height()); QColor penColor = rectItem->pen().color(); QColor brushColor = rectItem->brush().color(); element->setBrushColor(WhiteboardColor(brushColor.red(), brushColor.green(), brushColor.blue(), brushColor.alpha())); element->setPenColor(WhiteboardColor(penColor.red(), penColor.green(), penColor.blue(), penColor.alpha())); element->setPenWidth(rectItem->pen().width()); element->setID(rectItem->data(100).toString().toStdString()); el = element; } QGraphicsTextItem* textItem = qgraphicsitem_cast(item); if (textItem != 0) { QPointF point = textItem->pos(); WhiteboardTextElement::ref element = boost::make_shared(point.x(), point.y()); element->setText(textItem->toPlainText().toStdString()); element->setSize(textItem->font().pointSize()); QColor color = textItem->defaultTextColor(); element->setColor(WhiteboardColor(color.red(), color.green(), color.blue(), color.alpha())); element->setID(textItem->data(100).toString().toStdString()); el = element; } QGraphicsPolygonItem* polygonItem = qgraphicsitem_cast(item); if (polygonItem) { WhiteboardPolygonElement::ref element = boost::make_shared(); QPolygonF polygon = polygonItem->polygon(); std::vector > points; QVector::const_iterator it = polygon.begin(); for (; it != polygon.end(); ++it) { points.push_back(std::pair(it->x()+item->pos().x(), it->y()+item->pos().y())); } element->setPoints(points); QColor penColor = polygonItem->pen().color(); QColor brushColor = polygonItem->brush().color(); element->setPenColor(WhiteboardColor(penColor.red(), penColor.green(), penColor.blue(), penColor.alpha())); element->setBrushColor(WhiteboardColor(brushColor.red(), brushColor.green(), brushColor.blue(), brushColor.alpha())); element->setPenWidth(polygonItem->pen().width()); element->setID(polygonItem->data(100).toString().toStdString()); el = element; } QGraphicsEllipseItem* ellipseItem = qgraphicsitem_cast(item); if (ellipseItem) { QRectF rect = ellipseItem->rect(); int cx = rect.x()+rect.width()/2 + item->pos().x(); int cy = rect.y()+rect.height()/2 + item->pos().y(); int rx = rect.width()/2; int ry = rect.height()/2; WhiteboardEllipseElement::ref element = boost::make_shared(cx, cy, rx, ry); QColor penColor = ellipseItem->pen().color(); QColor brushColor = ellipseItem->brush().color(); element->setPenColor(WhiteboardColor(penColor.red(), penColor.green(), penColor.blue(), penColor.alpha())); element->setBrushColor(WhiteboardColor(brushColor.red(), brushColor.green(), brushColor.blue(), brushColor.alpha())); element->setPenWidth(ellipseItem->pen().width()); element->setID(ellipseItem->data(100).toString().toStdString()); el = element; } if (type == GView::New) { WhiteboardInsertOperation::ref insertOp = boost::make_shared(); insertOp->setPos(pos); insertOp->setElement(el); whiteboardSession_->sendOperation(insertOp); } else { WhiteboardUpdateOperation::ref updateOp = boost::make_shared(); updateOp->setPos(pos); if (type == GView::Update) { updateOp->setNewPos(pos); } else if (type == GView::MoveUp) { updateOp->setNewPos(pos+1); } else if (type == GView::MoveDown) { updateOp->setNewPos(pos-1); } updateOp->setElement(el); whiteboardSession_->sendOperation(updateOp); } } void QtWhiteboardWindow::handleItemDeleted(QString id, int pos) { WhiteboardDeleteOperation::ref deleteOp = boost::make_shared(); deleteOp->setElementID(Q2PSTRING(id)); deleteOp->setPos(pos); whiteboardSession_->sendOperation(deleteOp); } void QtWhiteboardWindow::handleSessionTerminate() { hide(); } void QtWhiteboardWindow::closeEvent(QCloseEvent* event) { QMessageBox box(this); box.setText(tr("Closing window is equivalent closing the session. Are you sure you want to do this?")); box.setStandardButtons(QMessageBox::Yes | QMessageBox::No); box.setIcon(QMessageBox::Question); if (box.exec() == QMessageBox::Yes) { whiteboardSession_->cancel(); } else { event->ignore(); } } } swift-im-2.0+dev6/Swift/QtUI/Whiteboard/ColorWidget.cpp0000644000175000017500000000143412227051774022621 0ustar kismithkismith/* * Copyright (c) 2012 Mateusz Piękos * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include "ColorWidget.h" #include #include namespace Swift { ColorWidget::ColorWidget(QWidget* parent) : QWidget(parent) { setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); } QSize ColorWidget::sizeHint() const { return QSize(20, 20); } void ColorWidget::setColor(QColor color) { this->color = color; update(); } void ColorWidget::paintEvent(QPaintEvent* /*event*/) { QPainter painter(this); painter.fillRect(0, 0, 20, 20, color); } void ColorWidget::mouseReleaseEvent(QMouseEvent* event) { if (event->button() == Qt::LeftButton) { emit clicked(); } } } swift-im-2.0+dev6/Swift/QtUI/Whiteboard/FreehandLineItem.h0000644000175000017500000000163512227051774023212 0ustar kismithkismith/* * Copyright (c) 2012 Mateusz Piękos * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include #include using namespace std; namespace Swift { class FreehandLineItem : public QGraphicsItem { public: enum {Type = UserType + 1}; FreehandLineItem(QGraphicsItem* parent = 0); QRectF boundingRect() const; void paint(QPainter* painter, const QStyleOptionGraphicsItem* /*option*/, QWidget* /*widget*/ = 0); void setStartPoint(QPointF point); void lineTo(QPointF point); bool collidesWithPath(const QPainterPath& path, Qt::ItemSelectionMode /*mode*/ = Qt::IntersectsItemShape) const; void setPen(const QPen& pen); QPen pen() const; const QVector& points() const; int type() const; private: QPen pen_; QVector points_; QRectF boundRect; }; } swift-im-2.0+dev6/Swift/QtUI/Whiteboard/QtWhiteboardWindow.h0000644000175000017500000000434512227051774023635 0ustar kismithkismith/* * Copyright (c) 2012 Mateusz Piękos * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "GView.h" #include "ColorWidget.h" namespace Swift { class QtWhiteboardWindow : public QWidget, public WhiteboardWindow { Q_OBJECT; public: QtWhiteboardWindow(WhiteboardSession::ref whiteboardSession); void show(); void setSession(WhiteboardSession::ref session); void activateWindow(); void setName(const std::string& name); private slots: void changeLineWidth(int i); void showColorDialog(); void showBrushColorDialog(); void setRubberMode(); void setLineMode(); void setRectMode(); void setCircleMode(); void setHandLineMode(); void setTextMode(); void setPolygonMode(); void setSelectMode(); void handleLastItemChanged(QGraphicsItem* item, int pos, GView::Type type); void handleItemDeleted(QString id, int pos); private: void handleSessionTerminate(); void handleWhiteboardOperationReceive(const WhiteboardOperation::ref operation); void closeEvent(QCloseEvent* event); private: QGraphicsScene* scene; GView* graphicsView; QVBoxLayout* layout; QVBoxLayout* sidebarLayout; QHBoxLayout* hLayout; QGridLayout* toolboxLayout; QHBoxLayout* strokeLayout; QHBoxLayout* fillLayout; ColorWidget* strokeColor; ColorWidget* fillColor; QWidget* widget; QPushButton* moveUpButton; QPushButton* moveDownButton; QSpinBox* widthBox; QToolButton* rubberButton; QToolButton* lineButton; QToolButton* rectButton; QToolButton* circleButton; QToolButton* handLineButton; QToolButton* textButton; QToolButton* polygonButton; QToolButton* selectButton; std::string lastOpID; WhiteboardSession::ref whiteboardSession_; }; } swift-im-2.0+dev6/Swift/QtUI/Whiteboard/FreehandLineItem.cpp0000644000175000017500000000364412227051774023547 0ustar kismithkismith/* * Copyright (c) 2012 Mateusz Piękos * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include "FreehandLineItem.h" namespace Swift { FreehandLineItem::FreehandLineItem(QGraphicsItem* parent) : QGraphicsItem(parent) { } QRectF FreehandLineItem::boundingRect() const { return boundRect; } void FreehandLineItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* /*option*/, QWidget* /*widget*/) { painter->setPen(pen_); if (points_.size() > 0) { QVector::const_iterator it = points_.begin(); QPointF previous = *it; ++it; for (; it != points_.end(); ++it) { painter->drawLine(previous, *it); previous = *it; } } } void FreehandLineItem::setStartPoint(QPointF point) { points_.clear(); points_.append(point); QRectF rect(point, point); prepareGeometryChange(); boundRect = rect; } void FreehandLineItem::lineTo(QPointF point) { qreal x1, x2, y1, y2; x1 = points_.last().x(); x2 = point.x(); y1 = points_.last().y(); y2 = point.y(); if (x1 > x2) { qreal temp = x1; x1 = x2; x2 = temp; } if (y1 > y2) { qreal temp = y1; y1 = y2; y2 = temp; } QRectF rect(x1-1, y1-1, x2+1-x1, y2+1-y1); points_.append(point); prepareGeometryChange(); boundRect |= rect; } bool FreehandLineItem::collidesWithPath(const QPainterPath& path, Qt::ItemSelectionMode /*mode*/) const { QVector::const_iterator it; QSizeF size(1,1); for (it = points_.begin(); it != points_.end(); ++it) { if (path.intersects(QRectF(*it, size))) { return true; } } return false; } void FreehandLineItem::setPen(const QPen& pen) { pen_ = pen; update(boundRect); } QPen FreehandLineItem::pen() const { return pen_; } const QVector& FreehandLineItem::points() const { return points_; } int FreehandLineItem::type() const { return Type; } } swift-im-2.0+dev6/Swift/QtUI/Whiteboard/GView.h0000644000175000017500000000377312227051774021075 0ustar kismithkismith/* * Copyright (c) 2012 Mateusz Piękos * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include #include #include #include #include #include "TextDialog.h" #include "FreehandLineItem.h" namespace Swift { class GView : public QGraphicsView { Q_OBJECT; public: enum Mode { Rubber, Line, Rect, Circle, HandLine, Text, Polygon, Select }; enum Type { New, Update, MoveUp, MoveDown }; GView(QGraphicsScene* scene, QWidget* parent = 0); void setLineWidth(int i); void setLineColor(QColor color); QColor getLineColor(); void setBrushColor(QColor color); QColor getBrushColor(); void setMode(Mode mode); void mouseMoveEvent(QMouseEvent* event); void mousePressEvent(QMouseEvent* event); void mouseReleaseEvent(QMouseEvent* /*event*/); void addItem(QGraphicsItem* item, QString id, int pos); void clear(); QGraphicsItem* getItem(QString id); void deleteItem(QString id); QString getNewID(); void move(QGraphicsItem* item, int npos); void deselect(QString id); public slots: void moveUpSelectedItem(); void moveDownSelectedItem(); private slots: void handleTextItemModified(QGraphicsTextItem*); private: void changePenAndBrush(QGraphicsItem* item, QPen pen, QBrush brush); void setActualPenAndBrushFromItem(QGraphicsItem* item); void deselect(); int zValue; bool mousePressed; QPen pen; QBrush brush; QPen defaultPen; QBrush defaultBrush; Mode mode; QGraphicsItem* lastItem; QGraphicsRectItem* selectionRect; TextDialog* textDialog; QMap itemsMap_; QList items_; IDGenerator idGenerator; signals: void lastItemChanged(QGraphicsItem* item, int pos, GView::Type type); void itemDeleted(QString id, int pos); void lineWidthChanged(int i); void lineColorChanged(QColor color); void brushColorChanged(QColor color); }; } swift-im-2.0+dev6/Swift/QtUI/Whiteboard/GView.cpp0000644000175000017500000003410312227051774021417 0ustar kismithkismith/* * Copyright (c) 2012 Mateusz Piękos * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include "GView.h" #include namespace Swift { GView::GView(QGraphicsScene* scene, QWidget* parent) : QGraphicsView(scene, parent), brush(QColor(Qt::white)), defaultBrush(QColor(Qt::white)) { selectionRect = 0; lastItem = 0; zValue = 0; } void GView::setLineWidth(int i) { pen.setWidth(i); if (selectionRect) { QGraphicsItem* item = selectionRect->data(1).value(); changePenAndBrush(selectionRect->data(1).value(), pen, brush); lastItemChanged(item, items_.indexOf(item)+1, Update); } else { defaultPen.setWidth(i); } } void GView::setLineColor(QColor color) { pen.setColor(color); if (selectionRect) { QGraphicsItem* item = selectionRect->data(1).value(); changePenAndBrush(selectionRect->data(1).value(), pen, brush); lastItemChanged(item, items_.indexOf(item)+1, Update); } else { defaultPen.setColor(color); } lineColorChanged(color); } QColor GView::getLineColor() { return pen.color(); } void GView::setBrushColor(QColor color) { brush.setColor(color); if (selectionRect) { QGraphicsItem* item = selectionRect->data(1).value(); changePenAndBrush(selectionRect->data(1).value(), pen, brush); lastItemChanged(item, items_.indexOf(item)+1, Update); } else { defaultBrush.setColor(color); } brushColorChanged(color); } QColor GView::getBrushColor() { return brush.color(); } void GView::setMode(Mode mode) { this->mode = mode; lastItem = 0; deselect(); } void GView::addItem(QGraphicsItem* item, QString id, int pos) { itemsMap_.insert(id, item); if (pos > items_.size()) { item->setZValue(zValue++); scene()->addItem(item); items_.append(item); } else { QGraphicsItem* temp = items_.at(pos-1); item->setZValue(temp->zValue()); scene()->addItem(item); item->stackBefore(temp); items_.insert(pos-1, item); } } void GView::clear() { scene()->clear(); items_.clear(); itemsMap_.clear(); lastItem = 0; selectionRect = 0; brush = QBrush(QColor(Qt::white)); defaultBrush = QBrush(QColor(Qt::white)); pen = QPen(); pen.setWidth(1); defaultPen = pen; lineWidthChanged(1); lineColorChanged(pen.color()); brushColorChanged(brush.color()); } QGraphicsItem* GView::getItem(QString id) { return itemsMap_.value(id); } void GView::deleteItem(QString id) { deselect(id); QGraphicsItem* item = itemsMap_.value(id); items_.removeOne(item); itemsMap_.remove(id); scene()->removeItem(item); delete item; } QString GView::getNewID() { return P2QSTRING(idGenerator.generateID()); } void GView::mouseMoveEvent(QMouseEvent* event) { if (!mousePressed) { return; } if (mode == Line) { QGraphicsLineItem* item = qgraphicsitem_cast(lastItem); if(item != 0) { QLineF line = item->line(); line.setP1(this->mapToScene(event->pos())); item->setLine(line); } } else if (mode == Rect) { QGraphicsRectItem* item = qgraphicsitem_cast(lastItem); if (item != 0) { QPointF beginPoint = item->data(0).toPointF(); QPointF newPoint = this->mapToScene(event->pos()); QRectF rect = item->rect(); if (beginPoint.x() <= newPoint.x() && beginPoint.y() <= newPoint.y()) { rect.setTopLeft(beginPoint); rect.setBottomRight(newPoint); } else if (beginPoint.x() > newPoint.x() && beginPoint.y() <= newPoint.y()) { rect.setTopRight(beginPoint); rect.setBottomLeft(newPoint); } else if (beginPoint.x() <= newPoint.x() && beginPoint.y() > newPoint.y()) { rect.setBottomLeft(beginPoint); rect.setTopRight(newPoint); } else if (beginPoint.x() > newPoint.x() && beginPoint.y() > newPoint.y()) { rect.setBottomRight(beginPoint); rect.setTopLeft(newPoint); } item->setRect(rect); } } else if (mode == Circle) { QGraphicsEllipseItem* item = qgraphicsitem_cast(lastItem); QPainterPath path; QPointF beginPoint = item->data(0).toPointF(); QPointF newPoint = this->mapToScene(event->pos()); QRectF rect = item->rect(); if (beginPoint.x() <= newPoint.x() && beginPoint.y() <= newPoint.y()) { rect.setTopLeft(beginPoint); rect.setBottomRight(newPoint); } else if (beginPoint.x() > newPoint.x() && beginPoint.y() <= newPoint.y()) { rect.setTopRight(beginPoint); rect.setBottomLeft(newPoint); } else if (beginPoint.x() <= newPoint.x() && beginPoint.y() > newPoint.y()) { rect.setBottomLeft(beginPoint); rect.setTopRight(newPoint); } else if (beginPoint.x() > newPoint.x() && beginPoint.y() > newPoint.y()) { rect.setBottomRight(beginPoint); rect.setTopLeft(newPoint); } item->setRect(rect); } else if (mode == HandLine) { FreehandLineItem* item = qgraphicsitem_cast(lastItem); if (item != 0) { QPointF newPoint = this->mapToScene(event->pos()); item->lineTo(newPoint); } } else if (mode == Polygon) { QGraphicsPolygonItem* item = qgraphicsitem_cast(lastItem); QPointF newPoint = this->mapToScene(event->pos()); QPolygonF polygon = item->polygon(); polygon.erase(polygon.end()-1); polygon.append(newPoint); item->setPolygon(polygon); } else if (mode == Select) { QGraphicsItem* item = selectionRect->data(1).value(); if (item != 0) { QPainterPath path; QPointF beginPoint = selectionRect->data(0).toPointF(); QPointF newPoint = this->mapToScene(event->pos()); item->setPos(beginPoint + newPoint); selectionRect->setPos(beginPoint + newPoint); } } } void GView::mousePressEvent(QMouseEvent *event) { mousePressed = true; deselect(); if (mode == Line) { QPointF point = this->mapToScene(event->pos()); QGraphicsItem* item = scene()->addLine(point.x(), point.y(), point.x(), point.y(), pen); QString id = getNewID(); item->setZValue(10000000); item->setData(100, id); item->setData(101, items_.size()); lastItem = item; } else if (mode == Rect) { QPointF point = this->mapToScene(event->pos()); QGraphicsRectItem* item = scene()->addRect(point.x(), point.y(), 0, 0, pen, brush); QString id = getNewID(); item->setZValue(10000000); item->setData(0, point); item->setData(100, id); item->setData(101, items_.size()); lastItem = item; } else if (mode == Rubber) { QPointF point = this->mapToScene(event->pos()); int w = pen.width(); QRectF rect(point.x()-w, point.y()-w, w*2, w*2); QList list = scene()->items(rect); if (!list.isEmpty()) { QGraphicsItem* item = scene()->items(rect).first(); QString id = item->data(100).toString(); int pos = items_.indexOf(item)+1; itemDeleted(id, pos); deleteItem(id); } } else if (mode == Circle) { QPointF point = this->mapToScene(event->pos()); QGraphicsEllipseItem* item = scene()->addEllipse(point.x(), point.y(), 0, 0, pen, brush); QString id = getNewID(); item->setZValue(10000000); item->setData(0, point); item->setData(100, id); item->setData(101, items_.size()); lastItem = item; } else if (mode == HandLine) { QPointF point = this->mapToScene(event->pos()); FreehandLineItem* item = new FreehandLineItem; QString id = getNewID(); item->setPen(pen); item->setStartPoint(point); item->setZValue(10000000); item->setData(100, id); item->setData(101, items_.size()); scene()->addItem(item); lastItem = item; } else if (mode == Text) { QPointF point = this->mapToScene(event->pos()); QGraphicsTextItem* item = scene()->addText(""); QString id = getNewID(); item->setData(100, id); item->setData(101, items_.size()); item->setDefaultTextColor(pen.color()); textDialog = new TextDialog(item, this); connect(textDialog, SIGNAL(accepted(QGraphicsTextItem*)), this, SLOT(handleTextItemModified(QGraphicsTextItem*))); textDialog->setAttribute(Qt::WA_DeleteOnClose); textDialog->show(); item->setPos(point); lastItem = item; } else if (mode == Polygon) { QPointF point = this->mapToScene(event->pos()); QGraphicsPolygonItem* item = dynamic_cast(lastItem); if (item == 0) { QPolygonF polygon; polygon.append(point); polygon.append(point); item = scene()->addPolygon(polygon, pen, brush); QString id = getNewID(); item->setZValue(10000000); item->setData(100, id); item->setData(101, items_.size()); lastItem = item; } else { QPolygonF polygon; polygon = item->polygon(); polygon.append(point); item->setPolygon(polygon); } } else if (mode == Select) { QPointF point = this->mapToScene(event->pos()); int w = pen.width(); if (w == 0) { w = 1; } QRectF rect(point.x()-w, point.y()-w, w*2, w*2); QList list = scene()->items(rect); if (!list.isEmpty()) { QPen pen; pen.setColor(QColor(Qt::gray)); pen.setStyle(Qt::DashLine); QGraphicsItem *item = scene()->items(rect).first(); selectionRect = scene()->addRect(item->boundingRect(), pen); selectionRect->setZValue(1000000); selectionRect->setData(0, item->pos()-point); selectionRect->setPos(item->pos()); QVariant var(QVariant::UserType); var.setValue(item); selectionRect->setData(1, var); setActualPenAndBrushFromItem(item); } } } void GView::mouseReleaseEvent(QMouseEvent* /*event*/) { mousePressed = false; QGraphicsPolygonItem* polygon = dynamic_cast(lastItem); if (polygon && polygon->polygon().size() >= 3) { lastItemChanged(polygon, items_.indexOf(polygon)+1, Update); } else if (lastItem) { zValue++; lastItem->setZValue(zValue++); items_.append(lastItem); itemsMap_.insert(lastItem->data(100).toString(), lastItem); lastItemChanged(lastItem, items_.size(), New); } else if (selectionRect){ QGraphicsItem* item = selectionRect->data(1).value(); lastItemChanged(item, items_.indexOf(item)+1, Update); } } void GView::handleTextItemModified(QGraphicsTextItem* item) { lastItemChanged(item, item->data(101).toInt(), Update); } void GView::moveUpSelectedItem() { if (selectionRect) { QGraphicsItem* item = selectionRect->data(1).value(); int pos = items_.indexOf(item); if (pos < items_.size()-1) { lastItemChanged(item, pos+1, MoveUp); move(item, pos+2); } } } void GView::moveDownSelectedItem() { if (selectionRect) { QGraphicsItem* item = selectionRect->data(1).value(); int pos = items_.indexOf(item); if (pos > 0) { lastItemChanged(item, pos+1, MoveDown); move(item, pos); } } } void GView::move(QGraphicsItem* item, int npos) { int pos = items_.indexOf(item); QGraphicsItem* itemAfter = NULL; if (npos-1 > pos) { if (npos == items_.size()) { item->setZValue(zValue++); } else { itemAfter = items_.at(npos); } items_.insert(npos, item); items_.removeAt(pos); } else if (npos-1 < pos) { itemAfter = items_.at(npos-1); items_.insert(npos-1, item); items_.removeAt(pos+1); } if (itemAfter) { item->setZValue(itemAfter->zValue()); item->stackBefore(itemAfter); } } void GView::changePenAndBrush(QGraphicsItem* item, QPen pen, QBrush brush) { QGraphicsLineItem* lineItem = qgraphicsitem_cast(item); if (lineItem) { lineItem->setPen(pen); } FreehandLineItem* handLineItem = qgraphicsitem_cast(item); if (handLineItem) { handLineItem->setPen(pen); } QGraphicsRectItem* rectItem = qgraphicsitem_cast(item); if (rectItem) { rectItem->setPen(pen); rectItem->setBrush(brush); } QGraphicsTextItem* textItem = qgraphicsitem_cast(item); if (textItem) { textItem->setDefaultTextColor(pen.color()); } QGraphicsPolygonItem* polygonItem = qgraphicsitem_cast(item); if (polygonItem) { polygonItem->setPen(pen); polygonItem->setBrush(brush); } QGraphicsEllipseItem* ellipseItem = qgraphicsitem_cast(item); if (ellipseItem) { ellipseItem->setPen(pen); ellipseItem->setBrush(brush); } lineColorChanged(pen.color()); brushColorChanged(brush.color()); } void GView::setActualPenAndBrushFromItem(QGraphicsItem* item) { QGraphicsLineItem* lineItem = qgraphicsitem_cast(item); if (lineItem) { pen = lineItem->pen(); } FreehandLineItem* handLineItem = qgraphicsitem_cast(item); if (handLineItem) { pen = handLineItem->pen(); } QGraphicsRectItem* rectItem = qgraphicsitem_cast(item); if (rectItem) { pen = rectItem->pen(); brush = rectItem->brush(); } QGraphicsTextItem* textItem = qgraphicsitem_cast(item); if (textItem) { pen.setColor(textItem->defaultTextColor()); } QGraphicsPolygonItem* polygonItem = qgraphicsitem_cast(item); if (polygonItem) { pen = polygonItem->pen(); brush = polygonItem->brush(); } QGraphicsEllipseItem* ellipseItem = qgraphicsitem_cast(item); if (ellipseItem) { pen = ellipseItem->pen(); brush = ellipseItem->brush(); } lineWidthChanged(pen.width()); lineColorChanged(pen.color()); brushColorChanged(brush.color()); } void GView::deselect() { if (selectionRect != 0) { pen = defaultPen; brush = defaultBrush; scene()->removeItem(selectionRect); delete selectionRect; selectionRect = 0; lineWidthChanged(pen.width()); lineColorChanged(pen.color()); brushColorChanged(brush.color()); } } void GView::deselect(QString id) { if (selectionRect != 0) { QGraphicsItem* item = getItem(id); if (item && selectionRect->data(1).value() == item) { pen = defaultPen; brush = defaultBrush; scene()->removeItem(selectionRect); delete selectionRect; selectionRect = 0; lineWidthChanged(pen.width()); lineColorChanged(pen.color()); brushColorChanged(brush.color()); } } } } swift-im-2.0+dev6/Swift/QtUI/QtJoinMUCWindow.ui0000644000175000017500000000645612227051774021114 0ustar kismithkismith QtJoinMUCWindow 0 0 410 224 0 0 Enter Room Room Address: Search ... Your Nickname: Room Password: Automatically configure newly created rooms Qt::Vertical 20 36 Qt::Horizontal 40 20 Enter automatically in future Enter Room room nickName joinAutomatically joinButton searchButton swift-im-2.0+dev6/Swift/QtUI/QtConnectionSettingsWindow.h0000644000175000017500000000126512227051774023273 0ustar kismithkismith/* * Copyright (c) 2012 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include "ui_QtConnectionSettings.h" #include namespace Swift { class QtConnectionSettingsWindow : public QDialog { Q_OBJECT public: QtConnectionSettingsWindow(const ClientOptions& options); ClientOptions getOptions(); private slots: void handleProxyTypeChanged(int); void handleAcceptRequested(); private: enum { NoProxy = 0, SystemProxy = 1, SOCKS5Proxy = 2, HTTPProxy = 3 }; Ui::QtConnectionSettings ui; }; } swift-im-2.0+dev6/Swift/QtUI/QtElidingLabel.h0000644000175000017500000000116212227051774020572 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include namespace Swift { class QtElidingLabel : public QLabel { Q_OBJECT public: QtElidingLabel(QWidget* parent = NULL, Qt::WindowFlags f = 0); QtElidingLabel(const QString &text, QWidget* parent = NULL, Qt::WindowFlags f = 0); void setText(const QString& text); virtual ~QtElidingLabel(); virtual void paintEvent(QPaintEvent* event); private: void setSizes(); bool dirty_; QString fullText_; QRect lastRect_; }; } swift-im-2.0+dev6/Swift/QtUI/QtWin32NotifierWindow.h0000644000175000017500000000112712227051774022052 0ustar kismithkismith/* * 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 #include "SwifTools/Notifier/Win32NotifierWindow.h" namespace Swift { class QtWin32NotifierWindow : public QWidget, public Win32NotifierWindow { public: QtWin32NotifierWindow(QWidget* parent = NULL) { setVisible(false); } bool winEvent (MSG* message, long* result ) { onMessageReceived(message); return false; } virtual HWND getID() const { return winId(); } }; } swift-im-2.0+dev6/Swift/QtUI/QtLineEdit.cpp0000644000175000017500000000070712227051774020313 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Swift/QtUI/QtLineEdit.h" #include namespace Swift { QtLineEdit::QtLineEdit(QWidget* parent) : QLineEdit(parent) { } void QtLineEdit::keyPressEvent(QKeyEvent* event) { if (event->key() == Qt::Key_Escape) { emit escapePressed(); } QLineEdit::keyPressEvent(event); } } swift-im-2.0+dev6/Swift/QtUI/QtXMLConsoleWidget.cpp0000644000175000017500000000535312227051774021747 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "QtXMLConsoleWidget.h" #include #include #include #include #include #include #include "QtSwiftUtil.h" #include namespace Swift { QtXMLConsoleWidget::QtXMLConsoleWidget() { setWindowTitle(tr("Console")); QVBoxLayout* layout = new QVBoxLayout(this); layout->setSpacing(0); layout->setContentsMargins(0,0,0,0); textEdit = new QTextEdit(this); textEdit->setReadOnly(true); layout->addWidget(textEdit); QWidget* bottom = new QWidget(this); layout->addWidget(bottom); bottom->setAutoFillBackground(true); QHBoxLayout* buttonLayout = new QHBoxLayout(bottom); buttonLayout->setContentsMargins(10,0,20,0); buttonLayout->setSpacing(0); enabled = new QCheckBox(tr("Trace input/output"), bottom); enabled->setChecked(true); buttonLayout->addWidget(enabled); buttonLayout->addStretch(); QPushButton* clearButton = new QPushButton(tr("Clear"), bottom); connect(clearButton, SIGNAL(clicked()), textEdit, SLOT(clear())); buttonLayout->addWidget(clearButton); setWindowTitle(tr("Debug Console")); emit titleUpdated(); } QtXMLConsoleWidget::~QtXMLConsoleWidget() { } void QtXMLConsoleWidget::showEvent(QShowEvent* event) { emit windowOpening(); emit titleUpdated(); /* This just needs to be somewhere after construction */ QWidget::showEvent(event); } void QtXMLConsoleWidget::show() { QWidget::show(); emit windowOpening(); } void QtXMLConsoleWidget::activate() { emit wantsToActivate(); } void QtXMLConsoleWidget::closeEvent(QCloseEvent* event) { emit windowClosing(); event->accept(); } void QtXMLConsoleWidget::handleDataRead(const SafeByteArray& data) { appendTextIfEnabled(std::string(tr("").toUtf8()) + "\n" + safeByteArrayToString(data) + "\n", QColor(33,98,33)); } void QtXMLConsoleWidget::handleDataWritten(const SafeByteArray& data) { appendTextIfEnabled(std::string(tr("").toUtf8()) + "\n" + safeByteArrayToString(data) + "\n", QColor(155,1,0)); } void QtXMLConsoleWidget::appendTextIfEnabled(const std::string& data, const QColor& color) { if (enabled->isChecked()) { QScrollBar* scrollBar = textEdit->verticalScrollBar(); bool scrollToBottom = (!scrollBar || scrollBar->value() == scrollBar->maximum()); QTextCursor cursor(textEdit->document()); cursor.beginEditBlock(); cursor.movePosition(QTextCursor::End); QTextCharFormat format; format.setForeground(QBrush(color)); cursor.mergeCharFormat(format); cursor.insertText(P2QSTRING(data)); cursor.endEditBlock(); if (scrollToBottom) { scrollBar->setValue(scrollBar->maximum()); } } } } swift-im-2.0+dev6/Swift/QtUI/QtChatWindowJSBridge.h0000644000175000017500000000073512227051774021705 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include namespace Swift { class FileTransferController; class QtChatWindowJSBridge : public QObject { Q_OBJECT public: QtChatWindowJSBridge(); virtual ~QtChatWindowJSBridge(); signals: void buttonClicked(QString id, QString arg1, QString arg2, QString arg3); }; } swift-im-2.0+dev6/Swift/QtUI/MessageSnippet.cpp0000644000175000017500000000312612227051774021236 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "MessageSnippet.h" #include #include namespace Swift { MessageSnippet::MessageSnippet(const QString& message, const QString& sender, const QDateTime& time, const QString& iconURI, bool isIncoming, bool appendToPrevious, QtChatTheme* theme, const QString& id) : ChatSnippet(appendToPrevious) { if (appendToPrevious) { setContinuationFallbackSnippet(boost::shared_ptr(new MessageSnippet(message, sender, time, iconURI, isIncoming, false, theme, id))); } if (isIncoming) { if (appendToPrevious) { content_ = theme->getIncomingNextContent(); } else { content_ = theme->getIncomingContent(); } } else { if (appendToPrevious) { content_ = theme->getOutgoingNextContent(); } else { content_ = theme->getOutgoingContent(); } } content_.replace("%message%", wrapResizable("" + escape(message) + "")); content_.replace("%wrapped_sender%", wrapResizable(escape(sender))); content_.replace("%sender%", escape(sender)); content_.replace("%time%", wrapResizable("" + timeToEscapedString(time) + "")); content_.replace("%userIconPath%", escape(iconURI)); content_ = "
" + content_ + "
"; content_ = "" + content_ + ""; } MessageSnippet::~MessageSnippet() { } } swift-im-2.0+dev6/Swift/QtUI/QtSwift.h0000644000175000017500000000521312227051774017354 0ustar kismithkismith/* * Copyright (c) 2010-2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include #include #include "Swiften/Base/Platform.h" #include "Swiften/EventLoop/Qt/QtEventLoop.h" #include "QtSettingsProvider.h" #if defined(SWIFTEN_PLATFORM_MACOSX) #include "SwifTools/Application/CocoaApplication.h" #include "CocoaApplicationActivateHelper.h" #endif #if defined(SWIFTEN_PLATFORM_WINDOWS) #include "WindowsNotifier.h" #endif #include "SwifTools/Idle/PlatformIdleQuerier.h" #include "SwifTools/Idle/ActualIdleDetector.h" namespace po = boost::program_options; class QSplitter; namespace Swift { class QtUIFactory; class CertificateStorageFactory; class Dock; class Notifier; class StoragesFactory; class AutoUpdater; class ApplicationPathProvider; class AvatarStorage; class CapsStorage; class MainController; class QtSystemTray; class QtChatTabs; class QtChatWindowFactory; class QtSoundPlayer; class QtMUCSearchWindowFactory; class QtUserSearchWindowFactory; class EventLoop; class URIHandler; class SettingsProviderHierachy; class XMLSettingsProvider; class QtSwift : public QObject { Q_OBJECT public: QtSwift(const po::variables_map& options); static po::options_description getOptionsDescription(); ~QtSwift(); private: XMLSettingsProvider* loadSettingsFile(const QString& fileName); QMap loadEmoticonsFile(const QString& fileName); private: QtEventLoop clientMainThreadCaller_; PlatformTLSFactories tlsFactories_; BoostNetworkFactories networkFactories_; QtChatWindowFactory* chatWindowFactory_; std::vector mainControllers_; std::vector systemTrays_; std::vector uiFactories_; QtSettingsProvider* qtSettings_; XMLSettingsProvider* xmlSettings_; SettingsProviderHierachy* settingsHierachy_; QSplitter* splitter_; QtSoundPlayer* soundPlayer_; Dock* dock_; URIHandler* uriHandler_; QtChatTabs* tabs_; ApplicationPathProvider* applicationPathProvider_; StoragesFactory* storagesFactory_; CertificateStorageFactory* certificateStorageFactory_; AutoUpdater* autoUpdater_; Notifier* notifier_; PlatformIdleQuerier idleQuerier_; ActualIdleDetector idleDetector_; #if defined(SWIFTEN_PLATFORM_MACOSX) CocoaApplication cocoaApplication_; CocoaApplicationActivateHelper cocoaApplicationActivateHelper_; #endif }; } swift-im-2.0+dev6/Swift/QtUI/QtMUCConfigurationWindow.h0000644000175000017500000000157712227051774022635 0ustar kismithkismith/* * Copyright (c) 2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include #include class QBoxLayout; class QCloseEvent; namespace Swift { class QtFormWidget; class QtMUCConfigurationWindow : public QWidget { Q_OBJECT public: QtMUCConfigurationWindow(Form::ref form); virtual ~QtMUCConfigurationWindow(); boost::signal onFormComplete; boost::signal onFormCancelled; private slots: void handleCancelClicked(); void handleOKClicked(); protected: virtual void closeEvent(QCloseEvent* event); private: QtFormWidget* formWidget_; QPushButton* okButton_; QPushButton* cancelButton_; bool closed_; }; } swift-im-2.0+dev6/Swift/QtUI/QtAvatarWidget.cpp0000644000175000017500000000552412227051774021202 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "QtAvatarWidget.h" #include #include #include #include #include #include #include #include #include #include #include #include namespace Swift { QtAvatarWidget::QtAvatarWidget(QWidget* parent) : QWidget(parent) { QVBoxLayout* layout = new QVBoxLayout(this); layout->setContentsMargins(0,0,0,0); QSizePolicy sp(QSizePolicy::Fixed, QSizePolicy::Fixed); sp.setHorizontalStretch(0); sp.setVerticalStretch(0); setSizePolicy(sp); setMinimumSize(QSize(96, 96)); setMaximumSize(QSize(96, 96)); label = new QLabel(this); label->setWordWrap(true); label->setSizePolicy(sp); label->setMinimumSize(QSize(96, 96)); label->setMaximumSize(QSize(96, 96)); label->setAlignment(Qt::AlignCenter); layout->addWidget(label); } void QtAvatarWidget::setAvatar(const ByteArray& data, const std::string& type) { this->data = data; this->type = type; QImage image; if (!data.empty()) { image.loadFromData(reinterpret_cast(vecptr(data)), data.size()); } if (image.isNull()) { image = QImage(":/icons/no-avatar.png"); QPainter painter(&image); painter.setPen(Qt::gray); QFont font = painter.font(); font.setPointSize(14); painter.setFont(font); painter.drawText(0, 0, image.height(), image.width(), Qt::AlignHCenter | Qt::AlignVCenter, tr("No picture")); } if (image.height() > label->height() || image.width() > label->width()) { image = image.scaled(label->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation); } label->setPixmap(QPixmap::fromImage(image)); } void QtAvatarWidget::mousePressEvent(QMouseEvent* event) { QMenu menu; QAction* selectPicture = new QAction(tr("Select picture ..."), this); menu.addAction(selectPicture); QAction* clearPicture = new QAction(tr("Clear picture"), this); menu.addAction(clearPicture); QAction* result = menu.exec(event->globalPos()); if (result == selectPicture) { QString fileName = QFileDialog::getOpenFileName(this, tr("Select picture"), "", tr("Image Files (*.png *.jpg *.jpeg *.gif)")); if (!fileName.isEmpty()) { ByteArray data; readByteArrayFromFile(data, Q2PSTRING(fileName)); QBuffer buffer; buffer.setData(reinterpret_cast(vecptr(data)), data.size()); buffer.open(QIODevice::ReadOnly); QString type = QImageReader::imageFormat(&buffer).toLower(); if (!type.isEmpty()) { type = "image/" + type; setAvatar(data, Q2PSTRING(type)); } else { QMessageBox::critical(this, tr("Error"), tr("The selected picture is in an unrecognized format")); } } } else if (result == clearPicture) { setAvatar(ByteArray(), ""); } } } swift-im-2.0+dev6/Swift/QtUI/QtTextEdit.h0000644000175000017500000000102312227051774020005 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include namespace Swift { class QtTextEdit : public QTextEdit { Q_OBJECT public: QtTextEdit(QWidget* parent = 0); virtual QSize sizeHint() const; signals: void returnPressed(); void unhandledKeyPressEvent(QKeyEvent* event); protected: virtual void keyPressEvent(QKeyEvent* event); private slots: void handleTextChanged(); }; } swift-im-2.0+dev6/Swift/QtUI/QtDBUSURIHandler.h0000644000175000017500000000054612227051774020677 0ustar kismithkismith/* * 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 #include namespace Swift { class QtDBUSURIHandler : public QObject, public URIHandler { public: QtDBUSURIHandler(); }; } swift-im-2.0+dev6/Swift/QtUI/QtChatView.h0000644000175000017500000000573112227051774017777 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #ifndef SWIFT_QtChatView_H #define SWIFT_QtChatView_H #include #include #include #include #include #include "ChatSnippet.h" #include class QWebPage; class QUrl; class QDate; namespace Swift { class QtWebView; class QtChatTheme; class QtChatView : public QWidget { Q_OBJECT public: QtChatView(QtChatTheme* theme, QWidget* parent, bool disableAutoScroll = false); void addMessageTop(boost::shared_ptr snippet); void addMessageBottom(boost::shared_ptr snippet); void addLastSeenLine(); void replaceLastMessage(const QString& newMessage); void replaceLastMessage(const QString& newMessage, const QString& note); void replaceMessage(const QString& newMessage, const QString& id, const QDateTime& time); void rememberScrolledToBottom(); void setAckXML(const QString& id, const QString& xml); void setReceiptXML(const QString& id, const QString& xml); void displayReceiptInfo(const QString& id, bool showIt); QString getLastSentMessage(); void addToJSEnvironment(const QString&, QObject*); void setFileTransferProgress(QString id, const int percentageDone); void setFileTransferStatus(QString id, const ChatWindow::FileTransferState state, const QString& msg); void setWhiteboardSessionStatus(QString id, const ChatWindow::WhiteboardSessionState state); void setMUCInvitationJoined(QString id); void showEmoticons(bool show); int getSnippetPositionByDate(const QDate& date); signals: void gotFocus(); void fontResized(int); void logCleared(); void scrollRequested(int pos); void scrollReachedTop(); void scrollReachedBottom(); public slots: void copySelectionToClipboard(); void scrollToBottom(); void handleLinkClicked(const QUrl&); void handleKeyPressEvent(QKeyEvent* event); void resetView(); void resetTopInsertPoint(); void increaseFontSize(int numSteps = 1); void decreaseFontSize(); void resizeFont(int fontSizeSteps); private slots: void handleViewLoadFinished(bool); void handleFrameSizeChanged(); void handleClearRequested(); void handleScrollRequested(int dx, int dy, const QRect& rectToScroll); private: void headerEncode(); void messageEncode(); void addToDOM(boost::shared_ptr snippet); QWebElement snippetToDOM(boost::shared_ptr snippet); bool viewReady_; bool isAtBottom_; bool topMessageAdded_; int scrollBarMaximum_; QtWebView* webView_; QWebPage* webPage_; int fontSizeSteps_; QtChatTheme* theme_; QWebElement newInsertPoint_; QWebElement topInsertPoint_; QWebElement lineSeparator_; QWebElement lastElement_; QWebElement firstElement_; QWebElement document_; bool disableAutoScroll_; }; } #endif swift-im-2.0+dev6/Swift/Controllers/0000755000175000017500000000000012227051774017265 5ustar kismithkismithswift-im-2.0+dev6/Swift/Controllers/SystemTrayController.h0000644000175000017500000000115012227051773023622 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include "Swiften/Elements/StatusShow.h" namespace Swift { class EventController; class SystemTray; class SystemTrayController { public: SystemTrayController(EventController* eventController, SystemTray* systemTray); void setMyStatusType(StatusShow::Type type); void setConnecting(); private: void handleEventQueueLengthChange(int length); private: EventController* eventController_; SystemTray* systemTray_; }; } swift-im-2.0+dev6/Swift/Controllers/SConscript0000644000175000017500000000602512227051773021301 0ustar kismithkismithImport("env") import Version ################################################################################ # Flags ################################################################################ if env["SCONS_STAGE"] == "flags" : env["SWIFT_CONTROLLERS_FLAGS"] = { "LIBPATH": [Dir(".")], "LIBS": ["SwiftControllers"] } ################################################################################ # Build ################################################################################ if env["SCONS_STAGE"] == "build" : myenv = env.Clone() myenv.BuildVersion("BuildVersion.h", project = "swift") myenv.UseFlags(env["SWIFTEN_FLAGS"]) myenv.UseFlags(env["SWIFTEN_DEP_FLAGS"]) myenv.StaticLibrary("SwiftControllers", [ "Chat/ChatController.cpp", "Chat/ChatControllerBase.cpp", "Chat/ChatsManager.cpp", "Chat/MUCController.cpp", "Chat/MUCSearchController.cpp", "Chat/UserSearchController.cpp", "MainController.cpp", "ProfileController.cpp", "ContactEditController.cpp", "FileTransfer/FileTransferController.cpp", "FileTransfer/FileTransferOverview.cpp", "FileTransfer/FileTransferProgressInfo.cpp", "Roster/RosterController.cpp", "Roster/RosterGroupExpandinessPersister.cpp", "Roster/ContactRosterItem.cpp", "Roster/GroupRosterItem.cpp", "Roster/RosterItem.cpp", "Roster/Roster.cpp", "Roster/TableRoster.cpp", "EventWindowController.cpp", "SoundEventController.cpp", "SystemTrayController.cpp", "XMLConsoleController.cpp", "HistoryViewController.cpp", "HistoryController.cpp", "FileTransferListController.cpp", "StatusTracker.cpp", "PresenceNotifier.cpp", "EventNotifier.cpp", "AdHocManager.cpp", "XMPPEvents/EventController.cpp", "UIEvents/UIEvent.cpp", "UIInterfaces/XMLConsoleWidget.cpp", "UIInterfaces/ChatListWindow.cpp", "PreviousStatusStore.cpp", "ProfileSettingsProvider.cpp", "Settings/SettingsProviderHierachy.cpp", "Settings/XMLSettingsProvider.cpp", "Storages/CertificateStorageFactory.cpp", "Storages/CertificateStorage.cpp", "Storages/CertificateFileStorage.cpp", "Storages/CertificateMemoryStorage.cpp", "Storages/AvatarFileStorage.cpp", "Storages/FileStorages.cpp", "Storages/RosterFileStorage.cpp", "Storages/CapsFileStorage.cpp", "Storages/VCardFileStorage.cpp", "StatusUtil.cpp", "Translator.cpp", "XMPPURIController.cpp", "ChatMessageSummarizer.cpp", "SettingConstants.cpp", "WhiteboardManager.cpp" ]) env.Append(UNITTEST_SOURCES = [ File("Roster/UnitTest/RosterControllerTest.cpp"), File("Roster/UnitTest/RosterTest.cpp"), File("Roster/UnitTest/LeastCommonSubsequenceTest.cpp"), File("Roster/UnitTest/TableRosterTest.cpp"), File("UnitTest/PreviousStatusStoreTest.cpp"), File("UnitTest/PresenceNotifierTest.cpp"), File("Chat/UnitTest/ChatsManagerTest.cpp"), File("Chat/UnitTest/MUCControllerTest.cpp"), File("UnitTest/MockChatWindow.cpp"), File("UnitTest/ChatMessageSummarizerTest.cpp"), File("Settings/UnitTest/SettingsProviderHierachyTest.cpp"), ]) swift-im-2.0+dev6/Swift/Controllers/HistoryController.h0000644000175000017500000000330412227051773023142 0ustar kismithkismith/* * Copyright (c) 2012 Catalin Badea * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include #include #include #include #include #include namespace Swift { class JID; class HistoryController { public: HistoryController(HistoryStorage* localHistoryStorage); ~HistoryController(); void addMessage(const std::string& message, const JID& fromJID, const JID& toJID, HistoryMessage::Type type, const boost::posix_time::ptime& timeStamp); std::vector getMessagesFromDate(const JID& selfJID, const JID& contactJID, HistoryMessage::Type type, const boost::gregorian::date& date) const; std::vector getMessagesFromPreviousDate(const JID& selfJID, const JID& contactJID, HistoryMessage::Type type, const boost::gregorian::date& date) const; std::vector getMessagesFromNextDate(const JID& selfJID, const JID& contactJID, HistoryMessage::Type type, const boost::gregorian::date& date) const; ContactsMap getContacts(const JID& selfJID, HistoryMessage::Type type, const std::string& keyword = std::string()) const; std::vector getMUCContext(const JID& selfJID, const JID& mucJID, const boost::posix_time::ptime& timeStamp) const; boost::posix_time::ptime getLastTimeStampFromMUC(const JID& selfJID, const JID& mucJID); boost::signal onNewMessage; private: HistoryStorage* localHistory_; bool remoteArchiveSupported_; }; } swift-im-2.0+dev6/Swift/Controllers/Translator.cpp0000644000175000017500000000106412227051773022122 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include namespace Swift { struct DefaultTranslator : public Translator { virtual std::string translate(const std::string& text, const std::string&) { return text; } } defaultTranslator; Translator* Translator::translator = &defaultTranslator; Translator::~Translator() { } void Translator::setInstance(Translator* t) { translator = t; } } swift-im-2.0+dev6/Swift/Controllers/PreviousStatusStore.h0000644000175000017500000000140412227051773023471 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include /* std::pair */ #include #include #include "Swiften/Elements/StatusShow.h" namespace Swift { typedef std::pair TypeStringPair; class PreviousStatusStore { public: PreviousStatusStore(); ~PreviousStatusStore(); void addStatus(StatusShow::Type status, const std::string& message); std::vector getSuggestions(const std::string& message); private: std::vector exactMatchSuggestions(StatusShow::Type status, const std::string& message); std::vector store_; }; } swift-im-2.0+dev6/Swift/Controllers/Roster/0000755000175000017500000000000012227051773020542 5ustar kismithkismithswift-im-2.0+dev6/Swift/Controllers/Roster/ContactRosterItem.h0000644000175000017500000000354412227051773024332 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include "Swiften/JID/JID.h" #include "Swift/Controllers/Roster/RosterItem.h" #include "Swiften/Elements/StatusShow.h" #include "Swiften/Elements/Presence.h" #include #include #include #include "Swiften/Base/boost_bsignals.h" #include namespace Swift { class GroupRosterItem; class ContactRosterItem : public RosterItem { public: enum Feature { FileTransferFeature, WhiteboardFeature, }; public: ContactRosterItem(const JID& jid, const JID& displayJID, const std::string& name, GroupRosterItem* parent); virtual ~ContactRosterItem(); StatusShow::Type getStatusShow() const; StatusShow::Type getSimplifiedStatusShow() const; std::string getStatusText() const; void setAvatarPath(const std::string& path); const std::string& getAvatarPath() const; const JID& getJID() const; void setDisplayJID(const JID& jid); const JID& getDisplayJID() const; void applyPresence(const std::string& resource, boost::shared_ptr presence); void clearPresence(); void calculateShownPresence(); const std::vector& getGroups() const; /** Only used so a contact can know about the groups it's in*/ void addGroup(const std::string& group); void removeGroup(const std::string& group); void setSupportedFeatures(const std::set& features); bool supportsFeature(Feature feature) const; private: JID jid_; JID displayJID_; std::string avatarPath_; std::map > presences_; boost::shared_ptr offlinePresence_; boost::shared_ptr shownPresence_; std::vector groups_; std::set features_; }; } swift-im-2.0+dev6/Swift/Controllers/Roster/RosterGroupExpandinessPersister.h0000644000175000017500000000127512227051773027316 0ustar kismithkismith/* * Copyright (c) 2010-2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include "Swift/Controllers/Roster/Roster.h" #include "Swift/Controllers/Settings/SettingsProvider.h" namespace Swift { class RosterGroupExpandinessPersister { public: RosterGroupExpandinessPersister(Roster* roster, SettingsProvider* settings); private: void handleExpandedChanged(GroupRosterItem* group, bool expanded); void handleGroupAdded(GroupRosterItem* group); void load(); void save(); std::set collapsed_; Roster* roster_; SettingsProvider* settings_; }; } swift-im-2.0+dev6/Swift/Controllers/Roster/RosterItemOperation.h0000644000175000017500000000161712227051773024676 0ustar kismithkismith/* * 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 "Swift/Controllers/Roster/RosterItem.h" namespace Swift { class RosterItemOperation { public: RosterItemOperation(bool requiresLookup = false, const JID& lookupJID = JID()) : requiresLookup_(requiresLookup), lookupJID_(lookupJID) {}; virtual ~RosterItemOperation() {}; bool requiresLookup() const {return requiresLookup_;}; const JID& lookupJID() const {return lookupJID_;}; /** * This is called when iterating over possible subjects, so must check it's * applying to the right items - even if requiresLookup() is true an item * with the same bare JID but different full JID may be passed. */ virtual void operator() (RosterItem*) const = 0; private: bool requiresLookup_; JID lookupJID_; }; } swift-im-2.0+dev6/Swift/Controllers/Roster/TableRoster.cpp0000644000175000017500000001465212227051773023504 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include namespace Swift { struct SectionNameEquals { bool operator()(const TableRoster::Section& s1, const TableRoster::Section& s2) const { return s1.name == s2.name; } }; template struct True { bool operator()(const T&, const T&) const { return true; } }; struct ItemEquals { bool operator()(const TableRoster::Item& i1, const TableRoster::Item& i2) const { return i1.jid == i2.jid; } }; struct ItemNeedsUpdate { bool operator()(const TableRoster::Item& i1, const TableRoster::Item& i2) const { return i1.status != i2.status || i1.description != i2.description || i1.name != i2.name || i1.avatarPath.empty() != i2.avatarPath.empty(); } }; struct CreateIndexForSection { CreateIndexForSection(size_t section) : section(section) { } TableRoster::Index operator()(size_t row) const { return TableRoster::Index(section, row); } size_t section; }; } using namespace Swift; TableRoster::TableRoster(Roster* model, TimerFactory* timerFactory, int updateDelay) : model(model), updatePending(false) { updateTimer = timerFactory->createTimer(updateDelay); updateTimer->onTick.connect(boost::bind(&TableRoster::handleUpdateTimerTick, this)); if (model) { model->onChildrenChanged.connect(boost::bind(&TableRoster::scheduleUpdate, this)); model->onGroupAdded.connect(boost::bind(&TableRoster::scheduleUpdate, this)); model->onDataChanged.connect(boost::bind(&TableRoster::scheduleUpdate, this)); } } TableRoster::~TableRoster() { updateTimer->stop(); updateTimer->onTick.disconnect(boost::bind(&TableRoster::handleUpdateTimerTick, this)); if (model) { model->onDataChanged.disconnect(boost::bind(&TableRoster::scheduleUpdate, this)); model->onGroupAdded.disconnect(boost::bind(&TableRoster::scheduleUpdate, this)); model->onChildrenChanged.disconnect(boost::bind(&TableRoster::scheduleUpdate, this)); } } size_t TableRoster::getNumberOfSections() const { return sections.size(); } const std::string& TableRoster::getSectionTitle(size_t section) { return sections[section].name; } size_t TableRoster::getNumberOfRowsInSection(size_t section) const { return sections[section].items.size(); } const TableRoster::Item& TableRoster::getItem(const Index& index) const { return sections[index.section].items[index.row]; } void TableRoster::handleUpdateTimerTick() { updateTimer->stop(); updatePending = false; // Get a model for the new roster std::vector
newSections; if (model) { foreach(RosterItem* item, model->getRoot()->getDisplayedChildren()) { if (GroupRosterItem* groupItem = boost::polymorphic_downcast(item)) { //std::cerr << "* " << groupItem->getDisplayName() << std::endl; Section section(groupItem->getDisplayName()); foreach(RosterItem* groupChildItem, groupItem->getDisplayedChildren()) { if (ContactRosterItem* contact = boost::polymorphic_downcast(groupChildItem)) { //std::cerr << " - " << contact->getDisplayJID() << std::endl; section.items.push_back(Item(contact->getDisplayName(), contact->getStatusText(), contact->getDisplayJID(), contact->getStatusShow(), contact->getAvatarPath())); } } newSections.push_back(section); } } } // Do a diff with the previous roster Update update; std::vector sectionUpdates; std::vector sectionPostUpdates; computeIndexDiff >(sections, newSections, sectionUpdates, sectionPostUpdates, update.deletedSections, update.insertedSections); assert(sectionUpdates.size() == sectionPostUpdates.size()); for (size_t i = 0; i < sectionUpdates.size(); ++i) { assert(sectionUpdates[i] < sections.size()); assert(sectionPostUpdates[i] < newSections.size()); std::vector itemUpdates; std::vector itemPostUpdates; std::vector itemRemoves; std::vector itemInserts; computeIndexDiff(sections[sectionUpdates[i]].items, newSections[sectionPostUpdates[i]].items, itemUpdates, itemPostUpdates, itemRemoves, itemInserts); size_t end = update.insertedRows.size(); update.insertedRows.resize(update.insertedRows.size() + itemInserts.size()); std::transform(itemInserts.begin(), itemInserts.end(), update.insertedRows.begin() + end, CreateIndexForSection(sectionPostUpdates[i])); end = update.deletedRows.size(); update.deletedRows.resize(update.deletedRows.size() + itemRemoves.size()); std::transform(itemRemoves.begin(), itemRemoves.end(), update.deletedRows.begin() + end, CreateIndexForSection(sectionUpdates[i])); end = update.updatedRows.size(); update.updatedRows.resize(update.updatedRows.size() + itemUpdates.size()); std::transform(itemUpdates.begin(), itemUpdates.end(), update.updatedRows.begin() + end, CreateIndexForSection(sectionPostUpdates[i])); } // Switch the old model with the new sections.swap(newSections); /* std::cerr << "-S: "; for (size_t i = 0; i < update.deletedSections.size(); ++i) { std::cerr << update.deletedSections[i] << " "; } std::cerr << std::endl; std::cerr << "+S: "; for (size_t i = 0; i < update.insertedSections.size(); ++i) { std::cerr << update.insertedSections[i] << " "; } std::cerr << std::endl; std::cerr << "-R: "; for (size_t i = 0; i < update.deletedRows.size(); ++i) { std::cerr << update.deletedRows[i].section << "," << update.deletedRows[i].row << " "; } std::cerr << std::endl; std::cerr << "*R: "; for (size_t i = 0; i < update.updatedRows.size(); ++i) { std::cerr << update.updatedRows[i].section << "," << update.updatedRows[i].row << " "; } std::cerr << std::endl; std::cerr << "+R: "; for (size_t i = 0; i < update.insertedRows.size(); ++i) { std::cerr << update.insertedRows[i].section << "," << update.insertedRows[i].row << " "; } std::cerr << std::endl; */ // Emit the update onUpdate(update); } void TableRoster::scheduleUpdate() { if (!updatePending) { updatePending = true; updateTimer->start(); } } swift-im-2.0+dev6/Swift/Controllers/Roster/TableRoster.h0000644000175000017500000000367712227051773023156 0ustar kismithkismith/* * 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 #include #include #include #include namespace Swift { class Roster; class TimerFactory; class Timer; class TableRoster { public: struct Item { Item(const std::string& name, const std::string& description, const JID& jid, StatusShow::Type status, const std::string& avatarPath) : name(name), description(description), jid(jid), status(status), avatarPath(avatarPath) { } std::string name; std::string description; JID jid; StatusShow::Type status; std::string avatarPath; }; struct Index { Index(size_t section = 0, size_t row = 0) : section(section), row(row) { } size_t section; size_t row; bool operator==(const Index& o) const { return o.section == section && o.row == row; } }; struct Update { std::vector updatedRows; std::vector insertedRows; std::vector deletedRows; std::vector insertedSections; std::vector deletedSections; }; TableRoster(Roster* model, TimerFactory* timerFactory, int updateDelay); ~TableRoster(); size_t getNumberOfSections() const; size_t getNumberOfRowsInSection(size_t section) const; const std::string& getSectionTitle(size_t); const Item& getItem(const Index&) const; boost::signal onUpdate; private: void handleUpdateTimerTick(); void scheduleUpdate(); private: friend struct SectionNameEquals; struct Section { Section(const std::string& name) : name(name) { } std::string name; std::vector items; }; Roster* model; std::vector
sections; bool updatePending; boost::shared_ptr updateTimer; }; } swift-im-2.0+dev6/Swift/Controllers/Roster/RosterGroupExpandinessPersister.cpp0000644000175000017500000000371212227051773027647 0ustar kismithkismith/* * Copyright (c) 2010-2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include namespace Swift { RosterGroupExpandinessPersister::RosterGroupExpandinessPersister(Roster* roster, SettingsProvider* settings) : roster_(roster), settings_(settings) { load(); roster_->onGroupAdded.connect(boost::bind(&RosterGroupExpandinessPersister::handleGroupAdded, this, _1)); } void RosterGroupExpandinessPersister::handleGroupAdded(GroupRosterItem* group) { if (collapsed_.find(group->getDisplayName()) != collapsed_.end()) { group->setExpanded(false); } else { group->setExpanded(true); } group->onExpandedChanged.connect(boost::bind(&RosterGroupExpandinessPersister::handleExpandedChanged, this, group, _1)); } void RosterGroupExpandinessPersister::handleExpandedChanged(GroupRosterItem* group, bool expanded) { if (expanded) { std::string displayName = group->getDisplayName(); //collapsed_.erase(std::remove(collapsed_.begin(), collapsed_.end(), displayName), collapsed_.end()); collapsed_.erase(displayName); } else { collapsed_.insert(group->getDisplayName()); } save(); } void RosterGroupExpandinessPersister::save() { std::string setting; foreach (const std::string& group, collapsed_) { if (!setting.empty()) { setting += "\n"; } setting += group; } settings_->storeSetting(SettingConstants::EXPANDED_ROSTER_GROUPS, setting); } void RosterGroupExpandinessPersister::load() { std::string saved = settings_->getSetting(SettingConstants::EXPANDED_ROSTER_GROUPS); std::vector collapsed = String::split(saved, '\n'); collapsed_.insert(collapsed.begin(), collapsed.end()); } } swift-im-2.0+dev6/Swift/Controllers/Roster/RosterController.h0000644000175000017500000000762112227051773024243 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include "Swiften/JID/JID.h" #include #include #include "Swiften/Elements/Presence.h" #include "Swiften/Elements/ErrorPayload.h" #include "Swiften/Elements/RosterPayload.h" #include "Swiften/Avatars/AvatarManager.h" #include "Swift/Controllers/UIEvents/UIEvent.h" #include "RosterGroupExpandinessPersister.h" #include "Swift/Controllers/FileTransfer/FileTransferOverview.h" #include "Swiften/Base/boost_bsignals.h" #include namespace Swift { class IQRouter; class Roster; class XMPPRoster; class XMPPRosterItem; class MainWindow; class MainWindowFactory; class OfflineRosterFilter; class NickResolver; class PresenceOracle; class SubscriptionManager; class EventController; class SubscriptionRequestEvent; class UIEventStream; class IQRouter; class SettingsProvider; class NickManager; class EntityCapsProvider; class FileTransferManager; class RosterController { public: RosterController(const JID& jid, XMPPRoster* xmppRoster, AvatarManager* avatarManager, MainWindowFactory* mainWindowFactory, NickManager* nickManager, NickResolver* nickResolver, PresenceOracle* presenceOracle, SubscriptionManager* subscriptionManager, EventController* eventController, UIEventStream* uiEventStream, IQRouter* iqRouter_, SettingsProvider* settings, EntityCapsProvider* entityCapsProvider, FileTransferOverview* fileTransferOverview); ~RosterController(); void showRosterWindow(); MainWindow* getWindow() {return mainWindow_;}; boost::signal onChangeStatusRequest; boost::signal onSignOutRequest; void handleAvatarChanged(const JID& jid); void setEnabled(bool enabled); boost::optional getItem(const JID&) const; std::set getGroups() const; void setContactGroups(const JID& jid, const std::vector& groups); void updateItem(const XMPPRosterItem&); private: void handleOnJIDAdded(const JID &jid); void handleRosterCleared(); void handleOnJIDRemoved(const JID &jid); void handleOnJIDUpdated(const JID &jid, const std::string& oldName, const std::vector& oldGroups); void handleStartChatRequest(const JID& contact); void handleChangeStatusRequest(StatusShow::Type show, const std::string &statusText); void handleShowOfflineToggled(bool state); void handleIncomingPresence(boost::shared_ptr newPresence); void handleSubscriptionRequest(const JID& jid, const std::string& message); void handleSubscriptionRequestAccepted(SubscriptionRequestEvent* event); void handleSubscriptionRequestDeclined(SubscriptionRequestEvent* event); void handleUIEvent(boost::shared_ptr event); void handleRosterSetError(ErrorPayload::ref error, boost::shared_ptr rosterPayload); void applyAllPresenceTo(const JID& jid); void handleEditProfileRequest(); void handleOnCapsChanged(const JID& jid); void handleSettingChanged(const std::string& settingPath); JID myJID_; XMPPRoster* xmppRoster_; MainWindowFactory* mainWindowFactory_; MainWindow* mainWindow_; Roster* roster_; OfflineRosterFilter* offlineFilter_; AvatarManager* avatarManager_; NickManager* nickManager_; NickResolver* nickResolver_; PresenceOracle* presenceOracle_; SubscriptionManager* subscriptionManager_; EventController* eventController_; RosterGroupExpandinessPersister* expandiness_; IQRouter* iqRouter_; SettingsProvider* settings_; UIEventStream* uiEventStream_; EntityCapsProvider* entityCapsManager_; FileTransferOverview* ftOverview_; boost::bsignals::scoped_connection changeStatusConnection_; boost::bsignals::scoped_connection signOutConnection_; boost::bsignals::scoped_connection uiEventConnection_; }; } swift-im-2.0+dev6/Swift/Controllers/Roster/AppearOffline.h0000644000175000017500000000113312227051773023424 0ustar kismithkismith/* * 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 #include namespace Swift { class RosterItem; class AppearOffline : public RosterItemOperation { public: AppearOffline() { } virtual void operator() (RosterItem* item) const { ContactRosterItem* contact = dynamic_cast(item); if (contact) { contact->clearPresence(); } } }; } swift-im-2.0+dev6/Swift/Controllers/Roster/Roster.h0000644000175000017500000000421112227051773022167 0ustar kismithkismith/* * 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 #include "Swiften/JID/JID.h" #include "Swift/Controllers/Roster/RosterItemOperation.h" #include "Swift/Controllers/Roster/RosterFilter.h" #include #include #include #include "Swiften/Base/boost_bsignals.h" #include namespace Swift { class RosterItem; class GroupRosterItem; class ContactRosterItem; class Roster { public: Roster(bool sortByStatus = true, bool fullJIDMapping = false); ~Roster(); void addContact(const JID& jid, const JID& displayJID, const std::string& name, const std::string& group, const std::string& avatarPath); void removeContact(const JID& jid); void removeContactFromGroup(const JID& jid, const std::string& group); void removeGroup(const std::string& group); void removeAll(); void applyOnItems(const RosterItemOperation& operation); void applyOnAllItems(const RosterItemOperation& operation); void applyOnItem(const RosterItemOperation& operation, const JID& jid); void addFilter(RosterFilter *filter) {filters_.push_back(filter);filterAll();}; void removeFilter(RosterFilter *filter); GroupRosterItem* getRoot(); std::vector getFilters() {return filters_;}; boost::signal onChildrenChanged; boost::signal onGroupAdded; boost::signal onDataChanged; GroupRosterItem* getGroup(const std::string& groupName); void setAvailableFeatures(const JID& jid, const std::set& features); private: void handleDataChanged(RosterItem* item); void handleChildrenChanged(GroupRosterItem* item); void filterGroup(GroupRosterItem* item); void filterContact(ContactRosterItem* contact, GroupRosterItem* group); void filterAll(); GroupRosterItem* root_; std::vector filters_; typedef std::map > ItemMap; ItemMap itemMap_; bool fullJIDMapping_; bool sortByStatus_; }; } swift-im-2.0+dev6/Swift/Controllers/Roster/ContactRosterItem.cpp0000644000175000017500000000701512227051773024662 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Swift/Controllers/Roster/ContactRosterItem.h" #include "Swift/Controllers/Roster/GroupRosterItem.h" #include namespace Swift { ContactRosterItem::ContactRosterItem(const JID& jid, const JID& displayJID, const std::string& name, GroupRosterItem* parent) : RosterItem(name, parent), jid_(jid), displayJID_(displayJID) { } ContactRosterItem::~ContactRosterItem() { } StatusShow::Type ContactRosterItem::getStatusShow() const { return shownPresence_ ? shownPresence_->getShow() : StatusShow::None; } StatusShow::Type ContactRosterItem::getSimplifiedStatusShow() const { switch (shownPresence_ ? shownPresence_->getShow() : StatusShow::None) { case StatusShow::Online: return StatusShow::Online; break; case StatusShow::Away: return StatusShow::Away; break; case StatusShow::XA: return StatusShow::Away; break; case StatusShow::FFC: return StatusShow::Online; break; case StatusShow::DND: return StatusShow::DND; break; case StatusShow::None: return StatusShow::None; break; } assert(false); return StatusShow::None; } std::string ContactRosterItem::getStatusText() const { return shownPresence_ ? shownPresence_->getStatus() : ""; } void ContactRosterItem::setAvatarPath(const std::string& path) { avatarPath_ = path; onDataChanged(); } const std::string& ContactRosterItem::getAvatarPath() const { return avatarPath_; } const JID& ContactRosterItem::getJID() const { return jid_; } void ContactRosterItem::setDisplayJID(const JID& jid) { displayJID_ = jid; } const JID& ContactRosterItem::getDisplayJID() const { return displayJID_; } typedef std::pair > StringPresencePair; void ContactRosterItem::calculateShownPresence() { shownPresence_ = offlinePresence_; foreach (StringPresencePair presencePair, presences_) { boost::shared_ptr presence = presencePair.second; if (!shownPresence_ || presence->getPriority() > shownPresence_->getPriority() || presence->getShow() < shownPresence_->getShow()) { shownPresence_ = presence; } } } void ContactRosterItem::clearPresence() { presences_.clear(); calculateShownPresence(); onDataChanged(); } void ContactRosterItem::applyPresence(const std::string& resource, boost::shared_ptr presence) { if (offlinePresence_) { offlinePresence_ = boost::shared_ptr(); } if (presence->getType() == Presence::Unavailable) { if (resource.empty()) { /* Unavailable from the bare JID means all resources are offline.*/ presences_.clear(); } else { if (presences_.find(resource) != presences_.end()) { presences_.erase(resource); } } if (presences_.empty()) { offlinePresence_ = presence; } } else { presences_[resource] = presence; } calculateShownPresence(); onDataChanged(); } const std::vector& ContactRosterItem::getGroups() const { return groups_; } /** Only used so a contact can know about the groups it's in*/ void ContactRosterItem::addGroup(const std::string& group) { groups_.push_back(group); } void ContactRosterItem::removeGroup(const std::string& group) { groups_.erase(std::remove(groups_.begin(), groups_.end(), group), groups_.end()); } void ContactRosterItem::setSupportedFeatures(const std::set& features) { features_ = features; } bool ContactRosterItem::supportsFeature(const Feature feature) const { return features_.find(feature) != features_.end(); } } swift-im-2.0+dev6/Swift/Controllers/Roster/UnitTest/0000755000175000017500000000000012227051773022321 5ustar kismithkismithswift-im-2.0+dev6/Swift/Controllers/Roster/UnitTest/LeastCommonSubsequenceTest.cpp0000644000175000017500000002755412227051773030326 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include using namespace Swift; struct IsBOrC { bool operator()(char c, char c2) const { CPPUNIT_ASSERT_EQUAL(c, c2); return c == 'b' || c == 'c'; } }; struct IsXOrY { bool operator()(char c, char c2) const { CPPUNIT_ASSERT_EQUAL(c, c2); return c == 'x' || c == 'y'; } }; struct IsArizonaOrNewJersey { bool operator()(const std::string& s, const std::string& s2) const { CPPUNIT_ASSERT_EQUAL(s, s2); return s == "Arizona" || s == "New Jersey"; } }; class LeastCommonSubsequenceTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(LeastCommonSubsequenceTest); CPPUNIT_TEST(testComputeLeastCommonSubsequenceMatrix_1); CPPUNIT_TEST(testComputeLeastCommonSubsequenceMatrix_2); CPPUNIT_TEST(testComputeLeastCommonSubsequenceMatrix_Sequence1Empty); CPPUNIT_TEST(testComputeLeastCommonSubsequenceMatrix_Sequence2Empty); CPPUNIT_TEST(testComputeLeastCommonSubsequenceMatrix_BothSequencesEmpty); CPPUNIT_TEST(testComputeLeastCommonSubsequenceMatrix_NoCommonSequence); CPPUNIT_TEST(testComputeLeastCommonSubsequenceMatrix_SameSequences); CPPUNIT_TEST(testComputeIndexDiff_1); CPPUNIT_TEST(testComputeIndexDiff_2); CPPUNIT_TEST(testComputeIndexDiff_Sequence1Empty); CPPUNIT_TEST(testComputeIndexDiff_Sequence2Empty); CPPUNIT_TEST(testComputeIndexDiff_BothSequencesEmpty); CPPUNIT_TEST(testComputeIndexDiff_NoCommonSequence); CPPUNIT_TEST(testComputeIndexDiff_SameSequences); CPPUNIT_TEST(testComputeIndexDiff_CommonPrefixAndSuffix); CPPUNIT_TEST_SUITE_END(); public: void testComputeLeastCommonSubsequenceMatrix_1() { std::vector x = boost::assign::list_of('x')('m')('j')('y')('a')('u')('z'); std::vector y = boost::assign::list_of('m')('z')('j')('a')('w')('x')('u'); std::vector result; Detail::computeLeastCommonSubsequenceMatrix::const_iterator, std::vector::const_iterator, int, std::equal_to >(x.begin(), x.end(), y.begin(), y.end(), result); std::vector expected = boost::assign::list_of (0)(0)(0)(0)(0)(0)(0)(0) (0)(0)(1)(1)(1)(1)(1)(1) (0)(0)(1)(1)(1)(1)(1)(2) (0)(0)(1)(2)(2)(2)(2)(2) (0)(0)(1)(2)(2)(3)(3)(3) (0)(0)(1)(2)(2)(3)(3)(3) (0)(1)(1)(2)(2)(3)(3)(3) (0)(1)(1)(2)(2)(3)(4)(4); CPPUNIT_ASSERT_EQUAL(expected, result); } void testComputeLeastCommonSubsequenceMatrix_2() { std::vector x = boost::assign::list_of('x')('x')('x')('m')('j')('y')('a')('u')('z'); std::vector y = boost::assign::list_of('m')('z')('j')('a')('w')('x')('u'); std::vector result; Detail::computeLeastCommonSubsequenceMatrix::const_iterator, std::vector::const_iterator, int, std::equal_to >(x.begin(), x.end(), y.begin(), y.end(), result); std::vector expected = boost::assign::list_of (0)(0)(0)(0)(0)(0)(0)(0)(0)(0) (0)(0)(0)(0)(1)(1)(1)(1)(1)(1) (0)(0)(0)(0)(1)(1)(1)(1)(1)(2) (0)(0)(0)(0)(1)(2)(2)(2)(2)(2) (0)(0)(0)(0)(1)(2)(2)(3)(3)(3) (0)(0)(0)(0)(1)(2)(2)(3)(3)(3) (0)(1)(1)(1)(1)(2)(2)(3)(3)(3) (0)(1)(1)(1)(1)(2)(2)(3)(4)(4); CPPUNIT_ASSERT_EQUAL(expected, result); } void testComputeLeastCommonSubsequenceMatrix_Sequence1Empty() { std::vector x; std::vector y = boost::assign::list_of('a')('b')('c'); std::vector result; Detail::computeLeastCommonSubsequenceMatrix::const_iterator, std::vector::const_iterator, int, std::equal_to >(x.begin(), x.end(), y.begin(), y.end(), result); std::vector expected = boost::assign::list_of (0) (0) (0) (0); CPPUNIT_ASSERT_EQUAL(expected, result); } void testComputeLeastCommonSubsequenceMatrix_Sequence2Empty() { std::vector x = boost::assign::list_of('a')('b')('c'); std::vector y; std::vector result; Detail::computeLeastCommonSubsequenceMatrix::const_iterator, std::vector::const_iterator, int, std::equal_to >(x.begin(), x.end(), y.begin(), y.end(), result); std::vector expected = boost::assign::list_of (0)(0)(0)(0); CPPUNIT_ASSERT_EQUAL(expected, result); } void testComputeLeastCommonSubsequenceMatrix_BothSequencesEmpty() { std::vector x; std::vector y; std::vector result; Detail::computeLeastCommonSubsequenceMatrix::const_iterator, std::vector::const_iterator, int, std::equal_to >(x.begin(), x.end(), y.begin(), y.end(), result); std::vector expected = boost::assign::list_of(0); CPPUNIT_ASSERT_EQUAL(expected, result); } void testComputeLeastCommonSubsequenceMatrix_NoCommonSequence() { std::vector x = boost::assign::list_of('a')('b')('c'); std::vector y = boost::assign::list_of('d')('e')('f')('g'); std::vector result; Detail::computeLeastCommonSubsequenceMatrix::const_iterator, std::vector::const_iterator, int, std::equal_to >(x.begin(), x.end(), y.begin(), y.end(), result); std::vector expected = boost::assign::list_of (0)(0)(0)(0) (0)(0)(0)(0) (0)(0)(0)(0) (0)(0)(0)(0) (0)(0)(0)(0); CPPUNIT_ASSERT_EQUAL(expected, result); } void testComputeLeastCommonSubsequenceMatrix_SameSequences() { std::vector x = boost::assign::list_of('a')('b')('c'); std::vector y = boost::assign::list_of('a')('b')('c'); std::vector result; Detail::computeLeastCommonSubsequenceMatrix::const_iterator, std::vector::const_iterator, int, std::equal_to >(x.begin(), x.end(), y.begin(), y.end(), result); std::vector expected = boost::assign::list_of (0)(0)(0)(0) (0)(1)(1)(1) (0)(1)(2)(2) (0)(1)(2)(3); CPPUNIT_ASSERT_EQUAL(expected, result); } void testComputeIndexDiff_1() { std::vector x = boost::assign::list_of("Arizona")("California")("Delaware")("New Jersey")("Washington"); std::vector y = boost::assign::list_of("Alaska")("Arizona")("California")("Georgia")("New Jersey")("Virginia"); std::vector updates; std::vector postUpdates; std::vector removes; std::vector inserts; computeIndexDiff, IsArizonaOrNewJersey >(x, y, updates, postUpdates, removes, inserts); std::vector expectedUpdates = boost::assign::list_of(3)(0); std::vector expectedPostUpdates = boost::assign::list_of(4)(1); std::vector expectedRemoves = boost::assign::list_of(4)(2); std::vector expectedInserts = boost::assign::list_of(5)(3)(0); CPPUNIT_ASSERT_EQUAL(expectedUpdates, updates); CPPUNIT_ASSERT_EQUAL(expectedPostUpdates, postUpdates); CPPUNIT_ASSERT_EQUAL(expectedRemoves, removes); CPPUNIT_ASSERT_EQUAL(expectedInserts, inserts); } void testComputeIndexDiff_2() { std::vector x = boost::assign::list_of('x')('y'); std::vector y = boost::assign::list_of('x'); std::vector updates; std::vector postUpdates; std::vector removes; std::vector inserts; computeIndexDiff, IsBOrC >(x, y, updates, postUpdates, removes, inserts); std::vector expectedRemoves = boost::assign::list_of(1); CPPUNIT_ASSERT(updates.empty()); CPPUNIT_ASSERT(postUpdates.empty()); CPPUNIT_ASSERT(inserts.empty()); CPPUNIT_ASSERT_EQUAL(expectedRemoves, removes); } void testComputeIndexDiff_Sequence1Empty() { std::vector x; std::vector y = boost::assign::list_of('a')('b')('c'); std::vector updates; std::vector postUpdates; std::vector removes; std::vector inserts; computeIndexDiff, IsBOrC >(x, y, updates, postUpdates, removes, inserts); std::vector expectedInserts = boost::assign::list_of(2)(1)(0); CPPUNIT_ASSERT(updates.empty()); CPPUNIT_ASSERT(postUpdates.empty()); CPPUNIT_ASSERT(removes.empty()); CPPUNIT_ASSERT_EQUAL(expectedInserts, inserts); } void testComputeIndexDiff_Sequence2Empty() { std::vector x = boost::assign::list_of('a')('b')('c'); std::vector y; std::vector updates; std::vector postUpdates; std::vector removes; std::vector inserts; computeIndexDiff, IsBOrC >(x, y, updates, postUpdates, removes, inserts); std::vector expectedRemoves = boost::assign::list_of(2)(1)(0); CPPUNIT_ASSERT(updates.empty()); CPPUNIT_ASSERT(postUpdates.empty()); CPPUNIT_ASSERT_EQUAL(expectedRemoves, removes); CPPUNIT_ASSERT(inserts.empty()); } void testComputeIndexDiff_BothSequencesEmpty() { std::vector x; std::vector y; std::vector updates; std::vector postUpdates; std::vector removes; std::vector inserts; computeIndexDiff, IsBOrC >(x, y, updates, postUpdates, removes, inserts); CPPUNIT_ASSERT(updates.empty()); CPPUNIT_ASSERT(postUpdates.empty()); CPPUNIT_ASSERT(removes.empty()); CPPUNIT_ASSERT(inserts.empty()); } void testComputeIndexDiff_NoCommonSequence() { std::vector x = boost::assign::list_of('a')('b')('c'); std::vector y = boost::assign::list_of('d')('e')('f')('g'); std::vector updates; std::vector postUpdates; std::vector removes; std::vector inserts; computeIndexDiff, IsBOrC >(x, y, updates, postUpdates, removes, inserts); std::vector expectedRemoves = boost::assign::list_of(2)(1)(0); std::vector expectedInserts = boost::assign::list_of(3)(2)(1)(0); CPPUNIT_ASSERT(updates.empty()); CPPUNIT_ASSERT(postUpdates.empty()); CPPUNIT_ASSERT_EQUAL(expectedRemoves, removes); CPPUNIT_ASSERT_EQUAL(expectedInserts, inserts); } void testComputeIndexDiff_SameSequences() { std::vector x = boost::assign::list_of('a')('b')('c'); std::vector y = boost::assign::list_of('a')('b')('c'); std::vector updates; std::vector postUpdates; std::vector removes; std::vector inserts; computeIndexDiff, IsBOrC >(x, y, updates, postUpdates, removes, inserts); std::vector expectedUpdates = boost::assign::list_of(1)(2); CPPUNIT_ASSERT_EQUAL(expectedUpdates, updates); CPPUNIT_ASSERT_EQUAL(expectedUpdates, postUpdates); CPPUNIT_ASSERT(removes.empty()); CPPUNIT_ASSERT(inserts.empty()); } void testComputeIndexDiff_CommonPrefixAndSuffix() { std::vector x = boost::assign::list_of('x')('x')('x')('x')('a')('b')('c')('d')('e')('y')('y')('y'); std::vector y = boost::assign::list_of('x')('x')('x')('x')('e')('a')('b')('f')('d')('g')('y')('y')('y'); std::vector updates; std::vector postUpdates; std::vector removes; std::vector inserts; computeIndexDiff, IsXOrY >(x, y, updates, postUpdates, removes, inserts); std::vector expectedUpdates = boost::assign::list_of(0)(1)(2)(3)(11)(10)(9); std::vector expectedPostUpdates = boost::assign::list_of(0)(1)(2)(3)(12)(11)(10); std::vector expectedRemoves = boost::assign::list_of(8)(6); std::vector expectedInserts = boost::assign::list_of(9)(7)(4); CPPUNIT_ASSERT_EQUAL(expectedUpdates, updates); CPPUNIT_ASSERT_EQUAL(expectedPostUpdates, postUpdates); CPPUNIT_ASSERT_EQUAL(expectedRemoves, removes); CPPUNIT_ASSERT_EQUAL(expectedInserts, inserts); } }; CPPUNIT_TEST_SUITE_REGISTRATION(LeastCommonSubsequenceTest); swift-im-2.0+dev6/Swift/Controllers/Roster/UnitTest/RosterTest.cpp0000644000175000017500000001405112227051773025144 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include "Swift/Controllers/Roster/Roster.h" #include "Swift/Controllers/Roster/GroupRosterItem.h" #include "Swift/Controllers/Roster/SetPresence.h" using namespace Swift; class RosterTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(RosterTest); CPPUNIT_TEST(testGetGroup); CPPUNIT_TEST(testRemoveContact); CPPUNIT_TEST(testRemoveSecondContact); CPPUNIT_TEST(testRemoveSecondContactSameBare); CPPUNIT_TEST(testApplyPresenceLikeMUC); CPPUNIT_TEST(testReSortLikeMUC); CPPUNIT_TEST_SUITE_END(); public: void setUp() { jid1_ = JID("a@b.c"); jid2_ = JID("b@c.d"); jid3_ = JID("c@d.e"); roster_ = new Roster(); } void tearDown() { delete roster_; } void testGetGroup() { roster_->addContact(jid1_, JID(), "Bert", "group1", ""); roster_->addContact(jid2_, JID(), "Ernie", "group2", ""); roster_->addContact(jid3_, JID(), "Cookie", "group1", ""); CPPUNIT_ASSERT_EQUAL(2, static_cast(roster_->getRoot()->getChildren().size())); CPPUNIT_ASSERT_EQUAL(std::string("group1"), roster_->getRoot()->getChildren()[0]->getDisplayName()); CPPUNIT_ASSERT_EQUAL(std::string("group2"), roster_->getRoot()->getChildren()[1]->getDisplayName()); CPPUNIT_ASSERT_EQUAL(std::string("Bert"), static_cast(roster_->getRoot()->getChildren()[0])->getChildren()[0]->getDisplayName()); CPPUNIT_ASSERT_EQUAL(std::string("Cookie"), static_cast(roster_->getRoot()->getChildren()[0])->getChildren()[1]->getDisplayName()); CPPUNIT_ASSERT_EQUAL(std::string("Ernie"), static_cast(roster_->getRoot()->getChildren()[1])->getChildren()[0]->getDisplayName()); } void testRemoveContact() { roster_->addContact(jid1_, jid1_, "Bert", "group1", ""); CPPUNIT_ASSERT_EQUAL(std::string("Bert"), static_cast(roster_->getRoot()->getChildren()[0])->getChildren()[0]->getDisplayName()); roster_->removeContact(jid1_); CPPUNIT_ASSERT_EQUAL(0, static_cast(static_cast(roster_->getRoot()->getChildren()[0])->getChildren().size())); } void testRemoveSecondContact() { roster_->addContact(jid1_, jid1_, "Bert", "group1", ""); roster_->addContact(jid2_, jid2_, "Cookie", "group1", ""); CPPUNIT_ASSERT_EQUAL(std::string("Cookie"), static_cast(roster_->getRoot()->getChildren()[0])->getChildren()[1]->getDisplayName()); roster_->removeContact(jid2_); CPPUNIT_ASSERT_EQUAL(1, static_cast(static_cast(roster_->getRoot()->getChildren()[0])->getChildren().size())); CPPUNIT_ASSERT_EQUAL(std::string("Bert"), static_cast(roster_->getRoot()->getChildren()[0])->getChildren()[0]->getDisplayName()); } void testRemoveSecondContactSameBare() { JID jid4a("a@b/c"); JID jid4b("a@b/d"); roster_->addContact(jid4a, JID(), "Bert", "group1", ""); roster_->addContact(jid4b, JID(), "Cookie", "group1", ""); CPPUNIT_ASSERT_EQUAL(std::string("Cookie"), static_cast(roster_->getRoot()->getChildren()[0])->getChildren()[1]->getDisplayName()); roster_->removeContact(jid4b); CPPUNIT_ASSERT_EQUAL(1, static_cast(static_cast(roster_->getRoot()->getChildren()[0])->getChildren().size())); CPPUNIT_ASSERT_EQUAL(std::string("Bert"), static_cast(roster_->getRoot()->getChildren()[0])->getChildren()[0]->getDisplayName()); } void testApplyPresenceLikeMUC() { JID jid4a("a@b/c"); JID jid4b("a@b/d"); JID jid4c("a@b/e"); roster_->addContact(jid4a, JID(), "Bird", "group1", ""); roster_->addContact(jid4b, JID(), "Cookie", "group1", ""); roster_->removeContact(jid4b); roster_->addContact(jid4c, JID(), "Bert", "group1", ""); roster_->addContact(jid4b, JID(), "Ernie", "group1", ""); boost::shared_ptr presence(new Presence()); presence->setShow(StatusShow::DND); presence->setFrom(jid4a); roster_->applyOnItems(SetPresence(presence, JID::WithResource)); presence->setFrom(jid4b); roster_->applyOnItems(SetPresence(presence, JID::WithResource)); presence->setFrom(jid4c); roster_->applyOnItems(SetPresence(presence, JID::WithResource)); presence = boost::make_shared(); presence->setFrom(jid4b); presence->setShow(StatusShow::Online); roster_->applyOnItems(SetPresence(presence, JID::WithResource)); std::vector children = static_cast(roster_->getRoot()->getDisplayedChildren()[0])->getDisplayedChildren(); CPPUNIT_ASSERT_EQUAL(3, static_cast(children.size())); /* Check order */ CPPUNIT_ASSERT_EQUAL(std::string("Ernie"), children[0]->getDisplayName()); CPPUNIT_ASSERT_EQUAL(std::string("Bert"), children[1]->getDisplayName()); CPPUNIT_ASSERT_EQUAL(std::string("Bird"), children[2]->getDisplayName()); presence = boost::make_shared(); presence->setFrom(jid4c); presence->setType(Presence::Unavailable); roster_->removeContact(jid4c); roster_->applyOnItems(SetPresence(presence, JID::WithResource)); } void testReSortLikeMUC() { JID jid4a("a@b/c"); JID jid4b("a@b/d"); JID jid4c("a@b/e"); roster_->addContact(jid4a, JID(), "Bird", "group1", ""); roster_->addContact(jid4b, JID(), "Cookie", "group2", ""); roster_->addContact(jid4b, JID(), "Ernie", "group1", ""); roster_->getGroup("group1")->setManualSort("2"); roster_->getGroup("group2")->setManualSort("1"); GroupRosterItem* root = roster_->getRoot(); const std::vector kids = root->getDisplayedChildren(); CPPUNIT_ASSERT_EQUAL(static_cast(2), kids.size()); CPPUNIT_ASSERT_EQUAL(std::string("group2"), kids[0]->getDisplayName()); CPPUNIT_ASSERT_EQUAL(std::string("group1"), kids[1]->getDisplayName()); } private: Roster *roster_; JID jid1_; JID jid2_; JID jid3_; }; CPPUNIT_TEST_SUITE_REGISTRATION(RosterTest); swift-im-2.0+dev6/Swift/Controllers/Roster/UnitTest/TableRosterTest.cpp0000644000175000017500000000566512227051773026127 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include std::ostream& operator<<(std::ostream& os, const Swift::TableRoster::Index& i) { os << "(" << i.section << ", " << i.row << ")"; return os; } #include #include #include #include #include #include #include #include using namespace Swift; class TableRosterTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TableRosterTest); CPPUNIT_TEST(testAddContact_EmptyRoster); CPPUNIT_TEST_SUITE_END(); public: void setUp() { timerFactory = new DummyTimerFactory(); roster = new Roster(); jid1 = JID("jid1@example.com"); jid2 = JID("jid2@example.com"); } void tearDown() { delete roster; delete timerFactory; } void testAddContact_EmptyRoster() { /* boost::shared_ptr tableRoster(createTestling()); addContact(jid1, "1", "group1"); CPPUNIT_ASSERT_EQUAL(4, static_cast(events.size())); CPPUNIT_ASSERT(boost::get(&events[0])); CPPUNIT_ASSERT(boost::get(&events[1])); CPPUNIT_ASSERT_EQUAL(1, static_cast(boost::get(events[1]).sections.size())); CPPUNIT_ASSERT_EQUAL(0, static_cast(boost::get(events[1]).sections[0])); CPPUNIT_ASSERT(boost::get(&events[2])); CPPUNIT_ASSERT_EQUAL(1, static_cast(boost::get(events[2]).rows.size())); CPPUNIT_ASSERT_EQUAL(TableRoster::Index(0, 0), boost::get(events[2]).rows[0]); CPPUNIT_ASSERT(boost::get(&events[3])); CPPUNIT_ASSERT_EQUAL(1, static_cast(tableRoster->getNumberOfSections())); CPPUNIT_ASSERT_EQUAL(std::string("group1"), tableRoster->getSectionTitle(0)); CPPUNIT_ASSERT_EQUAL(1, static_cast(tableRoster->getNumberOfRowsInSection(0))); CPPUNIT_ASSERT_EQUAL(jid1, tableRoster->getItem(TableRoster::Index(0, 0)).jid); */ } private: void addContact(const JID& jid, const std::string& name, const std::string& group) { roster->addContact(jid, JID(), name, group, ""); } TableRoster* createTestling() { TableRoster* result = new TableRoster(roster, timerFactory, 10); result->onUpdate.connect(boost::bind(&TableRosterTest::handleUpdate, this, _1)); return result; } void handleUpdate(const TableRoster::Update& update) { updates.push_back(update); } private: DummyTimerFactory* timerFactory; Roster* roster; JID jid1; JID jid2; std::vector updates; }; CPPUNIT_TEST_SUITE_REGISTRATION(TableRosterTest); swift-im-2.0+dev6/Swift/Controllers/Roster/UnitTest/RosterControllerTest.cpp0000644000175000017500000003500312227051773027210 0ustar kismithkismith /* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include "Swift/Controllers/Roster/RosterController.h" #include "Swift/Controllers/UnitTest/MockMainWindowFactory.h" // #include "Swiften/Elements/Payload.h" // #include "Swiften/Elements/RosterItemPayload.h" // #include "Swiften/Elements/RosterPayload.h" #include "Swiften/Queries/DummyIQChannel.h" #include "Swiften/Client/DummyStanzaChannel.h" #include "Swiften/Queries/IQRouter.h" #include "Swiften/Roster/XMPPRosterImpl.h" #include "Swift/Controllers/Roster/Roster.h" #include "Swift/Controllers/Roster/GroupRosterItem.h" #include "Swift/Controllers/Roster/ContactRosterItem.h" #include "Swift/Controllers/Settings/DummySettingsProvider.h" #include "Swiften/Avatars/NullAvatarManager.h" #include "Swift/Controllers/XMPPEvents/EventController.h" #include "Swiften/Presence/PresenceOracle.h" #include "Swiften/Presence/SubscriptionManager.h" #include "Swiften/Client/NickResolver.h" #include "Swift/Controllers/UIEvents/UIEventStream.h" #include "Swift/Controllers/UIEvents/RenameRosterItemUIEvent.h" #include "Swiften/MUC/MUCRegistry.h" #include #include #include #include #include #include #include using namespace Swift; #define CHILDREN mainWindow_->roster->getRoot()->getChildren() class DummyCapsProvider : public CapsProvider { DiscoInfo::ref getCaps(const std::string&) const {return DiscoInfo::ref(new DiscoInfo());} }; class RosterControllerTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(RosterControllerTest); CPPUNIT_TEST(testAdd); CPPUNIT_TEST(testAddSubscription); CPPUNIT_TEST(testReceiveRename); CPPUNIT_TEST(testReceiveRegroup); CPPUNIT_TEST(testSendRename); CPPUNIT_TEST(testPresence); CPPUNIT_TEST(testHighestPresence); CPPUNIT_TEST(testNotHighestPresence); CPPUNIT_TEST(testUnavailablePresence); CPPUNIT_TEST_SUITE_END(); public: void setUp() { jid_ = JID("testjid@swift.im/swift"); xmppRoster_ = new XMPPRosterImpl(); avatarManager_ = new NullAvatarManager(); mainWindowFactory_ = new MockMainWindowFactory(); mucRegistry_ = new MUCRegistry(); nickResolver_ = new NickResolver(jid_.toBare(), xmppRoster_, NULL, mucRegistry_); channel_ = new DummyIQChannel(); router_ = new IQRouter(channel_); stanzaChannel_ = new DummyStanzaChannel(); presenceOracle_ = new PresenceOracle(stanzaChannel_); subscriptionManager_ = new SubscriptionManager(stanzaChannel_); eventController_ = new EventController(); uiEventStream_ = new UIEventStream(); settings_ = new DummySettingsProvider(); nickManager_ = new DummyNickManager(); capsProvider_ = new DummyCapsProvider(); entityCapsManager_ = new EntityCapsManager(capsProvider_, stanzaChannel_); jingleSessionManager_ = new JingleSessionManager(router_); ftManager_ = new DummyFileTransferManager(); ftOverview_ = new FileTransferOverview(ftManager_); rosterController_ = new RosterController(jid_, xmppRoster_, avatarManager_, mainWindowFactory_, nickManager_, nickResolver_, presenceOracle_, subscriptionManager_, eventController_, uiEventStream_, router_, settings_, entityCapsManager_, ftOverview_); mainWindow_ = mainWindowFactory_->last; }; void tearDown() { delete rosterController_; delete ftManager_; delete jingleSessionManager_; delete nickManager_; delete nickResolver_; delete mucRegistry_; delete mainWindowFactory_; delete avatarManager_; delete router_; delete channel_; delete eventController_; delete subscriptionManager_; delete presenceOracle_; delete stanzaChannel_; delete uiEventStream_; delete settings_; delete xmppRoster_; }; GroupRosterItem* groupChild(size_t i) { return dynamic_cast(CHILDREN[i]); } JID withResource(const JID& jid, const std::string& resource) { return JID(jid.toBare().toString() + "/" + resource); } void testPresence() { std::vector groups; groups.push_back("testGroup1"); groups.push_back("testGroup2"); JID from("test@testdomain.com"); xmppRoster_->addContact(from, "name", groups, RosterItemPayload::Both); Presence::ref presence(new Presence()); presence->setFrom(withResource(from, "bob")); presence->setPriority(2); presence->setStatus("So totally here"); stanzaChannel_->onPresenceReceived(presence); ContactRosterItem* item = dynamic_cast(dynamic_cast(CHILDREN[0])->getChildren()[0]); CPPUNIT_ASSERT(item); CPPUNIT_ASSERT_EQUAL(presence->getStatus(), item->getStatusText()); ContactRosterItem* item2 = dynamic_cast(dynamic_cast(CHILDREN[1])->getChildren()[0]); CPPUNIT_ASSERT(item2); CPPUNIT_ASSERT_EQUAL(presence->getStatus(), item2->getStatusText()); }; void testHighestPresence() { std::vector groups; groups.push_back("testGroup1"); JID from("test@testdomain.com"); xmppRoster_->addContact(from, "name", groups, RosterItemPayload::Both); Presence::ref lowPresence(new Presence()); lowPresence->setFrom(withResource(from, "bob")); lowPresence->setPriority(2); lowPresence->setStatus("Not here"); Presence::ref highPresence(new Presence()); highPresence->setFrom(withResource(from, "bert")); highPresence->setPriority(10); highPresence->setStatus("So totally here"); stanzaChannel_->onPresenceReceived(lowPresence); stanzaChannel_->onPresenceReceived(highPresence); ContactRosterItem* item = dynamic_cast(dynamic_cast(CHILDREN[0])->getChildren()[0]); CPPUNIT_ASSERT(item); CPPUNIT_ASSERT_EQUAL(highPresence->getStatus(), item->getStatusText()); }; void testNotHighestPresence() { std::vector groups; groups.push_back("testGroup1"); JID from("test@testdomain.com"); xmppRoster_->addContact(from, "name", groups, RosterItemPayload::Both); Presence::ref lowPresence(new Presence()); lowPresence->setFrom(withResource(from, "bob")); lowPresence->setPriority(2); lowPresence->setStatus("Not here"); Presence::ref highPresence(new Presence()); highPresence->setFrom(withResource(from, "bert")); highPresence->setPriority(10); highPresence->setStatus("So totally here"); stanzaChannel_->onPresenceReceived(highPresence); stanzaChannel_->onPresenceReceived(lowPresence); ContactRosterItem* item = dynamic_cast(dynamic_cast(CHILDREN[0])->getChildren()[0]); CPPUNIT_ASSERT(item); CPPUNIT_ASSERT_EQUAL(highPresence->getStatus(), item->getStatusText()); }; void testUnavailablePresence() { std::vector groups; groups.push_back("testGroup1"); JID from("test@testdomain.com"); xmppRoster_->addContact(from, "name", groups, RosterItemPayload::Both); Presence::ref lowPresence(new Presence()); lowPresence->setFrom(withResource(from, "bob")); lowPresence->setPriority(2); lowPresence->setStatus("Not here"); Presence::ref highPresence(new Presence()); highPresence->setFrom(withResource(from, "bert")); highPresence->setPriority(10); highPresence->setStatus("So totally here"); Presence::ref highPresenceOffline(new Presence()); highPresenceOffline->setFrom(withResource(from, "bert")); highPresenceOffline->setType(Presence::Unavailable); Presence::ref lowPresenceOffline(new Presence()); lowPresenceOffline->setFrom(withResource(from, "bob")); lowPresenceOffline->setStatus("Signing out"); lowPresenceOffline->setType(Presence::Unavailable); stanzaChannel_->onPresenceReceived(lowPresence); stanzaChannel_->onPresenceReceived(highPresence); stanzaChannel_->onPresenceReceived(highPresenceOffline); ContactRosterItem* item = dynamic_cast(dynamic_cast(CHILDREN[0])->getChildren()[0]); CPPUNIT_ASSERT(item); /* A verification that if the test fails, it's the RosterController, not the PresenceOracle. */ Presence::ref high = presenceOracle_->getHighestPriorityPresence(from); CPPUNIT_ASSERT_EQUAL(Presence::Available, high->getType()); CPPUNIT_ASSERT_EQUAL(lowPresence->getStatus(), high->getStatus()); CPPUNIT_ASSERT_EQUAL(StatusShow::Online, item->getStatusShow()); CPPUNIT_ASSERT_EQUAL(lowPresence->getStatus(), item->getStatusText()); stanzaChannel_->onPresenceReceived(lowPresenceOffline); item = dynamic_cast(dynamic_cast(CHILDREN[0])->getChildren()[0]); CPPUNIT_ASSERT(item); /* A verification that if the test fails, it's the RosterController, not the PresenceOracle. */ high = presenceOracle_->getHighestPriorityPresence(from); CPPUNIT_ASSERT_EQUAL(Presence::Unavailable, high->getType()); CPPUNIT_ASSERT_EQUAL(lowPresenceOffline->getStatus(), high->getStatus()); CPPUNIT_ASSERT_EQUAL(StatusShow::None, item->getStatusShow()); CPPUNIT_ASSERT_EQUAL(lowPresenceOffline->getStatus(), item->getStatusText()); }; void testAdd() { std::vector groups; groups.push_back("testGroup1"); groups.push_back("testGroup2"); xmppRoster_->addContact(JID("test@testdomain.com/bob"), "name", groups, RosterItemPayload::Both); CPPUNIT_ASSERT_EQUAL(2, static_cast(CHILDREN.size())); //CPPUNIT_ASSERT_EQUAL(std::string("Bob"), xmppRoster_->getNameForJID(JID("foo@bar.com"))); }; void testAddSubscription() { std::vector groups; JID jid("test@testdomain.com"); xmppRoster_->addContact(jid, "name", groups, RosterItemPayload::None); CPPUNIT_ASSERT_EQUAL(1, static_cast(CHILDREN.size())); CPPUNIT_ASSERT_EQUAL(1, static_cast(groupChild(0)->getChildren().size())); xmppRoster_->addContact(jid, "name", groups, RosterItemPayload::To); CPPUNIT_ASSERT_EQUAL(1, static_cast(CHILDREN.size())); CPPUNIT_ASSERT_EQUAL(1, static_cast(groupChild(0)->getChildren().size())); xmppRoster_->addContact(jid, "name", groups, RosterItemPayload::Both); CPPUNIT_ASSERT_EQUAL(1, static_cast(CHILDREN.size())); CPPUNIT_ASSERT_EQUAL(1, static_cast(groupChild(0)->getChildren().size())); }; void testReceiveRename() { std::vector groups; JID jid("test@testdomain.com"); xmppRoster_->addContact(jid, "name", groups, RosterItemPayload::Both); CPPUNIT_ASSERT_EQUAL(1, static_cast(CHILDREN.size())); CPPUNIT_ASSERT_EQUAL(1, static_cast(groupChild(0)->getChildren().size())); CPPUNIT_ASSERT_EQUAL(std::string("name"), groupChild(0)->getChildren()[0]->getDisplayName()); xmppRoster_->addContact(jid, "NewName", groups, RosterItemPayload::Both); CPPUNIT_ASSERT_EQUAL(1, static_cast(CHILDREN.size())); CPPUNIT_ASSERT_EQUAL(1, static_cast(groupChild(0)->getChildren().size())); CPPUNIT_ASSERT_EQUAL(std::string("NewName"), groupChild(0)->getChildren()[0]->getDisplayName()); }; void testReceiveRegroup() { std::vector oldGroups; std::vector newGroups; newGroups.push_back("A Group"); std::vector newestGroups; newestGroups.push_back("Best Group"); JID jid("test@testdomain.com"); xmppRoster_->addContact(jid, "", oldGroups, RosterItemPayload::Both); CPPUNIT_ASSERT_EQUAL(1, static_cast(CHILDREN.size())); CPPUNIT_ASSERT_EQUAL(1, static_cast(groupChild(0)->getChildren().size())); CPPUNIT_ASSERT_EQUAL(jid.toString(), groupChild(0)->getChildren()[0]->getDisplayName()); xmppRoster_->addContact(jid, "new name", newGroups, RosterItemPayload::Both); CPPUNIT_ASSERT_EQUAL(1, static_cast(CHILDREN.size())); CPPUNIT_ASSERT_EQUAL(1, static_cast(groupChild(0)->getChildren().size())); CPPUNIT_ASSERT_EQUAL(std::string("new name"), groupChild(0)->getChildren()[0]->getDisplayName()); CPPUNIT_ASSERT_EQUAL(std::string("A Group"), groupChild(0)->getDisplayName()); xmppRoster_->addContact(jid, "new name", newestGroups, RosterItemPayload::Both); CPPUNIT_ASSERT_EQUAL(1, static_cast(CHILDREN.size())); CPPUNIT_ASSERT_EQUAL(1, static_cast(groupChild(0)->getChildren().size())); CPPUNIT_ASSERT_EQUAL(std::string("new name"), groupChild(0)->getChildren()[0]->getDisplayName()); CPPUNIT_ASSERT_EQUAL(std::string("Best Group"), groupChild(0)->getDisplayName()); }; void testSendRename() { JID jid("testling@wonderland.lit"); std::vector groups; groups.push_back("Friends"); groups.push_back("Enemies"); xmppRoster_->addContact(jid, "Bob", groups, RosterItemPayload::From); CPPUNIT_ASSERT_EQUAL(groups.size(), xmppRoster_->getGroupsForJID(jid).size()); uiEventStream_->send(boost::shared_ptr(new RenameRosterItemUIEvent(jid, "Robert"))); CPPUNIT_ASSERT_EQUAL(static_cast(1), channel_->iqs_.size()); CPPUNIT_ASSERT_EQUAL(IQ::Set, channel_->iqs_[0]->getType()); boost::shared_ptr payload = channel_->iqs_[0]->getPayload(); CPPUNIT_ASSERT_EQUAL(static_cast(1), payload->getItems().size()); RosterItemPayload item = payload->getItems()[0]; CPPUNIT_ASSERT_EQUAL(jid, item.getJID()); CPPUNIT_ASSERT_EQUAL(std::string("Robert"), item.getName()); CPPUNIT_ASSERT_EQUAL(groups.size(), item.getGroups().size()); assertVectorsEqual(groups, item.getGroups(), __LINE__); } void assertVectorsEqual(const std::vector& v1, const std::vector& v2, int line) { foreach (const std::string& entry, v1) { if (std::find(v2.begin(), v2.end(), entry) == v2.end()) { std::stringstream stream; stream << "Couldn't find " << entry << " in v2 (line " << line << ")"; CPPUNIT_FAIL(stream.str()); } } } private: JID jid_; XMPPRosterImpl* xmppRoster_; MUCRegistry* mucRegistry_; AvatarManager* avatarManager_; MockMainWindowFactory* mainWindowFactory_; NickManager* nickManager_; NickResolver* nickResolver_; RosterController* rosterController_; DummyIQChannel* channel_; DummyStanzaChannel* stanzaChannel_; IQRouter* router_; PresenceOracle* presenceOracle_; SubscriptionManager* subscriptionManager_; EventController* eventController_; UIEventStream* uiEventStream_; MockMainWindow* mainWindow_; DummySettingsProvider* settings_; DummyCapsProvider* capsProvider_; EntityCapsManager* entityCapsManager_; JingleSessionManager* jingleSessionManager_; FileTransferManager* ftManager_; FileTransferOverview* ftOverview_; }; CPPUNIT_TEST_SUITE_REGISTRATION(RosterControllerTest); swift-im-2.0+dev6/Swift/Controllers/Roster/SetAvatar.h0000644000175000017500000000170712227051773022612 0ustar kismithkismith/* * 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 "Swiften/Elements/Presence.h" #include "Swiften/JID/JID.h" #include "Swift/Controllers/Roster/RosterItemOperation.h" #include "Swift/Controllers/Roster/ContactRosterItem.h" namespace Swift { class RosterItem; class SetAvatar : public RosterItemOperation { public: SetAvatar(const JID& jid, const std::string& path, JID::CompareType compareType = JID::WithoutResource) : RosterItemOperation(true, jid), jid_(jid), path_(path), compareType_(compareType) { } virtual void operator() (RosterItem* item) const { ContactRosterItem* contact = dynamic_cast(item); if (contact && contact->getJID().equals(jid_, compareType_)) { contact->setAvatarPath(path_); } } private: JID jid_; std::string path_; JID::CompareType compareType_; }; } swift-im-2.0+dev6/Swift/Controllers/Roster/GroupRosterItem.cpp0000644000175000017500000001510112227051773024356 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Swift/Controllers/Roster/GroupRosterItem.h" #include //#include #include namespace Swift { GroupRosterItem::GroupRosterItem(const std::string& name, GroupRosterItem* parent, bool sortByStatus) : RosterItem(name, parent), sortByStatus_(sortByStatus), manualSort_(false) { expanded_ = true; } GroupRosterItem::~GroupRosterItem() { } void GroupRosterItem::setManualSort(const std::string& manualSortValue) { manualSort_ = true; bool changed = manualSortValue_ != manualSortValue; manualSortValue_ = manualSortValue; if (changed) { onChildrenChanged(); onDataChanged(); } } const std::string& GroupRosterItem::getSortableDisplayName() const { return manualSort_ ? manualSortValue_ : RosterItem::getSortableDisplayName(); } bool GroupRosterItem::isExpanded() const { return expanded_; } /** This has no effect, and is only used by the UI. If reTransmit is specified, dataChanged will be emitted on a change - This may be undesireable if called from the UI, so you can use reTransmit=false to avoid a loop in this case. */ void GroupRosterItem::setExpanded(bool expanded) { bool old = expanded_; expanded_ = expanded; if (expanded != old) { onExpandedChanged(expanded); } } const std::vector& GroupRosterItem::getChildren() const { return children_; } const std::vector& GroupRosterItem::getDisplayedChildren() const { return displayedChildren_; } void GroupRosterItem::addChild(RosterItem* item) { children_.push_back(item); GroupRosterItem* group = dynamic_cast(item); if (group) { group->onChildrenChanged.connect(boost::bind(&GroupRosterItem::handleChildrenChanged, this, group)); } else { item->onDataChanged.connect(boost::bind(&GroupRosterItem::handleDataChanged, this, item)); } onChildrenChanged(); onDataChanged(); } /** * Does not emit a changed signal. */ void GroupRosterItem::removeAll() { std::vector::iterator it = children_.begin(); displayedChildren_.clear(); while (it != children_.end()) { ContactRosterItem* contact = dynamic_cast(*it); if (contact) { delete contact; } else { GroupRosterItem* group = dynamic_cast(*it); if (group) { group->removeAll(); delete group; } } ++it; } children_.clear(); } /** * Returns the removed item - but only if it's the only one, otherwise * the return result is undefined. */ ContactRosterItem* GroupRosterItem::removeChild(const JID& jid) { std::vector::iterator it = children_.begin(); ContactRosterItem* removed = NULL; while (it != children_.end()) { ContactRosterItem* contact = dynamic_cast(*it); if (contact && contact->getJID() == jid) { displayedChildren_.erase(std::remove(displayedChildren_.begin(), displayedChildren_.end(), contact), displayedChildren_.end()); removed = contact; delete contact; it = children_.erase(it); continue; } GroupRosterItem* group = dynamic_cast(*it); if (group) { ContactRosterItem* groupRemoved = group->removeChild(jid); if (groupRemoved) { removed = groupRemoved; } } ++it; } onChildrenChanged(); onDataChanged(); return removed; } GroupRosterItem* GroupRosterItem::removeGroupChild(const std::string& groupName) { std::vector::iterator it = children_.begin(); GroupRosterItem* removed = NULL; while (it != children_.end()) { GroupRosterItem* group = dynamic_cast(*it); if (group && group->getDisplayName() == groupName) { displayedChildren_.erase(std::remove(displayedChildren_.begin(), displayedChildren_.end(), group), displayedChildren_.end()); removed = group; delete group; it = children_.erase(it); continue; } ++it; } onChildrenChanged(); onDataChanged(); return removed; } /** * Returns false if the list didn't need a resort */ bool GroupRosterItem::sortDisplayed() { /* Not doing this until we import boost::algorithm*/ // if (boost::is_sorted(displayedChildren_begin(), displayedChildren_.end(), itemLessThan)) { // return false; // } //Sholudn't need stable_sort here std::sort(displayedChildren_.begin(), displayedChildren_.end(), sortByStatus_? itemLessThanWithStatus : itemLessThanWithoutStatus); return true; } bool GroupRosterItem::itemLessThanWithoutStatus(const RosterItem* left, const RosterItem* right) { return left->getSortableDisplayName() < right->getSortableDisplayName(); } bool GroupRosterItem::itemLessThanWithStatus(const RosterItem* left, const RosterItem* right) { const ContactRosterItem* leftContact = dynamic_cast(left); const ContactRosterItem* rightContact = dynamic_cast(right); if (leftContact) { if (!rightContact) { return false; } StatusShow::Type leftType = leftContact->getSimplifiedStatusShow(); StatusShow::Type rightType = rightContact->getSimplifiedStatusShow(); if (leftType == rightType) { return left->getSortableDisplayName() < right->getSortableDisplayName(); } else { return leftType < rightType; } } else { if (rightContact) { return true; } return left->getSortableDisplayName() < right->getSortableDisplayName(); } } void GroupRosterItem::setDisplayed(RosterItem* item, bool displayed) { bool found = false; for (size_t i = 0; i < displayedChildren_.size(); i++) { if (displayedChildren_[i] == item) { found = true; } } if (found == displayed) { return; } if (displayed) { displayedChildren_.push_back(item); sortDisplayed(); } else { displayedChildren_.erase(std::remove(displayedChildren_.begin(), displayedChildren_.end(), item), displayedChildren_.end()); } onChildrenChanged(); onDataChanged(); } void GroupRosterItem::handleDataChanged(RosterItem* /*item*/) { if (sortDisplayed()) { onChildrenChanged(); } } void GroupRosterItem::handleChildrenChanged(GroupRosterItem* group) { size_t oldSize = getDisplayedChildren().size(); if (group->getDisplayedChildren().size() > 0) { bool found = false; for (size_t i = 0; i < displayedChildren_.size(); i++) { if (displayedChildren_[i] == group) { found = true; } } if (!found) { displayedChildren_.push_back(group); sortDisplayed(); } } else { displayedChildren_.erase(std::remove(displayedChildren_.begin(), displayedChildren_.end(), group), displayedChildren_.end()); } if (oldSize != getDisplayedChildren().size() || sortDisplayed()) { onChildrenChanged(); onDataChanged(); } } } swift-im-2.0+dev6/Swift/Controllers/Roster/SetName.h0000644000175000017500000000163412227051773022253 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include "Swiften/JID/JID.h" #include "Swift/Controllers/Roster/RosterItemOperation.h" #include "Swift/Controllers/Roster/ContactRosterItem.h" namespace Swift { class RosterItem; class SetName : public RosterItemOperation { public: SetName(const std::string& name, const JID& jid, JID::CompareType compareType = JID::WithoutResource) : RosterItemOperation(true, jid), name_(name), jid_(jid), compareType_(compareType) { } virtual void operator() (RosterItem* item) const { ContactRosterItem* contact = dynamic_cast(item); if (contact && contact->getJID().equals(jid_, compareType_)) { contact->setDisplayName(name_); } } private: std::string name_; JID jid_; JID::CompareType compareType_; }; } swift-im-2.0+dev6/Swift/Controllers/Roster/RosterController.cpp0000644000175000017500000003521312227051773024574 0ustar kismithkismith/* * Copyright (c) 2010-2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Swift/Controllers/Roster/RosterController.h" #include #include #include "Swiften/JID/JID.h" #include "Swiften/Base/foreach.h" #include "Swift/Controllers/UIInterfaces/MainWindow.h" #include "Swift/Controllers/UIInterfaces/MainWindowFactory.h" #include "Swiften/Client/NickResolver.h" #include "Swiften/Roster/GetRosterRequest.h" #include "Swiften/Roster/SetRosterRequest.h" #include "Swift/Controllers/XMPPEvents/SubscriptionRequestEvent.h" #include "Swift/Controllers/XMPPEvents/ErrorEvent.h" #include "Swiften/Presence/PresenceOracle.h" #include "Swiften/Presence/SubscriptionManager.h" #include "Swift/Controllers/XMPPEvents/EventController.h" #include "Swiften/Queries/IQRouter.h" #include "Swift/Controllers/Roster/Roster.h" #include "Swift/Controllers/Roster/SetPresence.h" #include "Swift/Controllers/Roster/AppearOffline.h" #include "Swift/Controllers/Roster/SetAvatar.h" #include "Swift/Controllers/Roster/SetName.h" #include "Swift/Controllers/Roster/OfflineRosterFilter.h" #include "Swift/Controllers/Roster/GroupRosterItem.h" #include "Swiften/Roster/XMPPRoster.h" #include "Swiften/Roster/XMPPRosterItem.h" #include "Swift/Controllers/UIEvents/AddContactUIEvent.h" #include "Swift/Controllers/UIEvents/RemoveRosterItemUIEvent.h" #include "Swift/Controllers/UIEvents/RenameRosterItemUIEvent.h" #include "Swift/Controllers/UIEvents/RenameGroupUIEvent.h" #include "Swift/Controllers/UIEvents/SendFileUIEvent.h" #include #include #include #include #include #include #include #include namespace Swift { /** * The controller does not gain ownership of these parameters. */ RosterController::RosterController(const JID& jid, XMPPRoster* xmppRoster, AvatarManager* avatarManager, MainWindowFactory* mainWindowFactory, NickManager* nickManager, NickResolver* nickResolver, PresenceOracle* presenceOracle, SubscriptionManager* subscriptionManager, EventController* eventController, UIEventStream* uiEventStream, IQRouter* iqRouter, SettingsProvider* settings, EntityCapsProvider* entityCapsManager, FileTransferOverview* fileTransferOverview) : myJID_(jid), xmppRoster_(xmppRoster), mainWindowFactory_(mainWindowFactory), mainWindow_(mainWindowFactory_->createMainWindow(uiEventStream)), roster_(new Roster()), offlineFilter_(new OfflineRosterFilter()), nickManager_(nickManager), nickResolver_(nickResolver), uiEventStream_(uiEventStream), entityCapsManager_(entityCapsManager), ftOverview_(fileTransferOverview) { assert(fileTransferOverview); iqRouter_ = iqRouter; presenceOracle_ = presenceOracle; subscriptionManager_ = subscriptionManager; eventController_ = eventController; settings_ = settings; expandiness_ = new RosterGroupExpandinessPersister(roster_, settings); mainWindow_->setRosterModel(roster_); changeStatusConnection_ = mainWindow_->onChangeStatusRequest.connect(boost::bind(&RosterController::handleChangeStatusRequest, this, _1, _2)); signOutConnection_ = mainWindow_->onSignOutRequest.connect(boost::bind(boost::ref(onSignOutRequest))); xmppRoster_->onJIDAdded.connect(boost::bind(&RosterController::handleOnJIDAdded, this, _1)); xmppRoster_->onJIDUpdated.connect(boost::bind(&RosterController::handleOnJIDUpdated, this, _1, _2, _3)); xmppRoster_->onJIDRemoved.connect(boost::bind(&RosterController::handleOnJIDRemoved, this, _1)); xmppRoster_->onRosterCleared.connect(boost::bind(&RosterController::handleRosterCleared, this)); subscriptionManager_->onPresenceSubscriptionRequest.connect(boost::bind(&RosterController::handleSubscriptionRequest, this, _1, _2)); presenceOracle_->onPresenceChange.connect(boost::bind(&RosterController::handleIncomingPresence, this, _1)); uiEventConnection_ = uiEventStream->onUIEvent.connect(boost::bind(&RosterController::handleUIEvent, this, _1)); avatarManager_ = avatarManager; avatarManager_->onAvatarChanged.connect(boost::bind(&RosterController::handleAvatarChanged, this, _1)); mainWindow_->setMyAvatarPath(avatarManager_->getAvatarPath(myJID_).string()); nickManager_->onOwnNickChanged.connect(boost::bind(&MainWindow::setMyNick, mainWindow_, _1)); mainWindow_->setMyJID(jid); mainWindow_->setMyNick(nickManager_->getOwnNick()); entityCapsManager_->onCapsChanged.connect(boost::bind(&RosterController::handleOnCapsChanged, this, _1)); settings_->onSettingChanged.connect(boost::bind(&RosterController::handleSettingChanged, this, _1)); handleShowOfflineToggled(settings_->getSetting(SettingConstants::SHOW_OFFLINE)); } RosterController::~RosterController() { settings_->onSettingChanged.disconnect(boost::bind(&RosterController::handleSettingChanged, this, _1)); nickManager_->onOwnNickChanged.disconnect(boost::bind(&MainWindow::setMyNick, mainWindow_, _1)); delete offlineFilter_; delete expandiness_; mainWindow_->setRosterModel(NULL); if (mainWindow_->canDelete()) { delete mainWindow_; } delete roster_; } void RosterController::setEnabled(bool enabled) { if (!enabled) { roster_->applyOnItems(AppearOffline()); } } void RosterController::handleShowOfflineToggled(bool state) { if (state) { roster_->removeFilter(offlineFilter_); } else { roster_->addFilter(offlineFilter_); } } void RosterController::handleChangeStatusRequest(StatusShow::Type show, const std::string &statusText) { onChangeStatusRequest(show, statusText); } void RosterController::handleOnJIDAdded(const JID& jid) { std::vector groups = xmppRoster_->getGroupsForJID(jid); std::string name = nickResolver_->jidToNick(jid); if (!groups.empty()) { foreach(const std::string& group, groups) { roster_->addContact(jid, jid, name, group, avatarManager_->getAvatarPath(jid).string()); } } else { roster_->addContact(jid, jid, name, QT_TRANSLATE_NOOP("", "Contacts"), avatarManager_->getAvatarPath(jid).string()); } applyAllPresenceTo(jid); } void RosterController::applyAllPresenceTo(const JID& jid) { foreach (Presence::ref presence, presenceOracle_->getAllPresence(jid)) { roster_->applyOnItems(SetPresence(presence)); } } void RosterController::handleRosterCleared() { roster_->removeAll(); } void RosterController::handleOnJIDRemoved(const JID& jid) { roster_->removeContact(jid); } void RosterController::handleOnJIDUpdated(const JID& jid, const std::string& oldName, const std::vector& passedOldGroups) { if (oldName != xmppRoster_->getNameForJID(jid)) { roster_->applyOnItems(SetName(nickResolver_->jidToNick(jid), jid)); } std::vector groups = xmppRoster_->getGroupsForJID(jid); std::vector oldGroups = passedOldGroups; std::string name = nickResolver_->jidToNick(jid); std::string contactsGroup = QT_TRANSLATE_NOOP("", "Contacts"); if (oldGroups.empty()) { oldGroups.push_back(contactsGroup); } if (groups.empty()) { groups.push_back(contactsGroup); } foreach(const std::string& group, groups) { if (std::find(oldGroups.begin(), oldGroups.end(), group) == oldGroups.end()) { roster_->addContact(jid, jid, name, group, avatarManager_->getAvatarPath(jid).string()); } } foreach(const std::string& group, oldGroups) { if (std::find(groups.begin(), groups.end(), group) == groups.end()) { roster_->removeContactFromGroup(jid, group); if (roster_->getGroup(group)->getChildren().size() == 0) { roster_->removeGroup(group); } } } applyAllPresenceTo(jid); } void RosterController::handleSettingChanged(const std::string& settingPath) { if (settingPath == SettingConstants::SHOW_OFFLINE.getKey()) { handleShowOfflineToggled(settings_->getSetting(SettingConstants::SHOW_OFFLINE)); } } void RosterController::handleUIEvent(boost::shared_ptr event) { if (boost::shared_ptr addContactEvent = boost::dynamic_pointer_cast(event)) { RosterItemPayload item; item.setName(addContactEvent->getName()); item.setJID(addContactEvent->getJID()); item.setGroups(std::vector(addContactEvent->getGroups().begin(), addContactEvent->getGroups().end())); boost::shared_ptr roster(new RosterPayload()); roster->addItem(item); SetRosterRequest::ref request = SetRosterRequest::create(roster, iqRouter_); request->onResponse.connect(boost::bind(&RosterController::handleRosterSetError, this, _1, roster)); request->send(); subscriptionManager_->requestSubscription(addContactEvent->getJID()); } else if (boost::shared_ptr removeEvent = boost::dynamic_pointer_cast(event)) { RosterItemPayload item(removeEvent->getJID(), "", RosterItemPayload::Remove); boost::shared_ptr roster(new RosterPayload()); roster->addItem(item); SetRosterRequest::ref request = SetRosterRequest::create(roster, iqRouter_); request->onResponse.connect(boost::bind(&RosterController::handleRosterSetError, this, _1, roster)); request->send(); } else if (boost::shared_ptr renameEvent = boost::dynamic_pointer_cast(event)) { JID contact(renameEvent->getJID()); RosterItemPayload item(contact, renameEvent->getNewName(), xmppRoster_->getSubscriptionStateForJID(contact)); item.setGroups(xmppRoster_->getGroupsForJID(contact)); boost::shared_ptr roster(new RosterPayload()); roster->addItem(item); SetRosterRequest::ref request = SetRosterRequest::create(roster, iqRouter_); request->onResponse.connect(boost::bind(&RosterController::handleRosterSetError, this, _1, roster)); request->send(); } else if (boost::shared_ptr renameGroupEvent = boost::dynamic_pointer_cast(event)) { std::vector items = xmppRoster_->getItems(); std::string group = renameGroupEvent->getGroup(); // FIXME: We should handle contacts groups specially to avoid clashes if (group == QT_TRANSLATE_NOOP("", "Contacts")) { group = ""; } foreach(XMPPRosterItem& item, items) { std::vector groups = item.getGroups(); if ( (group.empty() && groups.empty()) || std::find(groups.begin(), groups.end(), group) != groups.end()) { groups.erase(std::remove(groups.begin(), groups.end(), group), groups.end()); if (std::find(groups.begin(), groups.end(), renameGroupEvent->getNewName()) == groups.end()) { groups.push_back(renameGroupEvent->getNewName()); } item.setGroups(groups); updateItem(item); } } } else if (boost::shared_ptr sendFileEvent = boost::dynamic_pointer_cast(event)) { //TODO add send file dialog to ChatView of receipient jid ftOverview_->sendFile(sendFileEvent->getJID(), sendFileEvent->getFilename()); } } void RosterController::setContactGroups(const JID& jid, const std::vector& groups) { updateItem(XMPPRosterItem(jid, xmppRoster_->getNameForJID(jid), groups, xmppRoster_->getSubscriptionStateForJID(jid))); } void RosterController::updateItem(const XMPPRosterItem& item) { RosterItemPayload itemPayload(item.getJID(), item.getName(), item.getSubscription()); itemPayload.setGroups(item.getGroups()); RosterPayload::ref roster = boost::make_shared(); roster->addItem(itemPayload); SetRosterRequest::ref request = SetRosterRequest::create(roster, iqRouter_); request->onResponse.connect(boost::bind(&RosterController::handleRosterSetError, this, _1, roster)); request->send(); } void RosterController::handleRosterSetError(ErrorPayload::ref error, boost::shared_ptr rosterPayload) { if (!error) { return; } std::string text = str(format(QT_TRANSLATE_NOOP("", "Server %1% rejected contact list change to item '%2%'")) % myJID_.getDomain() % rosterPayload->getItems()[0].getJID().toString()); if (!error->getText().empty()) { text += ": " + error->getText(); } boost::shared_ptr errorEvent(new ErrorEvent(JID(myJID_.getDomain()), text)); eventController_->handleIncomingEvent(errorEvent); } void RosterController::handleIncomingPresence(Presence::ref newPresence) { if (newPresence->getType() == Presence::Error) { return; } roster_->applyOnItems(SetPresence(newPresence)); } void RosterController::handleSubscriptionRequest(const JID& jid, const std::string& message) { if (xmppRoster_->containsJID(jid) && (xmppRoster_->getSubscriptionStateForJID(jid) == RosterItemPayload::To || xmppRoster_->getSubscriptionStateForJID(jid) == RosterItemPayload::Both)) { subscriptionManager_->confirmSubscription(jid); return; } SubscriptionRequestEvent* eventPointer = new SubscriptionRequestEvent(jid, message); eventPointer->onAccept.connect(boost::bind(&RosterController::handleSubscriptionRequestAccepted, this, eventPointer)); eventPointer->onDecline.connect(boost::bind(&RosterController::handleSubscriptionRequestDeclined, this, eventPointer)); boost::shared_ptr event(eventPointer); eventController_->handleIncomingEvent(event); } void RosterController::handleSubscriptionRequestAccepted(SubscriptionRequestEvent* event) { subscriptionManager_->confirmSubscription(event->getJID()); if (!xmppRoster_->containsJID(event->getJID()) || xmppRoster_->getSubscriptionStateForJID(event->getJID()) == RosterItemPayload::None || xmppRoster_->getSubscriptionStateForJID(event->getJID()) == RosterItemPayload::From) { subscriptionManager_->requestSubscription(event->getJID()); } } void RosterController::handleSubscriptionRequestDeclined(SubscriptionRequestEvent* event) { subscriptionManager_->cancelSubscription(event->getJID()); } void RosterController::handleAvatarChanged(const JID& jid) { std::string path = avatarManager_->getAvatarPath(jid).string(); roster_->applyOnItems(SetAvatar(jid, path)); if (jid.equals(myJID_, JID::WithoutResource)) { mainWindow_->setMyAvatarPath(path); } } boost::optional RosterController::getItem(const JID& jid) const { return xmppRoster_->getItem(jid); } std::set RosterController::getGroups() const { return xmppRoster_->getGroups(); } void RosterController::handleOnCapsChanged(const JID& jid) { DiscoInfo::ref info = entityCapsManager_->getCaps(jid); if (info) { std::set features; if (info->hasFeature(DiscoInfo::JingleFeature) && info->hasFeature(DiscoInfo::JingleFTFeature) && info->hasFeature(DiscoInfo::JingleTransportsIBBFeature)) { features.insert(ContactRosterItem::FileTransferFeature); } if (info->hasFeature(DiscoInfo::WhiteboardFeature)) { features.insert(ContactRosterItem::WhiteboardFeature); } roster_->setAvailableFeatures(jid, features); } } } swift-im-2.0+dev6/Swift/Controllers/Roster/RosterItem.cpp0000644000175000017500000000206512227051773023346 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Swift/Controllers/Roster/RosterItem.h" #include #include "Swift/Controllers/Roster/GroupRosterItem.h" namespace Swift { RosterItem::RosterItem(const std::string& name, GroupRosterItem* parent) : name_(name), sortableDisplayName_(boost::to_lower_copy(name_)), parent_(parent) { /* The following would be good, but because of C++'s inheritance not working in constructors, it's not going to work. */ //if (parent) { // parent_->addChild(this); //} } RosterItem::~RosterItem() { } GroupRosterItem* RosterItem::getParent() const { return parent_; } void RosterItem::setDisplayName(const std::string& name) { name_ = name; sortableDisplayName_ = boost::to_lower_copy(name_); onDataChanged(); } const std::string& RosterItem::getDisplayName() const { return name_; } const std::string& RosterItem::getSortableDisplayName() const { return sortableDisplayName_; } } swift-im-2.0+dev6/Swift/Controllers/Roster/OfflineRosterFilter.h0000644000175000017500000000127612227051773024650 0ustar kismithkismith/* * 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 "Swift/Controllers/Roster/ContactRosterItem.h" #include "Swift/Controllers/Roster/RosterItem.h" #include "Swift/Controllers/Roster/RosterFilter.h" #include "Swiften/Elements/StatusShow.h" namespace Swift { class OfflineRosterFilter : public RosterFilter { public: virtual ~OfflineRosterFilter() {} virtual bool operator() (RosterItem *item) const { ContactRosterItem *contactItem = dynamic_cast(item); return contactItem && contactItem->getStatusShow() == StatusShow::None; } }; } swift-im-2.0+dev6/Swift/Controllers/Roster/Roster.cpp0000644000175000017500000001547212227051773022535 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Swift/Controllers/Roster/Roster.h" #include "Swiften/Base/foreach.h" #include #include "Swiften/JID/JID.h" #include "Swift/Controllers/Roster/ContactRosterItem.h" #include "Swift/Controllers/Roster/RosterItem.h" #include "Swift/Controllers/Roster/GroupRosterItem.h" #include "Swift/Controllers/Roster/RosterItemOperation.h" #include #include #include #include namespace Swift { Roster::Roster(bool sortByStatus, bool fullJIDMapping) { sortByStatus_ = sortByStatus; fullJIDMapping_ = fullJIDMapping; root_ = new GroupRosterItem("Dummy-Root", NULL, sortByStatus_); root_->onChildrenChanged.connect(boost::bind(&Roster::handleChildrenChanged, this, root_)); } Roster::~Roster() { std::deque queue; queue.push_back(root_); while (!queue.empty()) { RosterItem* item = *queue.begin(); queue.pop_front(); GroupRosterItem* group = dynamic_cast(item); if (group) { queue.insert(queue.begin(), group->getChildren().begin(), group->getChildren().end()); } delete item; } } GroupRosterItem* Roster::getRoot() { return root_; } GroupRosterItem* Roster::getGroup(const std::string& groupName) { foreach (RosterItem *item, root_->getChildren()) { GroupRosterItem *group = dynamic_cast(item); if (group && group->getDisplayName() == groupName) { return group; } } GroupRosterItem* group = new GroupRosterItem(groupName, root_, sortByStatus_); root_->addChild(group); group->onChildrenChanged.connect(boost::bind(&Roster::handleChildrenChanged, this, group)); group->onDataChanged.connect(boost::bind(&Roster::handleDataChanged, this, group)); return group; } void Roster::setAvailableFeatures(const JID& jid, const std::set& features) { ItemMap::const_iterator i = itemMap_.find(fullJIDMapping_ ? jid : jid.toBare()); if (i == itemMap_.end()) { return; } foreach(ContactRosterItem* item, i->second) { item->setSupportedFeatures(features); } } void Roster::removeGroup(const std::string& group) { root_->removeGroupChild(group); } void Roster::handleDataChanged(RosterItem* item) { onDataChanged(item); } void Roster::handleChildrenChanged(GroupRosterItem* item) { onChildrenChanged(item); } void Roster::addContact(const JID& jid, const JID& displayJID, const std::string& name, const std::string& groupName, const std::string& avatarPath) { GroupRosterItem* group(getGroup(groupName)); ContactRosterItem *item = new ContactRosterItem(jid, displayJID, name, group); item->setAvatarPath(avatarPath); group->addChild(item); ItemMap::iterator i = itemMap_.insert(std::make_pair(fullJIDMapping_ ? jid : jid.toBare(), std::vector())).first; if (!i->second.empty()) { foreach (const std::string& existingGroup, i->second[0]->getGroups()) { item->addGroup(existingGroup); } } i->second.push_back(item); item->onDataChanged.connect(boost::bind(&Roster::handleDataChanged, this, item)); filterContact(item, group); foreach (ContactRosterItem* item, i->second) { item->addGroup(groupName); } } struct JIDEqualsTo { JIDEqualsTo(const JID& jid) : jid(jid) {} bool operator()(ContactRosterItem* i) const { return jid == i->getJID(); } JID jid; }; void Roster::removeAll() { root_->removeAll(); itemMap_.clear(); onChildrenChanged(root_); onDataChanged(root_); } void Roster::removeContact(const JID& jid) { ItemMap::iterator item = itemMap_.find(fullJIDMapping_ ? jid : jid.toBare()); if (item != itemMap_.end()) { std::vector& items = item->second; items.erase(std::remove_if(items.begin(), items.end(), JIDEqualsTo(jid)), items.end()); if (items.empty()) { itemMap_.erase(item); } } //Causes the delete root_->removeChild(jid); } void Roster::removeContactFromGroup(const JID& jid, const std::string& groupName) { std::vector children = root_->getChildren(); std::vector::iterator it = children.begin(); ItemMap::iterator itemIt = itemMap_.find(fullJIDMapping_ ? jid : jid.toBare()); while (it != children.end()) { GroupRosterItem* group = dynamic_cast(*it); if (group && group->getDisplayName() == groupName) { ContactRosterItem* deleted = group->removeChild(jid); if (itemIt != itemMap_.end()) { std::vector& items = itemIt->second; items.erase(std::remove(items.begin(), items.end(), deleted), items.end()); } } ++it; } if (itemIt != itemMap_.end()) { foreach (ContactRosterItem* item, itemIt->second) { item->removeGroup(groupName); } } } void Roster::applyOnItems(const RosterItemOperation& operation) { if (operation.requiresLookup()) { applyOnItem(operation, operation.lookupJID()); } else { applyOnAllItems(operation); } } void Roster::applyOnItem(const RosterItemOperation& operation, const JID& jid) { ItemMap::iterator i = itemMap_.find(fullJIDMapping_ ? jid : jid.toBare()); if (i == itemMap_.end()) { return; } foreach (ContactRosterItem* item, i->second) { operation(item); filterContact(item, item->getParent()); } } void Roster::applyOnAllItems(const RosterItemOperation& operation) { std::deque queue; queue.push_back(root_); while (!queue.empty()) { RosterItem* item = *queue.begin(); queue.pop_front(); operation(item); GroupRosterItem* group = dynamic_cast(item); if (group) { queue.insert(queue.begin(), group->getChildren().begin(), group->getChildren().end()); } } filterAll(); } void Roster::removeFilter(RosterFilter *filter) { for (unsigned int i = 0; i < filters_.size(); i++) { if (filters_[i] == filter) { filters_.erase(filters_.begin() + i); break; } } filterAll(); } void Roster::filterContact(ContactRosterItem* contact, GroupRosterItem* group) { int oldDisplayedSize = group->getDisplayedChildren().size(); bool hide = true; foreach (RosterFilter *filter, filters_) { hide &= (*filter)(contact); } group->setDisplayed(contact, filters_.empty() || !hide); int newDisplayedSize = group->getDisplayedChildren().size(); if (oldDisplayedSize == 0 && newDisplayedSize > 0) { onGroupAdded(group); } } void Roster::filterGroup(GroupRosterItem* group) { foreach (RosterItem* child, group->getChildren()) { ContactRosterItem* contact = dynamic_cast(child); if (contact) { filterContact(contact, group); } } } void Roster::filterAll() { std::deque queue; queue.push_back(root_); while (!queue.empty()) { RosterItem *item = *queue.begin(); queue.pop_front(); GroupRosterItem* group = dynamic_cast(item); if (group) { queue.insert(queue.begin(), group->getChildren().begin(), group->getChildren().end()); filterGroup(group); } } } } swift-im-2.0+dev6/Swift/Controllers/Roster/RosterItem.h0000644000175000017500000000136412227051773023014 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include "Swiften/Base/boost_bsignals.h" #include #include namespace Swift { class GroupRosterItem; class RosterItem { public: RosterItem(const std::string& name, GroupRosterItem* parent); virtual ~RosterItem(); boost::signal onDataChanged; GroupRosterItem* getParent() const; void setDisplayName(const std::string& name); const std::string& getDisplayName() const; virtual const std::string& getSortableDisplayName() const; private: std::string name_; std::string sortableDisplayName_; GroupRosterItem* parent_; }; } swift-im-2.0+dev6/Swift/Controllers/Roster/SetPresence.h0000644000175000017500000000207712227051773023141 0ustar kismithkismith/* * 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 "Swiften/Elements/Presence.h" #include "Swiften/JID/JID.h" #include "Swift/Controllers/Roster/RosterItemOperation.h" #include "Swift/Controllers/Roster/ContactRosterItem.h" namespace Swift { class RosterItem; class SetPresence : public RosterItemOperation { public: SetPresence(Presence::ref presence, JID::CompareType compareType = JID::WithoutResource) : RosterItemOperation(true, compareType == JID::WithoutResource ? presence->getFrom().toBare() : presence->getFrom()), presence_(presence), compareType_(compareType) { } virtual void operator() (RosterItem* item) const { ContactRosterItem* contact = dynamic_cast(item); if (contact && contact->getJID().equals(presence_->getFrom(), compareType_)) { contact->applyPresence(presence_->getFrom().getResource(), presence_); } } private: Presence::ref presence_; JID::CompareType compareType_; }; } swift-im-2.0+dev6/Swift/Controllers/Roster/LeastCommonSubsequence.h0000644000175000017500000000720412227051773025342 0ustar kismithkismith/* * 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 namespace Swift { using std::equal_to; namespace Detail { template void computeLeastCommonSubsequenceMatrix(XIt xBegin, XIt xEnd, YIt yBegin, YIt yEnd, std::vector& result) { size_t width = std::distance(xBegin, xEnd) + 1; size_t height = std::distance(yBegin, yEnd) + 1; result.resize(width * height); // Initialize first row & column for (size_t i = 0; i < width; ++i) { result[i] = 0; } for (size_t j = 0; j < height; ++j) { result[j*width] = 0; } // Compute the LCS lengths for subsets Predicate predicate; for (size_t i = 1; i < width; ++i) { for (size_t j = 1; j < height; ++j) { result[i + j*width] = (predicate(*(xBegin + i-1), *(yBegin + j-1)) ? result[(i-1) + (j-1)*width] + 1 : std::max(result[i + (j-1)*width], result[i-1 + (j*width)])); } } } } template void computeIndexDiff(const std::vector& x, const std::vector& y, std::vector& updates, std::vector& postUpdates, std::vector& removes, std::vector& inserts) { InsertRemovePredicate insertRemovePredicate; UpdatePredicate updatePredicate; // Find & handle common prefix (Optimization to reduce LCS matrix size) typename std::vector::const_iterator xBegin = x.begin(); typename std::vector::const_iterator yBegin = y.begin(); while (xBegin < x.end() && yBegin < y.end() && insertRemovePredicate(*xBegin, *yBegin)) { if (updatePredicate(*xBegin, *yBegin)) { updates.push_back(std::distance(x.begin(), xBegin)); postUpdates.push_back(std::distance(y.begin(), yBegin)); } ++xBegin; ++yBegin; } size_t prefixLength = std::distance(x.begin(), xBegin); // Find & handle common suffix (Optimization to reduce LCS matrix size) typename std::vector::const_reverse_iterator xEnd = x.rbegin(); typename std::vector::const_reverse_iterator yEnd = y.rbegin(); while (xEnd.base() > xBegin && yEnd.base() > yBegin && insertRemovePredicate(*xEnd, *yEnd)) { if (updatePredicate(*xEnd, *yEnd)) { updates.push_back(std::distance(x.begin(), xEnd.base()) - 1); postUpdates.push_back(std::distance(y.begin(), yEnd.base()) - 1); } ++xEnd; ++yEnd; } // Compute lengths size_t xLength = std::distance(xBegin, xEnd.base()); size_t yLength = std::distance(yBegin, yEnd.base()); // Compute LCS matrix std::vector lcs; Detail::computeLeastCommonSubsequenceMatrix::const_iterator, typename std::vector::const_iterator, unsigned int, InsertRemovePredicate>(xBegin, xEnd.base(), yBegin, yEnd.base(), lcs); // Process LCS matrix size_t i = xLength; size_t j = yLength; const size_t width = xLength + 1; while (true) { if (i > 0 && j > 0 && insertRemovePredicate(x[prefixLength + i-1], y[prefixLength + j-1])) { // x[i-1] same if (updatePredicate(x[prefixLength + i - 1], y[prefixLength + j - 1])) { updates.push_back(prefixLength + i-1); postUpdates.push_back(prefixLength + j-1); } i -= 1; j -= 1; } else if (j > 0 && (i == 0 || lcs[i + (j-1)*width] >= lcs[i-1 + j*width])) { // y[j-1] added inserts.push_back(prefixLength + j-1); j -= 1; } else if (i > 0 && (j == 0 || lcs[i + (j-1)*width] < lcs[i-1 + j*width])) { // x[i-1] removed removes.push_back(prefixLength + i-1); i -= 1; } else { break; } } } } swift-im-2.0+dev6/Swift/Controllers/Roster/RosterFilter.h0000644000175000017500000000055612227051773023345 0ustar kismithkismith/* * 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 "Swift/Controllers/Roster/RosterItem.h" namespace Swift { class RosterFilter { public: virtual ~RosterFilter() {} virtual bool operator() (RosterItem* item) const = 0; }; } swift-im-2.0+dev6/Swift/Controllers/Roster/GroupRosterItem.h0000644000175000017500000000314112227051773024024 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include "Swift/Controllers/Roster/RosterItem.h" #include #include "Swift/Controllers/Roster/ContactRosterItem.h" #include namespace Swift { class GroupRosterItem : public RosterItem { public: GroupRosterItem(const std::string& name, GroupRosterItem* parent, bool sortByStatus); virtual ~GroupRosterItem(); const std::vector& getChildren() const; const std::vector& getDisplayedChildren() const; void addChild(RosterItem* item); ContactRosterItem* removeChild(const JID& jid); GroupRosterItem* removeGroupChild(const std::string& group); void removeAll(); void setDisplayed(RosterItem* item, bool displayed); boost::signal onChildrenChanged; static bool itemLessThanWithStatus(const RosterItem* left, const RosterItem* right); static bool itemLessThanWithoutStatus(const RosterItem* left, const RosterItem* right); void setExpanded(bool expanded); bool isExpanded() const; boost::signal onExpandedChanged; void setManualSort(const std::string& manualSortValue); virtual const std::string& getSortableDisplayName() const; private: void handleChildrenChanged(GroupRosterItem* group); void handleDataChanged(RosterItem* item); bool sortDisplayed(); std::string name_; bool expanded_; std::vector children_; std::vector displayedChildren_; bool sortByStatus_; bool manualSort_; std::string manualSortValue_; }; } swift-im-2.0+dev6/Swift/Controllers/XMPPURIController.cpp0000644000175000017500000000236612227051774023210 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include using namespace Swift; XMPPURIController::XMPPURIController(URIHandler* uriHandler, UIEventStream* uiEventStream) : uriHandler(uriHandler), uiEventStream(uiEventStream) { uriHandler->onURI.connect(boost::bind(&XMPPURIController::handleURI, this, _1)); } XMPPURIController::~XMPPURIController() { uriHandler->onURI.disconnect(boost::bind(&XMPPURIController::handleURI, this, _1)); } void XMPPURIController::handleURI(const std::string& s) { XMPPURI uri = XMPPURI::fromString(s); if (!uri.isNull()) { if (uri.getQueryType() == "join") { uiEventStream->send(boost::make_shared(uri.getPath())); } else { uiEventStream->send(boost::make_shared(uri.getPath())); } } } swift-im-2.0+dev6/Swift/Controllers/XMLConsoleController.h0000644000175000017500000000162312227051774023467 0ustar kismithkismith/* * 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 "Swiften/Base/boost_bsignals.h" #include #include #include "Swift/Controllers/UIEvents/UIEventStream.h" #include namespace Swift { class XMLConsoleWidgetFactory; class XMLConsoleWidget; class XMLConsoleController { public: XMLConsoleController(UIEventStream* uiEventStream, XMLConsoleWidgetFactory* xmlConsoleWidgetFactory); ~XMLConsoleController(); public: void handleDataRead(const SafeByteArray& data); void handleDataWritten(const SafeByteArray& data); private: void handleUIEvent(boost::shared_ptr event); private: XMLConsoleWidgetFactory* xmlConsoleWidgetFactory; XMLConsoleWidget* xmlConsoleWidget; }; } swift-im-2.0+dev6/Swift/Controllers/ContactEditController.cpp0000644000175000017500000001046012227051773024236 0ustar kismithkismith/* * Copyright (c) 2010-2012 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include namespace Swift { ContactEditController::ContactEditController(RosterController* rosterController, VCardManager* vcardManager, ContactEditWindowFactory* contactEditWindowFactory, UIEventStream* uiEventStream) : rosterController(rosterController), vcardManager(vcardManager), contactEditWindowFactory(contactEditWindowFactory), uiEventStream(uiEventStream), contactEditWindow(NULL) { uiEventStream->onUIEvent.connect(boost::bind(&ContactEditController::handleUIEvent, this, _1)); vcardManager->onVCardChanged.connect(boost::bind(&ContactEditController::handleVCardChanged, this, _1, _2)); } ContactEditController::~ContactEditController() { if (contactEditWindow) { contactEditWindow->onChangeContactRequest.disconnect(boost::bind(&ContactEditController::handleChangeContactRequest, this, _1, _2)); contactEditWindow->onRemoveContactRequest.disconnect(boost::bind(&ContactEditController::handleRemoveContactRequest, this)); delete contactEditWindow; } uiEventStream->onUIEvent.disconnect(boost::bind(&ContactEditController::handleUIEvent, this, _1)); } void ContactEditController::handleUIEvent(UIEvent::ref event) { RequestContactEditorUIEvent::ref editEvent = boost::dynamic_pointer_cast(event); if (!editEvent) { return; } if (!contactEditWindow) { contactEditWindow = contactEditWindowFactory->createContactEditWindow(); contactEditWindow->onRemoveContactRequest.connect(boost::bind(&ContactEditController::handleRemoveContactRequest, this)); contactEditWindow->onChangeContactRequest.connect(boost::bind(&ContactEditController::handleChangeContactRequest, this, _1, _2)); } currentContact = rosterController->getItem(editEvent->getJID()); assert(currentContact); jid = rosterController->getItem(editEvent->getJID())->getJID(); contactEditWindow->setContact(jid, currentContact->getName(), currentContact->getGroups(), rosterController->getGroups()); contactEditWindow->show(); if (vcardManager) { VCard::ref vcard = vcardManager->getVCardAndRequestWhenNeeded(jid); if (vcard) { handleVCardChanged(jid, vcard); } } } void ContactEditController::handleVCardChanged(const JID &jid, VCard::ref vcard) { if (jid == this->jid) { contactEditWindow->setNameSuggestions(nameSuggestionsFromVCard(vcard)); } } void ContactEditController::setAvailable(bool b) { if (contactEditWindow) { contactEditWindow->setEnabled(b); } } std::vector ContactEditController::nameSuggestionsFromVCard(VCard::ref vcard) { std::vector suggestions; if (!vcard->getNickname().empty()) { suggestions.push_back(vcard->getNickname()); } if (!vcard->getFullName().empty()) { suggestions.push_back(vcard->getFullName()); } if (!vcard->getGivenName().empty()) { std::string suggestedName; suggestedName = vcard->getGivenName(); boost::algorithm::trim(suggestedName); suggestions.push_back(suggestedName); } return suggestions; } void ContactEditController::handleRemoveContactRequest() { assert(currentContact); uiEventStream->send(boost::make_shared(currentContact->getJID())); contactEditWindow->hide(); } void ContactEditController::handleChangeContactRequest(const std::string& name, const std::set& newGroups) { std::vector oldGroupsVector = currentContact->getGroups(); std::set oldGroups(oldGroupsVector.begin(), oldGroupsVector.end()); if (oldGroups != newGroups || currentContact->getName() != name) { XMPPRosterItem newContact(*currentContact); newContact.setName(name); newContact.setGroups(std::vector(newGroups.begin(), newGroups.end())); rosterController->updateItem(newContact); } contactEditWindow->hide(); } } swift-im-2.0+dev6/Swift/Controllers/StatusTracker.h0000644000175000017500000000106612227051773022237 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include "Swiften/Elements/Presence.h" namespace Swift { class StatusTracker { public: StatusTracker(); boost::shared_ptr getNextPresence(); void setRequestedPresence(boost::shared_ptr presence); bool goAutoAway(); bool goAutoUnAway(); private: boost::shared_ptr queuedPresence_; bool isAutoAway_; }; } swift-im-2.0+dev6/Swift/Controllers/EventWindowController.cpp0000644000175000017500000000353212227051773024310 0ustar kismithkismith/* * Copyright (c) 2010-2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include namespace Swift { EventWindowController::EventWindowController(EventController* eventController, EventWindowFactory* windowFactory) { eventController_ = eventController; windowFactory_ = windowFactory; window_ = windowFactory_->createEventWindow(); eventAddedConnection_ = eventController_->onEventQueueEventAdded.connect(boost::bind(&EventWindowController::handleEventQueueEventAdded, this, _1)); } EventWindowController::~EventWindowController() { if (window_->canDelete()) { delete window_; } } void EventWindowController::handleEventQueueEventAdded(boost::shared_ptr event) { if (event->getConcluded()) { handleEventConcluded(event); } else { boost::shared_ptr message = boost::dynamic_pointer_cast(event); if (!(message && message->isReadable())) { event->onConclusion.connect(boost::bind(&EventWindowController::handleEventConcluded, this, event)); window_->addEvent(event, true); } } } void EventWindowController::handleEventConcluded(boost::shared_ptr event) { window_->removeEvent(event); bool includeAsCompleted = true; /* Because subscription requests get duplicated, don't add them back */ if (boost::dynamic_pointer_cast(event) || boost::dynamic_pointer_cast(event)) { includeAsCompleted = false; } if (includeAsCompleted) { window_->addEvent(event, false); } event->onConclusion.disconnect(boost::bind(&EventWindowController::handleEventConcluded, this, event)); } } swift-im-2.0+dev6/Swift/Controllers/SystemTrayController.cpp0000644000175000017500000000225512227051773024164 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Swift/Controllers/SystemTrayController.h" #include #include "Swift/Controllers/XMPPEvents/EventController.h" #include "Swift/Controllers/SystemTray.h" namespace Swift { SystemTrayController::SystemTrayController(EventController* eventController, SystemTray* systemTray) { systemTray_ = systemTray; eventController_ = eventController; eventController_->onEventQueueLengthChange.connect(boost::bind(&SystemTrayController::handleEventQueueLengthChange, this, _1)); } void SystemTrayController::handleEventQueueLengthChange(int /*length*/) { EventList events = eventController_->getEvents(); bool found = false; for (EventList::iterator it = events.begin(); it != events.end(); ++it) { if (boost::dynamic_pointer_cast(*it)) { found = true; break; } } systemTray_->setUnreadMessages(found); } void SystemTrayController::setMyStatusType(StatusShow::Type type) { systemTray_->setStatusType(type); } void SystemTrayController::setConnecting() { systemTray_->setConnecting(); } } swift-im-2.0+dev6/Swift/Controllers/HistoryViewController.h0000644000175000017500000000430712227051773024001 0ustar kismithkismith/* * Copyright (c) 2012 Catalin Badea * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include #include #include #include #include #include #include namespace Swift { class HistoryWindowFactory; class HistoryWindow; class Roster; class RosterItem; class ContactRosterItem; class HistoryController; class NickResolver; class AvatarManager; class HistoryViewController { public: HistoryViewController(const JID& selfJID, UIEventStream* uiEventStream, HistoryController* historyController, NickResolver* nickResolver, AvatarManager* avatarManager, PresenceOracle* presenceOracle, HistoryWindowFactory* historyWindowFactory); ~HistoryViewController(); private: void handleUIEvent(boost::shared_ptr event); void handleSelectedContactChanged(RosterItem* item); void handleNewMessage(const HistoryMessage& message); void handleReturnPressed(const std::string& keyword); void handleScrollReachedTop(const boost::gregorian::date& date); void handleScrollReachedBottom(const boost::gregorian::date& date); void handlePreviousButtonClicked(); void handleNextButtonClicked(); void handleCalendarClicked(const boost::gregorian::date& date); void handlePresenceChanged(Presence::ref presence); void handleAvatarChanged(const JID& jid); void addNewMessage(const HistoryMessage& message, bool addAtTheTop); void reset(); Presence::ref getPresence(const JID& jid, bool isMUC); private: JID selfJID_; UIEventStream* uiEventStream_; HistoryController* historyController_; NickResolver* nickResolver_; AvatarManager* avatarManager_; PresenceOracle* presenceOracle_; HistoryWindowFactory* historyWindowFactory_; HistoryWindow* historyWindow_; Roster* roster_; std::map contacts_; ContactRosterItem* selectedItem_; HistoryMessage::Type selectedItemType_; boost::gregorian::date currentResultDate_; }; } swift-im-2.0+dev6/Swift/Controllers/AdHocManager.h0000644000175000017500000000233412227051773021710 0ustar kismithkismith/* * Copyright (c) 2010-2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include #include #include #include #include #include namespace Swift { class IQRouter; class MainWindow; class UIEventStream; class AdHocCommandWindowFactory; class AdHocManager { public: AdHocManager(const JID& jid, AdHocCommandWindowFactory* factory, IQRouter* iqRouter, UIEventStream* uiEventStream, MainWindow* mainWindow); ~AdHocManager(); void setServerDiscoInfo(boost::shared_ptr info); private: void handleUIEvent(boost::shared_ptr event); void handleServerDiscoItemsResponse(boost::shared_ptr, ErrorPayload::ref error); JID jid_; IQRouter* iqRouter_; UIEventStream* uiEventStream_; MainWindow* mainWindow_; AdHocCommandWindowFactory* factory_; GetDiscoItemsRequest::ref discoItemsRequest_; }; } swift-im-2.0+dev6/Swift/Controllers/ProfileController.cpp0000644000175000017500000000612412227051773023437 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include namespace Swift { ProfileController::ProfileController(VCardManager* vcardManager, ProfileWindowFactory* profileWindowFactory, UIEventStream* uiEventStream) : vcardManager(vcardManager), profileWindowFactory(profileWindowFactory), uiEventStream(uiEventStream), available(true), profileWindow(NULL), gettingVCard(false) { uiEventStream->onUIEvent.connect(boost::bind(&ProfileController::handleUIEvent, this, _1)); } ProfileController::~ProfileController() { if (profileWindow) { vcardManager->onOwnVCardChanged.disconnect(boost::bind(&ProfileController::handleOwnVCardChanged, this, _1)); profileWindow->onVCardChangeRequest.disconnect(boost::bind(&ProfileController::handleVCardChangeRequest, this, _1)); delete profileWindow; } uiEventStream->onUIEvent.disconnect(boost::bind(&ProfileController::handleUIEvent, this, _1)); } void ProfileController::handleUIEvent(UIEvent::ref event) { if (!boost::dynamic_pointer_cast(event)) { return; } if (!profileWindow) { profileWindow = profileWindowFactory->createProfileWindow(); profileWindow->onVCardChangeRequest.connect(boost::bind(&ProfileController::handleVCardChangeRequest, this, _1)); vcardManager->onOwnVCardChanged.connect(boost::bind(&ProfileController::handleOwnVCardChanged, this, _1)); } gettingVCard = true; updateDialogStatus(); vcardManager->requestOwnVCard(); profileWindow->show(); } void ProfileController::handleVCardChangeRequest(VCard::ref vcard) { assert(!pendingSetVCardRequest); profileWindow->setError(""); pendingSetVCardRequest = vcardManager->createSetVCardRequest(vcard); pendingSetVCardRequest->onResponse.connect(boost::bind(&ProfileController::handleSetVCardResponse, this, _2)); pendingSetVCardRequest->send(); updateDialogStatus(); } void ProfileController::handleSetVCardResponse(ErrorPayload::ref error) { pendingSetVCardRequest.reset(); updateDialogStatus(); if (error) { profileWindow->setError(QT_TRANSLATE_NOOP("", "There was an error publishing your profile data")); } else { profileWindow->setError(""); profileWindow->hide(); } } void ProfileController::handleOwnVCardChanged(VCard::ref vcard) { if (profileWindow) { profileWindow->setVCard(vcard); gettingVCard = false; updateDialogStatus(); } } void ProfileController::setAvailable(bool b) { available = b; if (!available) { pendingSetVCardRequest.reset(); } updateDialogStatus(); } void ProfileController::updateDialogStatus() { if (profileWindow) { profileWindow->setEnabled(available && !gettingVCard && !pendingSetVCardRequest); profileWindow->setProcessing(gettingVCard || pendingSetVCardRequest); } } } swift-im-2.0+dev6/Swift/Controllers/StatusTracker.cpp0000644000175000017500000000227612227051773022576 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Swift/Controllers/StatusTracker.h" #include namespace Swift { StatusTracker::StatusTracker() { isAutoAway_ = false; queuedPresence_ = boost::make_shared(); } boost::shared_ptr StatusTracker::getNextPresence() { boost::shared_ptr presence; if (isAutoAway_) { presence = boost::make_shared(); presence->setShow(StatusShow::Away); presence->setStatus(queuedPresence_->getStatus()); } else { presence = queuedPresence_; } return presence; } void StatusTracker::setRequestedPresence(boost::shared_ptr presence) { isAutoAway_ = false; queuedPresence_ = presence; // if (presence->getType() == Presence::Unavailable) { // queuedPresence_ = boost::make_shared(); // } } bool StatusTracker::goAutoAway() { if (queuedPresence_->getShow() != StatusShow::Online) { return false; } isAutoAway_ = true; return true; } bool StatusTracker::goAutoUnAway() { if (!isAutoAway_) { return false; } isAutoAway_ = false; return true; } } swift-im-2.0+dev6/Swift/Controllers/XMPPURIController.h0000644000175000017500000000123612227051774022650 0ustar kismithkismith/* * 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 #include namespace Swift { class URIHandler; class JID; class UIEventStream; class XMPPURIController { public: XMPPURIController(URIHandler* uriHandler, UIEventStream* uiEventStream); ~XMPPURIController(); boost::signal onStartChat; boost::signal onJoinMUC; private: void handleURI(const std::string&); private: URIHandler* uriHandler; UIEventStream* uiEventStream; }; } swift-im-2.0+dev6/Swift/Controllers/SoundEventController.h0000644000175000017500000000160312227051773023573 0ustar kismithkismith/* * Copyright (c) 2010-2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include namespace Swift { class EventController; class SoundPlayer; class SoundEventController { public: SoundEventController(EventController* eventController, SoundPlayer* soundPlayer, SettingsProvider* settings); void setPlaySounds(bool playSounds); bool getSoundEnabled() {return playSounds_;}; private: void handleSettingChanged(const std::string& settingPath); void handleEventQueueEventAdded(boost::shared_ptr event); EventController* eventController_; SoundPlayer* soundPlayer_; bool playSounds_; SettingsProvider* settings_; }; } swift-im-2.0+dev6/Swift/Controllers/StatusUtil.cpp0000644000175000017500000000135712227051773022117 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include namespace Swift { std::string statusShowTypeToFriendlyName(StatusShow::Type type) { switch (type) { case StatusShow::Online: return QT_TRANSLATE_NOOP("", "Available"); case StatusShow::FFC: return QT_TRANSLATE_NOOP("", "Available"); case StatusShow::Away: return QT_TRANSLATE_NOOP("", "Away"); case StatusShow::XA: return QT_TRANSLATE_NOOP("", "Away"); case StatusShow::DND: return QT_TRANSLATE_NOOP("", "Busy"); case StatusShow::None: return QT_TRANSLATE_NOOP("", "Offline"); } return ""; } } swift-im-2.0+dev6/Swift/Controllers/UIEvents/0000755000175000017500000000000012227051773020766 5ustar kismithkismithswift-im-2.0+dev6/Swift/Controllers/UIEvents/CancelWhiteboardSessionUIEvent.h0000644000175000017500000000110112227051773027132 0ustar kismithkismith/* * Copyright (c) 2012 Mateusz Piękos * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include #include namespace Swift { class CancelWhiteboardSessionUIEvent : public UIEvent { typedef boost::shared_ptr ref; public: CancelWhiteboardSessionUIEvent(const JID& jid) : jid_(jid) {} const JID& getContact() const {return jid_;} private: JID jid_; }; } swift-im-2.0+dev6/Swift/Controllers/UIEvents/RequestProfileEditorUIEvent.h0000644000175000017500000000053312227051773026520 0ustar kismithkismith/* * 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 "Swift/Controllers/UIEvents/UIEvent.h" namespace Swift { class RequestProfileEditorUIEvent : public UIEvent { public: RequestProfileEditorUIEvent() {} }; } swift-im-2.0+dev6/Swift/Controllers/UIEvents/UIEventStream.h0000644000175000017500000000075412227051773023640 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include "Swiften/Base/boost_bsignals.h" #include #include "Swift/Controllers/UIEvents/UIEvent.h" namespace Swift { class UIEventStream { public: boost::signal)> onUIEvent; void send(boost::shared_ptr event) { onUIEvent(event); }; }; } swift-im-2.0+dev6/Swift/Controllers/UIEvents/AcceptWhiteboardSessionUIEvent.h0000644000175000017500000000110112227051773027144 0ustar kismithkismith/* * Copyright (c) 2012 Mateusz Piękos * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include #include namespace Swift { class AcceptWhiteboardSessionUIEvent : public UIEvent { typedef boost::shared_ptr ref; public: AcceptWhiteboardSessionUIEvent(const JID& jid) : jid_(jid) {} const JID& getContact() const {return jid_;} private: JID jid_; }; } swift-im-2.0+dev6/Swift/Controllers/UIEvents/RequestXMLConsoleUIEvent.h0000644000175000017500000000044712227051773025740 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include "Swift/Controllers/UIEvents/UIEvent.h" namespace Swift { class RequestXMLConsoleUIEvent : public UIEvent { }; } swift-im-2.0+dev6/Swift/Controllers/UIEvents/JoinMUCUIEvent.h0000644000175000017500000000256212227051773023650 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include #include namespace Swift { class JoinMUCUIEvent : public UIEvent { public: typedef boost::shared_ptr ref; JoinMUCUIEvent(const JID& jid, const boost::optional& password = boost::optional(), const boost::optional& nick = boost::optional(), bool joinAutomaticallyInFuture = false, bool createAsReservedRoomIfNew = false) : jid_(jid), nick_(nick), joinAutomatically_(joinAutomaticallyInFuture), createAsReservedRoomIfNew_(createAsReservedRoomIfNew), password_(password) {}; const boost::optional& getNick() const {return nick_;} const JID& getJID() const {return jid_;} bool getShouldJoinAutomatically() const {return joinAutomatically_;} bool getCreateAsReservedRoomIfNew() const {return createAsReservedRoomIfNew_;} const boost::optional& getPassword() const {return password_;} private: JID jid_; boost::optional nick_; bool joinAutomatically_; bool createAsReservedRoomIfNew_; boost::optional password_; }; } swift-im-2.0+dev6/Swift/Controllers/UIEvents/UIEvent.cpp0000644000175000017500000000037312227051773023014 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Swift/Controllers/UIEvents/UIEvent.h" namespace Swift { UIEvent::~UIEvent() { } } swift-im-2.0+dev6/Swift/Controllers/UIEvents/RequestHistoryUIEvent.h0000644000175000017500000000045012227051773025410 0ustar kismithkismith/* * Copyright (c) 2012 Catalin Badea * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include namespace Swift { class RequestHistoryUIEvent : public UIEvent { }; } swift-im-2.0+dev6/Swift/Controllers/UIEvents/RemoveMUCBookmarkUIEvent.h0000644000175000017500000000104712227051773025671 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include "Swift/Controllers/UIEvents/UIEvent.h" #include "Swiften/MUC/MUCBookmark.h" namespace Swift { class RemoveMUCBookmarkUIEvent : public UIEvent { public: RemoveMUCBookmarkUIEvent(const MUCBookmark& bookmark) : bookmark(bookmark) {}; const MUCBookmark& getBookmark() { return bookmark; } private: MUCBookmark bookmark; }; } swift-im-2.0+dev6/Swift/Controllers/UIEvents/RemoveRosterItemUIEvent.h0000644000175000017500000000074512227051773025660 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include "Swiften/JID/JID.h" #include "Swift/Controllers/UIEvents/UIEvent.h" namespace Swift { class RemoveRosterItemUIEvent : public UIEvent { public: RemoveRosterItemUIEvent(const JID& jid) : jid_(jid) {}; virtual ~RemoveRosterItemUIEvent() {}; JID getJID() {return jid_;}; private: JID jid_; }; } swift-im-2.0+dev6/Swift/Controllers/UIEvents/UIEvent.h0000644000175000017500000000050212227051773022453 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include namespace Swift { class UIEvent { public: typedef boost::shared_ptr ref; virtual ~UIEvent(); }; } swift-im-2.0+dev6/Swift/Controllers/UIEvents/RequestAdHocUIEvent.h0000644000175000017500000000103612227051773024726 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include namespace Swift { class RequestAdHocUIEvent : public UIEvent { public: RequestAdHocUIEvent(const DiscoItems::Item& command) : command_(command) {}; const DiscoItems::Item& getCommand() const {return command_;} private: DiscoItems::Item command_; }; } swift-im-2.0+dev6/Swift/Controllers/UIEvents/RequestJoinMUCUIEvent.h0000644000175000017500000000112112227051773025207 0ustar kismithkismith/* * 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 #include #include #include namespace Swift { class RequestJoinMUCUIEvent : public UIEvent { public: typedef boost::shared_ptr ref; RequestJoinMUCUIEvent(const JID& room = JID()) : room(room) { } const JID& getRoom() const { return room; } private: JID room; }; } swift-im-2.0+dev6/Swift/Controllers/UIEvents/RenameGroupUIEvent.h0000644000175000017500000000116412227051773024625 0ustar kismithkismith/* * 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 #include namespace Swift { class RenameGroupUIEvent : public UIEvent { public: RenameGroupUIEvent(const std::string& group, const std::string& newName) : group(group), newName(newName) { } const std::string& getGroup() const { return group; } const std::string& getNewName() const { return newName; } private: std::string group; std::string newName; }; } swift-im-2.0+dev6/Swift/Controllers/UIEvents/RequestContactEditorUIEvent.h0000644000175000017500000000104212227051773026507 0ustar kismithkismith/* * 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 #include namespace Swift { class RequestContactEditorUIEvent : public UIEvent { public: typedef boost::shared_ptr ref; RequestContactEditorUIEvent(const JID& jid) : jid(jid) { } const JID& getJID() const { return jid; } private: JID jid; }; } swift-im-2.0+dev6/Swift/Controllers/UIEvents/SendFileUIEvent.h0000644000175000017500000000125012227051773024066 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include #include namespace Swift { class SendFileUIEvent : public UIEvent { public: typedef boost::shared_ptr ref; SendFileUIEvent(const JID& jid, const std::string& filename) : jid(jid), filename(filename) { } const JID& getJID() const { return jid; } const std::string& getFilename() const { return filename; } private: JID jid; std::string filename; }; } swift-im-2.0+dev6/Swift/Controllers/UIEvents/RequestWhiteboardUIEvent.h0000644000175000017500000000075012227051773026042 0ustar kismithkismith/* * Copyright (c) 2012 Mateusz Piękos * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include "Swiften/JID/JID.h" #include "Swift/Controllers/UIEvents/UIEvent.h" namespace Swift { class RequestWhiteboardUIEvent : public UIEvent { public: RequestWhiteboardUIEvent(const JID& contact) : contact_(contact) {}; const JID& getContact() const {return contact_;} private: JID contact_; }; } swift-im-2.0+dev6/Swift/Controllers/UIEvents/RequestChatWithUserDialogUIEvent.h0000644000175000017500000000046012227051773027442 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include "Swift/Controllers/UIEvents/UIEvent.h" namespace Swift { class RequestChatWithUserDialogUIEvent : public UIEvent { }; } swift-im-2.0+dev6/Swift/Controllers/UIEvents/RequestFileTransferListUIEvent.h0000644000175000017500000000046312227051773027173 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include namespace Swift { class RequestFileTransferListUIEvent : public UIEvent { }; } swift-im-2.0+dev6/Swift/Controllers/UIEvents/RequestAddUserDialogUIEvent.h0000644000175000017500000000136312227051773026422 0ustar kismithkismith/* * Copyright (c) 2010-2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include "Swift/Controllers/UIEvents/UIEvent.h" #include #include namespace Swift { class RequestAddUserDialogUIEvent : public UIEvent { public: RequestAddUserDialogUIEvent(const JID& predefinedJID, const std::string& predefinedName) : preJID_(predefinedJID), preName_(predefinedName) {}; RequestAddUserDialogUIEvent() : preJID_(), preName_() {}; const JID& getPredefinedJID() const { return preJID_; }; const std::string& getPredefinedName() const { return preName_; }; private: JID preJID_; std::string preName_; }; } swift-im-2.0+dev6/Swift/Controllers/UIEvents/RequestChatUIEvent.h0000644000175000017500000000071612227051773024633 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include "Swiften/JID/JID.h" #include "Swift/Controllers/UIEvents/UIEvent.h" namespace Swift { class RequestChatUIEvent : public UIEvent { public: RequestChatUIEvent(const JID& contact) : contact_(contact) {}; JID getContact() {return contact_;} private: JID contact_; }; } swift-im-2.0+dev6/Swift/Controllers/UIEvents/RenameRosterItemUIEvent.h0000644000175000017500000000117212227051773025625 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include "Swift/Controllers/UIEvents/UIEvent.h" #include "Swiften/MUC/MUCBookmark.h" namespace Swift { class RenameRosterItemUIEvent : public UIEvent { public: RenameRosterItemUIEvent(const JID& jid, const std::string& newName) : jid_(jid), newName_(newName) {} const JID& getJID() const {return jid_;} const std::string& getNewName() const {return newName_;} private: JID jid_; std::string newName_; }; } swift-im-2.0+dev6/Swift/Controllers/UIEvents/AddContactUIEvent.h0000644000175000017500000000136412227051773024407 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include "Swift/Controllers/UIEvents/UIEvent.h" namespace Swift { class AddContactUIEvent : public UIEvent { public: AddContactUIEvent(const JID& jid, const std::string& name, const std::set& groups) : jid_(jid), name_(name), groups_(groups) {}; const std::string& getName() const { return name_; }; const JID& getJID() const { return jid_; }; const std::set& getGroups() const { return groups_; } private: JID jid_; std::string name_; std::set groups_; }; } swift-im-2.0+dev6/Swift/Controllers/UIEvents/ShowWhiteboardUIEvent.h0000644000175000017500000000074312227051773025334 0ustar kismithkismith/* * Copyright (c) 2012 Mateusz Piękos * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include "Swiften/JID/JID.h" #include "Swift/Controllers/UIEvents/UIEvent.h" namespace Swift { class ShowWhiteboardUIEvent : public UIEvent { public: ShowWhiteboardUIEvent(const JID& contact) : contact_(contact) {}; const JID& getContact() const {return contact_;} private: JID contact_; }; } swift-im-2.0+dev6/Swift/Controllers/UIEvents/AddMUCBookmarkUIEvent.h0000644000175000017500000000104112227051773025116 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include "Swift/Controllers/UIEvents/UIEvent.h" #include "Swiften/MUC/MUCBookmark.h" namespace Swift { class AddMUCBookmarkUIEvent : public UIEvent { public: AddMUCBookmarkUIEvent(const MUCBookmark& bookmark) : bookmark(bookmark) {}; const MUCBookmark& getBookmark() { return bookmark; } private: MUCBookmark bookmark; }; } swift-im-2.0+dev6/Swift/Controllers/UIEvents/EditMUCBookmarkUIEvent.h0000644000175000017500000000131212227051773025314 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include "Swift/Controllers/UIEvents/UIEvent.h" #include "Swiften/MUC/MUCBookmark.h" namespace Swift { class EditMUCBookmarkUIEvent : public UIEvent { public: EditMUCBookmarkUIEvent(const MUCBookmark& oldBookmark, const MUCBookmark& newBookmark) : oldBookmark(oldBookmark) , newBookmark(newBookmark) {}; const MUCBookmark& getOldBookmark() {return oldBookmark;}; const MUCBookmark& getNewBookmark() {return newBookmark;}; private: MUCBookmark oldBookmark; MUCBookmark newBookmark; }; } swift-im-2.0+dev6/Swift/Controllers/SettingConstants.cpp0000644000175000017500000000312312227051773023301 0ustar kismithkismith/* * Copyright (c) 2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { const SettingsProvider::Setting SettingConstants::IDLE_GOES_OFFLINE = SettingsProvider::Setting("idleGoesOffline", false); const SettingsProvider::Setting SettingConstants::IDLE_TIMEOUT = SettingsProvider::Setting("idleTimeout", 600); const SettingsProvider::Setting SettingConstants::SHOW_NOTIFICATIONS = SettingsProvider::Setting("showNotifications", true); const SettingsProvider::Setting SettingConstants::REQUEST_DELIVERYRECEIPTS = SettingsProvider::Setting("requestDeliveryReceipts", false); const SettingsProvider::Setting SettingConstants::FORGET_PASSWORDS = SettingsProvider::Setting("forgetPasswords", false); const SettingsProvider::Setting SettingConstants::REMEMBER_RECENT_CHATS = SettingsProvider::Setting("rememberRecentChats", true); const SettingsProvider::Setting SettingConstants::LAST_LOGIN_JID = SettingsProvider::Setting("lastLoginJID", ""); const SettingsProvider::Setting SettingConstants::LOGIN_AUTOMATICALLY = SettingsProvider::Setting("loginAutomatically", false); const SettingsProvider::Setting SettingConstants::SHOW_OFFLINE("showOffline", false); const SettingsProvider::Setting SettingConstants::EXPANDED_ROSTER_GROUPS("GroupExpandiness", ""); const SettingsProvider::Setting SettingConstants::PLAY_SOUNDS("playSounds", true); } swift-im-2.0+dev6/Swift/Controllers/Translator.h0000644000175000017500000000102312227051773021562 0ustar kismithkismith/* * 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 namespace Swift { class Translator { public: virtual ~Translator(); virtual std::string translate(const std::string& text, const std::string& context) = 0; static void setInstance(Translator* translator); static Translator* getInstance() { return translator; } private: static Translator* translator; }; } swift-im-2.0+dev6/Swift/Controllers/UIInterfaces/0000755000175000017500000000000012227051774021606 5ustar kismithkismithswift-im-2.0+dev6/Swift/Controllers/UIInterfaces/ChatWindow.h0000644000175000017500000001544112227051773024032 0ustar kismithkismith/* * Copyright (c) 2010-2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include #include #include #include #include #include #include namespace Swift { class AvatarManager; class TreeWidget; class Roster; class TabComplete; class RosterItem; class ContactRosterItem; class FileTransferController; class InviteToChatWindow; class ChatWindow { public: enum AckState {Pending, Received, Failed}; enum ReceiptState {ReceiptRequested, ReceiptReceived}; enum Tristate {Yes, No, Maybe}; enum OccupantAction {Kick, Ban, MakeModerator, MakeParticipant, MakeVisitor, AddContact}; enum RoomAction {ChangeSubject, Configure, Affiliations, Destroy, Invite}; enum FileTransferState {WaitingForAccept, Negotiating, Transferring, Canceled, Finished, FTFailed}; enum WhiteboardSessionState {WhiteboardAccepted, WhiteboardTerminated, WhiteboardRejected}; ChatWindow() {} virtual ~ChatWindow() {}; /** Add message to window. * @return id of added message (for acks). */ virtual std::string addMessage(const std::string& message, const std::string& senderName, bool senderIsSelf, boost::shared_ptr label, const std::string& avatarPath, const boost::posix_time::ptime& time) = 0; /** Adds action to window. * @return id of added message (for acks); */ virtual std::string addAction(const std::string& message, const std::string& senderName, bool senderIsSelf, boost::shared_ptr label, const std::string& avatarPath, const boost::posix_time::ptime& time) = 0; virtual void addSystemMessage(const std::string& message) = 0; virtual void addPresenceMessage(const std::string& message) = 0; virtual void addErrorMessage(const std::string& message) = 0; virtual void replaceMessage(const std::string& message, const std::string& id, const boost::posix_time::ptime& time) = 0; virtual void replaceWithAction(const std::string& message, const std::string& id, const boost::posix_time::ptime& time) = 0; // File transfer related stuff virtual std::string addFileTransfer(const std::string& senderName, bool senderIsSelf, const std::string& filename, const boost::uintmax_t sizeInBytes) = 0; virtual void setFileTransferProgress(std::string, const int percentageDone) = 0; virtual void setFileTransferStatus(std::string, const FileTransferState state, const std::string& msg = "") = 0; virtual void addMUCInvitation(const std::string& senderName, const JID& jid, const std::string& reason, const std::string& password, bool direct = true) = 0; virtual std::string addWhiteboardRequest(bool senderIsSelf) = 0; virtual void setWhiteboardSessionStatus(std::string id, const ChatWindow::WhiteboardSessionState state) = 0; // message receipts virtual void setMessageReceiptState(const std::string& id, ChatWindow::ReceiptState state) = 0; virtual void setContactChatState(ChatState::ChatStateType state) = 0; virtual void setName(const std::string& name) = 0; virtual void show() = 0; virtual void activate() = 0; virtual void setAvailableSecurityLabels(const std::vector& labels) = 0; virtual void setSecurityLabelsEnabled(bool enabled) = 0; virtual void setCorrectionEnabled(Tristate enabled) = 0; virtual void setUnreadMessageCount(int count) = 0; virtual void convertToMUC() = 0; // virtual TreeWidget *getTreeWidget() = 0; virtual void setSecurityLabelsError() = 0; virtual SecurityLabelsCatalog::Item getSelectedSecurityLabel() = 0; virtual void setInputEnabled(bool enabled) = 0; virtual void setRosterModel(Roster* model) = 0; virtual void setTabComplete(TabComplete* completer) = 0; virtual void replaceLastMessage(const std::string& message) = 0; virtual void setAckState(const std::string& id, AckState state) = 0; virtual void flash() = 0; virtual void setSubject(const std::string& subject) = 0; virtual void setAffiliations(MUCOccupant::Affiliation, const std::vector&) = 0; virtual void setAvailableRoomActions(const std::vector &actions) = 0; /** * Set an alert on the window. * @param alertText Description of alert (required). * @param buttonText Button text to use (optional, no button is shown if empty). */ virtual void setAlert(const std::string& alertText, const std::string& buttonText = "") = 0; /** * Removes an alert. */ virtual void cancelAlert() = 0; /** * Actions that can be performed on the selected occupant. */ virtual void setAvailableOccupantActions(const std::vector& actions) = 0; /** * A room configuration has been requested, show the form. * If the form is cancelled, must emit onConfigurationFormCancelled(). */ virtual void showRoomConfigurationForm(Form::ref) = 0; virtual InviteToChatWindow* createInviteToChatWindow() = 0; boost::signal onClosed; boost::signal onAllMessagesRead; boost::signal onSendMessageRequest; boost::signal onSendCorrectionMessageRequest; boost::signal onUserTyping; boost::signal onUserCancelsTyping; boost::signal onAlertButtonClicked; boost::signal onOccupantSelectionChanged; boost::signal onOccupantActionSelected; boost::signal onChangeSubjectRequest; boost::signal onConfigureRequest; boost::signal onDestroyRequest; boost::signal onInvitePersonToThisMUCRequest; boost::signal onConfigurationFormCancelled; boost::signal onGetAffiliationsRequest; boost::signal onSetAffiliationRequest; boost::signal >& changes)> onChangeAffiliationsRequest; boost::signal onLogCleared; // File transfer related boost::signal onFileTransferCancel; boost::signal onFileTransferStart; boost::signal onFileTransferAccept; boost::signal onSendFileRequest; //Whiteboard related boost::signal onWhiteboardSessionAccept; boost::signal onWhiteboardSessionCancel; boost::signal onWhiteboardWindowShow; }; } swift-im-2.0+dev6/Swift/Controllers/UIInterfaces/FileTransferListWidget.h0000644000175000017500000000066012227051773026344 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once namespace Swift { class FileTransferOverview; class FileTransferListWidget { public: virtual ~FileTransferListWidget() {} virtual void show() = 0; virtual void activate() = 0; virtual void setFileTransferOverview(FileTransferOverview*) = 0; }; } swift-im-2.0+dev6/Swift/Controllers/UIInterfaces/UIFactory.h0000644000175000017500000000333712227051773023631 0ustar kismithkismith/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Swift { class UIFactory : public ChatListWindowFactory, public ChatWindowFactory, public HistoryWindowFactory, public EventWindowFactory, public LoginWindowFactory, public MainWindowFactory, public MUCSearchWindowFactory, public XMLConsoleWidgetFactory, public UserSearchWindowFactory, public JoinMUCWindowFactory, public ProfileWindowFactory, public ContactEditWindowFactory, public AdHocCommandWindowFactory, public FileTransferListWidgetFactory, public WhiteboardWindowFactory { public: virtual ~UIFactory() {} }; } swift-im-2.0+dev6/Swift/Controllers/UIInterfaces/MainWindowFactory.h0000644000175000017500000000106412227051773025363 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #ifndef SWIFTEN_MainWindowFactory_H #define SWIFTEN_MainWindowFactory_H #include "Swiften/JID/JID.h" #include "Swift/Controllers/UIEvents/UIEventStream.h" namespace Swift { class MainWindow; class MainWindowFactory { public: virtual ~MainWindowFactory() {}; /** * Transfers ownership of result. */ virtual MainWindow* createMainWindow(UIEventStream* eventStream) = 0; }; } #endif swift-im-2.0+dev6/Swift/Controllers/UIInterfaces/JoinMUCWindow.h0000644000175000017500000000104312227051773024410 0ustar kismithkismith/* * 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 #include #include #include namespace Swift { class JoinMUCWindow { public: virtual ~JoinMUCWindow() {}; virtual void setNick(const std::string& nick) = 0; virtual void setMUC(const std::string& nick) = 0; virtual void show() = 0; boost::signal onSearchMUC; }; } swift-im-2.0+dev6/Swift/Controllers/UIInterfaces/ProfileWindow.h0000644000175000017500000000124112227051773024544 0ustar kismithkismith/* * 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 #include #include namespace Swift { class ProfileWindow { public: virtual ~ProfileWindow() {}; virtual void setVCard(VCard::ref vcard) = 0; virtual void setEnabled(bool b) = 0; virtual void setProcessing(bool b) = 0; virtual void setError(const std::string&) = 0; virtual void show() = 0; virtual void hide() = 0; boost::signal onVCardChangeRequest; }; } swift-im-2.0+dev6/Swift/Controllers/UIInterfaces/ChatListWindow.h0000644000175000017500000000433612227051773024667 0ustar kismithkismith/* * Copyright (c) 2010-2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include #include #include namespace Swift { class ChatListWindow { public: class Chat { public: Chat(const JID& jid, const std::string& chatName, const std::string& activity, int unreadCount, StatusShow::Type statusType, const boost::filesystem::path& avatarPath, bool isMUC, const std::string& nick = "") : jid(jid), chatName(chatName), activity(activity), statusType(statusType), isMUC(isMUC), nick(nick), unreadCount(unreadCount), avatarPath(avatarPath) {} /** Assume that nicks and other transient features aren't important for equality */ bool operator==(const Chat& other) const { return jid.toBare() == other.jid.toBare() && isMUC == other.isMUC; }; void setUnreadCount(int unread) { unreadCount = unread; } void setStatusType(StatusShow::Type type) { statusType = type; } void setAvatarPath(const boost::filesystem::path& path) { avatarPath = path; } JID jid; std::string chatName; std::string activity; StatusShow::Type statusType; bool isMUC; std::string nick; int unreadCount; boost::filesystem::path avatarPath; }; virtual ~ChatListWindow(); virtual void setBookmarksEnabled(bool enabled) = 0; virtual void addMUCBookmark(const MUCBookmark& bookmark) = 0; virtual void addWhiteboardSession(const ChatListWindow::Chat& chat) = 0; virtual void removeWhiteboardSession(const JID& jid) = 0; virtual void removeMUCBookmark(const MUCBookmark& bookmark) = 0; virtual void setRecents(const std::list& recents) = 0; virtual void setUnreadCount(int unread) = 0; virtual void clearBookmarks() = 0; boost::signal onMUCBookmarkActivated; boost::signal onRecentActivated; boost::signal onWhiteboardActivated; boost::signal onClearRecentsRequested; }; } swift-im-2.0+dev6/Swift/Controllers/UIInterfaces/AdHocCommandWindowFactory.h0000644000175000017500000000117712227051773026761 0ustar kismithkismith/* * Copyright (c) 2010-2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include namespace Swift { class AdHocCommandWindowFactory { public: virtual ~AdHocCommandWindowFactory() {} /** * The UI should deal with the lifetime of this window (i.e. DeleteOnClose), * so the result isn't returned. */ virtual void createAdHocCommandWindow(boost::shared_ptr command) = 0; }; } swift-im-2.0+dev6/Swift/Controllers/UIInterfaces/EventWindow.h0000644000175000017500000000121412227051773024225 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include "Swift/Controllers/XMPPEvents/StanzaEvent.h" namespace Swift { class EventWindow { public: EventWindow(bool candelete = true) : canDelete_(candelete) {} bool canDelete() const { return canDelete_; } virtual ~EventWindow() {}; virtual void addEvent(boost::shared_ptr event, bool active) = 0; virtual void removeEvent(boost::shared_ptr event) = 0; private: bool canDelete_; }; } swift-im-2.0+dev6/Swift/Controllers/UIInterfaces/WhiteboardWindow.h0000644000175000017500000000110212227051773025230 0ustar kismithkismith/* * Copyright (c) 2012 Mateusz Piękos * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include "Swiften/Base/boost_bsignals.h" #include namespace Swift { class WhiteboardSession; class WhiteboardElement; class WhiteboardWindow { public: virtual ~WhiteboardWindow() {} virtual void show() = 0; virtual void setSession(boost::shared_ptr session) = 0; virtual void activateWindow() = 0; virtual void setName(const std::string& name) = 0; }; } swift-im-2.0+dev6/Swift/Controllers/UIInterfaces/AdHocCommandWindow.h0000644000175000017500000000041612227051773025424 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once namespace Swift { class AdHocCommandWindow { public: virtual ~AdHocCommandWindow() {}; }; } swift-im-2.0+dev6/Swift/Controllers/UIInterfaces/UserSearchWindowFactory.h0000644000175000017500000000102412227051773026537 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include "Swift/Controllers/UIInterfaces/UserSearchWindow.h" namespace Swift { class UIEventStream; class UserSearchWindowFactory { public: virtual ~UserSearchWindowFactory() {}; virtual UserSearchWindow* createUserSearchWindow(UserSearchWindow::Type type, UIEventStream* eventStream, const std::set& groups) = 0; }; } swift-im-2.0+dev6/Swift/Controllers/UIInterfaces/LoginWindowFactory.h0000644000175000017500000000100612227051773025543 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #ifndef SWIFTEN_LoginWindowFactory_H #define SWIFTEN_LoginWindowFactory_H namespace Swift { class LoginWindow; class UIEventStream; class LoginWindowFactory { public: virtual ~LoginWindowFactory() {}; /** * Transfers ownership of result. */ virtual LoginWindow* createLoginWindow(UIEventStream* uiEventStream) = 0; }; } #endif swift-im-2.0+dev6/Swift/Controllers/UIInterfaces/ChatListWindow.cpp0000644000175000017500000000042512227051773025215 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Swift/Controllers/UIInterfaces/ChatListWindow.h" namespace Swift { ChatListWindow::~ChatListWindow() { } } swift-im-2.0+dev6/Swift/Controllers/UIInterfaces/MUCSearchWindowFactory.h0000644000175000017500000000064312227051773026253 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include "Swift/Controllers/UIInterfaces/MUCSearchWindow.h" namespace Swift { class UIEventStream; class MUCSearchWindowFactory { public: virtual ~MUCSearchWindowFactory() {}; virtual MUCSearchWindow* createMUCSearchWindow() = 0; }; } swift-im-2.0+dev6/Swift/Controllers/UIInterfaces/UserSearchWindow.h0000644000175000017500000000252512227051773025216 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include "Swiften/Base/boost_bsignals.h" #include #include #include "Swiften/JID/JID.h" #include "Swift/Controllers/Chat/UserSearchController.h" namespace Swift { class UserSearchWindow { public: enum Type {AddContact, ChatToContact}; virtual ~UserSearchWindow() {} virtual void clear() = 0; virtual void setResults(const std::vector& results) = 0; virtual void setResultsForm(const Form::ref results) = 0; virtual void addSavedServices(const std::vector& services) = 0; virtual void setSelectedService(const JID& service) = 0; virtual void setServerSupportsSearch(bool support) = 0; virtual void setSearchError(bool support) = 0; virtual void setSearchFields(boost::shared_ptr fields) = 0; virtual void setNameSuggestions(const std::vector& suggestions) = 0; virtual void prepopulateJIDAndName(const JID& jid, const std::string& name) = 0; virtual void show() = 0; boost::signal onFormRequested; boost::signal, const JID&)> onSearchRequested; boost::signal onNameSuggestionRequested; }; } swift-im-2.0+dev6/Swift/Controllers/UIInterfaces/ContactEditWindowFactory.h0000644000175000017500000000063212227051773026700 0ustar kismithkismith/* * 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 namespace Swift { class ContactEditWindowFactory { public: virtual ~ContactEditWindowFactory() {}; virtual ContactEditWindow* createContactEditWindow() = 0; }; } swift-im-2.0+dev6/Swift/Controllers/UIInterfaces/ProfileWindowFactory.h0000644000175000017500000000060612227051773026100 0ustar kismithkismith/* * 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 namespace Swift { class ProfileWindowFactory { public: virtual ~ProfileWindowFactory() {}; virtual ProfileWindow* createProfileWindow() = 0; }; } swift-im-2.0+dev6/Swift/Controllers/UIInterfaces/ContactEditWindow.h0000644000175000017500000000165412227051773025355 0ustar kismithkismith/* * 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 #include #include #include #include namespace Swift { class JID; class VCardManager; class ContactEditWindow { public: virtual ~ContactEditWindow() {} virtual void setEnabled(bool b) = 0; virtual void setNameSuggestions(const std::vector& suggestions) = 0; virtual void setContact(const JID& jid, const std::string& name, const std::vector& groups, const std::set& allGroups) = 0; virtual void show() = 0; virtual void hide() = 0; boost::signal onRemoveContactRequest; boost::signal& /* groups */)> onChangeContactRequest; }; } swift-im-2.0+dev6/Swift/Controllers/UIInterfaces/ChatWindowFactory.h0000644000175000017500000000104712227051773025357 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #ifndef SWIFTEN_CHATWINDOWFACTORY_H #define SWIFTEN_CHATWINDOWFACTORY_H #include "Swiften/JID/JID.h" namespace Swift { class ChatWindow; class UIEventStream; class ChatWindowFactory { public: virtual ~ChatWindowFactory() {}; /** * Transfers ownership of result. */ virtual ChatWindow* createChatWindow(const JID &contact, UIEventStream* eventStream) = 0; }; } #endif swift-im-2.0+dev6/Swift/Controllers/UIInterfaces/HistoryWindowFactory.h0000644000175000017500000000066612227051773026147 0ustar kismithkismith/* * Copyright (c) 2012 Catalin Badea * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include namespace Swift { class UIEventStream; class HistoryWindowFactory { public: virtual ~HistoryWindowFactory() {}; virtual HistoryWindow* createHistoryWindow(UIEventStream* eventStream) = 0; }; } swift-im-2.0+dev6/Swift/Controllers/UIInterfaces/InviteToChatWindow.h0000644000175000017500000000123512227051773025510 0ustar kismithkismith/* * Copyright (c) 2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include #include namespace Swift { class InviteToChatWindow { public: virtual ~InviteToChatWindow() {}; virtual void setAutoCompletions(std::vector > completions) = 0; virtual std::string getReason() const = 0; virtual std::vector getJIDs() const = 0; boost::signal onCompleted; boost::signal onDismissed; }; } swift-im-2.0+dev6/Swift/Controllers/UIInterfaces/ChatListWindowFactory.h0000644000175000017500000000067012227051773026214 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include "Swift/Controllers/UIInterfaces/ChatListWindow.h" namespace Swift { class UIEventStream; class ChatListWindowFactory { public: virtual ~ChatListWindowFactory() {} virtual ChatListWindow* createChatListWindow(UIEventStream* uiEventStream) = 0; }; } swift-im-2.0+dev6/Swift/Controllers/UIInterfaces/JoinMUCWindowFactory.h0000644000175000017500000000067012227051773025745 0ustar kismithkismith/* * 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 namespace Swift { class UIEventStream; class JoinMUCWindowFactory { public: virtual ~JoinMUCWindowFactory() {}; virtual JoinMUCWindow* createJoinMUCWindow(UIEventStream* uiEventStream) = 0; }; } swift-im-2.0+dev6/Swift/Controllers/UIInterfaces/MainWindow.h0000644000175000017500000000300412227051773024027 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include "Swiften/JID/JID.h" #include "Swiften/Elements/StatusShow.h" #include "Swiften/Elements/DiscoItems.h" #include "Swiften/TLS/Certificate.h" #include "Swiften/Base/boost_bsignals.h" #include namespace Swift { class Roster; class MainWindow { public: MainWindow(bool candelete = true) : canDelete_(candelete) {} virtual ~MainWindow() {}; bool canDelete() const { return canDelete_; } virtual void setMyNick(const std::string& name) = 0; virtual void setMyJID(const JID& jid) = 0; virtual void setMyAvatarPath(const std::string& path) = 0; virtual void setMyStatusText(const std::string& status) = 0; virtual void setMyStatusType(StatusShow::Type type) = 0; /** Must be able to cope with NULL to clear the roster */ virtual void setRosterModel(Roster* roster) = 0; virtual void setConnecting() = 0; virtual void setAvailableAdHocCommands(const std::vector& commands) = 0; virtual void setStreamEncryptionStatus(bool tlsInPlaceAndValid) = 0; virtual void openCertificateDialog(const std::vector& chain) = 0; boost::signal onChangeStatusRequest; boost::signal onSignOutRequest; boost::signal onShowCertificateRequest; private: bool canDelete_; }; } swift-im-2.0+dev6/Swift/Controllers/UIInterfaces/WhiteboardWindowFactory.h0000644000175000017500000000070212227051773026565 0ustar kismithkismith/* * Copyright (c) 2012 Mateusz Piękos * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once namespace Swift { class WhiteboardSession; class WhiteboardWindow; class WhiteboardWindowFactory { public : virtual ~WhiteboardWindowFactory() {}; virtual WhiteboardWindow* createWhiteboardWindow(boost::shared_ptr whiteboardSession) = 0; }; } swift-im-2.0+dev6/Swift/Controllers/UIInterfaces/LoginWindow.h0000644000175000017500000000356212227051773024224 0ustar kismithkismith/* * Copyright (c) 2010-2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include #include #include namespace Swift { class MainWindow; class LoginWindow { public: virtual ~LoginWindow() {}; virtual void selectUser(const std::string&) = 0; virtual void morphInto(MainWindow *mainWindow) = 0; virtual void loggedOut() = 0; virtual void setShowNotificationToggle(bool) = 0; virtual void setMessage(const std::string&) = 0; virtual void setIsLoggingIn(bool loggingIn) = 0; virtual void addAvailableAccount(const std::string& defaultJID, const std::string& defaultPassword, const std::string& defaultCertificate, const ClientOptions& options) = 0; virtual void removeAvailableAccount(const std::string& jid) = 0; /** The certificate is what is used for login, the certificatePath is used for remembering paths to populate the loginwindow with*/ boost::signal onLoginRequest; virtual void setLoginAutomatically(bool loginAutomatically) = 0; virtual void quit() = 0; /** Blocking request whether a cert should be permanently trusted.*/ virtual bool askUserToTrustCertificatePermanently(const std::string& message, const std::vector& certificateChain) = 0; boost::signal onCancelLoginRequest; boost::signal onQuitRequest; boost::signal onPurgeSavedLoginRequest; }; } swift-im-2.0+dev6/Swift/Controllers/UIInterfaces/FileTransferListWidgetFactory.h0000644000175000017500000000065712227051773027702 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include "Swift/Controllers/UIInterfaces/FileTransferListWidget.h" namespace Swift { class FileTransferListWidgetFactory { public: virtual ~FileTransferListWidgetFactory() {} virtual FileTransferListWidget* createFileTransferListWidget() = 0; }; } swift-im-2.0+dev6/Swift/Controllers/UIInterfaces/XMLConsoleWidget.h0000644000175000017500000000075712227051774025117 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include namespace Swift { class XMLConsoleWidget { public: virtual ~XMLConsoleWidget(); virtual void handleDataRead(const SafeByteArray& data) = 0; virtual void handleDataWritten(const SafeByteArray& data) = 0; virtual void show() = 0; virtual void activate() = 0; }; } swift-im-2.0+dev6/Swift/Controllers/UIInterfaces/MUCSearchWindow.h0000644000175000017500000000147412227051773024726 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include "Swiften/Base/boost_bsignals.h" #include #include #include #include "Swiften/JID/JID.h" #include namespace Swift { class MUCSearchWindow { public: virtual ~MUCSearchWindow() {}; virtual void clearList() = 0; virtual void addService(const MUCService& service) = 0; virtual void addSavedServices(const std::list& services) = 0; virtual void setSearchInProgress(bool searching) = 0; virtual void show() = 0; boost::signal onSearchService; boost::signal&)> onFinished; }; } swift-im-2.0+dev6/Swift/Controllers/UIInterfaces/XMLConsoleWidget.cpp0000644000175000017500000000043312227051774025441 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Swift/Controllers/UIInterfaces/XMLConsoleWidget.h" namespace Swift { XMLConsoleWidget::~XMLConsoleWidget() { } } swift-im-2.0+dev6/Swift/Controllers/UIInterfaces/XMLConsoleWidgetFactory.h0000644000175000017500000000065012227051774026437 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include "Swift/Controllers/UIInterfaces/XMLConsoleWidget.h" namespace Swift { class UIEventStream; class XMLConsoleWidgetFactory { public: virtual ~XMLConsoleWidgetFactory() {}; virtual XMLConsoleWidget* createXMLConsoleWidget() = 0; }; } swift-im-2.0+dev6/Swift/Controllers/UIInterfaces/HistoryWindow.h0000644000175000017500000000253612227051773024615 0ustar kismithkismith/* * Copyright (c) 2012 Catalin Badea * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include namespace Swift { class HistoryWindow { public: virtual ~HistoryWindow() {}; virtual void activate() = 0; virtual void setRosterModel(Roster*) = 0; virtual void addMessage(const std::string &message, const std::string &senderName, bool senderIsSelf, const std::string& avatarPath, const boost::posix_time::ptime& time, bool addAtTheTop) = 0; virtual void resetConversationView() = 0; virtual void resetConversationViewTopInsertPoint() = 0; // this is a temporary fix used in adding messages at the top virtual void setDate(const boost::gregorian::date& date) = 0; virtual std::string getSearchBoxText() = 0; virtual boost::gregorian::date getLastVisibleDate() = 0; boost::signal onSelectedContactChanged; boost::signal onReturnPressed; boost::signal onScrollReachedTop; boost::signal onScrollReachedBottom; boost::signal onPreviousButtonClicked; boost::signal onNextButtonClicked; boost::signal onCalendarClicked; }; } swift-im-2.0+dev6/Swift/Controllers/UIInterfaces/EventWindowFactory.h0000644000175000017500000000061212227051773025556 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once namespace Swift { class EventWindow; class EventWindowFactory { public: virtual ~EventWindowFactory() {}; /** * Transfers ownership of result. */ virtual EventWindow* createEventWindow() = 0; }; } swift-im-2.0+dev6/Swift/Controllers/PreviousStatusStore.cpp0000644000175000017500000000262212227051773024027 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "PreviousStatusStore.h" #include "Swiften/Base/foreach.h" namespace Swift { PreviousStatusStore::PreviousStatusStore() { } PreviousStatusStore::~PreviousStatusStore() { } void PreviousStatusStore::addStatus(StatusShow::Type status, const std::string& message) { //FIXME: remove old entries store_.push_back(TypeStringPair(status, message)); } std::vector PreviousStatusStore::exactMatchSuggestions(StatusShow::Type status, const std::string& message) { std::vector suggestions; suggestions.push_back(TypeStringPair(status, message)); return suggestions; } std::vector PreviousStatusStore::getSuggestions(const std::string& message) { std::vector suggestions; foreach (TypeStringPair status, store_) { if (status.second == message) { suggestions.clear(); suggestions.push_back(status); break; } else if (status.second.find(message) != std::string::npos) { suggestions.push_back(status); } } if (suggestions.empty()) { TypeStringPair suggestion(StatusShow::Online, message); suggestions.push_back(suggestion); } if (suggestions.size() == 1) { suggestions = exactMatchSuggestions(suggestions[0].first, suggestions[0].second); } return suggestions; } } swift-im-2.0+dev6/Swift/Controllers/EventWindowController.h0000644000175000017500000000153212227051773023753 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include "Swift/Controllers/UIInterfaces/EventWindowFactory.h" #include "Swift/Controllers/UIInterfaces/EventWindow.h" #include "Swift/Controllers/XMPPEvents/EventController.h" namespace Swift { class EventWindowController { public: EventWindowController(EventController* eventController, EventWindowFactory* windowFactory); ~EventWindowController(); private: void handleEventQueueEventAdded(boost::shared_ptr event); void handleEventConcluded(boost::shared_ptr event); EventController* eventController_; EventWindowFactory* windowFactory_; EventWindow* window_; boost::bsignals::scoped_connection eventAddedConnection_; }; } swift-im-2.0+dev6/Swift/Controllers/ProfileController.h0000644000175000017500000000221512227051773023101 0ustar kismithkismith/* * 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 #include #include #include namespace Swift { class UIEventStream; class ProfileWindowFactory; class ProfileWindow; class VCardManager; class ProfileController { public: ProfileController(VCardManager* vcardManager, ProfileWindowFactory* profileWindowFactory, UIEventStream* uiEventStream); ~ProfileController(); void setAvailable(bool b); private: void handleUIEvent(UIEvent::ref event); void handleVCardChangeRequest(VCard::ref vcard); void handleSetVCardResponse(ErrorPayload::ref); void handleOwnVCardChanged(VCard::ref vcard); void updateDialogStatus(); private: VCardManager* vcardManager; ProfileWindowFactory* profileWindowFactory; UIEventStream* uiEventStream; bool available; SetVCardRequest::ref pendingSetVCardRequest; ProfileWindow* profileWindow; bool gettingVCard; }; } swift-im-2.0+dev6/Swift/Controllers/ContactEditController.h0000644000175000017500000000267412227051773023713 0ustar kismithkismith/* * 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 #include #include #include #include #include #include #include namespace Swift { class UIEventStream; class ContactEditWindowFactory; class ContactEditWindow; class RosterController; class VCardManager; class ContactEditController { public: ContactEditController(RosterController* rosterController, VCardManager* vcardManager, ContactEditWindowFactory* contactEditWindowFactory, UIEventStream* uiEventStream); ~ContactEditController(); void setAvailable(bool b); public: static std::vector nameSuggestionsFromVCard(VCard::ref vcard); private: void handleRemoveContactRequest(); void handleChangeContactRequest(const std::string& name, const std::set& groups); private: void handleUIEvent(UIEvent::ref event); void handleVCardChanged(const JID& jid, VCard::ref vcard); private: boost::optional currentContact; RosterController* rosterController; VCardManager* vcardManager; ContactEditWindowFactory* contactEditWindowFactory; UIEventStream* uiEventStream; JID jid; ContactEditWindow* contactEditWindow; }; } swift-im-2.0+dev6/Swift/Controllers/XMLConsoleController.cpp0000644000175000017500000000263312227051774024024 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Swift/Controllers/XMLConsoleController.h" #include "Swift/Controllers/UIInterfaces/XMLConsoleWidgetFactory.h" #include "Swift/Controllers/UIEvents/RequestXMLConsoleUIEvent.h" namespace Swift { XMLConsoleController::XMLConsoleController(UIEventStream* uiEventStream, XMLConsoleWidgetFactory* xmlConsoleWidgetFactory) : xmlConsoleWidgetFactory(xmlConsoleWidgetFactory), xmlConsoleWidget(NULL) { uiEventStream->onUIEvent.connect(boost::bind(&XMLConsoleController::handleUIEvent, this, _1)); } XMLConsoleController::~XMLConsoleController() { delete xmlConsoleWidget; } void XMLConsoleController::handleUIEvent(boost::shared_ptr rawEvent) { boost::shared_ptr event = boost::dynamic_pointer_cast(rawEvent); if (event != NULL) { if (xmlConsoleWidget == NULL) { xmlConsoleWidget = xmlConsoleWidgetFactory->createXMLConsoleWidget(); } xmlConsoleWidget->show(); xmlConsoleWidget->activate(); } } void XMLConsoleController::handleDataRead(const SafeByteArray& data) { if (xmlConsoleWidget) { xmlConsoleWidget->handleDataRead(data); } } void XMLConsoleController::handleDataWritten(const SafeByteArray& data) { if (xmlConsoleWidget) { xmlConsoleWidget->handleDataWritten(data); } } } swift-im-2.0+dev6/Swift/Controllers/ChatMessageSummarizer.cpp0000644000175000017500000000240312227051773024232 0ustar kismithkismith/* * Copyright (c) 2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include using namespace Swift; using namespace std; string ChatMessageSummarizer::getSummary(const string& current, const vector& unreads) { vector others; int currentUnread = 0; int otherCount = 0; foreach (UnreadPair unread, unreads) { if (unread.first == current) { currentUnread += unread.second; } else { if (unread.second > 0) { otherCount += unread.second; others.push_back(unread); } } } string myString(current); if (currentUnread > 0) { string result(QT_TRANSLATE_NOOP("", "%1% (%2%)")); myString = str(format(result) % current % currentUnread); } if (others.size() > 1) { string result(QT_TRANSLATE_NOOP("", "%1% and %2% others (%3%)")); myString = str(format(result) % myString % others.size() % otherCount); } else if (!others.empty()) { string result(QT_TRANSLATE_NOOP("", "%1%, %2% (%3%)")); myString = str(format(result) % myString % others[0].first % otherCount); } return myString; } swift-im-2.0+dev6/Swift/Controllers/Intl.h0000644000175000017500000000050012227051773020336 0ustar kismithkismith/* * 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 #define QT_TRANSLATE_NOOP(context, text) \ Swift::Translator::getInstance()->translate(text, context) swift-im-2.0+dev6/Swift/Controllers/HistoryViewController.cpp0000644000175000017500000003114612227051773024335 0ustar kismithkismith/* * Copyright (c) 2012 Catalin Badea * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include #include #include #include #include #include #include #include #include #include namespace Swift { static const std::string category[] = { "Contacts", "MUC", "Contacts" }; HistoryViewController::HistoryViewController( const JID& selfJID, UIEventStream* uiEventStream, HistoryController* historyController, NickResolver* nickResolver, AvatarManager* avatarManager, PresenceOracle* presenceOracle, HistoryWindowFactory* historyWindowFactory) : selfJID_(selfJID), uiEventStream_(uiEventStream), historyController_(historyController), nickResolver_(nickResolver), avatarManager_(avatarManager), presenceOracle_(presenceOracle), historyWindowFactory_(historyWindowFactory), historyWindow_(NULL), selectedItem_(NULL), currentResultDate_(boost::gregorian::not_a_date_time) { uiEventStream_->onUIEvent.connect(boost::bind(&HistoryViewController::handleUIEvent, this, _1)); roster_ = new Roster(false, true); } HistoryViewController::~HistoryViewController() { uiEventStream_->onUIEvent.disconnect(boost::bind(&HistoryViewController::handleUIEvent, this, _1)); if (historyWindow_) { historyWindow_->onSelectedContactChanged.disconnect(boost::bind(&HistoryViewController::handleSelectedContactChanged, this, _1)); historyWindow_->onReturnPressed.disconnect(boost::bind(&HistoryViewController::handleReturnPressed, this, _1)); historyWindow_->onScrollReachedTop.disconnect(boost::bind(&HistoryViewController::handleScrollReachedTop, this, _1)); historyWindow_->onScrollReachedBottom.disconnect(boost::bind(&HistoryViewController::handleScrollReachedBottom, this, _1)); historyWindow_->onPreviousButtonClicked.disconnect(boost::bind(&HistoryViewController::handlePreviousButtonClicked, this)); historyWindow_->onNextButtonClicked.disconnect(boost::bind(&HistoryViewController::handleNextButtonClicked, this)); historyWindow_->onCalendarClicked.disconnect(boost::bind(&HistoryViewController::handleCalendarClicked, this, _1)); historyController_->onNewMessage.disconnect(boost::bind(&HistoryViewController::handleNewMessage, this, _1)); presenceOracle_->onPresenceChange.disconnect(boost::bind(&HistoryViewController::handlePresenceChanged, this, _1)); avatarManager_->onAvatarChanged.disconnect(boost::bind(&HistoryViewController::handleAvatarChanged, this, _1)); delete historyWindow_; } delete roster_; } void HistoryViewController::handleUIEvent(boost::shared_ptr rawEvent) { boost::shared_ptr event = boost::dynamic_pointer_cast(rawEvent); if (event != NULL) { if (historyWindow_ == NULL) { historyWindow_ = historyWindowFactory_->createHistoryWindow(uiEventStream_); historyWindow_->onSelectedContactChanged.connect(boost::bind(&HistoryViewController::handleSelectedContactChanged, this, _1)); historyWindow_->onReturnPressed.connect(boost::bind(&HistoryViewController::handleReturnPressed, this, _1)); historyWindow_->onScrollReachedTop.connect(boost::bind(&HistoryViewController::handleScrollReachedTop, this, _1)); historyWindow_->onScrollReachedBottom.connect(boost::bind(&HistoryViewController::handleScrollReachedBottom, this, _1)); historyWindow_->onPreviousButtonClicked.connect(boost::bind(&HistoryViewController::handlePreviousButtonClicked, this)); historyWindow_->onNextButtonClicked.connect(boost::bind(&HistoryViewController::handleNextButtonClicked, this)); historyWindow_->onCalendarClicked.connect(boost::bind(&HistoryViewController::handleCalendarClicked, this, _1)); historyController_->onNewMessage.connect(boost::bind(&HistoryViewController::handleNewMessage, this, _1)); presenceOracle_->onPresenceChange.connect(boost::bind(&HistoryViewController::handlePresenceChanged, this, _1)); avatarManager_->onAvatarChanged.connect(boost::bind(&HistoryViewController::handleAvatarChanged, this, _1)); historyWindow_->setRosterModel(roster_); } // populate roster by doing an empty search handleReturnPressed(std::string()); historyWindow_->activate(); } } void HistoryViewController::handleSelectedContactChanged(RosterItem* newContact) { // FIXME: signal is triggerd twice. ContactRosterItem* contact = dynamic_cast(newContact); if (contact && selectedItem_ != contact) { selectedItem_ = contact; historyWindow_->resetConversationView(); } else { return; } JID contactJID = contact->getJID(); std::vector messages; for (int it = HistoryMessage::Chat; it <= HistoryMessage::PrivateMessage; it++) { HistoryMessage::Type type = static_cast(it); if (contacts_[type].count(contactJID)) { currentResultDate_ = *contacts_[type][contactJID].rbegin(); selectedItemType_ = type; messages = historyController_->getMessagesFromDate(selfJID_, contactJID, type, currentResultDate_); } } historyWindow_->setDate(currentResultDate_); foreach (const HistoryMessage& message, messages) { addNewMessage(message, false); } } void HistoryViewController::handleNewMessage(const HistoryMessage& message) { JID contactJID = message.getFromJID().toBare() == selfJID_ ? message.getToJID() : message.getFromJID(); JID displayJID; if (message.getType() == HistoryMessage::PrivateMessage) { displayJID = contactJID; } else { displayJID = contactJID.toBare(); } // check current conversation if (selectedItem_ && selectedItem_->getJID() == displayJID) { if (historyWindow_->getLastVisibleDate() == message.getTime().date()) { addNewMessage(message, false); } } // check if the new message matches the query if (message.getMessage().find(historyWindow_->getSearchBoxText()) == std::string::npos) { return; } // update contacts if (!contacts_[message.getType()].count(displayJID)) { roster_->addContact(displayJID, displayJID, nickResolver_->jidToNick(displayJID), category[message.getType()], avatarManager_->getAvatarPath(displayJID).string()); } contacts_[message.getType()][displayJID].insert(message.getTime().date()); } void HistoryViewController::addNewMessage(const HistoryMessage& message, bool addAtTheTop) { bool senderIsSelf = message.getFromJID().toBare() == selfJID_; std::string avatarPath = avatarManager_->getAvatarPath(message.getFromJID()).string(); std::string nick = message.getType() != HistoryMessage::Groupchat ? nickResolver_->jidToNick(message.getFromJID()) : message.getFromJID().getResource(); historyWindow_->addMessage(message.getMessage(), nick, senderIsSelf, avatarPath, message.getTime(), addAtTheTop); } void HistoryViewController::handleReturnPressed(const std::string& keyword) { reset(); for (int it = HistoryMessage::Chat; it <= HistoryMessage::PrivateMessage; it++) { HistoryMessage::Type type = static_cast(it); contacts_[type] = historyController_->getContacts(selfJID_, type, keyword); for (ContactsMap::const_iterator contact = contacts_[type].begin(); contact != contacts_[type].end(); contact++) { const JID& jid = contact->first; std::string nick; if (type == HistoryMessage::PrivateMessage) { nick = jid.toString(); } else { nick = nickResolver_->jidToNick(jid); } roster_->addContact(jid, jid, nick, category[type], avatarManager_->getAvatarPath(jid).string()); Presence::ref presence = getPresence(jid, type == HistoryMessage::Groupchat); if (presence.get()) { roster_->applyOnItem(SetPresence(presence, JID::WithoutResource), jid); } } } } void HistoryViewController::handleScrollReachedTop(const boost::gregorian::date& date) { if (!selectedItem_) { return; } std::vector messages = historyController_->getMessagesFromPreviousDate(selfJID_, selectedItem_->getJID(), selectedItemType_, date); foreach (const HistoryMessage& message, messages) { addNewMessage(message, true); } historyWindow_->resetConversationViewTopInsertPoint(); } void HistoryViewController::handleScrollReachedBottom(const boost::gregorian::date& date) { if (!selectedItem_) { return; } std::vector messages = historyController_->getMessagesFromNextDate(selfJID_, selectedItem_->getJID(), selectedItemType_, date); foreach (const HistoryMessage& message, messages) { addNewMessage(message, false); } } void HistoryViewController::handleNextButtonClicked() { if (!selectedItem_) { return; } std::set::iterator date = contacts_[selectedItemType_][selectedItem_->getJID()].find(currentResultDate_); if (*date == *contacts_[selectedItemType_][selectedItem_->getJID()].rbegin()) { return; } historyWindow_->resetConversationView(); currentResultDate_ = *(++date); std::vector messages = historyController_->getMessagesFromDate(selfJID_, selectedItem_->getJID(), selectedItemType_, currentResultDate_); historyWindow_->setDate(currentResultDate_); foreach (const HistoryMessage& message, messages) { addNewMessage(message, false); } } void HistoryViewController::handlePreviousButtonClicked() { if (!selectedItem_) { return; } std::set::iterator date = contacts_[selectedItemType_][selectedItem_->getJID()].find(currentResultDate_); if (date == contacts_[selectedItemType_][selectedItem_->getJID()].begin()) { return; } historyWindow_->resetConversationView(); currentResultDate_ = *(--date); std::vector messages = historyController_->getMessagesFromDate(selfJID_, selectedItem_->getJID(), selectedItemType_, currentResultDate_); historyWindow_->setDate(currentResultDate_); foreach (const HistoryMessage& message, messages) { addNewMessage(message, false); } } void HistoryViewController::reset() { roster_->removeAll(); contacts_.clear(); selectedItem_ = NULL; historyWindow_->resetConversationView(); } void HistoryViewController::handleCalendarClicked(const boost::gregorian::date& date) { if (!selectedItem_) { return; } boost::gregorian::date newDate; if (contacts_[selectedItemType_][selectedItem_->getJID()].count(date)) { newDate = date; } else if (date < currentResultDate_) { foreach(const boost::gregorian::date& current, contacts_[selectedItemType_][selectedItem_->getJID()]) { if (current > date) { newDate = current; break; } } } else { reverse_foreach(const boost::gregorian::date& current, contacts_[selectedItemType_][selectedItem_->getJID()]) { if (current < date) { newDate = current; break; } } } historyWindow_->setDate(newDate); if (newDate == currentResultDate_) { return; } currentResultDate_ = newDate; historyWindow_->resetConversationView(); std::vector messages = historyController_->getMessagesFromDate(selfJID_, selectedItem_->getJID(), selectedItemType_, currentResultDate_); historyWindow_->setDate(currentResultDate_); foreach (const HistoryMessage& message, messages) { addNewMessage(message, false); } } void HistoryViewController::handlePresenceChanged(Presence::ref presence) { JID jid = presence->getFrom(); if (contacts_[HistoryMessage::Chat].count(jid.toBare())) { roster_->applyOnItems(SetPresence(presence, JID::WithoutResource)); return; } if (contacts_[HistoryMessage::Groupchat].count(jid.toBare())) { Presence::ref availablePresence = boost::make_shared(Presence()); availablePresence->setFrom(jid.toBare()); roster_->applyOnItems(SetPresence(availablePresence, JID::WithResource)); } if (contacts_[HistoryMessage::PrivateMessage].count(jid)) { roster_->applyOnItems(SetPresence(presence, JID::WithResource)); } } void HistoryViewController::handleAvatarChanged(const JID& jid) { std::string path = avatarManager_->getAvatarPath(jid).string(); roster_->applyOnItems(SetAvatar(jid, path)); } Presence::ref HistoryViewController::getPresence(const JID& jid, bool isMUC) { if (jid.isBare() && !isMUC) { return presenceOracle_->getHighestPriorityPresence(jid); } std::vector mucPresence = presenceOracle_->getAllPresence(jid.toBare()); if (isMUC && !mucPresence.empty()) { Presence::ref presence = boost::make_shared(Presence()); presence->setFrom(jid); return presence; } foreach (Presence::ref presence, mucPresence) { if (presence.get() && presence->getFrom() == jid) { return presence; } } return Presence::create(); } } swift-im-2.0+dev6/Swift/Controllers/PresenceNotifier.cpp0000644000175000017500000001000212227051773023225 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Swift/Controllers/PresenceNotifier.h" #include #include "Swiften/Client/StanzaChannel.h" #include "Swiften/Base/ByteArray.h" #include "Swiften/MUC/MUCRegistry.h" #include "Swiften/Roster/XMPPRoster.h" #include "Swiften/Presence/PresenceOracle.h" #include "Swiften/Network/TimerFactory.h" #include "Swiften/Client/NickResolver.h" #include namespace Swift { PresenceNotifier::PresenceNotifier(StanzaChannel* stanzaChannel, Notifier* notifier, const MUCRegistry* mucRegistry, AvatarManager* avatarManager, NickResolver* nickResolver, const PresenceOracle* presenceOracle, TimerFactory* timerFactory) : stanzaChannel(stanzaChannel), notifier(notifier), mucRegistry(mucRegistry), avatarManager(avatarManager), nickResolver(nickResolver), presenceOracle(presenceOracle), timerFactory(timerFactory) { justInitialized = true; inQuietPeriod = false; stanzaChannel->onPresenceReceived.connect(boost::bind(&PresenceNotifier::handlePresenceReceived, this, _1)); stanzaChannel->onAvailableChanged.connect(boost::bind(&PresenceNotifier::handleStanzaChannelAvailableChanged, this, _1)); setInitialQuietPeriodMS(3000); } PresenceNotifier::~PresenceNotifier() { if (timer) { timer->stop(); timer->onTick.disconnect(boost::bind(&PresenceNotifier::handleTimerTick, this)); timer.reset(); } stanzaChannel->onAvailableChanged.disconnect(boost::bind(&PresenceNotifier::handleStanzaChannelAvailableChanged, this, _1)); stanzaChannel->onPresenceReceived.disconnect(boost::bind(&PresenceNotifier::handlePresenceReceived, this, _1)); } void PresenceNotifier::handlePresenceReceived(boost::shared_ptr presence) { JID from = presence->getFrom(); if (mucRegistry->isMUC(from.toBare())) { return; } if (justInitialized) { justInitialized = false; if (timer) { inQuietPeriod = true; } } if (inQuietPeriod) { timer->stop(); timer->start(); return; } std::set::iterator i = availableUsers.find(from); if (presence->isAvailable()) { if (i != availableUsers.end()) { showNotification(from, Notifier::ContactStatusChange); } else { showNotification(from, Notifier::ContactAvailable); availableUsers.insert(from); } } else { if (i != availableUsers.end()) { showNotification(from, Notifier::ContactUnavailable); availableUsers.erase(i); } } } void PresenceNotifier::handleStanzaChannelAvailableChanged(bool available) { if (available) { availableUsers.clear(); justInitialized = true; if (timer) { timer->stop(); } } } void PresenceNotifier::showNotification(const JID& jid, Notifier::Type type) { std::string name = nickResolver->jidToNick(jid); std::string title = name + " (" + getStatusType(jid) + ")"; std::string message = getStatusMessage(jid); notifier->showMessage(type, title, message, avatarManager->getAvatarPath(jid), boost::bind(&PresenceNotifier::handleNotificationActivated, this, jid)); } void PresenceNotifier::handleNotificationActivated(JID jid) { onNotificationActivated(jid); } std::string PresenceNotifier::getStatusType(const JID& jid) const { Presence::ref presence = presenceOracle->getLastPresence(jid); if (presence) { return statusShowTypeToFriendlyName(presence->getShow()); } else { return "Unavailable"; } } std::string PresenceNotifier::getStatusMessage(const JID& jid) const { Presence::ref presence = presenceOracle->getLastPresence(jid); if (presence) { return presence->getStatus(); } else { return std::string(); } } void PresenceNotifier::setInitialQuietPeriodMS(int ms) { if (timer) { timer->stop(); timer->onTick.disconnect(boost::bind(&PresenceNotifier::handleTimerTick, this)); timer.reset(); } if (ms > 0) { timer = timerFactory->createTimer(ms); timer->onTick.connect(boost::bind(&PresenceNotifier::handleTimerTick, this)); } } void PresenceNotifier::handleTimerTick() { inQuietPeriod = false; timer->stop(); } } swift-im-2.0+dev6/Swift/Controllers/AdHocManager.cpp0000644000175000017500000000512012227051773022237 0ustar kismithkismith/* * Copyright (c) 2010-2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include #include namespace Swift { AdHocManager::AdHocManager(const JID& jid, AdHocCommandWindowFactory* factory, IQRouter* iqRouter, UIEventStream* uiEventStream, MainWindow* mainWindow) : jid_(jid) { iqRouter_ = iqRouter; uiEventStream_ = uiEventStream; mainWindow_ = mainWindow; factory_ = factory; uiEventStream_->onUIEvent.connect(boost::bind(&AdHocManager::handleUIEvent, this, _1)); } AdHocManager::~AdHocManager() { uiEventStream_->onUIEvent.disconnect(boost::bind(&AdHocManager::handleUIEvent, this, _1)); } void AdHocManager::setServerDiscoInfo(boost::shared_ptr info) { if (iqRouter_->isAvailable() && info->hasFeature(DiscoInfo::CommandsFeature)) { if (discoItemsRequest_) { discoItemsRequest_->onResponse.disconnect(boost::bind(&AdHocManager::handleServerDiscoItemsResponse, this, _1, _2)); discoItemsRequest_.reset(); } discoItemsRequest_ = GetDiscoItemsRequest::create(JID(jid_.getDomain()), DiscoInfo::CommandsFeature, iqRouter_); discoItemsRequest_->onResponse.connect(boost::bind(&AdHocManager::handleServerDiscoItemsResponse, this, _1, _2)); discoItemsRequest_->send(); } else { mainWindow_->setAvailableAdHocCommands(std::vector()); } } void AdHocManager::handleServerDiscoItemsResponse(boost::shared_ptr items, ErrorPayload::ref error) { std::vector commands; if (!error) { foreach (DiscoItems::Item item, items->getItems()) { if (item.getNode() != "http://isode.com/xmpp/commands#test") { commands.push_back(item); } } } mainWindow_->setAvailableAdHocCommands(commands); } void AdHocManager::handleUIEvent(boost::shared_ptr event) { boost::shared_ptr adHocEvent = boost::dynamic_pointer_cast(event); if (adHocEvent) { factory_->createAdHocCommandWindow(boost::make_shared(adHocEvent->getCommand().getJID(), adHocEvent->getCommand().getNode(), iqRouter_)); } } } swift-im-2.0+dev6/Swift/Controllers/ProfileSettingsProvider.cpp0000644000175000017500000000405512227051773024630 0ustar kismithkismith/* * Copyright (c) 2010-2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Swift/Controllers/ProfileSettingsProvider.h" namespace Swift { ProfileSettingsProvider::ProfileSettingsProvider(const std::string& profile, SettingsProvider* provider) : profile_(profile) { provider_ = provider; bool found = false; foreach (std::string existingProfile, provider->getAvailableProfiles()) { if (existingProfile == profile) { found = true; } } if (!found) { provider_->createProfile(profile); } } ProfileSettingsProvider::~ProfileSettingsProvider() { } std::string ProfileSettingsProvider::getStringSetting(const std::string &settingPath) { //FIXME: Remove shim SettingsProvider::Setting setting(profileSettingPath(settingPath), ""); return provider_->getSetting(setting); } void ProfileSettingsProvider::storeString(const std::string &settingPath, const std::string &settingValue) { //FIXME: Remove shim if (!getIsSettingFinal(settingPath)) { SettingsProvider::Setting setting(profileSettingPath(settingPath), ""); provider_->storeSetting(setting, settingValue); } } int ProfileSettingsProvider::getIntSetting(const std::string& settingPath, int defaultValue) { //FIXME: Remove shim SettingsProvider::Setting setting(profileSettingPath(settingPath), defaultValue); return provider_->getSetting(setting); } void ProfileSettingsProvider::storeInt(const std::string& settingPath, int settingValue) { //FIXME: Remove shim if (!getIsSettingFinal(settingPath)) { SettingsProvider::Setting setting(profileSettingPath(settingPath), 0); provider_->storeSetting(setting, settingValue); } } bool ProfileSettingsProvider::getIsSettingFinal(const std::string& settingPath) { //FIXME: Remove shim SettingsProvider::Setting setting(settingPath, 0); return provider_->getIsSettingFinal(setting); } std::string ProfileSettingsProvider::profileSettingPath(const std::string &settingPath) { return profile_ + ":" + settingPath; } } swift-im-2.0+dev6/Swift/Controllers/Storages/0000755000175000017500000000000012227051773021053 5ustar kismithkismithswift-im-2.0+dev6/Swift/Controllers/Storages/StoragesFactory.h0000644000175000017500000000055412227051773024347 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once namespace Swift { class Storages; class JID; class StoragesFactory { public: virtual ~StoragesFactory() {} virtual Storages* createStorages(const JID& profile) const = 0; }; } swift-im-2.0+dev6/Swift/Controllers/Storages/RosterFileStorage.cpp0000644000175000017500000000153512227051773025166 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include using namespace Swift; typedef GenericPayloadPersister RosterPersister; RosterFileStorage::RosterFileStorage(const boost::filesystem::path& path) : path(path) { } boost::shared_ptr RosterFileStorage::getRoster() const { return RosterPersister().loadPayloadGeneric(path); } void RosterFileStorage::setRoster(boost::shared_ptr roster) { RosterPersister().savePayload(roster, path); } swift-im-2.0+dev6/Swift/Controllers/Storages/CertificateMemoryStorage.cpp0000644000175000017500000000126512227051773026523 0ustar kismithkismith/* * Copyright (c) 2011 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include using namespace Swift; CertificateMemoryStorage::CertificateMemoryStorage() { } bool CertificateMemoryStorage::hasCertificate(Certificate::ref certificate) const { foreach(Certificate::ref storedCert, certificates) { if (storedCert->toDER() == certificate->toDER()) { return true; } } return false; } void CertificateMemoryStorage::addCertificate(Certificate::ref certificate) { certificates.push_back(certificate); } swift-im-2.0+dev6/Swift/Controllers/Storages/VCardFileStorage.cpp0000644000175000017500000000661112227051773024707 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Swift/Controllers/Storages/VCardFileStorage.h" #include #include #include #include #include #include #include #include #include "Swiften/JID/JID.h" #include "Swiften/Elements/VCard.h" #include "Swiften/Serializer/PayloadSerializers/VCardSerializer.h" #include "Swiften/Parser/PayloadParsers/UnitTest/PayloadParserTester.h" #include "Swiften/Parser/PayloadParsers/VCardParser.h" using namespace Swift; typedef GenericPayloadPersister VCardPersister; VCardFileStorage::VCardFileStorage(boost::filesystem::path dir) : vcardsPath(dir) { cacheFile = vcardsPath / "phashes"; if (boost::filesystem::exists(cacheFile)) { try { boost::filesystem::ifstream file(cacheFile); std::string line; if (file.is_open()) { while (!file.eof()) { getline(file, line); std::pair r = String::getSplittedAtFirst(line, ' '); JID jid(r.second); if (jid.isValid()) { photoHashes.insert(std::make_pair(jid, r.first)); } else if (!r.first.empty() || !r.second.empty()) { std::cerr << "Invalid entry in phashes file" << std::endl; } } } } catch (...) { std::cerr << "Error reading phashes file" << std::endl; } } } boost::shared_ptr VCardFileStorage::getVCard(const JID& jid) const { boost::shared_ptr result = VCardPersister().loadPayloadGeneric(getVCardPath(jid)); getAndUpdatePhotoHash(jid, result); return result; } void VCardFileStorage::setVCard(const JID& jid, VCard::ref v) { VCardPersister().savePayload(v, getVCardPath(jid)); getAndUpdatePhotoHash(jid, v); } boost::filesystem::path VCardFileStorage::getVCardPath(const JID& jid) const { try { std::string file(jid.toString()); String::replaceAll(file, '/', "%2f"); return boost::filesystem::path(vcardsPath / (file + ".xml")); } catch (const boost::filesystem::filesystem_error& e) { std::cerr << "ERROR: " << e.what() << std::endl; return boost::filesystem::path(); } } std::string VCardFileStorage::getPhotoHash(const JID& jid) const { PhotoHashMap::const_iterator i = photoHashes.find(jid); if (i != photoHashes.end()) { return i->second; } else { VCard::ref vCard = getVCard(jid); return getAndUpdatePhotoHash(jid, vCard); } } std::string VCardFileStorage::getAndUpdatePhotoHash(const JID& jid, VCard::ref vCard) const { std::string hash; if (vCard && !vCard->getPhoto().empty()) { hash = Hexify::hexify(SHA1::getHash(vCard->getPhoto())); } std::pair r = photoHashes.insert(std::make_pair(jid, hash)); if (r.second) { savePhotoHashes(); } else if (r.first->second != hash) { r.first->second = hash; savePhotoHashes(); } return hash; } void VCardFileStorage::savePhotoHashes() const { try { boost::filesystem::ofstream file(cacheFile); for (PhotoHashMap::const_iterator i = photoHashes.begin(); i != photoHashes.end(); ++i) { file << i->second << " " << i->first.toString() << std::endl; } file.close(); } catch (...) { std::cerr << "Error writing vcards file" << std::endl; } } swift-im-2.0+dev6/Swift/Controllers/Storages/CertificateStorageFactory.cpp0000644000175000017500000000046512227051773026663 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include namespace Swift { CertificateStorageFactory::~CertificateStorageFactory() { } } swift-im-2.0+dev6/Swift/Controllers/Storages/CertificateStorage.h0000644000175000017500000000074112227051773024775 0ustar kismithkismith/* * 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 #include namespace Swift { class CertificateStorage { public: virtual ~CertificateStorage(); virtual bool hasCertificate(Certificate::ref certificate) const = 0; virtual void addCertificate(Certificate::ref certificate) = 0; }; } swift-im-2.0+dev6/Swift/Controllers/Storages/CapsFileStorage.h0000644000175000017500000000126012227051773024236 0ustar kismithkismith/* * 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 #include "Swiften/Disco/CapsStorage.h" #include namespace Swift { class CapsFileStorage : public CapsStorage { public: CapsFileStorage(const boost::filesystem::path& path); virtual DiscoInfo::ref getDiscoInfo(const std::string& hash) const; virtual void setDiscoInfo(const std::string& hash, DiscoInfo::ref discoInfo); private: boost::filesystem::path getCapsPath(const std::string& hash) const; private: boost::filesystem::path path; }; } swift-im-2.0+dev6/Swift/Controllers/Storages/CertificateStorage.cpp0000644000175000017500000000044012227051773025324 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Swift/Controllers/Storages/CertificateStorage.h" namespace Swift { CertificateStorage::~CertificateStorage() { } } swift-im-2.0+dev6/Swift/Controllers/Storages/VCardFileStorage.h0000644000175000017500000000172312227051773024353 0ustar kismithkismith/* * 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 #include #include #include #include "Swiften/VCards/VCardStorage.h" namespace Swift { class VCardFileStorage : public VCardStorage { public: VCardFileStorage(boost::filesystem::path dir); virtual VCard::ref getVCard(const JID& jid) const; virtual void setVCard(const JID& jid, VCard::ref v); virtual std::string getPhotoHash(const JID&) const; private: boost::filesystem::path getVCardPath(const JID&) const; std::string getAndUpdatePhotoHash(const JID& jid, VCard::ref vcard) const; void savePhotoHashes() const; private: boost::filesystem::path vcardsPath; boost::filesystem::path cacheFile; typedef std::map PhotoHashMap; mutable PhotoHashMap photoHashes; }; } swift-im-2.0+dev6/Swift/Controllers/Storages/CertificateStorageTrustChecker.h0000644000175000017500000000207412227051773027325 0ustar kismithkismith/* * 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 #include namespace Swift { /** * A certificate trust checker that trusts certificates in a certificate storage. */ class CertificateStorageTrustChecker : public CertificateTrustChecker { public: CertificateStorageTrustChecker(CertificateStorage* storage) : storage(storage) { } virtual bool isCertificateTrusted(const std::vector& certificateChain) { lastCertificateChain = std::vector(certificateChain.begin(), certificateChain.end()); return certificateChain.empty() ? false : storage->hasCertificate(certificateChain[0]); } const std::vector& getLastCertificateChain() const { return lastCertificateChain; } private: CertificateStorage* storage; std::vector lastCertificateChain; }; } swift-im-2.0+dev6/Swift/Controllers/Storages/RosterFileStorage.h0000644000175000017500000000107012227051773024625 0ustar kismithkismith/* * 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 #include namespace Swift { class RosterFileStorage : public RosterStorage { public: RosterFileStorage(const boost::filesystem::path& path); virtual boost::shared_ptr getRoster() const; virtual void setRoster(boost::shared_ptr); private: boost::filesystem::path path; }; } swift-im-2.0+dev6/Swift/Controllers/Storages/AvatarFileStorage.cpp0000644000175000017500000000625712227051773025134 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include namespace Swift { AvatarFileStorage::AvatarFileStorage(const boost::filesystem::path& avatarsDir, const boost::filesystem::path& avatarsFile) : avatarsDir(avatarsDir), avatarsFile(avatarsFile) { if (boost::filesystem::exists(avatarsFile)) { try { boost::filesystem::ifstream file(avatarsFile); std::string line; if (file.is_open()) { while (!file.eof()) { getline(file, line); std::pair r = String::getSplittedAtFirst(line, ' '); JID jid(r.second); if (jid.isValid()) { jidAvatars.insert(std::make_pair(jid, r.first)); } else if (!r.first.empty() || !r.second.empty()) { std::cerr << "Invalid entry in avatars file: " << r.second << std::endl; } } } } catch (...) { std::cerr << "Error reading avatars file" << std::endl; } } } bool AvatarFileStorage::hasAvatar(const std::string& hash) const { return boost::filesystem::exists(getAvatarPath(hash)); } void AvatarFileStorage::addAvatar(const std::string& hash, const ByteArray& avatar) { assert(Hexify::hexify(SHA1::getHash(avatar)) == hash); boost::filesystem::path avatarPath = getAvatarPath(hash); if (!boost::filesystem::exists(avatarPath.parent_path())) { try { boost::filesystem::create_directories(avatarPath.parent_path()); } catch (const boost::filesystem::filesystem_error& e) { std::cerr << "ERROR: " << e.what() << std::endl; } } boost::filesystem::ofstream file(avatarPath, boost::filesystem::ofstream::binary|boost::filesystem::ofstream::out); file.write(reinterpret_cast(vecptr(avatar)), static_cast(avatar.size())); file.close(); } boost::filesystem::path AvatarFileStorage::getAvatarPath(const std::string& hash) const { return avatarsDir / hash; } ByteArray AvatarFileStorage::getAvatar(const std::string& hash) const { ByteArray data; readByteArrayFromFile(data, getAvatarPath(hash).string()); return data; } void AvatarFileStorage::setAvatarForJID(const JID& jid, const std::string& hash) { std::pair r = jidAvatars.insert(std::make_pair(jid, hash)); if (r.second) { saveJIDAvatars(); } else if (r.first->second != hash) { r.first->second = hash; saveJIDAvatars(); } } std::string AvatarFileStorage::getAvatarForJID(const JID& jid) const { JIDAvatarMap::const_iterator i = jidAvatars.find(jid); return i == jidAvatars.end() ? "" : i->second; } void AvatarFileStorage::saveJIDAvatars() { try { boost::filesystem::ofstream file(avatarsFile); for (JIDAvatarMap::const_iterator i = jidAvatars.begin(); i != jidAvatars.end(); ++i) { file << i->second << " " << i->first.toString() << std::endl; } file.close(); } catch (...) { std::cerr << "Error writing avatars file" << std::endl; } } } swift-im-2.0+dev6/Swift/Controllers/Storages/FileStoragesFactory.h0000644000175000017500000000116312227051773025144 0ustar kismithkismith/* * 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 "Swift/Controllers/Storages/StoragesFactory.h" #include "Swift/Controllers/Storages/FileStorages.h" namespace Swift { class FileStoragesFactory : public StoragesFactory { public: FileStoragesFactory(const boost::filesystem::path& basePath) : basePath(basePath) {} virtual Storages* createStorages(const JID& profile) const { return new FileStorages(basePath, profile); } private: boost::filesystem::path basePath; }; } swift-im-2.0+dev6/Swift/Controllers/Storages/CertificateFileStorage.cpp0000644000175000017500000000411412227051773026126 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include namespace Swift { CertificateFileStorage::CertificateFileStorage(const boost::filesystem::path& path, CertificateFactory* certificateFactory) : path(path), certificateFactory(certificateFactory) { } bool CertificateFileStorage::hasCertificate(Certificate::ref certificate) const { boost::filesystem::path certificatePath = getCertificatePath(certificate); if (boost::filesystem::exists(certificatePath)) { ByteArray data; readByteArrayFromFile(data, certificatePath.string()); Certificate::ref storedCertificate = certificateFactory->createCertificateFromDER(data); if (storedCertificate && storedCertificate->toDER() == certificate->toDER()) { return true; } else { SWIFT_LOG(warning) << "Stored certificate does not match received certificate" << std::endl; return false; } } else { return false; } } void CertificateFileStorage::addCertificate(Certificate::ref certificate) { boost::filesystem::path certificatePath = getCertificatePath(certificate); if (!boost::filesystem::exists(certificatePath.parent_path())) { try { boost::filesystem::create_directories(certificatePath.parent_path()); } catch (const boost::filesystem::filesystem_error& e) { std::cerr << "ERROR: " << e.what() << std::endl; } } boost::filesystem::ofstream file(certificatePath, boost::filesystem::ofstream::binary|boost::filesystem::ofstream::out); ByteArray data = certificate->toDER(); file.write(reinterpret_cast(vecptr(data)), data.size()); file.close(); } boost::filesystem::path CertificateFileStorage::getCertificatePath(Certificate::ref certificate) const { return path / Hexify::hexify(SHA1::getHash(certificate->toDER())); } } swift-im-2.0+dev6/Swift/Controllers/Storages/FileStorages.cpp0000644000175000017500000000324512227051773024152 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Swift/Controllers/Storages/FileStorages.h" #include "Swift/Controllers/Storages/VCardFileStorage.h" #include "Swift/Controllers/Storages/AvatarFileStorage.h" #include "Swift/Controllers/Storages/CapsFileStorage.h" #include "Swift/Controllers/Storages/RosterFileStorage.h" #include 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"); rosterStorage = new RosterFileStorage(baseDir / profile / "roster.xml"); #ifdef SWIFT_EXPERIMENTAL_HISTORY historyStorage = new SQLiteHistoryStorage((baseDir / "history.db").string()); #endif } FileStorages::~FileStorages() { delete rosterStorage; delete avatarStorage; delete capsStorage; delete vcardStorage; #ifdef SWIFT_EXPERIMENTAL_HISTORY delete historyStorage; #endif } VCardStorage* FileStorages::getVCardStorage() const { return vcardStorage; } CapsStorage* FileStorages::getCapsStorage() const { return capsStorage; } AvatarStorage* FileStorages::getAvatarStorage() const { return avatarStorage; } RosterStorage* FileStorages::getRosterStorage() const { return rosterStorage; } HistoryStorage* FileStorages::getHistoryStorage() const { #ifdef SWIFT_EXPERIMENTAL_HISTORY return historyStorage; #else return NULL; #endif } } swift-im-2.0+dev6/Swift/Controllers/Storages/CapsFileStorage.cpp0000644000175000017500000000234212227051773024573 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Swift/Controllers/Storages/CapsFileStorage.h" #include #include "Swiften/Serializer/PayloadSerializers/DiscoInfoSerializer.h" #include "Swiften/Parser/PayloadParsers/DiscoInfoParser.h" #include "Swiften/StringCodecs/Hexify.h" #include "Swiften/StringCodecs/Base64.h" using namespace Swift; typedef GenericPayloadPersister DiscoInfoPersister; CapsFileStorage::CapsFileStorage(const boost::filesystem::path& path) : path(path) { } DiscoInfo::ref CapsFileStorage::getDiscoInfo(const std::string& hash) const { return DiscoInfoPersister().loadPayloadGeneric(getCapsPath(hash)); } void CapsFileStorage::setDiscoInfo(const std::string& hash, DiscoInfo::ref discoInfo) { DiscoInfo::ref bareDiscoInfo(new DiscoInfo(*discoInfo.get())); bareDiscoInfo->setNode(""); DiscoInfoPersister().savePayload(bareDiscoInfo, getCapsPath(hash)); } boost::filesystem::path CapsFileStorage::getCapsPath(const std::string& hash) const { return path / (Hexify::hexify(Base64::decode(hash)) + ".xml"); } swift-im-2.0+dev6/Swift/Controllers/Storages/MemoryStoragesFactory.h0000644000175000017500000000077712227051773025547 0ustar kismithkismith/* * 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 "Swift/Controllers/Storages/StoragesFactory.h" #include "Swiften/Client/MemoryStorages.h" namespace Swift { class JID; class MemoryStoragesFactory : public StoragesFactory { public: MemoryStoragesFactory() {} virtual Storages* createStorages(const JID& profile) const { return new MemoryStorages(); } }; } swift-im-2.0+dev6/Swift/Controllers/Storages/FileStorages.h0000644000175000017500000000315012227051773023612 0ustar kismithkismith/* * 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 #include "Swiften/Client/Storages.h" namespace Swift { class VCardFileStorage; class AvatarFileStorage; class CapsFileStorage; class RosterFileStorage; class HistoryStorage; 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: /avatars * - VCards: //vcards * - Entity capabilities: /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; virtual RosterStorage* getRosterStorage() const; virtual HistoryStorage* getHistoryStorage() const; private: VCardFileStorage* vcardStorage; AvatarFileStorage* avatarStorage; CapsFileStorage* capsStorage; RosterFileStorage* rosterStorage; HistoryStorage* historyStorage; }; } swift-im-2.0+dev6/Swift/Controllers/Storages/CertificateFileStorage.h0000644000175000017500000000145012227051773025573 0ustar kismithkismith/* * 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 #include "Swift/Controllers/Storages/CertificateStorage.h" namespace Swift { class CertificateFactory; class CertificateFileStorage : public CertificateStorage { public: CertificateFileStorage(const boost::filesystem::path& path, CertificateFactory* certificateFactory); virtual bool hasCertificate(Certificate::ref certificate) const; virtual void addCertificate(Certificate::ref certificate); private: boost::filesystem::path getCertificatePath(Certificate::ref certificate) const; private: boost::filesystem::path path; CertificateFactory* certificateFactory; }; } swift-im-2.0+dev6/Swift/Controllers/Storages/AvatarFileStorage.h0000644000175000017500000000222712227051773024572 0ustar kismithkismith/* * 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 #include #include #include #include "Swiften/Base/ByteArray.h" #include "Swiften/Avatars/AvatarStorage.h" namespace Swift { class AvatarFileStorage : public AvatarStorage { public: AvatarFileStorage(const boost::filesystem::path& avatarsDir, const boost::filesystem::path& avatarsFile); virtual bool hasAvatar(const std::string& hash) const; virtual void addAvatar(const std::string& hash, const ByteArray& avatar); virtual ByteArray getAvatar(const std::string& hash) const; virtual boost::filesystem::path getAvatarPath(const std::string& hash) const; virtual void setAvatarForJID(const JID& jid, const std::string& hash); virtual std::string getAvatarForJID(const JID& jid) const; private: void saveJIDAvatars(); private: boost::filesystem::path avatarsDir; boost::filesystem::path avatarsFile; typedef std::map JIDAvatarMap; JIDAvatarMap jidAvatars; }; } swift-im-2.0+dev6/Swift/Controllers/Storages/CertificateStorageFactory.h0000644000175000017500000000063412227051773026326 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once namespace Swift { class CertificateStorage; class JID; class CertificateStorageFactory { public: virtual ~CertificateStorageFactory(); virtual CertificateStorage* createCertificateStorage(const JID& profile) const = 0; }; } swift-im-2.0+dev6/Swift/Controllers/Storages/CertificateMemoryStorage.h0000644000175000017500000000107312227051773026165 0ustar kismithkismith/* * 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 #include namespace Swift { class CertificateMemoryStorage : public CertificateStorage { public: CertificateMemoryStorage(); virtual bool hasCertificate(Certificate::ref certificate) const; virtual void addCertificate(Certificate::ref certificate); private: std::vector certificates; }; } swift-im-2.0+dev6/Swift/Controllers/Storages/CertificateFileStorageFactory.h0000644000175000017500000000170212227051773027123 0ustar kismithkismith/* * 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 #include namespace Swift { class CertificateFactory; class CertificateFileStorageFactory : public CertificateStorageFactory { public: CertificateFileStorageFactory(const boost::filesystem::path& basePath, CertificateFactory* certificateFactory) : basePath(basePath), certificateFactory(certificateFactory) {} virtual CertificateStorage* createCertificateStorage(const JID& profile) const { boost::filesystem::path profilePath = basePath / profile.toString(); return new CertificateFileStorage(profilePath / "certificates", certificateFactory); } private: boost::filesystem::path basePath; CertificateFactory* certificateFactory; }; } swift-im-2.0+dev6/Swift/Controllers/HistoryController.cpp0000644000175000017500000000507512227051773023504 0ustar kismithkismith/* * Copyright (c) 2012 Catalin Badea * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include #include #include #include namespace Swift { HistoryController::HistoryController(HistoryStorage* localHistoryStorage) : localHistory_(localHistoryStorage) { } HistoryController::~HistoryController() { } void HistoryController::addMessage(const std::string& message, const JID& fromJID, const JID& toJID, HistoryMessage::Type type, const boost::posix_time::ptime& timeStamp) { // note: using localtime timestamps boost::posix_time::ptime localTime = boost::date_time::c_local_adjustor::utc_to_local(timeStamp); int offset = (localTime - timeStamp).hours(); HistoryMessage historyMessage(message, fromJID, toJID, type, localTime, offset); localHistory_->addMessage(historyMessage); onNewMessage(historyMessage); } std::vector HistoryController::getMessagesFromDate(const JID& selfJID, const JID& contactJID, HistoryMessage::Type type, const boost::gregorian::date& date) const { return localHistory_->getMessagesFromDate(selfJID, contactJID, type, date); } std::vector HistoryController::getMUCContext(const JID& selfJID, const JID& mucJID, const boost::posix_time::ptime& timeStamp) const { boost::posix_time::ptime localTime = boost::date_time::c_local_adjustor::utc_to_local(timeStamp); return getMessagesFromDate(selfJID, mucJID, HistoryMessage::Groupchat, localTime.date()); } std::vector HistoryController::getMessagesFromPreviousDate(const JID& selfJID, const JID& contactJID, HistoryMessage::Type type, const boost::gregorian::date& date) const { return localHistory_->getMessagesFromPreviousDate(selfJID, contactJID, type, date); } std::vector HistoryController::getMessagesFromNextDate(const JID& selfJID, const JID& contactJID, HistoryMessage::Type type, const boost::gregorian::date& date) const { return localHistory_->getMessagesFromNextDate(selfJID, contactJID, type, date); } ContactsMap HistoryController::getContacts(const JID& selfJID, HistoryMessage::Type type, const std::string& keyword) const { return localHistory_->getContacts(selfJID, type, keyword); } boost::posix_time::ptime HistoryController::getLastTimeStampFromMUC(const JID& selfJID, const JID& mucJID) { return localHistory_->getLastTimeStampFromMUC(selfJID, mucJID); } } swift-im-2.0+dev6/Swift/Controllers/MainController.h0000644000175000017500000001346612227051773022377 0ustar kismithkismith/* * Copyright (c) 2010-2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include "Swiften/Network/Timer.h" #include #include "Swiften/Client/ClientError.h" #include "Swiften/JID/JID.h" #include "Swiften/Elements/DiscoInfo.h" #include "Swiften/Elements/VCard.h" #include "Swiften/Elements/ErrorPayload.h" #include "Swiften/Elements/Presence.h" #include "Swift/Controllers/Settings/SettingsProvider.h" #include "Swift/Controllers/ProfileSettingsProvider.h" #include "Swiften/Elements/CapsInfo.h" #include "Swift/Controllers/XMPPEvents/ErrorEvent.h" #include "Swift/Controllers/UIEvents/UIEvent.h" #include "Swiften/Client/ClientXMLTracer.h" namespace Swift { class IdleDetector; class UIFactory; class EventLoop; class Client; class ChatController; class ChatsManager; class CertificateStorageFactory; class CertificateStorage; class CertificateStorageTrustChecker; class EventController; class MainWindow; class RosterController; class LoginWindow; class EventLoop; class MUCController; class Notifier; class ProfileController; class ContactEditController; class TogglableNotifier; class PresenceNotifier; class EventNotifier; class SystemTray; class SystemTrayController; class SoundEventController; class SoundPlayer; class XMLConsoleController; class HistoryViewController; class HistoryController; class FileTransferListController; class UIEventStream; class EventWindowFactory; class EventWindowController; class MUCSearchController; class UserSearchController; class StatusTracker; class Dock; class Storages; class StoragesFactory; class NetworkFactories; class URIHandler; class XMPPURIController; class AdHocManager; class AdHocCommandWindowFactory; class FileTransferOverview; class WhiteboardManager; class MainController { public: MainController( EventLoop* eventLoop, NetworkFactories* networkFactories, UIFactory* uiFactories, SettingsProvider *settings, SystemTray* systemTray, SoundPlayer* soundPlayer, StoragesFactory* storagesFactory, CertificateStorageFactory* certificateStorageFactory, Dock* dock, Notifier* notifier, URIHandler* uriHandler, IdleDetector* idleDetector, bool useDelayForLatency); ~MainController(); private: void resetClient(); void handleConnected(); void handleLoginRequest(const std::string& username, const std::string& password, const std::string& certificatePath, CertificateWithKey::ref certificate, const ClientOptions& options, bool remember, bool loginAutomatically); void handleCancelLoginRequest(); void handleQuitRequest(); void handleChangeStatusRequest(StatusShow::Type show, const std::string &statusText); void handleDisconnected(const boost::optional& error); void handleServerDiscoInfoResponse(boost::shared_ptr, ErrorPayload::ref); void handleEventQueueLengthChange(int count); void handleVCardReceived(const JID& j, VCard::ref vCard); void handleSettingChanged(const std::string& settingPath); void handlePurgeSavedLoginRequest(const std::string& username); void sendPresence(boost::shared_ptr presence); void handleInputIdleChanged(bool); void handleShowCertificateRequest(); void logout(); void signOut(); void setReconnectTimer(); void resetPendingReconnects(); void resetCurrentError(); void performLoginFromCachedCredentials(); void reconnectAfterError(); void setManagersOffline(); void handleNotificationClicked(const JID& jid); void handleForceQuit(); void purgeCachedCredentials(); std::string serializeClientOptions(const ClientOptions& options); ClientOptions parseClientOptions(const std::string& optionString); private: EventLoop* eventLoop_; NetworkFactories* networkFactories_; UIFactory* uiFactory_; StoragesFactory* storagesFactory_; Storages* storages_; CertificateStorageFactory* certificateStorageFactory_; CertificateStorage* certificateStorage_; CertificateStorageTrustChecker* certificateTrustChecker_; bool clientInitialized_; boost::shared_ptr client_; SettingsProvider *settings_; ProfileSettingsProvider* profileSettings_; Dock* dock_; URIHandler* uriHandler_; IdleDetector* idleDetector_; TogglableNotifier* notifier_; PresenceNotifier* presenceNotifier_; EventNotifier* eventNotifier_; RosterController* rosterController_; EventController* eventController_; EventWindowController* eventWindowController_; AdHocManager* adHocManager_; LoginWindow* loginWindow_; UIEventStream* uiEventStream_; XMLConsoleController* xmlConsoleController_; HistoryViewController* historyViewController_; HistoryController* historyController_; FileTransferListController* fileTransferListController_; ChatsManager* chatsManager_; ProfileController* profileController_; ContactEditController* contactEditController_; JID jid_; JID boundJID_; SystemTrayController* systemTrayController_; SoundEventController* soundEventController_; XMPPURIController* xmppURIController_; std::string vCardPhotoHash_; std::string password_; CertificateWithKey::ref certificate_; ClientOptions clientOptions_; boost::shared_ptr lastDisconnectError_; bool useDelayForLatency_; UserSearchController* userSearchControllerChat_; UserSearchController* userSearchControllerAdd_; int timeBeforeNextReconnect_; Timer::ref reconnectTimer_; StatusTracker* statusTracker_; bool myStatusLooksOnline_; bool quitRequested_; bool offlineRequested_; static const int SecondsToWaitBeforeForceQuitting; FileTransferOverview* ftOverview_; WhiteboardManager* whiteboardManager_; }; } swift-im-2.0+dev6/Swift/Controllers/SoundEventController.cpp0000644000175000017500000000304612227051773024131 0ustar kismithkismith/* * Copyright (c) 2010-2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include namespace Swift { SoundEventController::SoundEventController(EventController* eventController, SoundPlayer* soundPlayer, SettingsProvider* settings) { settings_ = settings; eventController_ = eventController; soundPlayer_ = soundPlayer; eventController_->onEventQueueEventAdded.connect(boost::bind(&SoundEventController::handleEventQueueEventAdded, this, _1)); settings_->onSettingChanged.connect(boost::bind(&SoundEventController::handleSettingChanged, this, _1)); playSounds_ = settings->getSetting(SettingConstants::PLAY_SOUNDS); } void SoundEventController::handleEventQueueEventAdded(boost::shared_ptr event) { if (playSounds_ && !event->getConcluded()) { soundPlayer_->playSound(SoundPlayer::MessageReceived); } } void SoundEventController::setPlaySounds(bool playSounds) { playSounds_ = playSounds; settings_->storeSetting(SettingConstants::PLAY_SOUNDS, playSounds); } void SoundEventController::handleSettingChanged(const std::string& settingPath) { if (SettingConstants::PLAY_SOUNDS.getKey() == settingPath) { playSounds_ = settings_->getSetting(SettingConstants::PLAY_SOUNDS); } } } swift-im-2.0+dev6/Swift/Controllers/UnitTest/0000755000175000017500000000000012227051774021044 5ustar kismithkismithswift-im-2.0+dev6/Swift/Controllers/UnitTest/MockMainWindow.h0000644000175000017500000000207612227051774024110 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include "Swift/Controllers/UIInterfaces/MainWindow.h" namespace Swift { class Roster; class MockMainWindow : public MainWindow { public: MockMainWindow() : roster(NULL) {}; virtual ~MockMainWindow() {}; virtual void setRosterModel(Roster* roster) {this->roster = roster;}; virtual void setMyNick(const std::string& /*name*/) {};; virtual void setMyJID(const JID& /*jid*/) {};; virtual void setMyAvatarPath(const std::string& /*path*/) {}; virtual void setMyStatusText(const std::string& /*status*/) {}; virtual void setMyStatusType(StatusShow::Type /*type*/) {}; virtual void setAvailableAdHocCommands(const std::vector& /*commands*/) {}; virtual void setConnecting() {}; virtual void setStreamEncryptionStatus(bool /*tlsInPlaceAndValid*/) {} virtual void openCertificateDialog(const std::vector& /*chain*/) {} Roster* roster; }; } swift-im-2.0+dev6/Swift/Controllers/UnitTest/MockChatWindow.cpp0000644000175000017500000000042012227051774024425 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Swift/Controllers/UnitTest/MockChatWindow.h" namespace Swift { MockChatWindow::~MockChatWindow() { } } swift-im-2.0+dev6/Swift/Controllers/UnitTest/PresenceNotifierTest.cpp0000644000175000017500000002733512227051774025666 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include "Swift/Controllers/PresenceNotifier.h" #include "Swiften/Client/NickResolver.h" #include "SwifTools/Notifier/LoggingNotifier.h" #include "Swiften/Client/DummyStanzaChannel.h" #include "Swiften/MUC/MUCRegistry.h" #include "Swiften/Roster/XMPPRosterImpl.h" #include "Swiften/Presence/PresenceOracle.h" #include "Swiften/Avatars/DummyAvatarManager.h" #include "Swiften/Network/DummyTimerFactory.h" using namespace Swift; class PresenceNotifierTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(PresenceNotifierTest); CPPUNIT_TEST(testReceiveFirstPresenceCreatesAvailableNotification); CPPUNIT_TEST(testReceiveSecondPresenceCreatesStatusChangeNotification); CPPUNIT_TEST(testReceiveUnavailablePresenceAfterAvailablePresenceCreatesUnavailableNotification); CPPUNIT_TEST(testReceiveUnavailablePresenceWithoutAvailableDoesNotCreateNotification); CPPUNIT_TEST(testReceiveAvailablePresenceAfterUnavailableCreatesAvailableNotification); CPPUNIT_TEST(testReceiveAvailablePresenceAfterReconnectCreatesAvailableNotification); CPPUNIT_TEST(testReceiveAvailablePresenceFromMUCDoesNotCreateNotification); CPPUNIT_TEST(testNotificationSubjectContainsNameForJIDInRoster); CPPUNIT_TEST(testNotificationSubjectContainsJIDForJIDNotInRoster); CPPUNIT_TEST(testNotificationSubjectContainsStatus); CPPUNIT_TEST(testNotificationMessageContainsStatusMessage); CPPUNIT_TEST(testNotificationPicture); CPPUNIT_TEST(testNotificationActivationEmitsSignal); CPPUNIT_TEST(testReceiveFirstPresenceWithQuietPeriodDoesNotNotify); CPPUNIT_TEST(testReceiveFirstPresenceWithQuietPeriodDoesNotCountAsQuietPeriod); CPPUNIT_TEST(testReceivePresenceDuringQuietPeriodDoesNotNotify); CPPUNIT_TEST(testReceivePresenceDuringQuietPeriodResetsTimer); CPPUNIT_TEST(testReceivePresenceAfterQuietPeriodNotifies); CPPUNIT_TEST(testReceiveFirstPresenceAfterReconnectWithQuietPeriodDoesNotNotify); CPPUNIT_TEST_SUITE_END(); public: void setUp() { stanzaChannel = new DummyStanzaChannel(); notifier = new LoggingNotifier(); mucRegistry = new MUCRegistry(); user1 = JID("user1@bar.com/bla"); user2 = JID("user2@foo.com/baz"); avatarManager = new DummyAvatarManager(); roster = new XMPPRosterImpl(); nickResolver = new NickResolver(JID("foo@bar.com"), roster, NULL, mucRegistry); presenceOracle = new PresenceOracle(stanzaChannel); timerFactory = new DummyTimerFactory(); } void tearDown() { delete timerFactory; delete presenceOracle; delete nickResolver; delete roster; delete avatarManager; delete mucRegistry; delete notifier; delete stanzaChannel; } void testReceiveFirstPresenceCreatesAvailableNotification() { boost::shared_ptr testling = createNotifier(); sendPresence(user1, StatusShow::Online); CPPUNIT_ASSERT_EQUAL(1, static_cast(notifier->notifications.size())); CPPUNIT_ASSERT_EQUAL(Notifier::ContactAvailable, notifier->notifications[0].type); } void testReceiveSecondPresenceCreatesStatusChangeNotification() { boost::shared_ptr testling = createNotifier(); sendPresence(user1, StatusShow::Away); notifier->notifications.clear(); sendPresence(user1, StatusShow::Online); CPPUNIT_ASSERT_EQUAL(1, static_cast(notifier->notifications.size())); CPPUNIT_ASSERT_EQUAL(Notifier::ContactStatusChange, notifier->notifications[0].type); } void testReceiveUnavailablePresenceAfterAvailablePresenceCreatesUnavailableNotification() { boost::shared_ptr testling = createNotifier(); sendPresence(user1, StatusShow::Away); notifier->notifications.clear(); sendUnavailablePresence(user1); CPPUNIT_ASSERT_EQUAL(1, static_cast(notifier->notifications.size())); CPPUNIT_ASSERT_EQUAL(Notifier::ContactUnavailable, notifier->notifications[0].type); } void testReceiveUnavailablePresenceWithoutAvailableDoesNotCreateNotification() { boost::shared_ptr testling = createNotifier(); sendUnavailablePresence(user1); CPPUNIT_ASSERT_EQUAL(0, static_cast(notifier->notifications.size())); } void testReceiveAvailablePresenceAfterUnavailableCreatesAvailableNotification() { boost::shared_ptr testling = createNotifier(); sendPresence(user1, StatusShow::Away); sendUnavailablePresence(user1); notifier->notifications.clear(); sendPresence(user1, StatusShow::Away); CPPUNIT_ASSERT_EQUAL(1, static_cast(notifier->notifications.size())); CPPUNIT_ASSERT_EQUAL(Notifier::ContactAvailable, notifier->notifications[0].type); } void testReceiveAvailablePresenceAfterReconnectCreatesAvailableNotification() { boost::shared_ptr testling = createNotifier(); sendPresence(user1, StatusShow::Away); stanzaChannel->setAvailable(false); stanzaChannel->setAvailable(true); notifier->notifications.clear(); sendPresence(user1, StatusShow::Away); CPPUNIT_ASSERT_EQUAL(1, static_cast(notifier->notifications.size())); CPPUNIT_ASSERT_EQUAL(Notifier::ContactAvailable, notifier->notifications[0].type); } void testReceiveAvailablePresenceFromMUCDoesNotCreateNotification() { boost::shared_ptr testling = createNotifier(); mucRegistry->addMUC(JID("teaparty@wonderland.lit")); sendPresence(JID("teaparty@wonderland.lit/Alice"), StatusShow::Away); CPPUNIT_ASSERT_EQUAL(0, static_cast(notifier->notifications.size())); } void testNotificationPicture() { boost::shared_ptr testling = createNotifier(); avatarManager->avatars[user1] = createByteArray("abcdef"); sendPresence(user1, StatusShow::Online); CPPUNIT_ASSERT_EQUAL(1, static_cast(notifier->notifications.size())); CPPUNIT_ASSERT_EQUAL(boost::filesystem::path("/avatars/user1@bar.com/bla"), notifier->notifications[0].picture); } void testNotificationActivationEmitsSignal() { boost::shared_ptr testling = createNotifier(); sendPresence(user1, StatusShow::Online); CPPUNIT_ASSERT(notifier->notifications[0].callback); notifier->notifications[0].callback(); CPPUNIT_ASSERT_EQUAL(1, static_cast(activatedNotifications.size())); CPPUNIT_ASSERT_EQUAL(user1, activatedNotifications[0]); } void testNotificationSubjectContainsNameForJIDInRoster() { boost::shared_ptr testling = createNotifier(); roster->addContact(user1.toBare(), "User 1", std::vector(), RosterItemPayload::Both); sendPresence(user1, StatusShow::Online); CPPUNIT_ASSERT_EQUAL(1, static_cast(notifier->notifications.size())); std::string subject = notifier->notifications[0].subject; CPPUNIT_ASSERT(subject.find("User 1") != std::string::npos); } void testNotificationSubjectContainsJIDForJIDNotInRoster() { boost::shared_ptr testling = createNotifier(); sendPresence(user1, StatusShow::Online); CPPUNIT_ASSERT_EQUAL(1, static_cast(notifier->notifications.size())); std::string subject = notifier->notifications[0].subject; CPPUNIT_ASSERT(subject.find(user1.toBare().toString()) != std::string::npos); } void testNotificationSubjectContainsStatus() { boost::shared_ptr testling = createNotifier(); sendPresence(user1, StatusShow::Away); CPPUNIT_ASSERT_EQUAL(1, static_cast(notifier->notifications.size())); std::string subject = notifier->notifications[0].subject; CPPUNIT_ASSERT(subject.find("Away") != std::string::npos); } void testNotificationMessageContainsStatusMessage() { boost::shared_ptr testling = createNotifier(); sendPresence(user1, StatusShow::Away); CPPUNIT_ASSERT_EQUAL(1, static_cast(notifier->notifications.size())); CPPUNIT_ASSERT(notifier->notifications[0].description.find("Status Message") != std::string::npos); } void testReceiveFirstPresenceWithQuietPeriodDoesNotNotify() { boost::shared_ptr testling = createNotifier(); testling->setInitialQuietPeriodMS(10); sendPresence(user1, StatusShow::Online); CPPUNIT_ASSERT_EQUAL(0, static_cast(notifier->notifications.size())); } void testReceivePresenceDuringQuietPeriodDoesNotNotify() { boost::shared_ptr testling = createNotifier(); testling->setInitialQuietPeriodMS(10); sendPresence(user1, StatusShow::Online); timerFactory->setTime(1); sendPresence(user2, StatusShow::Away); CPPUNIT_ASSERT_EQUAL(0, static_cast(notifier->notifications.size())); } void testReceivePresenceDuringQuietPeriodResetsTimer() { boost::shared_ptr testling = createNotifier(); testling->setInitialQuietPeriodMS(10); sendPresence(user1, StatusShow::Online); timerFactory->setTime(9); sendPresence(user2, StatusShow::Away); timerFactory->setTime(18); sendPresence(user1, StatusShow::Away); CPPUNIT_ASSERT_EQUAL(0, static_cast(notifier->notifications.size())); } void testReceivePresenceAfterQuietPeriodNotifies() { boost::shared_ptr testling = createNotifier(); testling->setInitialQuietPeriodMS(10); sendPresence(user1, StatusShow::Online); timerFactory->setTime(11); sendPresence(user2, StatusShow::Away); CPPUNIT_ASSERT_EQUAL(1, static_cast(notifier->notifications.size())); } void testReceiveFirstPresenceWithQuietPeriodDoesNotCountAsQuietPeriod() { boost::shared_ptr testling = createNotifier(); testling->setInitialQuietPeriodMS(10); timerFactory->setTime(11); sendPresence(user1, StatusShow::Away); CPPUNIT_ASSERT_EQUAL(0, static_cast(notifier->notifications.size())); } void testReceiveFirstPresenceAfterReconnectWithQuietPeriodDoesNotNotify() { boost::shared_ptr testling = createNotifier(); testling->setInitialQuietPeriodMS(10); sendPresence(user1, StatusShow::Online); timerFactory->setTime(15); notifier->notifications.clear(); stanzaChannel->setAvailable(false); stanzaChannel->setAvailable(true); sendPresence(user1, StatusShow::Online); timerFactory->setTime(21); sendPresence(user2, StatusShow::Online); CPPUNIT_ASSERT_EQUAL(0, static_cast(notifier->notifications.size())); } private: boost::shared_ptr createNotifier() { boost::shared_ptr result(new PresenceNotifier(stanzaChannel, notifier, mucRegistry, avatarManager, nickResolver, presenceOracle, timerFactory)); result->onNotificationActivated.connect(boost::bind(&PresenceNotifierTest::handleNotificationActivated, this, _1)); result->setInitialQuietPeriodMS(0); return result; } void sendPresence(const JID& jid, StatusShow::Type type) { boost::shared_ptr presence(new Presence()); presence->setFrom(jid); presence->setShow(type); presence->setStatus("Status Message"); stanzaChannel->onPresenceReceived(presence); } void sendUnavailablePresence(const JID& jid) { boost::shared_ptr presence(new Presence()); presence->setType(Presence::Unavailable); presence->setFrom(jid); stanzaChannel->onPresenceReceived(presence); } void handleNotificationActivated(const JID& j) { activatedNotifications.push_back(j); } private: DummyStanzaChannel* stanzaChannel; LoggingNotifier* notifier; MUCRegistry* mucRegistry; DummyAvatarManager* avatarManager; XMPPRosterImpl* roster; NickResolver* nickResolver; PresenceOracle* presenceOracle; DummyTimerFactory* timerFactory; JID user1; JID user2; std::vector activatedNotifications; }; CPPUNIT_TEST_SUITE_REGISTRATION(PresenceNotifierTest); swift-im-2.0+dev6/Swift/Controllers/UnitTest/MockMainWindowFactory.h0000644000175000017500000000120612227051774025432 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include "Swift/Controllers/UIInterfaces/MainWindowFactory.h" #include "Swift/Controllers/UnitTest/MockMainWindow.h" namespace Swift { class MockMainWindowFactory : public MainWindowFactory { public: MockMainWindowFactory() : last(NULL) {}; virtual ~MockMainWindowFactory() {}; /** * Transfers ownership of result. */ virtual MainWindow* createMainWindow(UIEventStream*) {last = new MockMainWindow();return last;}; MockMainWindow* last; }; } swift-im-2.0+dev6/Swift/Controllers/UnitTest/MockChatWindow.h0000644000175000017500000000765512227051774024113 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include "Swift/Controllers/UIInterfaces/ChatWindow.h" namespace Swift { class MockChatWindow : public ChatWindow { public: MockChatWindow() : labelsEnabled_(false) {}; virtual ~MockChatWindow(); virtual std::string addMessage(const std::string& message, const std::string& /*senderName*/, bool /*senderIsSelf*/, boost::shared_ptr /*label*/, const std::string& /*avatarPath*/, const boost::posix_time::ptime&) {lastMessageBody_ = message; return "";}; virtual std::string addAction(const std::string& message, const std::string& /*senderName*/, bool /*senderIsSelf*/, boost::shared_ptr /*label*/, const std::string& /*avatarPath*/, const boost::posix_time::ptime&) {lastMessageBody_ = message; return "";}; virtual void addSystemMessage(const std::string& /*message*/) {}; virtual void addErrorMessage(const std::string& /*message*/) {}; virtual void addPresenceMessage(const std::string& /*message*/) {}; // File transfer related stuff virtual std::string addFileTransfer(const std::string& /*senderName*/, bool /*senderIsSelf*/,const std::string& /*filename*/, const boost::uintmax_t /*sizeInBytes*/) { return 0; }; virtual void setFileTransferProgress(std::string /*id*/, const int /*alreadyTransferedBytes*/) { }; virtual void setFileTransferStatus(std::string /*id*/, const FileTransferState /*state*/, const std::string& /*msg*/) { }; virtual void setMessageReceiptState(const std::string &/* id */, ReceiptState /* state */) { } virtual void setContactChatState(ChatState::ChatStateType /*state*/) {}; virtual void setName(const std::string& name) {name_ = name;}; virtual void show() {}; virtual void activate() {}; virtual void setAvailableSecurityLabels(const std::vector& labels) {labels_ = labels;}; virtual void setSecurityLabelsEnabled(bool enabled) {labelsEnabled_ = enabled;}; virtual void setUnreadMessageCount(int /*count*/) {}; virtual void convertToMUC() {}; virtual void setSecurityLabelsError() {}; virtual SecurityLabelsCatalog::Item getSelectedSecurityLabel() {return label_;} virtual void setInputEnabled(bool /*enabled*/) {}; virtual void setRosterModel(Roster* /*roster*/) {}; virtual void setTabComplete(TabComplete*) {}; virtual void replaceLastMessage(const std::string&) {}; virtual void replaceMessage(const std::string&, const std::string&, const boost::posix_time::ptime&) {}; virtual void replaceWithAction(const std::string& /*message*/, const std::string& /*id*/, const boost::posix_time::ptime& /*time*/) {}; void setAckState(const std::string& /*id*/, AckState /*state*/) {}; virtual void flash() {}; virtual void setAlert(const std::string& /*alertText*/, const std::string& /*buttonText*/) {}; virtual void cancelAlert() {}; virtual void setCorrectionEnabled(Tristate /*enabled*/) {} void setAvailableOccupantActions(const std::vector&/* actions*/) {} void setSubject(const std::string& /*subject*/) {} virtual void showRoomConfigurationForm(Form::ref) {} virtual void addMUCInvitation(const std::string& /*senderName*/, const JID& /*jid*/, const std::string& /*reason*/, const std::string& /*password*/, bool = true) {}; virtual std::string addWhiteboardRequest(bool) {return "";}; virtual void setWhiteboardSessionStatus(std::string, const ChatWindow::WhiteboardSessionState){}; virtual void setAffiliations(MUCOccupant::Affiliation, const std::vector&) {} virtual void setAvailableRoomActions(const std::vector &) {}; virtual InviteToChatWindow* createInviteToChatWindow() {return NULL;} std::string name_; std::string lastMessageBody_; std::vector labels_; bool labelsEnabled_; SecurityLabelsCatalog::Item label_; }; } swift-im-2.0+dev6/Swift/Controllers/UnitTest/ChatMessageSummarizerTest.cpp0000644000175000017500000000741312227051774026660 0ustar kismithkismith/* * Copyright (c) 2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include "Swift/Controllers/ChatMessageSummarizer.h" using namespace Swift; using namespace std; class ChatMessageSummarizerTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(ChatMessageSummarizerTest); CPPUNIT_TEST(testEmpty); CPPUNIT_TEST(testCurrentNone); CPPUNIT_TEST(testCurrentCount); CPPUNIT_TEST(testCurrentCountOthersNone); CPPUNIT_TEST(testCurrentCountOtherCount); CPPUNIT_TEST(testCurrentNoneOtherCount); CPPUNIT_TEST(testCurrentCountOthersCount); CPPUNIT_TEST(testCurrentNoneOthersCount); CPPUNIT_TEST(testCurrentCountSomeOthersCount); CPPUNIT_TEST_SUITE_END(); public: ChatMessageSummarizerTest() {}; void setUp() { } void testEmpty() { string current(""); vector unreads; ChatMessageSummarizer summary; CPPUNIT_ASSERT_EQUAL(current, summary.getSummary(current, unreads)); } void testCurrentNone() { string current("Bob"); vector unreads; unreads.push_back(UnreadPair("Bob", 0)); ChatMessageSummarizer summary; CPPUNIT_ASSERT_EQUAL(current, summary.getSummary(current, unreads)); } void testCurrentCount() { string current("Bob"); vector unreads; unreads.push_back(UnreadPair("Bob", 3)); ChatMessageSummarizer summary; CPPUNIT_ASSERT_EQUAL(string("Bob (3)"), summary.getSummary(current, unreads)); } void testCurrentCountOthersNone() { string current("Bob"); vector unreads; unreads.push_back(UnreadPair("Bert", 0)); unreads.push_back(UnreadPair("Bob", 3)); unreads.push_back(UnreadPair("Betty", 0)); ChatMessageSummarizer summary; CPPUNIT_ASSERT_EQUAL(string("Bob (3)"), summary.getSummary(current, unreads)); } void testCurrentCountOtherCount() { string current("Bob"); vector unreads; unreads.push_back(UnreadPair("Bert", 0)); unreads.push_back(UnreadPair("Bob", 3)); unreads.push_back(UnreadPair("Betty", 7)); ChatMessageSummarizer summary; CPPUNIT_ASSERT_EQUAL(string("Bob (3), Betty (7)"), summary.getSummary(current, unreads)); } void testCurrentNoneOtherCount() { string current("Bob"); vector unreads; unreads.push_back(UnreadPair("Bert", 0)); unreads.push_back(UnreadPair("Bob", 0)); unreads.push_back(UnreadPair("Betty", 7)); ChatMessageSummarizer summary; CPPUNIT_ASSERT_EQUAL(string("Bob, Betty (7)"), summary.getSummary(current, unreads)); } void testCurrentNoneOthersCount() { string current("Bob"); vector unreads; unreads.push_back(UnreadPair("Bert", 2)); unreads.push_back(UnreadPair("Bob", 0)); unreads.push_back(UnreadPair("Betty", 7)); ChatMessageSummarizer summary; CPPUNIT_ASSERT_EQUAL(string("Bob and 2 others (9)"), summary.getSummary(current, unreads)); } void testCurrentCountOthersCount() { string current("Bob"); vector unreads; unreads.push_back(UnreadPair("Bert", 2)); unreads.push_back(UnreadPair("Bob", 11)); unreads.push_back(UnreadPair("Betty", 7)); ChatMessageSummarizer summary; CPPUNIT_ASSERT_EQUAL(string("Bob (11) and 2 others (9)"), summary.getSummary(current, unreads)); } void testCurrentCountSomeOthersCount() { string current("Bob"); vector unreads; unreads.push_back(UnreadPair("Bert", 2)); unreads.push_back(UnreadPair("Beverly", 0)); unreads.push_back(UnreadPair("Bob", 11)); unreads.push_back(UnreadPair("Beatrice", 0)); unreads.push_back(UnreadPair("Betty", 7)); ChatMessageSummarizer summary; CPPUNIT_ASSERT_EQUAL(string("Bob (11) and 2 others (9)"), summary.getSummary(current, unreads)); } }; CPPUNIT_TEST_SUITE_REGISTRATION(ChatMessageSummarizerTest); swift-im-2.0+dev6/Swift/Controllers/UnitTest/PreviousStatusStoreTest.cpp0000644000175000017500000000216612227051774026452 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include "Swift/Controllers/PreviousStatusStore.h" using namespace Swift; class PreviousStatusStoreTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(PreviousStatusStoreTest); CPPUNIT_TEST(testGetAll); //CPPUNIT_TEST(testGetAllLimited); //CPPUNIT_TEST(testGetSuggestionsInexact); //CPPUNIT_TEST(testGetSuggestionsExact); CPPUNIT_TEST_SUITE_END(); public: void setUp() { store_ = new PreviousStatusStore(); store_->addStatus(StatusShow::Online, "At home in the study"); store_->addStatus(StatusShow::DND, "In a meeting"); store_->addStatus(StatusShow::DND, "With a client"); store_->addStatus(StatusShow::Away, "Walking the elephant"); store_->addStatus(StatusShow::Online, "In the office, at my desk"); } void tearDown() { } void testGetAll() { } private: PreviousStatusStore* store_; }; CPPUNIT_TEST_SUITE_REGISTRATION(PreviousStatusStoreTest); swift-im-2.0+dev6/Swift/Controllers/ChatMessageSummarizer.h0000644000175000017500000000066412227051773023706 0ustar kismithkismith/* * Copyright (c) 2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include namespace Swift { typedef std::pair UnreadPair; class ChatMessageSummarizer { public: std::string getSummary(const std::string& current, const std::vector& unreads); }; } swift-im-2.0+dev6/Swift/Controllers/Chat/0000755000175000017500000000000012227051773020143 5ustar kismithkismithswift-im-2.0+dev6/Swift/Controllers/Chat/MUCController.cpp0000644000175000017500000007254412227051773023353 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MUC_JOIN_WARNING_TIMEOUT_MILLISECONDS 60000 namespace Swift { /** * The controller does not gain ownership of the stanzaChannel, nor the factory. */ MUCController::MUCController ( const JID& self, MUC::ref muc, const boost::optional& password, const std::string &nick, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, PresenceOracle* presenceOracle, AvatarManager* avatarManager, UIEventStream* uiEventStream, bool useDelayForLatency, TimerFactory* timerFactory, EventController* eventController, EntityCapsProvider* entityCapsProvider, XMPPRoster* roster, HistoryController* historyController, MUCRegistry* mucRegistry) : ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, muc->getJID(), presenceOracle, avatarManager, useDelayForLatency, uiEventStream, eventController, timerFactory, entityCapsProvider, historyController, mucRegistry), muc_(muc), nick_(nick), desiredNick_(nick), password_(password) { parting_ = true; joined_ = false; lastWasPresence_ = false; shouldJoinOnReconnect_ = true; doneGettingHistory_ = false; events_ = uiEventStream; inviteWindow_ = NULL; xmppRoster_ = roster; roster_ = new Roster(false, true); completer_ = new TabComplete(); chatWindow_->setRosterModel(roster_); chatWindow_->setTabComplete(completer_); chatWindow_->setName(muc->getJID().getNode()); chatWindow_->onClosed.connect(boost::bind(&MUCController::handleWindowClosed, this)); chatWindow_->onOccupantSelectionChanged.connect(boost::bind(&MUCController::handleWindowOccupantSelectionChanged, this, _1)); chatWindow_->onOccupantActionSelected.connect(boost::bind(&MUCController::handleActionRequestedOnOccupant, this, _1, _2)); chatWindow_->onChangeSubjectRequest.connect(boost::bind(&MUCController::handleChangeSubjectRequest, this, _1)); chatWindow_->onConfigureRequest.connect(boost::bind(&MUCController::handleConfigureRequest, this, _1)); chatWindow_->onConfigurationFormCancelled.connect(boost::bind(&MUCController::handleConfigurationCancelled, this)); chatWindow_->onDestroyRequest.connect(boost::bind(&MUCController::handleDestroyRoomRequest, this)); chatWindow_->onInvitePersonToThisMUCRequest.connect(boost::bind(&MUCController::handleInvitePersonToThisMUCRequest, this)); chatWindow_->onGetAffiliationsRequest.connect(boost::bind(&MUCController::handleGetAffiliationsRequest, this)); chatWindow_->onChangeAffiliationsRequest.connect(boost::bind(&MUCController::handleChangeAffiliationsRequest, this, _1)); muc_->onJoinComplete.connect(boost::bind(&MUCController::handleJoinComplete, this, _1)); muc_->onJoinFailed.connect(boost::bind(&MUCController::handleJoinFailed, this, _1)); muc_->onOccupantJoined.connect(boost::bind(&MUCController::handleOccupantJoined, this, _1)); muc_->onOccupantPresenceChange.connect(boost::bind(&MUCController::handleOccupantPresenceChange, this, _1)); muc_->onOccupantLeft.connect(boost::bind(&MUCController::handleOccupantLeft, this, _1, _2, _3)); muc_->onOccupantRoleChanged.connect(boost::bind(&MUCController::handleOccupantRoleChanged, this, _1, _2, _3)); muc_->onOccupantAffiliationChanged.connect(boost::bind(&MUCController::handleOccupantAffiliationChanged, this, _1, _2, _3)); muc_->onConfigurationFailed.connect(boost::bind(&MUCController::handleConfigurationFailed, this, _1)); muc_->onConfigurationFormReceived.connect(boost::bind(&MUCController::handleConfigurationFormReceived, this, _1)); muc_->onRoleChangeFailed.connect(boost::bind(&MUCController::handleOccupantRoleChangeFailed, this, _1, _2, _3)); muc_->onAffiliationListReceived.connect(boost::bind(&MUCController::handleAffiliationListReceived, this, _1, _2)); if (timerFactory) { loginCheckTimer_ = boost::shared_ptr(timerFactory->createTimer(MUC_JOIN_WARNING_TIMEOUT_MILLISECONDS)); loginCheckTimer_->onTick.connect(boost::bind(&MUCController::handleJoinTimeoutTick, this)); loginCheckTimer_->start(); } chatWindow_->convertToMUC(); setOnline(true); if (avatarManager_ != NULL) { avatarChangedConnection_ = (avatarManager_->onAvatarChanged.connect(boost::bind(&MUCController::handleAvatarChanged, this, _1))); } handleBareJIDCapsChanged(muc->getJID()); } MUCController::~MUCController() { chatWindow_->setRosterModel(NULL); delete roster_; if (loginCheckTimer_) { loginCheckTimer_->stop(); } chatWindow_->setTabComplete(NULL); delete completer_; } void MUCController::cancelReplaces() { lastWasPresence_ = false; } void MUCController::handleWindowOccupantSelectionChanged(ContactRosterItem* item) { std::vector actions; if (item) { MUCOccupant::Affiliation affiliation = muc_->getOccupant(getNick()).getAffiliation(); MUCOccupant::Role role = muc_->getOccupant(getNick()).getRole(); if (role == MUCOccupant::Moderator) { if (affiliation == MUCOccupant::Admin || affiliation == MUCOccupant::Owner) { actions.push_back(ChatWindow::Ban); } actions.push_back(ChatWindow::Kick); actions.push_back(ChatWindow::MakeModerator); actions.push_back(ChatWindow::MakeParticipant); actions.push_back(ChatWindow::MakeVisitor); } // Add contact is available only if the real JID is also available if (muc_->getOccupant(item->getJID().getResource()).getRealJID()) { actions.push_back(ChatWindow::AddContact); } } chatWindow_->setAvailableOccupantActions(actions); } void MUCController::handleActionRequestedOnOccupant(ChatWindow::OccupantAction action, ContactRosterItem* item) { JID mucJID = item->getJID(); MUCOccupant occupant = muc_->getOccupant(mucJID.getResource()); JID realJID; if (occupant.getRealJID()) { realJID = occupant.getRealJID().get(); } switch (action) { case ChatWindow::Kick: muc_->kickOccupant(mucJID);break; case ChatWindow::Ban: muc_->changeAffiliation(realJID, MUCOccupant::Outcast);break; case ChatWindow::MakeModerator: muc_->changeOccupantRole(mucJID, MUCOccupant::Moderator);break; case ChatWindow::MakeParticipant: muc_->changeOccupantRole(mucJID, MUCOccupant::Participant);break; case ChatWindow::MakeVisitor: muc_->changeOccupantRole(mucJID, MUCOccupant::Visitor);break; case ChatWindow::AddContact: if (occupant.getRealJID()) events_->send(boost::make_shared(realJID, occupant.getNick()));break; } } void MUCController::handleBareJIDCapsChanged(const JID& /*jid*/) { ChatWindow::Tristate support = ChatWindow::Yes; bool any = false; foreach (const std::string& nick, currentOccupants_) { DiscoInfo::ref disco = entityCapsProvider_->getCaps(toJID_.toBare().toString() + "/" + nick); if (disco && disco->hasFeature(DiscoInfo::MessageCorrectionFeature)) { any = true; } else { support = ChatWindow::Maybe; } } if (!any) { support = ChatWindow::No; } chatWindow_->setCorrectionEnabled(support); } /** * Join the MUC if not already in it. */ void MUCController::rejoin() { if (parting_) { joined_ = false; parting_ = false; if (password_) { muc_->setPassword(*password_); } //FIXME: check for received activity #ifdef SWIFT_EXPERIMENTAL_HISTORY if (lastActivity_ == boost::posix_time::not_a_date_time && historyController_) { lastActivity_ = historyController_->getLastTimeStampFromMUC(selfJID_, toJID_); } #endif if (lastActivity_ == boost::posix_time::not_a_date_time) { muc_->joinAs(nick_); } else { muc_->joinWithContextSince(nick_, lastActivity_); } } } bool MUCController::isJoined() { return joined_; } const std::string& MUCController::getNick() { return nick_; } void MUCController::handleJoinTimeoutTick() { receivedActivity(); chatWindow_->addSystemMessage(str(format(QT_TRANSLATE_NOOP("", "Room %1% is not responding. This operation may never complete.")) % toJID_.toString())); } void MUCController::receivedActivity() { if (loginCheckTimer_) { loginCheckTimer_->stop(); } } void MUCController::handleJoinFailed(boost::shared_ptr error) { receivedActivity(); std::string errorMessage = QT_TRANSLATE_NOOP("", "Unable to enter this room"); std::string rejoinNick; if (error) { switch (error->getCondition()) { case ErrorPayload::Conflict: rejoinNick = nick_ + "_"; errorMessage = str(format(QT_TRANSLATE_NOOP("", "Unable to enter this room as %1%, retrying as %2%")) % nick_ % rejoinNick); break; case ErrorPayload::JIDMalformed: errorMessage += ": "; errorMessage += QT_TRANSLATE_NOOP("", "No nickname specified"); break; case ErrorPayload::NotAuthorized: errorMessage += ": "; errorMessage += QT_TRANSLATE_NOOP("", "The correct room password is needed"); break; case ErrorPayload::RegistrationRequired: errorMessage += ": "; errorMessage += QT_TRANSLATE_NOOP("", "Only members may enter"); break; case ErrorPayload::Forbidden: errorMessage += ": "; errorMessage += QT_TRANSLATE_NOOP("", "You are banned from the room"); break; case ErrorPayload::ServiceUnavailable: errorMessage += ": "; errorMessage += QT_TRANSLATE_NOOP("", "The room is full"); break; case ErrorPayload::ItemNotFound: errorMessage += ": "; errorMessage += QT_TRANSLATE_NOOP("", "The room does not exist"); break; default: break; } } errorMessage = str(format(QT_TRANSLATE_NOOP("", "Couldn't join room: %1%.")) % errorMessage); chatWindow_->addErrorMessage(errorMessage); parting_ = true; if (!rejoinNick.empty()) { nick_ = rejoinNick; rejoin(); } } void MUCController::handleJoinComplete(const std::string& nick) { receivedActivity(); joined_ = true; std::string joinMessage = str(format(QT_TRANSLATE_NOOP("", "You have entered room %1% as %2%.")) % toJID_.toString() % nick); nick_ = nick; chatWindow_->addSystemMessage(joinMessage); #ifdef SWIFT_EXPERIMENTAL_HISTORY addRecentLogs(); #endif clearPresenceQueue(); shouldJoinOnReconnect_ = true; setEnabled(true); MUCOccupant occupant = muc_->getOccupant(nick); setAvailableRoomActions(occupant.getAffiliation(), occupant.getRole()); onUserJoined(); } void MUCController::handleAvatarChanged(const JID& jid) { if (parting_ || !jid.equals(toJID_, JID::WithoutResource)) { return; } std::string path = avatarManager_->getAvatarPath(jid).string(); roster_->applyOnItems(SetAvatar(jid, path, JID::WithResource)); } void MUCController::handleWindowClosed() { parting_ = true; shouldJoinOnReconnect_ = false; muc_->part(); onUserLeft(); } void MUCController::handleOccupantJoined(const MUCOccupant& occupant) { if (nick_ != occupant.getNick()) { completer_->addWord(occupant.getNick()); } receivedActivity(); JID jid(nickToJID(occupant.getNick())); JID realJID; if (occupant.getRealJID()) { realJID = occupant.getRealJID().get(); } currentOccupants_.insert(occupant.getNick()); NickJoinPart event(occupant.getNick(), Join); appendToJoinParts(joinParts_, event); std::string groupName(roleToGroupName(occupant.getRole())); roster_->addContact(jid, realJID, occupant.getNick(), groupName, avatarManager_->getAvatarPath(jid).string()); roster_->getGroup(groupName)->setManualSort(roleToSortName(occupant.getRole())); if (joined_) { std::string joinString; MUCOccupant::Role role = occupant.getRole(); if (role != MUCOccupant::NoRole && role != MUCOccupant::Participant) { joinString = str(format(QT_TRANSLATE_NOOP("", "%1% has entered the room as a %2%.")) % occupant.getNick() % roleToFriendlyName(role)); } else { joinString = str(format(QT_TRANSLATE_NOOP("", "%1% has entered the room.")) % occupant.getNick()); } if (shouldUpdateJoinParts()) { updateJoinParts(); } else { addPresenceMessage(joinString); } } if (avatarManager_ != NULL) { handleAvatarChanged(jid); } } void MUCController::addPresenceMessage(const std::string& message) { lastWasPresence_ = true; chatWindow_->addPresenceMessage(message); } void MUCController::setAvailableRoomActions(const MUCOccupant::Affiliation& affiliation, const MUCOccupant::Role& role) { std::vector actions; if (role <= MUCOccupant::Participant) { actions.push_back(ChatWindow::ChangeSubject); } if (affiliation == MUCOccupant::Owner) { actions.push_back(ChatWindow::Configure); } if (affiliation <= MUCOccupant::Admin) { actions.push_back(ChatWindow::Affiliations); } if (affiliation == MUCOccupant::Owner) { actions.push_back(ChatWindow::Destroy); } if (role <= MUCOccupant::Visitor) { actions.push_back(ChatWindow::Invite); } chatWindow_->setAvailableRoomActions(actions); } void MUCController::clearPresenceQueue() { lastWasPresence_ = false; joinParts_.clear(); } std::string MUCController::roleToFriendlyName(MUCOccupant::Role role) { switch (role) { case MUCOccupant::Moderator: return QT_TRANSLATE_NOOP("", "moderator"); case MUCOccupant::Participant: return QT_TRANSLATE_NOOP("", "participant"); case MUCOccupant::Visitor: return QT_TRANSLATE_NOOP("", "visitor"); case MUCOccupant::NoRole: return ""; } return ""; } std::string MUCController::roleToSortName(MUCOccupant::Role role) { switch (role) { case MUCOccupant::Moderator: return "1"; case MUCOccupant::Participant: return "2"; case MUCOccupant::Visitor: return "3"; case MUCOccupant::NoRole: return "4"; } return "5"; } JID MUCController::nickToJID(const std::string& nick) { return JID(toJID_.getNode(), toJID_.getDomain(), nick); } bool MUCController::messageTargetsMe(boost::shared_ptr message) { std::string stringRegexp(".*\\b" + boost::to_lower_copy(nick_) + "\\b.*"); boost::regex myRegexp(stringRegexp); return boost::regex_match(boost::to_lower_copy(message->getBody()), myRegexp); } void MUCController::preHandleIncomingMessage(boost::shared_ptr messageEvent) { if (messageEvent->getStanza()->getType() == Message::Groupchat) { lastActivity_ = boost::posix_time::microsec_clock::universal_time(); } clearPresenceQueue(); boost::shared_ptr message = messageEvent->getStanza(); if (joined_ && messageEvent->getStanza()->getFrom().getResource() != nick_ && messageTargetsMe(message) && !message->getPayload() && messageEvent->isReadable() ) { chatWindow_->flash(); } else { messageEvent->setTargetsMe(false); } if (joined_) { std::string nick = message->getFrom().getResource(); if (nick != nick_ && currentOccupants_.find(nick) != currentOccupants_.end()) { completer_->addWord(nick); } } /*Buggy implementations never send the status code, so use an incoming message as a hint that joining's done (e.g. the old ejabberd on psi-im.org).*/ receivedActivity(); joined_ = true; if (message->hasSubject() && message->getBody().empty()) { chatWindow_->addSystemMessage(str(format(QT_TRANSLATE_NOOP("", "The room subject is now: %1%")) % message->getSubject()));; chatWindow_->setSubject(message->getSubject()); doneGettingHistory_ = true; } if (!doneGettingHistory_ && !message->getPayload()) { doneGettingHistory_ = true; } if (!doneGettingHistory_) { checkDuplicates(message); messageEvent->conclude(); } } void MUCController::postHandleIncomingMessage(boost::shared_ptr messageEvent) { boost::shared_ptr message = messageEvent->getStanza(); if (joined_ && messageEvent->getStanza()->getFrom().getResource() != nick_ && messageTargetsMe(message) && !message->getPayload()) { eventController_->handleIncomingEvent(messageEvent); } } void MUCController::handleOccupantRoleChanged(const std::string& nick, const MUCOccupant& occupant, const MUCOccupant::Role& oldRole) { clearPresenceQueue(); receivedActivity(); JID jid(nickToJID(nick)); roster_->removeContactFromGroup(jid, roleToGroupName(oldRole)); JID realJID; if (occupant.getRealJID()) { realJID = occupant.getRealJID().get(); } std::string group(roleToGroupName(occupant.getRole())); roster_->addContact(jid, realJID, nick, group, avatarManager_->getAvatarPath(jid).string()); roster_->getGroup(group)->setManualSort(roleToSortName(occupant.getRole())); chatWindow_->addSystemMessage(str(format(QT_TRANSLATE_NOOP("", "%1% is now a %2%")) % nick % roleToFriendlyName(occupant.getRole()))); if (nick == nick_) { setAvailableRoomActions(occupant.getAffiliation(), occupant.getRole()); } } void MUCController::handleOccupantAffiliationChanged(const std::string& nick, const MUCOccupant::Affiliation& affiliation, const MUCOccupant::Affiliation& /*oldAffiliation*/) { if (nick == nick_) { setAvailableRoomActions(affiliation, muc_->getOccupant(nick_).getRole()); } } std::string MUCController::roleToGroupName(MUCOccupant::Role role) { std::string result; switch (role) { case MUCOccupant::Moderator: result = QT_TRANSLATE_NOOP("", "Moderators"); break; case MUCOccupant::Participant: result = QT_TRANSLATE_NOOP("", "Participants"); break; case MUCOccupant::Visitor: result = QT_TRANSLATE_NOOP("", "Visitors"); break; case MUCOccupant::NoRole: result = QT_TRANSLATE_NOOP("", "Occupants"); break; default: assert(false); } return result; } void MUCController::setOnline(bool online) { ChatControllerBase::setOnline(online); if (!online) { muc_->part(); parting_ = true; processUserPart(); } else { if (shouldJoinOnReconnect_) { chatWindow_->addSystemMessage(str(format(QT_TRANSLATE_NOOP("", "Trying to enter room %1%")) % toJID_.toString())); if (loginCheckTimer_) { loginCheckTimer_->start(); } nick_ = desiredNick_; rejoin(); } } } void MUCController::processUserPart() { roster_->removeAll(); /* handleUserLeft won't throw a part back up unless this is called when it doesn't yet know we've left - which only happens on disconnect, so call with disconnect here so if the signal does bubble back up, it'll be with the right type.*/ muc_->handleUserLeft(MUC::Disconnect); setEnabled(false); } bool MUCController::shouldUpdateJoinParts() { return lastWasPresence_; } void MUCController::handleOccupantLeft(const MUCOccupant& occupant, MUC::LeavingType type, const std::string& reason) { NickJoinPart event(occupant.getNick(), Part); appendToJoinParts(joinParts_, event); currentOccupants_.erase(occupant.getNick()); completer_->removeWord(occupant.getNick()); std::string partMessage; bool clearAfter = false; if (occupant.getNick() != nick_) { std::string partType; switch (type) { case MUC::LeaveKick: clearPresenceQueue(); clearAfter = true; partType = " (kicked)"; break; case MUC::LeaveBan: clearPresenceQueue(); clearAfter = true; partType = " (banned)"; break; case MUC::LeaveNotMember: clearPresenceQueue(); clearAfter = true; partType = " (no longer a member)"; break; case MUC::LeaveDestroy: case MUC::Disconnect: case MUC::LeavePart: break; } partMessage = str(format(QT_TRANSLATE_NOOP("", "%1% has left the room%2%")) % occupant.getNick() % partType); } else { switch (type) { case MUC::LeaveKick: clearPresenceQueue(); clearAfter = true; partMessage = QT_TRANSLATE_NOOP("", "You have been kicked out of the room"); break; case MUC::LeaveBan: clearPresenceQueue(); clearAfter = true; partMessage = QT_TRANSLATE_NOOP("", "You have been banned from the room"); break; case MUC::LeaveNotMember: clearPresenceQueue(); clearAfter = true; partMessage = QT_TRANSLATE_NOOP("", "You are no longer a member of the room and have been removed"); break; case MUC::LeaveDestroy: clearPresenceQueue(); clearAfter = true; partMessage = QT_TRANSLATE_NOOP("", "The room has been destroyed"); break; case MUC::Disconnect: case MUC::LeavePart: partMessage = QT_TRANSLATE_NOOP("", "You have left the room"); } } if (!reason.empty()) { partMessage += " (" + reason + ")"; } partMessage += "."; if (occupant.getNick() != nick_) { if (shouldUpdateJoinParts()) { updateJoinParts(); } else { addPresenceMessage(partMessage); } roster_->removeContact(JID(toJID_.getNode(), toJID_.getDomain(), occupant.getNick())); } else { addPresenceMessage(partMessage); parting_ = true; processUserPart(); } if (clearAfter) { clearPresenceQueue(); } } void MUCController::handleOccupantPresenceChange(boost::shared_ptr presence) { receivedActivity(); roster_->applyOnItems(SetPresence(presence, JID::WithResource)); } bool MUCController::isIncomingMessageFromMe(boost::shared_ptr message) { JID from = message->getFrom(); return nick_ == from.getResource(); } std::string MUCController::senderDisplayNameFromMessage(const JID& from) { return from.getResource(); } void MUCController::preSendMessageRequest(boost::shared_ptr message) { message->setType(Swift::Message::Groupchat); } boost::optional MUCController::getMessageTimestamp(boost::shared_ptr message) const { return message->getTimestampFrom(toJID_); } void MUCController::updateJoinParts() { chatWindow_->replaceLastMessage(generateJoinPartString(joinParts_)); } void MUCController::appendToJoinParts(std::vector& joinParts, const NickJoinPart& newEvent) { std::vector::iterator it = joinParts.begin(); bool matched = false; for (; it != joinParts.end(); ++it) { if ((*it).nick == newEvent.nick) { matched = true; JoinPart type = (*it).type; switch (newEvent.type) { case Join: type = (type == Part) ? PartThenJoin : Join; break; case Part: type = (type == Join) ? JoinThenPart : Part; break; default: /*Nothing to see here */;break; } (*it).type = type; break; } } if (!matched) { joinParts.push_back(newEvent); } } std::string MUCController::concatenateListOfNames(const std::vector& joinParts) { std::string result; for (size_t i = 0; i < joinParts.size(); i++) { if (i > 0) { if (i < joinParts.size() - 1) { result += ", "; } else { result += QT_TRANSLATE_NOOP("", " and "); } } NickJoinPart event = joinParts[i]; result += event.nick; } return result; } std::string MUCController::generateJoinPartString(const std::vector& joinParts) { std::vector sorted[4]; std::string eventStrings[4]; foreach (NickJoinPart event, joinParts) { sorted[event.type].push_back(event); } std::string result; std::vector populatedEvents; for (size_t i = 0; i < 4; i++) { std::string names = concatenateListOfNames(sorted[i]); if (!names.empty()) { std::string eventString; switch (i) { case Join: if (sorted[i].size() > 1) { eventString = QT_TRANSLATE_NOOP("", "%1% have entered the room"); } else { eventString = QT_TRANSLATE_NOOP("", "%1% has entered the room"); } break; case Part: if (sorted[i].size() > 1) { eventString = QT_TRANSLATE_NOOP("", "%1% have left the room"); } else { eventString = QT_TRANSLATE_NOOP("", "%1% has left the room"); } break; case JoinThenPart: if (sorted[i].size() > 1) { eventString = QT_TRANSLATE_NOOP("", "%1% have entered then left the room"); } else { eventString = QT_TRANSLATE_NOOP("", "%1% has entered then left the room"); } break; case PartThenJoin: if (sorted[i].size() > 1) { eventString = QT_TRANSLATE_NOOP("", "%1% have left then returned to the room"); } else { eventString = QT_TRANSLATE_NOOP("", "%1% has left then returned to the room"); } break; } populatedEvents.push_back(static_cast(i)); eventStrings[i] = str(boost::format(eventString) % names); } } for (size_t i = 0; i < populatedEvents.size(); i++) { if (i > 0) { if (i < populatedEvents.size() - 1) { result += ", "; } else { result += QT_TRANSLATE_NOOP("", " and "); } } result += eventStrings[populatedEvents[i]]; } return result; } void MUCController::handleChangeSubjectRequest(const std::string& subject) { muc_->changeSubject(subject); } void MUCController::handleConfigureRequest(Form::ref form) { if (form) { muc_->configureRoom(form); } else { muc_->requestConfigurationForm(); } } void MUCController::handleConfigurationFailed(ErrorPayload::ref error) { std::string errorMessage = getErrorMessage(error); errorMessage = str(format(QT_TRANSLATE_NOOP("", "Room configuration failed: %1%.")) % errorMessage); chatWindow_->addErrorMessage(errorMessage); } void MUCController::handleOccupantRoleChangeFailed(ErrorPayload::ref error, const JID&, MUCOccupant::Role) { std::string errorMessage = getErrorMessage(error); errorMessage = str(format(QT_TRANSLATE_NOOP("", "Occupant role change failed: %1%.")) % errorMessage); chatWindow_->addErrorMessage(errorMessage); } void MUCController::handleConfigurationFormReceived(Form::ref form) { chatWindow_->showRoomConfigurationForm(form); } void MUCController::handleConfigurationCancelled() { muc_->cancelConfigureRoom(); } void MUCController::handleDestroyRoomRequest() { muc_->destroyRoom(); } void MUCController::handleInvitePersonToThisMUCRequest() { if (!inviteWindow_) { inviteWindow_ = chatWindow_->createInviteToChatWindow(); inviteWindow_->onCompleted.connect(boost::bind(&MUCController::handleInviteToMUCWindowCompleted, this)); inviteWindow_->onDismissed.connect(boost::bind(&MUCController::handleInviteToMUCWindowDismissed, this)); } std::vector > autoCompletes; foreach (XMPPRosterItem item, xmppRoster_->getItems()) { std::pair jidName; jidName.first = item.getJID(); jidName.second = item.getName(); autoCompletes.push_back(jidName); //std::cerr << "MUCController adding " << item.getJID().toString() << std::endl; } inviteWindow_->setAutoCompletions(autoCompletes); } void MUCController::handleInviteToMUCWindowDismissed() { inviteWindow_= NULL; } void MUCController::handleInviteToMUCWindowCompleted() { foreach (const JID& jid, inviteWindow_->getJIDs()) { muc_->invitePerson(jid, inviteWindow_->getReason()); } inviteWindow_ = NULL; } void MUCController::handleGetAffiliationsRequest() { muc_->requestAffiliationList(MUCOccupant::Owner); muc_->requestAffiliationList(MUCOccupant::Admin); muc_->requestAffiliationList(MUCOccupant::Member); muc_->requestAffiliationList(MUCOccupant::Outcast); } typedef std::pair AffiliationChangePair; void MUCController::handleChangeAffiliationsRequest(const std::vector >& changes) { std::set addedJIDs; foreach (const AffiliationChangePair& change, changes) { if (change.first != MUCOccupant::NoAffiliation) { addedJIDs.insert(change.second); } } foreach (const AffiliationChangePair& change, changes) { if (change.first != MUCOccupant::NoAffiliation || addedJIDs.find(change.second) == addedJIDs.end()) { muc_->changeAffiliation(change.second, change.first); } } } void MUCController::handleAffiliationListReceived(MUCOccupant::Affiliation affiliation, const std::vector& jids) { chatWindow_->setAffiliations(affiliation, jids); } void MUCController::logMessage(const std::string& message, const JID& fromJID, const JID& toJID, const boost::posix_time::ptime& timeStamp, bool isIncoming) { // log only incoming messages if (isIncoming && historyController_) { historyController_->addMessage(message, fromJID, toJID, HistoryMessage::Groupchat, timeStamp); } } void MUCController::addRecentLogs() { if (!historyController_) { return; } joinContext_ = historyController_->getMUCContext(selfJID_, toJID_, lastActivity_); foreach (const HistoryMessage& message, joinContext_) { bool senderIsSelf = nick_ == message.getFromJID().getResource(); // the chatWindow uses utc timestamps addMessage(message.getMessage(), senderDisplayNameFromMessage(message.getFromJID()), senderIsSelf, boost::shared_ptr(new SecurityLabel()), std::string(avatarManager_->getAvatarPath(message.getFromJID()).string()), message.getTime() - boost::posix_time::hours(message.getOffset())); } } void MUCController::checkDuplicates(boost::shared_ptr newMessage) { std::string body = newMessage->getBody(); JID jid = newMessage->getFrom(); boost::optional time = newMessage->getTimestamp(); reverse_foreach (const HistoryMessage& message, joinContext_) { boost::posix_time::ptime messageTime = message.getTime() - boost::posix_time::hours(message.getOffset()); if (time && time < messageTime) { break; } if (time && time != messageTime) { continue; } if (message.getFromJID() != jid) { continue; } if (message.getMessage() != body) { continue; } // Mark the message as unreadable newMessage->setBody(""); } } } swift-im-2.0+dev6/Swift/Controllers/Chat/ChatController.h0000644000175000017500000000750112227051773023242 0ustar kismithkismith/* * Copyright (c) 2010-2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include namespace Swift { class AvatarManager; class ChatStateNotifier; class ChatStateTracker; class NickResolver; class EntityCapsProvider; class FileTransferController; class SettingsProvider; class HistoryController; class ChatController : public ChatControllerBase { public: ChatController(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &contact, NickResolver* nickResolver, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool isInMUC, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController, TimerFactory* timerFactory, EntityCapsProvider* entityCapsProvider, bool userWantsReceipts, SettingsProvider* settings, HistoryController* historyController, MUCRegistry* mucRegistry); virtual ~ChatController(); virtual void setToJID(const JID& jid); virtual void setOnline(bool online); virtual void handleNewFileTransferController(FileTransferController* ftc); virtual void handleWhiteboardSessionRequest(bool senderIsSelf); virtual void handleWhiteboardStateChange(const ChatWindow::WhiteboardSessionState state); virtual void setContactIsReceivingPresence(bool /*isReceivingPresence*/); protected: void cancelReplaces(); JID getBaseJID(); void logMessage(const std::string& message, const JID& fromJID, const JID& toJID, const boost::posix_time::ptime& timeStamp, bool isIncoming); private: void handlePresenceChange(boost::shared_ptr newPresence); std::string getStatusChangeString(boost::shared_ptr presence); bool isIncomingMessageFromMe(boost::shared_ptr message); void postSendMessage(const std::string &body, boost::shared_ptr sentStanza); void preHandleIncomingMessage(boost::shared_ptr messageEvent); void postHandleIncomingMessage(boost::shared_ptr messageEvent); void preSendMessageRequest(boost::shared_ptr); std::string senderDisplayNameFromMessage(const JID& from); virtual boost::optional getMessageTimestamp(boost::shared_ptr) const; void handleStanzaAcked(boost::shared_ptr stanza); void dayTicked() {lastWasPresence_ = false;} void handleContactNickChanged(const JID& jid, const std::string& /*oldNick*/); void handleBareJIDCapsChanged(const JID& jid); void handleFileTransferCancel(std::string /* id */); void handleFileTransferStart(std::string /* id */, std::string /* description */); void handleFileTransferAccept(std::string /* id */, std::string /* filename */); void handleSendFileRequest(std::string filename); void handleWhiteboardSessionAccept(); void handleWhiteboardSessionCancel(); void handleWhiteboardWindowShow(); void handleSettingChanged(const std::string& settingPath); void checkForDisplayingDisplayReceiptsAlert(); private: NickResolver* nickResolver_; ChatStateNotifier* chatStateNotifier_; ChatStateTracker* chatStateTracker_; std::string myLastMessageUIID_; bool isInMUC_; bool lastWasPresence_; std::string lastStatusChangeString_; std::map, std::string> unackedStanzas_; std::map requestedReceipts_; StatusShow::Type lastShownStatus_; UIEventStream* eventStream_; ChatWindow::Tristate contactSupportsReceipts_; bool receivingPresenceFromUs_; bool userWantsReceipts_; std::map ftControllers; SettingsProvider* settings_; std::string lastWbID_; }; } swift-im-2.0+dev6/Swift/Controllers/Chat/MUCController.h0000644000175000017500000001350012227051773023003 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Swift { class StanzaChannel; class IQRouter; class ChatWindowFactory; class Roster; class AvatarManager; class UIEventStream; class TimerFactory; class TabComplete; class InviteToChatWindow; class XMPPRoster; enum JoinPart {Join, Part, JoinThenPart, PartThenJoin}; struct NickJoinPart { NickJoinPart(const std::string& nick, JoinPart type) : nick(nick), type(type) {}; std::string nick; JoinPart type; }; class MUCController : public ChatControllerBase { public: MUCController(const JID& self, MUC::ref muc, const boost::optional& password, const std::string &nick, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, PresenceOracle* presenceOracle, AvatarManager* avatarManager, UIEventStream* events, bool useDelayForLatency, TimerFactory* timerFactory, EventController* eventController, EntityCapsProvider* entityCapsProvider, XMPPRoster* roster, HistoryController* historyController, MUCRegistry* mucRegistry); ~MUCController(); boost::signal onUserLeft; boost::signal onUserJoined; virtual void setOnline(bool online); void rejoin(); static void appendToJoinParts(std::vector& joinParts, const NickJoinPart& newEvent); static std::string generateJoinPartString(const std::vector& joinParts); static std::string concatenateListOfNames(const std::vector& joinParts); bool isJoined(); const std::string& getNick(); protected: void preSendMessageRequest(boost::shared_ptr message); bool isIncomingMessageFromMe(boost::shared_ptr message); std::string senderDisplayNameFromMessage(const JID& from); boost::optional getMessageTimestamp(boost::shared_ptr message) const; void preHandleIncomingMessage(boost::shared_ptr); void postHandleIncomingMessage(boost::shared_ptr); void cancelReplaces(); void logMessage(const std::string& message, const JID& fromJID, const JID& toJID, const boost::posix_time::ptime& timeStamp, bool isIncoming); private: void setAvailableRoomActions(const MUCOccupant::Affiliation& affiliation, const MUCOccupant::Role& role); void clearPresenceQueue(); void addPresenceMessage(const std::string& message); void handleWindowOccupantSelectionChanged(ContactRosterItem* item); void handleActionRequestedOnOccupant(ChatWindow::OccupantAction, ContactRosterItem* item); void handleWindowClosed(); void handleAvatarChanged(const JID& jid); void handleOccupantJoined(const MUCOccupant& occupant); void handleOccupantLeft(const MUCOccupant& occupant, MUC::LeavingType type, const std::string& reason); void handleOccupantPresenceChange(boost::shared_ptr presence); void handleOccupantRoleChanged(const std::string& nick, const MUCOccupant& occupant,const MUCOccupant::Role& oldRole); void handleOccupantAffiliationChanged(const std::string& nick, const MUCOccupant::Affiliation& affiliation,const MUCOccupant::Affiliation& oldAffiliation); void handleJoinComplete(const std::string& nick); void handleJoinFailed(boost::shared_ptr error); void handleJoinTimeoutTick(); void handleChangeSubjectRequest(const std::string&); std::string roleToGroupName(MUCOccupant::Role role); std::string roleToSortName(MUCOccupant::Role role); JID nickToJID(const std::string& nick); std::string roleToFriendlyName(MUCOccupant::Role role); void receivedActivity(); bool messageTargetsMe(boost::shared_ptr message); void updateJoinParts(); bool shouldUpdateJoinParts(); void dayTicked() {clearPresenceQueue();} void processUserPart(); void handleBareJIDCapsChanged(const JID& jid); void handleConfigureRequest(Form::ref); void handleConfigurationFailed(ErrorPayload::ref); void handleConfigurationFormReceived(Form::ref); void handleDestroyRoomRequest(); void handleInvitePersonToThisMUCRequest(); void handleConfigurationCancelled(); void handleOccupantRoleChangeFailed(ErrorPayload::ref, const JID&, MUCOccupant::Role); void handleGetAffiliationsRequest(); void handleAffiliationListReceived(MUCOccupant::Affiliation affiliation, const std::vector& jids); void handleChangeAffiliationsRequest(const std::vector >& changes); void handleInviteToMUCWindowDismissed(); void handleInviteToMUCWindowCompleted(); void addRecentLogs(); void checkDuplicates(boost::shared_ptr newMessage); private: MUC::ref muc_; UIEventStream* events_; std::string nick_; std::string desiredNick_; Roster* roster_; TabComplete* completer_; bool parting_; bool joined_; bool lastWasPresence_; bool shouldJoinOnReconnect_; bool doneGettingHistory_; boost::bsignals::scoped_connection avatarChangedConnection_; boost::shared_ptr loginCheckTimer_; std::set currentOccupants_; std::vector joinParts_; boost::posix_time::ptime lastActivity_; boost::optional password_; InviteToChatWindow* inviteWindow_; XMPPRoster* xmppRoster_; std::vector joinContext_; }; } swift-im-2.0+dev6/Swift/Controllers/Chat/MUCSearchController.cpp0000644000175000017500000001352212227051773024470 0ustar kismithkismith/* * Copyright (c) 2010-2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Swift/Controllers/Chat/MUCSearchController.h" #include #include #include #include #include #include #include #include #include #include #include namespace Swift { static const std::string SEARCHED_SERVICES = "searchedServices"; MUCSearchController::MUCSearchController(const JID& jid, MUCSearchWindowFactory* factory, IQRouter* iqRouter, ProfileSettingsProvider* settings) : jid_(jid), factory_(factory), iqRouter_(iqRouter), settings_(settings), window_(NULL), walker_(NULL) { itemsInProgress_ = 0; loadSavedServices(); } MUCSearchController::~MUCSearchController() { delete walker_; delete window_; } void MUCSearchController::openSearchWindow() { if (!window_) { window_ = factory_->createMUCSearchWindow(); window_->onSearchService.connect(boost::bind(&MUCSearchController::handleSearchService, this, _1)); window_->onFinished.connect(boost::bind(&MUCSearchController::handleMUCSearchFinished, this, _1)); window_->addSavedServices(savedServices_); } handleSearchService(JID(jid_.getDomain())); window_->show(); } void MUCSearchController::loadSavedServices() { savedServices_.clear(); foreach (std::string stringItem, String::split(settings_->getStringSetting(SEARCHED_SERVICES), '\n')) { savedServices_.push_back(JID(stringItem)); } } void MUCSearchController::addToSavedServices(const JID& jid) { savedServices_.erase(std::remove(savedServices_.begin(), savedServices_.end(), jid), savedServices_.end()); savedServices_.push_front(jid); std::string collapsed; int i = 0; foreach (JID jidItem, savedServices_) { if (i >= 15) { break; } if (!collapsed.empty()) { collapsed += "\n"; } collapsed += jidItem.toString(); ++i; } settings_->storeString(SEARCHED_SERVICES, collapsed); window_->addSavedServices(savedServices_); } void MUCSearchController::handleSearchService(const JID& jid) { if (!jid.isValid()) { //Set Window to say error this isn't valid return; } addToSavedServices(jid); services_.clear(); serviceDetails_.clear(); window_->setSearchInProgress(true); refreshView(); if (walker_) { walker_->endWalk(); walker_->onServiceFound.disconnect(boost::bind(&MUCSearchController::handleDiscoServiceFound, this, _1, _2)); walker_->onWalkComplete.disconnect(boost::bind(&MUCSearchController::handleDiscoWalkFinished, this)); delete walker_; } SWIFT_LOG(debug) << "Starting walking MUC services" << std::endl; itemsInProgress_ = 0; walker_ = new DiscoServiceWalker(jid, iqRouter_); walker_->onServiceFound.connect(boost::bind(&MUCSearchController::handleDiscoServiceFound, this, _1, _2)); walker_->onWalkComplete.connect(boost::bind(&MUCSearchController::handleDiscoWalkFinished, this)); walker_->beginWalk(); } void MUCSearchController::handleDiscoServiceFound(const JID& jid, boost::shared_ptr info) { bool isMUC = false; std::string name; foreach (DiscoInfo::Identity identity, info->getIdentities()) { if ((identity.getCategory() == "directory" && identity.getType() == "chatroom") || (identity.getCategory() == "conference" && identity.getType() == "text")) { isMUC = true; name = identity.getName(); } } if (isMUC) { SWIFT_LOG(debug) << "MUC Service found: " << jid << std::endl; services_.erase(std::remove(services_.begin(), services_.end(), jid), services_.end()); services_.push_back(jid); serviceDetails_[jid].setName(name); serviceDetails_[jid].setJID(jid); serviceDetails_[jid].setComplete(false); itemsInProgress_++; SWIFT_LOG(debug) << "Requesting items of " << jid << " (" << itemsInProgress_ << " item requests in progress)" << std::endl; GetDiscoItemsRequest::ref discoItemsRequest = GetDiscoItemsRequest::create(jid, iqRouter_); discoItemsRequest->onResponse.connect(boost::bind(&MUCSearchController::handleRoomsItemsResponse, this, _1, _2, jid)); discoItemsRequest->send(); } else { removeService(jid); } refreshView(); } void MUCSearchController::handleDiscoWalkFinished() { SWIFT_LOG(debug) << "MUC Walk finished" << std::endl; updateInProgressness(); } void MUCSearchController::removeService(const JID& jid) { serviceDetails_.erase(jid); services_.erase(std::remove(services_.begin(), services_.end(), jid), services_.end()); refreshView(); } void MUCSearchController::handleRoomsItemsResponse(boost::shared_ptr items, ErrorPayload::ref error, const JID& jid) { itemsInProgress_--; SWIFT_LOG(debug) << "Items received for " << jid << " (" << itemsInProgress_ << " item requests in progress)" << std::endl; updateInProgressness(); if (error) { handleDiscoError(jid, error); return; } serviceDetails_[jid].clearRooms(); foreach (DiscoItems::Item item, items->getItems()) { serviceDetails_[jid].addRoom(MUCService::MUCRoom(item.getJID().getNode(), item.getName(), -1)); } serviceDetails_[jid].setComplete(true); refreshView(); } void MUCSearchController::handleDiscoError(const JID& jid, ErrorPayload::ref error) { serviceDetails_[jid].setComplete(true); serviceDetails_[jid].setError(error->getText()); } void MUCSearchController::refreshView() { window_->clearList(); foreach (JID jid, services_) { window_->addService(serviceDetails_[jid]); } } void MUCSearchController::updateInProgressness() { window_->setSearchInProgress((walker_ && walker_->isActive()) || itemsInProgress_ > 0); } void MUCSearchController::handleMUCSearchFinished(const boost::optional& result) { if (result) { onMUCSelected(*result); } } } swift-im-2.0+dev6/Swift/Controllers/Chat/MUCSearchController.h0000644000175000017500000000620212227051773024132 0ustar kismithkismith/* * Copyright (c) 2010-2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include "Swiften/Base/boost_bsignals.h" #include #include "Swiften/JID/JID.h" #include "Swift/Controllers/UIEvents/UIEvent.h" #include "Swift/Controllers/ProfileSettingsProvider.h" #include "Swiften/Elements/DiscoInfo.h" #include "Swiften/Elements/DiscoItems.h" #include "Swiften/Elements/ErrorPayload.h" namespace Swift { class UIEventStream; class MUCSearchWindow; class MUCSearchWindowFactory; class IQRouter; class DiscoServiceWalker; class NickResolver; class MUCService { public: class MUCRoom { public: MUCRoom(const std::string& node, const std::string& name, int occupants) : node_(node), name_(name), occupants_(occupants) {} std::string getNode() {return node_;} std::string getName() {return name_;} int getOccupantCount() {return occupants_;} private: std::string node_; std::string name_; int occupants_; }; MUCService() {error_ = false; complete_ = false;} void setComplete(bool complete) { complete_ = complete; } void setName(const std::string& name) { name_ = name; } void setJID(const JID& jid) { jid_ = jid; } bool getComplete() const { return complete_; } JID getJID() const { return jid_; } std::string getName() const { return name_; } void setError(const std::string& errorText) {error_ = true; errorText_ = errorText;} void clearRooms() {rooms_.clear();} void addRoom(const MUCRoom& room) {rooms_.push_back(room);} std::vector getRooms() const {return rooms_;} private: std::string name_; JID jid_; std::vector rooms_; bool complete_; bool error_; std::string errorText_; }; class MUCSearchController { public: MUCSearchController(const JID& jid, MUCSearchWindowFactory* mucSearchWindowFactory, IQRouter* iqRouter, ProfileSettingsProvider* settings); ~MUCSearchController(); void openSearchWindow(); public: boost::signal onMUCSelected; private: void handleSearchService(const JID& jid); void handleRoomsItemsResponse(boost::shared_ptr items, ErrorPayload::ref error, const JID& jid); void handleDiscoError(const JID& jid, ErrorPayload::ref error); void handleDiscoServiceFound(const JID&, boost::shared_ptr); void handleDiscoWalkFinished(); void handleMUCSearchFinished(const boost::optional& result); void removeService(const JID& jid); void refreshView(); void loadSavedServices(); void addToSavedServices(const JID& jid); void updateInProgressness(); private: JID jid_; MUCSearchWindowFactory* factory_; IQRouter* iqRouter_; ProfileSettingsProvider* settings_; MUCSearchWindow* window_; DiscoServiceWalker* walker_; std::list services_; std::list savedServices_; std::map serviceDetails_; std::vector walksInProgress_; int itemsInProgress_; }; } swift-im-2.0+dev6/Swift/Controllers/Chat/ChatController.cpp0000644000175000017500000004137412227051773023603 0ustar kismithkismith/* * Copyright (c) 2010-2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Swift/Controllers/Chat/ChatController.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Swift { /** * The controller does not gain ownership of the stanzaChannel, nor the factory. */ ChatController::ChatController(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &contact, NickResolver* nickResolver, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool isInMUC, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController, TimerFactory* timerFactory, EntityCapsProvider* entityCapsProvider, bool userWantsReceipts, SettingsProvider* settings, HistoryController* historyController, MUCRegistry* mucRegistry) : ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, contact, presenceOracle, avatarManager, useDelayForLatency, eventStream, eventController, timerFactory, entityCapsProvider, historyController, mucRegistry), eventStream_(eventStream), userWantsReceipts_(userWantsReceipts), settings_(settings) { isInMUC_ = isInMUC; lastWasPresence_ = false; chatStateNotifier_ = new ChatStateNotifier(stanzaChannel, contact, entityCapsProvider); chatStateTracker_ = new ChatStateTracker(); nickResolver_ = nickResolver; presenceOracle_->onPresenceChange.connect(boost::bind(&ChatController::handlePresenceChange, this, _1)); chatStateTracker_->onChatStateChange.connect(boost::bind(&ChatWindow::setContactChatState, chatWindow_, _1)); stanzaChannel_->onStanzaAcked.connect(boost::bind(&ChatController::handleStanzaAcked, this, _1)); nickResolver_->onNickChanged.connect(boost::bind(&ChatController::handleContactNickChanged, this, _1, _2)); std::string nick = nickResolver_->jidToNick(toJID_); chatWindow_->setName(nick); std::string startMessage; Presence::ref theirPresence; if (isInMUC) { startMessage = str(format(QT_TRANSLATE_NOOP("", "Starting chat with %1% in chatroom %2%")) % nick % contact.toBare().toString()); theirPresence = presenceOracle->getLastPresence(contact); } else { startMessage = str(format(QT_TRANSLATE_NOOP("", "Starting chat with %1% - %2%")) % nick % contact.toBare().toString()); theirPresence = contact.isBare() ? presenceOracle->getHighestPriorityPresence(contact.toBare()) : presenceOracle->getLastPresence(contact); } startMessage += ": " + statusShowTypeToFriendlyName(theirPresence ? theirPresence->getShow() : StatusShow::None); if (theirPresence && !theirPresence->getStatus().empty()) { startMessage += " (" + theirPresence->getStatus() + ")"; } lastShownStatus_ = theirPresence ? theirPresence->getShow() : StatusShow::None; chatStateNotifier_->setContactIsOnline(theirPresence && theirPresence->getType() == Presence::Available); startMessage += "."; chatWindow_->addSystemMessage(startMessage); chatWindow_->onUserTyping.connect(boost::bind(&ChatStateNotifier::setUserIsTyping, chatStateNotifier_)); chatWindow_->onUserCancelsTyping.connect(boost::bind(&ChatStateNotifier::userCancelledNewMessage, chatStateNotifier_)); chatWindow_->onFileTransferStart.connect(boost::bind(&ChatController::handleFileTransferStart, this, _1, _2)); chatWindow_->onFileTransferAccept.connect(boost::bind(&ChatController::handleFileTransferAccept, this, _1, _2)); chatWindow_->onFileTransferCancel.connect(boost::bind(&ChatController::handleFileTransferCancel, this, _1)); chatWindow_->onSendFileRequest.connect(boost::bind(&ChatController::handleSendFileRequest, this, _1)); chatWindow_->onWhiteboardSessionAccept.connect(boost::bind(&ChatController::handleWhiteboardSessionAccept, this)); chatWindow_->onWhiteboardSessionCancel.connect(boost::bind(&ChatController::handleWhiteboardSessionCancel, this)); chatWindow_->onWhiteboardWindowShow.connect(boost::bind(&ChatController::handleWhiteboardWindowShow, this)); handleBareJIDCapsChanged(toJID_); settings_->onSettingChanged.connect(boost::bind(&ChatController::handleSettingChanged, this, _1)); } void ChatController::handleContactNickChanged(const JID& jid, const std::string& /*oldNick*/) { if (jid.toBare() == toJID_.toBare()) { chatWindow_->setName(nickResolver_->jidToNick(jid)); } } ChatController::~ChatController() { settings_->onSettingChanged.disconnect(boost::bind(&ChatController::handleSettingChanged, this, _1)); nickResolver_->onNickChanged.disconnect(boost::bind(&ChatController::handleContactNickChanged, this, _1, _2)); delete chatStateNotifier_; delete chatStateTracker_; } JID ChatController::getBaseJID() { return isInMUC_ ? toJID_ : ChatControllerBase::getBaseJID(); } void ChatController::cancelReplaces() { lastWasPresence_ = false; } void ChatController::handleBareJIDCapsChanged(const JID& /*jid*/) { DiscoInfo::ref disco = entityCapsProvider_->getCaps(toJID_); if (disco) { if (disco->hasFeature(DiscoInfo::MessageCorrectionFeature)) { chatWindow_->setCorrectionEnabled(ChatWindow::Yes); } else { chatWindow_->setCorrectionEnabled(ChatWindow::No); } if (disco->hasFeature(DiscoInfo::MessageDeliveryReceiptsFeature)) { contactSupportsReceipts_ = ChatWindow::Yes; } else { contactSupportsReceipts_ = ChatWindow::No; } } else { SWIFT_LOG(debug) << "No disco info :(" << std::endl; chatWindow_->setCorrectionEnabled(ChatWindow::Maybe); contactSupportsReceipts_ = ChatWindow::Maybe; } checkForDisplayingDisplayReceiptsAlert(); } void ChatController::setToJID(const JID& jid) { chatStateNotifier_->setContact(jid); ChatControllerBase::setToJID(jid); Presence::ref presence; if (isInMUC_) { presence = presenceOracle_->getLastPresence(jid); } else { presence = jid.isBare() ? presenceOracle_->getHighestPriorityPresence(jid.toBare()) : presenceOracle_->getLastPresence(jid); } chatStateNotifier_->setContactIsOnline(presence && presence->getType() == Presence::Available); handleBareJIDCapsChanged(toJID_); } bool ChatController::isIncomingMessageFromMe(boost::shared_ptr) { return false; } void ChatController::preHandleIncomingMessage(boost::shared_ptr messageEvent) { if (messageEvent->isReadable()) { chatWindow_->flash(); lastWasPresence_ = false; } boost::shared_ptr message = messageEvent->getStanza(); JID from = message->getFrom(); if (!from.equals(toJID_, JID::WithResource)) { if (toJID_.equals(from, JID::WithoutResource) && toJID_.isBare()){ setToJID(from); } } chatStateTracker_->handleMessageReceived(message); chatStateNotifier_->receivedMessageFromContact(message->getPayload()); if (boost::shared_ptr receipt = message->getPayload()) { SWIFT_LOG(debug) << "received receipt for id: " << receipt->getReceivedID() << std::endl; if (requestedReceipts_.find(receipt->getReceivedID()) != requestedReceipts_.end()) { chatWindow_->setMessageReceiptState(requestedReceipts_[receipt->getReceivedID()], ChatWindow::ReceiptReceived); requestedReceipts_.erase(receipt->getReceivedID()); } } else if (message->getPayload()) { if (receivingPresenceFromUs_) { boost::shared_ptr receiptMessage = boost::make_shared(); receiptMessage->setTo(toJID_); receiptMessage->addPayload(boost::make_shared(message->getID())); stanzaChannel_->sendMessage(receiptMessage); } } } void ChatController::postHandleIncomingMessage(boost::shared_ptr messageEvent) { eventController_->handleIncomingEvent(messageEvent); } void ChatController::preSendMessageRequest(boost::shared_ptr message) { chatStateNotifier_->addChatStateRequest(message); if (userWantsReceipts_ && (contactSupportsReceipts_ != ChatWindow::No) && message) { message->addPayload(boost::make_shared()); } } void ChatController::setContactIsReceivingPresence(bool isReceivingPresence) { receivingPresenceFromUs_ = isReceivingPresence; } void ChatController::handleSettingChanged(const std::string& settingPath) { if (settingPath == SettingConstants::REQUEST_DELIVERYRECEIPTS.getKey()) { userWantsReceipts_ = settings_->getSetting(SettingConstants::REQUEST_DELIVERYRECEIPTS); checkForDisplayingDisplayReceiptsAlert(); } } void ChatController::checkForDisplayingDisplayReceiptsAlert() { if (userWantsReceipts_ && (contactSupportsReceipts_ == ChatWindow::No)) { chatWindow_->setAlert(QT_TRANSLATE_NOOP("", "This chat doesn't support delivery receipts.")); } else if (userWantsReceipts_ && (contactSupportsReceipts_ == ChatWindow::Maybe)) { chatWindow_->setAlert(QT_TRANSLATE_NOOP("", "This chat may not support delivery receipts. You might not receive delivery receipts for the messages you sent.")); } else { chatWindow_->cancelAlert(); } } void ChatController::postSendMessage(const std::string& body, boost::shared_ptr sentStanza) { boost::shared_ptr replace = sentStanza->getPayload(); if (replace) { eraseIf(unackedStanzas_, PairSecondEquals, std::string>(myLastMessageUIID_)); replaceMessage(body, myLastMessageUIID_, boost::posix_time::microsec_clock::universal_time()); } else { myLastMessageUIID_ = addMessage(body, QT_TRANSLATE_NOOP("", "me"), true, labelsEnabled_ ? chatWindow_->getSelectedSecurityLabel().getLabel() : boost::shared_ptr(), std::string(avatarManager_->getAvatarPath(selfJID_).string()), boost::posix_time::microsec_clock::universal_time()); } if (stanzaChannel_->getStreamManagementEnabled() && !myLastMessageUIID_.empty() ) { chatWindow_->setAckState(myLastMessageUIID_, ChatWindow::Pending); unackedStanzas_[sentStanza] = myLastMessageUIID_; } if (sentStanza->getPayload()) { requestedReceipts_[sentStanza->getID()] = myLastMessageUIID_; chatWindow_->setMessageReceiptState(myLastMessageUIID_, ChatWindow::ReceiptRequested); } lastWasPresence_ = false; chatStateNotifier_->userSentMessage(); } void ChatController::handleStanzaAcked(boost::shared_ptr stanza) { std::map, std::string>::iterator unackedStanza = unackedStanzas_.find(stanza); if (unackedStanza != unackedStanzas_.end()) { chatWindow_->setAckState(unackedStanza->second, ChatWindow::Received); unackedStanzas_.erase(unackedStanza); } } void ChatController::setOnline(bool online) { if (!online) { std::map, std::string>::iterator it = unackedStanzas_.begin(); for ( ; it != unackedStanzas_.end(); ++it) { chatWindow_->setAckState(it->second, ChatWindow::Failed); } unackedStanzas_.clear(); Presence::ref fakeOffline(new Presence()); fakeOffline->setFrom(toJID_); fakeOffline->setType(Presence::Unavailable); chatStateTracker_->handlePresenceChange(fakeOffline); } ChatControllerBase::setOnline(online); } void ChatController::handleNewFileTransferController(FileTransferController* ftc) { std::string nick = senderDisplayNameFromMessage(ftc->getOtherParty()); std::string ftID = ftc->setChatWindow(chatWindow_, nick); ftControllers[ftID] = ftc; } void ChatController::handleWhiteboardSessionRequest(bool senderIsSelf) { lastWbID_ = chatWindow_->addWhiteboardRequest(senderIsSelf); } void ChatController::handleWhiteboardStateChange(const ChatWindow::WhiteboardSessionState state) { chatWindow_->setWhiteboardSessionStatus(lastWbID_, state); } void ChatController::handleFileTransferCancel(std::string id) { SWIFT_LOG(debug) << "handleFileTransferCancel(" << id << ")" << std::endl; if (ftControllers.find(id) != ftControllers.end()) { ftControllers[id]->cancel(); } else { std::cerr << "unknown file transfer UI id" << std::endl; } } void ChatController::handleFileTransferStart(std::string id, std::string description) { SWIFT_LOG(debug) << "handleFileTransferStart(" << id << ", " << description << ")" << std::endl; if (ftControllers.find(id) != ftControllers.end()) { ftControllers[id]->start(description); } else { std::cerr << "unknown file transfer UI id" << std::endl; } } void ChatController::handleFileTransferAccept(std::string id, std::string filename) { SWIFT_LOG(debug) "handleFileTransferAccept(" << id << ", " << filename << ")" << std::endl; if (ftControllers.find(id) != ftControllers.end()) { ftControllers[id]->accept(filename); } else { std::cerr << "unknown file transfer UI id" << std::endl; } } void ChatController::handleSendFileRequest(std::string filename) { SWIFT_LOG(debug) << "ChatController::handleSendFileRequest(" << filename << ")" << std::endl; eventStream_->send(boost::make_shared(getToJID(), filename)); } void ChatController::handleWhiteboardSessionAccept() { eventStream_->send(boost::make_shared(toJID_)); } void ChatController::handleWhiteboardSessionCancel() { eventStream_->send(boost::make_shared(toJID_)); } void ChatController::handleWhiteboardWindowShow() { eventStream_->send(boost::make_shared(toJID_)); } std::string ChatController::senderDisplayNameFromMessage(const JID& from) { return nickResolver_->jidToNick(from); } std::string ChatController::getStatusChangeString(boost::shared_ptr presence) { std::string nick = senderDisplayNameFromMessage(presence->getFrom()); std::string response; if (!presence || presence->getType() == Presence::Unavailable || presence->getType() == Presence::Error) { response = QT_TRANSLATE_NOOP("", "%1% has gone offline"); } else if (presence->getType() == Presence::Available) { StatusShow::Type show = presence->getShow(); if (show == StatusShow::Online || show == StatusShow::FFC) { response = QT_TRANSLATE_NOOP("", "%1% has become available"); } else if (show == StatusShow::Away || show == StatusShow::XA) { response = QT_TRANSLATE_NOOP("", "%1% has gone away"); } else if (show == StatusShow::DND) { response = QT_TRANSLATE_NOOP("", "%1% is now busy"); } } if (!response.empty()) { response = str(format(response) % nick); } if (!presence->getStatus().empty()) { response += " (" + presence->getStatus() + ")"; } return response + "."; } void ChatController::handlePresenceChange(boost::shared_ptr newPresence) { bool me = false; if (toJID_.isBare()) { newPresence = presenceOracle_->getHighestPriorityPresence(toJID_); if ((newPresence ? newPresence->getShow() : StatusShow::None) != lastShownStatus_) { me = true; } } else if (toJID_.equals(newPresence->getFrom(), JID::WithResource)) { me = true; } if (!me) { return; } if (!newPresence) { newPresence = boost::make_shared(); newPresence->setType(Presence::Unavailable); } lastShownStatus_ = newPresence->getShow(); chatStateTracker_->handlePresenceChange(newPresence); chatStateNotifier_->setContactIsOnline(newPresence->getType() == Presence::Available); std::string newStatusChangeString = getStatusChangeString(newPresence); if (newStatusChangeString != lastStatusChangeString_) { if (lastWasPresence_) { chatWindow_->replaceLastMessage(newStatusChangeString); } else { chatWindow_->addPresenceMessage(newStatusChangeString); } lastStatusChangeString_ = newStatusChangeString; lastWasPresence_ = true; } } boost::optional ChatController::getMessageTimestamp(boost::shared_ptr message) const { return message->getTimestamp(); } void ChatController::logMessage(const std::string& message, const JID& fromJID, const JID& toJID, const boost::posix_time::ptime& timeStamp, bool /* isIncoming */) { HistoryMessage::Type type; if (mucRegistry_->isMUC(fromJID.toBare()) || mucRegistry_->isMUC(toJID.toBare())) { type = HistoryMessage::PrivateMessage; } else { type = HistoryMessage::Chat; } if (historyController_) { historyController_->addMessage(message, fromJID, toJID, type, timeStamp); } } } swift-im-2.0+dev6/Swift/Controllers/Chat/UserSearchController.h0000644000175000017500000000447312227051773024434 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include #include #include #include #include #include #include #include namespace Swift { class UIEventStream; class UIEvent; class UserSearchWindow; class UserSearchWindowFactory; class IQRouter; class DiscoServiceWalker; class RosterController; class VCardManager; class UserSearchResult { public: UserSearchResult(const JID& jid, const std::map& fields) : jid_(jid), fields_(fields) {} const JID& getJID() const {return jid_;} const std::map& getFields() const {return fields_;} private: JID jid_; std::map fields_; }; class UserSearchController { public: enum Type {AddContact, StartChat}; UserSearchController(Type type, const JID& jid, UIEventStream* uiEventStream, VCardManager* vcardManager, UserSearchWindowFactory* userSearchWindowFactory, IQRouter* iqRouter, RosterController* rosterController); ~UserSearchController(); private: void handleUIEvent(boost::shared_ptr event); void handleFormRequested(const JID& service); void handleDiscoServiceFound(const JID& jid, boost::shared_ptr info); void handleDiscoWalkFinished(); void handleFormResponse(boost::shared_ptr items, ErrorPayload::ref error); void handleSearch(boost::shared_ptr fields, const JID& jid); void handleSearchResponse(boost::shared_ptr results, ErrorPayload::ref error); void handleNameSuggestionRequest(const JID& jid); void handleVCardChanged(const JID& jid, VCard::ref vcard); void endDiscoWalker(); private: Type type_; JID jid_; JID suggestionsJID_; UIEventStream* uiEventStream_; VCardManager* vcardManager_; UserSearchWindowFactory* factory_; IQRouter* iqRouter_; RosterController* rosterController_; UserSearchWindow* window_; DiscoServiceWalker* discoWalker_; }; } swift-im-2.0+dev6/Swift/Controllers/Chat/UserSearchController.cpp0000644000175000017500000001627112227051773024766 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Swift { UserSearchController::UserSearchController(Type type, const JID& jid, UIEventStream* uiEventStream, VCardManager* vcardManager, UserSearchWindowFactory* factory, IQRouter* iqRouter, RosterController* rosterController) : type_(type), jid_(jid), uiEventStream_(uiEventStream), vcardManager_(vcardManager), factory_(factory), iqRouter_(iqRouter), rosterController_(rosterController) { uiEventStream_->onUIEvent.connect(boost::bind(&UserSearchController::handleUIEvent, this, _1)); vcardManager_->onVCardChanged.connect(boost::bind(&UserSearchController::handleVCardChanged, this, _1, _2)); window_ = NULL; discoWalker_ = NULL; } UserSearchController::~UserSearchController() { endDiscoWalker(); delete discoWalker_; if (window_) { window_->onNameSuggestionRequested.disconnect(boost::bind(&UserSearchController::handleNameSuggestionRequest, this, _1)); window_->onFormRequested.disconnect(boost::bind(&UserSearchController::handleFormRequested, this, _1)); window_->onSearchRequested.disconnect(boost::bind(&UserSearchController::handleSearch, this, _1, _2)); delete window_; } vcardManager_->onVCardChanged.disconnect(boost::bind(&UserSearchController::handleVCardChanged, this, _1, _2)); uiEventStream_->onUIEvent.disconnect(boost::bind(&UserSearchController::handleUIEvent, this, _1)); } void UserSearchController::handleUIEvent(boost::shared_ptr event) { bool handle = false; boost::shared_ptr request = boost::shared_ptr(); if (type_ == AddContact) { if ((request = boost::dynamic_pointer_cast(event))) { handle = true; } } else { if (boost::dynamic_pointer_cast(event)) { handle = true; } } if (handle) { if (!window_) { window_ = factory_->createUserSearchWindow(type_ == AddContact ? UserSearchWindow::AddContact : UserSearchWindow::ChatToContact, uiEventStream_, rosterController_->getGroups()); window_->onNameSuggestionRequested.connect(boost::bind(&UserSearchController::handleNameSuggestionRequest, this, _1)); window_->onFormRequested.connect(boost::bind(&UserSearchController::handleFormRequested, this, _1)); window_->onSearchRequested.connect(boost::bind(&UserSearchController::handleSearch, this, _1, _2)); window_->setSelectedService(JID(jid_.getDomain())); window_->clear(); } window_->show(); if (request) { const std::string& name = request->getPredefinedName(); const JID& jid = request->getPredefinedJID(); if (!name.empty() && jid.isValid()) { window_->prepopulateJIDAndName(jid, name); } } return; } } void UserSearchController::handleFormRequested(const JID& service) { window_->setSearchError(false); window_->setServerSupportsSearch(true); //Abort a previous search if is active endDiscoWalker(); delete discoWalker_; discoWalker_ = new DiscoServiceWalker(service, iqRouter_); discoWalker_->onServiceFound.connect(boost::bind(&UserSearchController::handleDiscoServiceFound, this, _1, _2)); discoWalker_->onWalkComplete.connect(boost::bind(&UserSearchController::handleDiscoWalkFinished, this)); discoWalker_->beginWalk(); } void UserSearchController::endDiscoWalker() { if (discoWalker_) { discoWalker_->endWalk(); discoWalker_->onServiceFound.disconnect(boost::bind(&UserSearchController::handleDiscoServiceFound, this, _1, _2)); discoWalker_->onWalkComplete.disconnect(boost::bind(&UserSearchController::handleDiscoWalkFinished, this)); } } void UserSearchController::handleDiscoServiceFound(const JID& jid, boost::shared_ptr info) { //bool isUserDirectory = false; bool supports55 = false; foreach (DiscoInfo::Identity identity, info->getIdentities()) { if ((identity.getCategory() == "directory" && identity.getType() == "user")) { //isUserDirectory = true; } } std::vector features = info->getFeatures(); supports55 = std::find(features.begin(), features.end(), DiscoInfo::JabberSearchFeature) != features.end(); if (/*isUserDirectory && */supports55) { //FIXME: once M-Link correctly advertises directoryness. /* Abort further searches.*/ endDiscoWalker(); boost::shared_ptr > searchRequest(new GenericRequest(IQ::Get, jid, boost::make_shared(), iqRouter_)); searchRequest->onResponse.connect(boost::bind(&UserSearchController::handleFormResponse, this, _1, _2)); searchRequest->send(); } } void UserSearchController::handleFormResponse(boost::shared_ptr fields, ErrorPayload::ref error) { if (error || !fields) { window_->setServerSupportsSearch(false); return; } window_->setSearchFields(fields); } void UserSearchController::handleSearch(boost::shared_ptr fields, const JID& jid) { boost::shared_ptr > searchRequest(new GenericRequest(IQ::Set, jid, fields, iqRouter_)); searchRequest->onResponse.connect(boost::bind(&UserSearchController::handleSearchResponse, this, _1, _2)); searchRequest->send(); } void UserSearchController::handleSearchResponse(boost::shared_ptr resultsPayload, ErrorPayload::ref error) { if (error || !resultsPayload) { window_->setSearchError(true); return; } std::vector results; if (resultsPayload->getForm()) { window_->setResultsForm(resultsPayload->getForm()); } else { foreach (SearchPayload::Item item, resultsPayload->getItems()) { JID jid(item.jid); std::map fields; fields["first"] = item.first; fields["last"] = item.last; fields["nick"] = item.nick; fields["email"] = item.email; UserSearchResult result(jid, fields); results.push_back(result); } window_->setResults(results); } } void UserSearchController::handleNameSuggestionRequest(const JID &jid) { suggestionsJID_= jid; VCard::ref vcard = vcardManager_->getVCardAndRequestWhenNeeded(jid); if (vcard) { handleVCardChanged(jid, vcard); } } void UserSearchController::handleVCardChanged(const JID& jid, VCard::ref vcard) { if (jid == suggestionsJID_) { window_->setNameSuggestions(ContactEditController::nameSuggestionsFromVCard(vcard)); suggestionsJID_ = JID(); } } void UserSearchController::handleDiscoWalkFinished() { window_->setServerSupportsSearch(false); endDiscoWalker(); } } swift-im-2.0+dev6/Swift/Controllers/Chat/UnitTest/0000755000175000017500000000000012227051773021722 5ustar kismithkismithswift-im-2.0+dev6/Swift/Controllers/Chat/UnitTest/MockChatListWindow.h0000644000175000017500000000147112227051773025613 0ustar kismithkismith/* * Copyright (c) 2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include "Swift/Controllers/UIInterfaces/ChatListWindow.h" namespace Swift { class MockChatListWindow : public ChatListWindow { public: MockChatListWindow() {}; virtual ~MockChatListWindow() {}; void addMUCBookmark(const MUCBookmark& /*bookmark*/) {} void removeMUCBookmark(const MUCBookmark& /*bookmark*/) {} void addWhiteboardSession(const ChatListWindow::Chat& /*chat*/) {}; void removeWhiteboardSession(const JID& /*jid*/) {}; void setBookmarksEnabled(bool /*enabled*/) {} void setRecents(const std::list& /*recents*/) {} void setUnreadCount(int /*unread*/) {} void clearBookmarks() {} }; } swift-im-2.0+dev6/Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp0000644000175000017500000003610712227051773025765 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include "Swift/Controllers/XMPPEvents/EventController.h" #include "Swiften/Presence/DirectedPresenceSender.h" #include "Swiften/Presence/StanzaChannelPresenceSender.h" #include "Swiften/Avatars/NullAvatarManager.h" #include "Swift/Controllers/Chat/MUCController.h" #include "Swift/Controllers/UIInterfaces/ChatWindow.h" #include "Swift/Controllers/UIInterfaces/ChatWindowFactory.h" #include "Swiften/Client/NickResolver.h" #include "Swiften/Roster/XMPPRoster.h" #include "Swift/Controllers/UIEvents/UIEventStream.h" #include "Swift/Controllers/UnitTest/MockChatWindow.h" #include "Swiften/Client/DummyStanzaChannel.h" #include "Swiften/Queries/DummyIQChannel.h" #include "Swiften/Presence/PresenceOracle.h" #include "Swiften/Network/TimerFactory.h" #include "Swiften/Elements/MUCUserPayload.h" #include "Swiften/Disco/DummyEntityCapsProvider.h" using namespace Swift; class MUCControllerTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(MUCControllerTest); CPPUNIT_TEST(testJoinPartStringContructionSimple); CPPUNIT_TEST(testJoinPartStringContructionMixed); CPPUNIT_TEST(testAppendToJoinParts); CPPUNIT_TEST(testAddressedToSelf); CPPUNIT_TEST(testNotAddressedToSelf); CPPUNIT_TEST(testAddressedToSelfBySelf); CPPUNIT_TEST(testMessageWithEmptyLabelItem); CPPUNIT_TEST(testMessageWithLabelItem); CPPUNIT_TEST(testCorrectMessageWithLabelItem); CPPUNIT_TEST_SUITE_END(); public: void setUp() { self_ = JID("girl@wonderland.lit/rabbithole"); nick_ = "aLiCe"; mucJID_ = JID("teaparty@rooms.wonderland.lit"); mocks_ = new MockRepository(); stanzaChannel_ = new DummyStanzaChannel(); iqChannel_ = new DummyIQChannel(); iqRouter_ = new IQRouter(iqChannel_); eventController_ = new EventController(); chatWindowFactory_ = mocks_->InterfaceMock(); presenceOracle_ = new PresenceOracle(stanzaChannel_); presenceSender_ = new StanzaChannelPresenceSender(stanzaChannel_); directedPresenceSender_ = new DirectedPresenceSender(presenceSender_); uiEventStream_ = new UIEventStream(); avatarManager_ = new NullAvatarManager(); TimerFactory* timerFactory = NULL; window_ = new MockChatWindow(); mucRegistry_ = new MUCRegistry(); entityCapsProvider_ = new DummyEntityCapsProvider(); muc_ = boost::make_shared(stanzaChannel_, iqRouter_, directedPresenceSender_, mucJID_, mucRegistry_); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(muc_->getJID(), uiEventStream_).Return(window_); controller_ = new MUCController (self_, muc_, boost::optional(), nick_, stanzaChannel_, iqRouter_, chatWindowFactory_, presenceOracle_, avatarManager_, uiEventStream_, false, timerFactory, eventController_, entityCapsProvider_, NULL, NULL, mucRegistry_); }; void tearDown() { delete entityCapsProvider_; delete controller_; delete eventController_; delete presenceOracle_; delete mocks_; delete uiEventStream_; delete stanzaChannel_; delete presenceSender_; delete directedPresenceSender_; delete iqRouter_; delete iqChannel_; delete mucRegistry_; delete avatarManager_; } void finishJoin() { Presence::ref presence(new Presence()); presence->setFrom(JID(muc_->getJID().toString() + "/" + nick_)); MUCUserPayload::ref status(new MUCUserPayload()); MUCUserPayload::StatusCode code; code.code = 110; status->addStatusCode(code); presence->addPayload(status); stanzaChannel_->onPresenceReceived(presence); } void testAddressedToSelf() { finishJoin(); Message::ref message(new Message()); message = Message::ref(new Message()); message->setFrom(JID(muc_->getJID().toString() + "/otherperson")); message->setBody("basic " + nick_ + " test."); message->setType(Message::Groupchat); controller_->handleIncomingMessage(MessageEvent::ref(new MessageEvent(message))); CPPUNIT_ASSERT_EQUAL((size_t)1, eventController_->getEvents().size()); message = Message::ref(new Message()); message->setFrom(JID(muc_->getJID().toString() + "/otherperson")); message->setBody(nick_ + ": hi there"); message->setType(Message::Groupchat); controller_->handleIncomingMessage(MessageEvent::ref(new MessageEvent(message))); CPPUNIT_ASSERT_EQUAL((size_t)2, eventController_->getEvents().size()); message->setFrom(JID(muc_->getJID().toString() + "/other")); message->setBody("Hi there " + nick_); message->setType(Message::Groupchat); controller_->handleIncomingMessage(MessageEvent::ref(new MessageEvent(message))); CPPUNIT_ASSERT_EQUAL((size_t)3, eventController_->getEvents().size()); message = Message::ref(new Message()); message->setFrom(JID(muc_->getJID().toString() + "/other2")); message->setBody("Hi " + boost::to_lower_copy(nick_) + "."); message->setType(Message::Groupchat); controller_->handleIncomingMessage(MessageEvent::ref(new MessageEvent(message))); CPPUNIT_ASSERT_EQUAL((size_t)4, eventController_->getEvents().size()); message = Message::ref(new Message()); message->setFrom(JID(muc_->getJID().toString() + "/other3")); message->setBody("Hi bert."); message->setType(Message::Groupchat); controller_->handleIncomingMessage(MessageEvent::ref(new MessageEvent(message))); CPPUNIT_ASSERT_EQUAL((size_t)4, eventController_->getEvents().size()); message = Message::ref(new Message()); message->setFrom(JID(muc_->getJID().toString() + "/other2")); message->setBody("Hi " + boost::to_lower_copy(nick_) + "ie."); message->setType(Message::Groupchat); controller_->handleIncomingMessage(MessageEvent::ref(new MessageEvent(message))); CPPUNIT_ASSERT_EQUAL((size_t)4, eventController_->getEvents().size()); } void testNotAddressedToSelf() { finishJoin(); Message::ref message(new Message()); message->setFrom(JID(muc_->getJID().toString() + "/other3")); message->setBody("Hi there Hatter"); message->setType(Message::Groupchat); controller_->handleIncomingMessage(MessageEvent::ref(new MessageEvent(message))); CPPUNIT_ASSERT_EQUAL((size_t)0, eventController_->getEvents().size()); } void testAddressedToSelfBySelf() { finishJoin(); Message::ref message(new Message()); message->setFrom(JID(muc_->getJID().toString() + "/" + nick_)); message->setBody("Hi there " + nick_); message->setType(Message::Groupchat); controller_->handleIncomingMessage(MessageEvent::ref(new MessageEvent(message))); CPPUNIT_ASSERT_EQUAL((size_t)0, eventController_->getEvents().size()); } void testMessageWithEmptyLabelItem() { SecurityLabelsCatalog::Item label; label.setSelector("Bob"); window_->label_ = label; boost::shared_ptr features = boost::make_shared(); features->addFeature(DiscoInfo::SecurityLabelsCatalogFeature); controller_->setAvailableServerFeatures(features); IQ::ref iq = iqChannel_->iqs_[iqChannel_->iqs_.size() - 1]; SecurityLabelsCatalog::ref labelPayload = boost::make_shared(); labelPayload->addItem(label); IQ::ref result = IQ::createResult(self_, iq->getID(), labelPayload); iqChannel_->onIQReceived(result); std::string messageBody("agamemnon"); window_->onSendMessageRequest(messageBody, false); boost::shared_ptr rawStanza = stanzaChannel_->sentStanzas[stanzaChannel_->sentStanzas.size() - 1]; Message::ref message = boost::dynamic_pointer_cast(rawStanza); CPPUNIT_ASSERT_EQUAL(iq->getTo(), result->getFrom()); CPPUNIT_ASSERT(window_->labelsEnabled_); CPPUNIT_ASSERT(stanzaChannel_->isAvailable()); /* Otherwise will prevent sends. */ CPPUNIT_ASSERT(message); CPPUNIT_ASSERT_EQUAL(messageBody, message->getBody()); CPPUNIT_ASSERT(!message->getPayload()); } void testMessageWithLabelItem() { SecurityLabel::ref label = boost::make_shared(); label->setLabel("a"); SecurityLabelsCatalog::Item labelItem; labelItem.setSelector("Bob"); labelItem.setLabel(label); window_->label_ = labelItem; boost::shared_ptr features = boost::make_shared(); features->addFeature(DiscoInfo::SecurityLabelsCatalogFeature); controller_->setAvailableServerFeatures(features); IQ::ref iq = iqChannel_->iqs_[iqChannel_->iqs_.size() - 1]; SecurityLabelsCatalog::ref labelPayload = boost::make_shared(); labelPayload->addItem(labelItem); IQ::ref result = IQ::createResult(self_, iq->getID(), labelPayload); iqChannel_->onIQReceived(result); std::string messageBody("agamemnon"); window_->onSendMessageRequest(messageBody, false); boost::shared_ptr rawStanza = stanzaChannel_->sentStanzas[stanzaChannel_->sentStanzas.size() - 1]; Message::ref message = boost::dynamic_pointer_cast(rawStanza); CPPUNIT_ASSERT_EQUAL(iq->getTo(), result->getFrom()); CPPUNIT_ASSERT(window_->labelsEnabled_); CPPUNIT_ASSERT(stanzaChannel_->isAvailable()); /* Otherwise will prevent sends. */ CPPUNIT_ASSERT(message); CPPUNIT_ASSERT_EQUAL(messageBody, message->getBody()); CPPUNIT_ASSERT_EQUAL(label, message->getPayload()); } void testCorrectMessageWithLabelItem() { SecurityLabel::ref label = boost::make_shared(); label->setLabel("a"); SecurityLabelsCatalog::Item labelItem; labelItem.setSelector("Bob"); labelItem.setLabel(label); SecurityLabel::ref label2 = boost::make_shared(); label->setLabel("b"); SecurityLabelsCatalog::Item labelItem2; labelItem2.setSelector("Charlie"); labelItem2.setLabel(label2); window_->label_ = labelItem; boost::shared_ptr features = boost::make_shared(); features->addFeature(DiscoInfo::SecurityLabelsCatalogFeature); controller_->setAvailableServerFeatures(features); IQ::ref iq = iqChannel_->iqs_[iqChannel_->iqs_.size() - 1]; SecurityLabelsCatalog::ref labelPayload = boost::make_shared(); labelPayload->addItem(labelItem); IQ::ref result = IQ::createResult(self_, iq->getID(), labelPayload); iqChannel_->onIQReceived(result); std::string messageBody("agamemnon"); window_->onSendMessageRequest(messageBody, false); boost::shared_ptr rawStanza = stanzaChannel_->sentStanzas[stanzaChannel_->sentStanzas.size() - 1]; Message::ref message = boost::dynamic_pointer_cast(rawStanza); CPPUNIT_ASSERT_EQUAL(iq->getTo(), result->getFrom()); CPPUNIT_ASSERT(window_->labelsEnabled_); CPPUNIT_ASSERT(stanzaChannel_->isAvailable()); /* Otherwise will prevent sends. */ CPPUNIT_ASSERT(message); CPPUNIT_ASSERT_EQUAL(messageBody, message->getBody()); CPPUNIT_ASSERT_EQUAL(label, message->getPayload()); window_->label_ = labelItem2; window_->onSendMessageRequest(messageBody, true); rawStanza = stanzaChannel_->sentStanzas[stanzaChannel_->sentStanzas.size() - 1]; message = boost::dynamic_pointer_cast(rawStanza); CPPUNIT_ASSERT_EQUAL(messageBody, message->getBody()); CPPUNIT_ASSERT_EQUAL(label, message->getPayload()); } void checkEqual(const std::vector& expected, const std::vector& actual) { CPPUNIT_ASSERT_EQUAL(expected.size(), actual.size()); for (size_t i = 0; i < expected.size(); i++) { CPPUNIT_ASSERT_EQUAL(expected[i].nick, actual[i].nick); CPPUNIT_ASSERT_EQUAL(expected[i].type, actual[i].type); } } void testAppendToJoinParts() { std::vector list; std::vector gold; MUCController::appendToJoinParts(list, NickJoinPart("Kev", Join)); gold.push_back(NickJoinPart("Kev", Join)); checkEqual(gold, list); MUCController::appendToJoinParts(list, NickJoinPart("Remko", Join)); gold.push_back(NickJoinPart("Remko", Join)); checkEqual(gold, list); MUCController::appendToJoinParts(list, NickJoinPart("Bert", Join)); gold.push_back(NickJoinPart("Bert", Join)); checkEqual(gold, list); MUCController::appendToJoinParts(list, NickJoinPart("Bert", Part)); gold[2].type = JoinThenPart; checkEqual(gold, list); MUCController::appendToJoinParts(list, NickJoinPart("Kev", Part)); gold[0].type = JoinThenPart; checkEqual(gold, list); MUCController::appendToJoinParts(list, NickJoinPart("Remko", Part)); gold[1].type = JoinThenPart; checkEqual(gold, list); MUCController::appendToJoinParts(list, NickJoinPart("Ernie", Part)); gold.push_back(NickJoinPart("Ernie", Part)); checkEqual(gold, list); MUCController::appendToJoinParts(list, NickJoinPart("Ernie", Join)); gold[3].type = PartThenJoin; checkEqual(gold, list); MUCController::appendToJoinParts(list, NickJoinPart("Kev", Join)); gold[0].type = Join; checkEqual(gold, list); MUCController::appendToJoinParts(list, NickJoinPart("Ernie", Part)); gold[3].type = Part; checkEqual(gold, list); } void testJoinPartStringContructionSimple() { std::vector list; list.push_back(NickJoinPart("Kev", Join)); CPPUNIT_ASSERT_EQUAL(std::string("Kev has entered the room"), MUCController::generateJoinPartString(list)); list.push_back(NickJoinPart("Remko", Part)); CPPUNIT_ASSERT_EQUAL(std::string("Kev has entered the room and Remko has left the room"), MUCController::generateJoinPartString(list)); list.push_back(NickJoinPart("Bert", Join)); CPPUNIT_ASSERT_EQUAL(std::string("Kev and Bert have entered the room and Remko has left the room"), MUCController::generateJoinPartString(list)); list.push_back(NickJoinPart("Ernie", Join)); CPPUNIT_ASSERT_EQUAL(std::string("Kev, Bert and Ernie have entered the room and Remko has left the room"), MUCController::generateJoinPartString(list)); } void testJoinPartStringContructionMixed() { std::vector list; list.push_back(NickJoinPart("Kev", JoinThenPart)); CPPUNIT_ASSERT_EQUAL(std::string("Kev has entered then left the room"), MUCController::generateJoinPartString(list)); list.push_back(NickJoinPart("Remko", Part)); CPPUNIT_ASSERT_EQUAL(std::string("Remko has left the room and Kev has entered then left the room"), MUCController::generateJoinPartString(list)); list.push_back(NickJoinPart("Bert", PartThenJoin)); CPPUNIT_ASSERT_EQUAL(std::string("Remko has left the room, Kev has entered then left the room and Bert has left then returned to the room"), MUCController::generateJoinPartString(list)); list.push_back(NickJoinPart("Ernie", JoinThenPart)); CPPUNIT_ASSERT_EQUAL(std::string("Remko has left the room, Kev and Ernie have entered then left the room and Bert has left then returned to the room"), MUCController::generateJoinPartString(list)); } private: JID self_; JID mucJID_; MUC::ref muc_; std::string nick_; DummyStanzaChannel* stanzaChannel_; DummyIQChannel* iqChannel_; IQRouter* iqRouter_; EventController* eventController_; ChatWindowFactory* chatWindowFactory_; MUCController* controller_; // NickResolver* nickResolver_; PresenceOracle* presenceOracle_; AvatarManager* avatarManager_; StanzaChannelPresenceSender* presenceSender_; DirectedPresenceSender* directedPresenceSender_; MockRepository* mocks_; UIEventStream* uiEventStream_; MockChatWindow* window_; MUCRegistry* mucRegistry_; DummyEntityCapsProvider* entityCapsProvider_; }; CPPUNIT_TEST_SUITE_REGISTRATION(MUCControllerTest); swift-im-2.0+dev6/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp0000644000175000017500000005153612227051773025635 0ustar kismithkismith/* * Copyright (c) 2010-2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include "Swift/Controllers/Chat/ChatsManager.h" #include "Swift/Controllers/Chat/UnitTest/MockChatListWindow.h" #include "Swift/Controllers/UIInterfaces/ChatWindow.h" #include "Swift/Controllers/Settings/DummySettingsProvider.h" #include "Swift/Controllers/UIInterfaces/ChatWindowFactory.h" #include "Swift/Controllers/UIInterfaces/ChatListWindowFactory.h" #include "Swift/Controllers/UIInterfaces/WhiteboardWindowFactory.h" #include "Swift/Controllers/UIInterfaces/JoinMUCWindowFactory.h" #include "Swift/Controllers/UIInterfaces/MUCSearchWindowFactory.h" #include "Swiften/Client/Client.h" #include "Swiften/Disco/EntityCapsManager.h" #include "Swiften/Disco/CapsProvider.h" #include "Swiften/MUC/MUCManager.h" #include "Swift/Controllers/Chat/ChatController.h" #include "Swift/Controllers/XMPPEvents/EventController.h" #include "Swift/Controllers/Chat/MUCController.h" #include "Swiften/Presence/StanzaChannelPresenceSender.h" #include "Swiften/Avatars/NullAvatarManager.h" #include "Swiften/Avatars/AvatarMemoryStorage.h" #include "Swiften/VCards/VCardManager.h" #include "Swiften/VCards/VCardMemoryStorage.h" #include "Swiften/Client/NickResolver.h" #include "Swiften/Presence/DirectedPresenceSender.h" #include "Swiften/Roster/XMPPRosterImpl.h" #include "Swift/Controllers/UnitTest/MockChatWindow.h" #include "Swiften/Client/DummyStanzaChannel.h" #include "Swiften/Queries/DummyIQChannel.h" #include "Swiften/Presence/PresenceOracle.h" #include "Swiften/Jingle/JingleSessionManager.h" #include "Swiften/FileTransfer/UnitTest/DummyFileTransferManager.h" #include "Swift/Controllers/UIEvents/RequestChatUIEvent.h" #include "Swift/Controllers/UIEvents/JoinMUCUIEvent.h" #include "Swift/Controllers/UIEvents/UIEventStream.h" #include #include "Swift/Controllers/FileTransfer/FileTransferOverview.h" #include "Swiften/Elements/DeliveryReceiptRequest.h" #include "Swiften/Elements/DeliveryReceipt.h" #include #include #include #include using namespace Swift; class DummyCapsProvider : public CapsProvider { DiscoInfo::ref getCaps(const std::string&) const {return DiscoInfo::ref(new DiscoInfo());} }; class ChatsManagerTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(ChatsManagerTest); CPPUNIT_TEST(testFirstOpenWindowIncoming); CPPUNIT_TEST(testSecondOpenWindowIncoming); CPPUNIT_TEST(testFirstOpenWindowOutgoing); CPPUNIT_TEST(testFirstOpenWindowBareToFull); CPPUNIT_TEST(testSecondWindow); CPPUNIT_TEST(testUnbindRebind); CPPUNIT_TEST(testNoDuplicateUnbind); CPPUNIT_TEST(testThreeMUCWindows); CPPUNIT_TEST(testChatControllerPresenceAccessUpdatedOnRemoveFromRoster); CPPUNIT_TEST(testChatControllerPresenceAccessUpdatedOnAddToRoster); CPPUNIT_TEST(testChatControllerPresenceAccessUpdatedOnSubscriptionChangeToBoth); CPPUNIT_TEST(testChatControllerPresenceAccessUpdatedOnSubscriptionChangeToFrom); CPPUNIT_TEST_SUITE_END(); public: void setUp() { mocks_ = new MockRepository(); jid_ = JID("test@test.com/resource"); stanzaChannel_ = new DummyStanzaChannel(); iqChannel_ = new DummyIQChannel(); iqRouter_ = new IQRouter(iqChannel_); capsProvider_ = new DummyCapsProvider(); eventController_ = new EventController(); chatWindowFactory_ = mocks_->InterfaceMock(); joinMUCWindowFactory_ = mocks_->InterfaceMock(); xmppRoster_ = new XMPPRosterImpl(); mucRegistry_ = new MUCRegistry(); nickResolver_ = new NickResolver(jid_.toBare(), xmppRoster_, NULL, mucRegistry_); presenceOracle_ = new PresenceOracle(stanzaChannel_); serverDiscoInfo_ = boost::make_shared(); presenceSender_ = new StanzaChannelPresenceSender(stanzaChannel_); directedPresenceSender_ = new DirectedPresenceSender(presenceSender_); mucManager_ = new MUCManager(stanzaChannel_, iqRouter_, directedPresenceSender_, mucRegistry_); uiEventStream_ = new UIEventStream(); entityCapsManager_ = new EntityCapsManager(capsProvider_, stanzaChannel_); chatListWindowFactory_ = mocks_->InterfaceMock(); mucSearchWindowFactory_ = mocks_->InterfaceMock(); settings_ = new DummySettingsProvider(); profileSettings_ = new ProfileSettingsProvider("a", settings_); chatListWindow_ = new MockChatListWindow(); ftManager_ = new DummyFileTransferManager(); ftOverview_ = new FileTransferOverview(ftManager_); avatarManager_ = new NullAvatarManager(); wbSessionManager_ = new WhiteboardSessionManager(iqRouter_, stanzaChannel_, presenceOracle_, entityCapsManager_); wbManager_ = new WhiteboardManager(whiteboardWindowFactory_, uiEventStream_, nickResolver_, wbSessionManager_); mocks_->ExpectCall(chatListWindowFactory_, ChatListWindowFactory::createChatListWindow).With(uiEventStream_).Return(chatListWindow_); manager_ = new ChatsManager(jid_, stanzaChannel_, iqRouter_, eventController_, chatWindowFactory_, joinMUCWindowFactory_, nickResolver_, presenceOracle_, directedPresenceSender_, uiEventStream_, chatListWindowFactory_, true, NULL, mucRegistry_, entityCapsManager_, mucManager_, mucSearchWindowFactory_, profileSettings_, ftOverview_, xmppRoster_, false, settings_, NULL, wbManager_); manager_->setAvatarManager(avatarManager_); }; void tearDown() { //delete chatListWindowFactory delete profileSettings_; delete avatarManager_; delete manager_; delete ftOverview_; delete ftManager_; delete wbSessionManager_; delete wbManager_; delete directedPresenceSender_; delete presenceSender_; delete presenceOracle_; delete nickResolver_; delete mucRegistry_; delete stanzaChannel_; delete eventController_; delete iqRouter_; delete iqChannel_; delete uiEventStream_; delete mucManager_; delete xmppRoster_; delete entityCapsManager_; delete capsProvider_; delete chatListWindow_; delete mocks_; delete settings_; } void testFirstOpenWindowIncoming() { JID messageJID("testling@test.com/resource1"); MockChatWindow* window = new MockChatWindow();//mocks_->InterfaceMock(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID, uiEventStream_).Return(window); boost::shared_ptr message(new Message()); message->setFrom(messageJID); std::string body("This is a legible message. >HEH@)oeueu"); message->setBody(body); manager_->handleIncomingMessage(message); CPPUNIT_ASSERT_EQUAL(body, window->lastMessageBody_); } void testSecondOpenWindowIncoming() { JID messageJID1("testling@test.com/resource1"); MockChatWindow* window1 = new MockChatWindow();//mocks_->InterfaceMock(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID1, uiEventStream_).Return(window1); boost::shared_ptr message1(new Message()); message1->setFrom(messageJID1); std::string body1("This is a legible message. >HEH@)oeueu"); message1->setBody(body1); manager_->handleIncomingMessage(message1); CPPUNIT_ASSERT_EQUAL(body1, window1->lastMessageBody_); JID messageJID2("testling@test.com/resource2"); //MockChatWindow* window2 = new MockChatWindow();//mocks_->InterfaceMock(); //mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID2, uiEventStream_).Return(window2); boost::shared_ptr message2(new Message()); message2->setFrom(messageJID2); std::string body2("This is a legible message. .cmaulm.chul"); message2->setBody(body2); manager_->handleIncomingMessage(message2); CPPUNIT_ASSERT_EQUAL(body2, window1->lastMessageBody_); } void testFirstOpenWindowOutgoing() { std::string messageJIDString("testling@test.com"); ChatWindow* window = new MockChatWindow();//mocks_->InterfaceMock(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(JID(messageJIDString), uiEventStream_).Return(window); uiEventStream_->send(boost::shared_ptr(new RequestChatUIEvent(JID(messageJIDString)))); } void testFirstOpenWindowBareToFull() { std::string bareJIDString("testling@test.com"); std::string fullJIDString("testling@test.com/resource1"); MockChatWindow* window = new MockChatWindow();//mocks_->InterfaceMock(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(JID(bareJIDString), uiEventStream_).Return(window); uiEventStream_->send(boost::shared_ptr(new RequestChatUIEvent(JID(bareJIDString)))); boost::shared_ptr message(new Message()); message->setFrom(JID(fullJIDString)); std::string body("This is a legible message. mjuga3089gm8G(*>M)@*("); message->setBody(body); manager_->handleIncomingMessage(message); CPPUNIT_ASSERT_EQUAL(body, window->lastMessageBody_); } void testSecondWindow() { std::string messageJIDString1("testling1@test.com"); ChatWindow* window1 = new MockChatWindow();//mocks_->InterfaceMock(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(JID(messageJIDString1), uiEventStream_).Return(window1); uiEventStream_->send(boost::shared_ptr(new RequestChatUIEvent(JID(messageJIDString1)))); std::string messageJIDString2("testling2@test.com"); ChatWindow* window2 = new MockChatWindow();//mocks_->InterfaceMock(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(JID(messageJIDString2), uiEventStream_).Return(window2); uiEventStream_->send(boost::shared_ptr(new RequestChatUIEvent(JID(messageJIDString2)))); } /** Complete cycle. Create unbound window. Bind it. Unbind it. Rebind it. */ void testUnbindRebind() { std::string bareJIDString("testling@test.com"); std::string fullJIDString1("testling@test.com/resource1"); std::string fullJIDString2("testling@test.com/resource2"); MockChatWindow* window = new MockChatWindow();//mocks_->InterfaceMock(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(JID(bareJIDString), uiEventStream_).Return(window); uiEventStream_->send(boost::shared_ptr(new RequestChatUIEvent(JID(bareJIDString)))); boost::shared_ptr message1(new Message()); message1->setFrom(JID(fullJIDString1)); std::string messageBody1("This is a legible message."); message1->setBody(messageBody1); manager_->handleIncomingMessage(message1); CPPUNIT_ASSERT_EQUAL(messageBody1, window->lastMessageBody_); boost::shared_ptr jid1Online(new Presence()); jid1Online->setFrom(JID(fullJIDString1)); boost::shared_ptr jid1Offline(new Presence()); jid1Offline->setFrom(JID(fullJIDString1)); jid1Offline->setType(Presence::Unavailable); presenceOracle_->onPresenceChange(jid1Offline); boost::shared_ptr message2(new Message()); message2->setFrom(JID(fullJIDString2)); std::string messageBody2("This is another legible message."); message2->setBody(messageBody2); manager_->handleIncomingMessage(message2); CPPUNIT_ASSERT_EQUAL(messageBody2, window->lastMessageBody_); } /** * Test that MUC PMs get opened in the right windows */ void testThreeMUCWindows() { JID muc("testling@test.com"); ChatWindow* mucWindow = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(muc, uiEventStream_).Return(mucWindow); uiEventStream_->send(boost::make_shared(muc, std::string("nick"))); std::string messageJIDString1("testling@test.com/1"); ChatWindow* window1 = new MockChatWindow();//mocks_->InterfaceMock(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(JID(messageJIDString1), uiEventStream_).Return(window1); uiEventStream_->send(boost::shared_ptr(new RequestChatUIEvent(JID(messageJIDString1)))); std::string messageJIDString2("testling@test.com/2"); ChatWindow* window2 = new MockChatWindow();//mocks_->InterfaceMock(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(JID(messageJIDString2), uiEventStream_).Return(window2); uiEventStream_->send(boost::shared_ptr(new RequestChatUIEvent(JID(messageJIDString2)))); std::string messageJIDString3("testling@test.com/3"); ChatWindow* window3 = new MockChatWindow();//mocks_->InterfaceMock(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(JID(messageJIDString3), uiEventStream_).Return(window3); uiEventStream_->send(boost::shared_ptr(new RequestChatUIEvent(JID(messageJIDString3)))); /* Refetch an earlier window */ /* We do not expect a new window to be created */ uiEventStream_->send(boost::shared_ptr(new RequestChatUIEvent(JID(messageJIDString1)))); } /** Test that a second window isn't unbound where there's already an unbound one. Bind 1 Bind 2 Unbind 1 Unbind 2 (but it doesn't) Sent to bound 2 Rebind 1 */ void testNoDuplicateUnbind() { JID messageJID1("testling@test.com/resource1"); MockChatWindow* window1 = new MockChatWindow();//mocks_->InterfaceMock(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID1, uiEventStream_).Return(window1); boost::shared_ptr message1(new Message()); message1->setFrom(messageJID1); message1->setBody("This is a legible message1."); manager_->handleIncomingMessage(message1); JID messageJID2("testling@test.com/resource2"); //MockChatWindow* window2 = new MockChatWindow();//mocks_->InterfaceMock(); //mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID2, uiEventStream_).Return(window2); boost::shared_ptr message2(new Message()); message2->setFrom(messageJID2); message2->setBody("This is a legible message2."); manager_->handleIncomingMessage(message2); boost::shared_ptr jid1Online(new Presence()); jid1Online->setFrom(JID(messageJID1)); boost::shared_ptr jid1Offline(new Presence()); jid1Offline->setFrom(JID(messageJID1)); jid1Offline->setType(Presence::Unavailable); presenceOracle_->onPresenceChange(jid1Offline); boost::shared_ptr jid2Online(new Presence()); jid2Online->setFrom(JID(messageJID2)); boost::shared_ptr jid2Offline(new Presence()); jid2Offline->setFrom(JID(messageJID2)); jid2Offline->setType(Presence::Unavailable); presenceOracle_->onPresenceChange(jid2Offline); JID messageJID3("testling@test.com/resource3"); boost::shared_ptr message3(new Message()); message3->setFrom(messageJID3); std::string body3("This is a legible message3."); message3->setBody(body3); manager_->handleIncomingMessage(message3); CPPUNIT_ASSERT_EQUAL(body3, window1->lastMessageBody_); boost::shared_ptr message2b(new Message()); message2b->setFrom(messageJID2); std::string body2b("This is a legible message2b."); message2b->setBody(body2b); manager_->handleIncomingMessage(message2b); CPPUNIT_ASSERT_EQUAL(body2b, window1->lastMessageBody_); } /** * Test that ChatController doesn't send receipts anymore after removal of the contact from the roster. */ void testChatControllerPresenceAccessUpdatedOnRemoveFromRoster() { JID messageJID("testling@test.com/resource1"); xmppRoster_->addContact(messageJID, "foo", std::vector(), RosterItemPayload::Both); MockChatWindow* window = new MockChatWindow();//mocks_->InterfaceMock(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID, uiEventStream_).Return(window); settings_->storeSetting(SettingConstants::REQUEST_DELIVERYRECEIPTS, true); boost::shared_ptr message = makeDeliveryReceiptTestMessage(messageJID, "1"); manager_->handleIncomingMessage(message); Stanza::ref stanzaContactOnRoster = stanzaChannel_->getStanzaAtIndex(0); CPPUNIT_ASSERT_EQUAL(st(1), stanzaChannel_->sentStanzas.size()); CPPUNIT_ASSERT(stanzaContactOnRoster->getPayload() != 0); xmppRoster_->removeContact(messageJID); message->setID("2"); manager_->handleIncomingMessage(message); CPPUNIT_ASSERT_EQUAL(st(1), stanzaChannel_->sentStanzas.size()); } /** * Test that ChatController sends receipts after the contact has been added to the roster. */ void testChatControllerPresenceAccessUpdatedOnAddToRoster() { JID messageJID("testling@test.com/resource1"); MockChatWindow* window = new MockChatWindow();//mocks_->InterfaceMock(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID, uiEventStream_).Return(window); settings_->storeSetting(SettingConstants::REQUEST_DELIVERYRECEIPTS, true); boost::shared_ptr message = makeDeliveryReceiptTestMessage(messageJID, "1"); manager_->handleIncomingMessage(message); CPPUNIT_ASSERT_EQUAL(st(0), stanzaChannel_->sentStanzas.size()); xmppRoster_->addContact(messageJID, "foo", std::vector(), RosterItemPayload::Both); message->setID("2"); manager_->handleIncomingMessage(message); CPPUNIT_ASSERT_EQUAL(st(1), stanzaChannel_->sentStanzas.size()); Stanza::ref stanzaContactOnRoster = stanzaChannel_->getStanzaAtIndex(0); CPPUNIT_ASSERT(stanzaContactOnRoster->getPayload() != 0); } /** * Test that ChatController sends receipts if requested after change from subscription state To to subscription state Both. */ void testChatControllerPresenceAccessUpdatedOnSubscriptionChangeToBoth() { testhelperChatControllerPresenceAccessUpdatedOnSubscriptionChangeReceiptsAllowed(RosterItemPayload::To, RosterItemPayload::Both); } /** * Test that ChatController sends receipts if requested after change from subscription state To to subscription state From. */ void testChatControllerPresenceAccessUpdatedOnSubscriptionChangeToFrom() { testhelperChatControllerPresenceAccessUpdatedOnSubscriptionChangeReceiptsAllowed(RosterItemPayload::To, RosterItemPayload::From); } void testhelperChatControllerPresenceAccessUpdatedOnSubscriptionChangeReceiptsAllowed(RosterItemPayload::Subscription from, RosterItemPayload::Subscription to) { JID messageJID("testling@test.com/resource1"); xmppRoster_->addContact(messageJID, "foo", std::vector(), from); MockChatWindow* window = new MockChatWindow();//mocks_->InterfaceMock(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID, uiEventStream_).Return(window); settings_->storeSetting(SettingConstants::REQUEST_DELIVERYRECEIPTS, true); boost::shared_ptr message = makeDeliveryReceiptTestMessage(messageJID, "1"); manager_->handleIncomingMessage(message); CPPUNIT_ASSERT_EQUAL(st(0), stanzaChannel_->sentStanzas.size()); xmppRoster_->addContact(messageJID, "foo", std::vector(), to); message->setID("2"); manager_->handleIncomingMessage(message); CPPUNIT_ASSERT_EQUAL(st(1), stanzaChannel_->sentStanzas.size()); Stanza::ref stanzaContactOnRoster = stanzaChannel_->getStanzaAtIndex(0); CPPUNIT_ASSERT(stanzaContactOnRoster->getPayload() != 0); } private: boost::shared_ptr makeDeliveryReceiptTestMessage(const JID& from, const std::string& id) { boost::shared_ptr message = boost::make_shared(); message->setFrom(from); message->setID(id); message->setBody("This will cause the window to open"); message->addPayload(boost::make_shared()); return message; } size_t st(int i) { return static_cast(i); } private: JID jid_; ChatsManager* manager_; DummyStanzaChannel* stanzaChannel_; IQChannel* iqChannel_; IQRouter* iqRouter_; EventController* eventController_; ChatWindowFactory* chatWindowFactory_; JoinMUCWindowFactory* joinMUCWindowFactory_; NickResolver* nickResolver_; PresenceOracle* presenceOracle_; AvatarManager* avatarManager_; boost::shared_ptr serverDiscoInfo_; XMPPRosterImpl* xmppRoster_; PresenceSender* presenceSender_; MockRepository* mocks_; UIEventStream* uiEventStream_; ChatListWindowFactory* chatListWindowFactory_; WhiteboardWindowFactory* whiteboardWindowFactory_; MUCSearchWindowFactory* mucSearchWindowFactory_; MUCRegistry* mucRegistry_; DirectedPresenceSender* directedPresenceSender_; EntityCapsManager* entityCapsManager_; CapsProvider* capsProvider_; MUCManager* mucManager_; DummySettingsProvider* settings_; ProfileSettingsProvider* profileSettings_; ChatListWindow* chatListWindow_; FileTransferOverview* ftOverview_; FileTransferManager* ftManager_; WhiteboardSessionManager* wbSessionManager_; WhiteboardManager* wbManager_; }; CPPUNIT_TEST_SUITE_REGISTRATION(ChatsManagerTest); swift-im-2.0+dev6/Swift/Controllers/Chat/ChatControllerBase.h0000644000175000017500000001224212227051773024033 0ustar kismithkismith/* * Copyright (c) 2010-2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include "Swiften/Base/boost_bsignals.h" #include #include #include #include "Swiften/Network/Timer.h" #include "Swiften/Network/TimerFactory.h" #include "Swiften/Elements/Stanza.h" #include #include "Swiften/Elements/DiscoInfo.h" #include "Swift/Controllers/XMPPEvents/MessageEvent.h" #include #include "Swiften/JID/JID.h" #include "Swiften/Elements/SecurityLabelsCatalog.h" #include "Swiften/Elements/ErrorPayload.h" #include "Swiften/Presence/PresenceOracle.h" #include "Swiften/Queries/IQRouter.h" #include "Swiften/Base/IDGenerator.h" #include #include namespace Swift { class IQRouter; class StanzaChannel; class ChatWindow; class ChatWindowFactory; class AvatarManager; class UIEventStream; class EventController; class EntityCapsProvider; class ChatControllerBase : public boost::bsignals::trackable { public: virtual ~ChatControllerBase(); void showChatWindow(); void activateChatWindow(); void setAvailableServerFeatures(boost::shared_ptr info); void handleIncomingMessage(boost::shared_ptr message); std::string addMessage(const std::string& message, const std::string& senderName, bool senderIsSelf, boost::shared_ptr label, const std::string& avatarPath, const boost::posix_time::ptime& time); void replaceMessage(const std::string& message, const std::string& id, const boost::posix_time::ptime& time); virtual void setOnline(bool online); virtual void setEnabled(bool enabled); virtual void setToJID(const JID& jid) {toJID_ = jid;}; /** Used for determining when something is recent.*/ boost::signal onActivity; boost::signal onUnreadCountChanged; int getUnreadCount(); const JID& getToJID() {return toJID_;} void handleCapsChanged(const JID& jid); protected: ChatControllerBase(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &toJID, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController, TimerFactory* timerFactory, EntityCapsProvider* entityCapsProvider, HistoryController* historyController, MUCRegistry* mucRegistry); /** * Pass the Message appended, and the stanza used to send it. */ virtual void postSendMessage(const std::string&, boost::shared_ptr) {}; virtual std::string senderDisplayNameFromMessage(const JID& from) = 0; virtual bool isIncomingMessageFromMe(boost::shared_ptr) = 0; virtual void preHandleIncomingMessage(boost::shared_ptr) {}; virtual void postHandleIncomingMessage(boost::shared_ptr) {}; virtual void preSendMessageRequest(boost::shared_ptr) {}; virtual bool isFromContact(const JID& from); virtual boost::optional getMessageTimestamp(boost::shared_ptr) const = 0; virtual void dayTicked() {}; virtual void handleBareJIDCapsChanged(const JID& jid) = 0; std::string getErrorMessage(boost::shared_ptr); virtual void setContactIsReceivingPresence(bool /* isReceivingPresence */) {} virtual void cancelReplaces() = 0; /** JID any iq for account should go to - bare except for PMs */ virtual JID getBaseJID(); virtual void logMessage(const std::string& message, const JID& fromJID, const JID& toJID, const boost::posix_time::ptime& timeStamp, bool isIncoming) = 0; private: IDGenerator idGenerator_; std::string lastSentMessageStanzaID_; void createDayChangeTimer(); void handleSendMessageRequest(const std::string &body, bool isCorrectionMessage); void handleAllMessagesRead(); void handleSecurityLabelsCatalogResponse(boost::shared_ptr, ErrorPayload::ref error); void handleDayChangeTick(); void handleMUCInvitation(Message::ref message); void handleMediatedMUCInvitation(Message::ref message); void handleGeneralMUCInvitation(MUCInviteEvent::ref event); void handleLogCleared(); protected: JID selfJID_; std::vector > unreadMessages_; std::vector > targetedUnreadMessages_; StanzaChannel* stanzaChannel_; IQRouter* iqRouter_; ChatWindowFactory* chatWindowFactory_; ChatWindow* chatWindow_; JID toJID_; bool labelsEnabled_; std::map lastMessagesUIID_; PresenceOracle* presenceOracle_; AvatarManager* avatarManager_; bool useDelayForLatency_; EventController* eventController_; boost::shared_ptr dateChangeTimer_; TimerFactory* timerFactory_; EntityCapsProvider* entityCapsProvider_; SecurityLabelsCatalog::Item lastLabel_; HistoryController* historyController_; MUCRegistry* mucRegistry_; }; } swift-im-2.0+dev6/Swift/Controllers/Chat/ChatControllerBase.cpp0000644000175000017500000003422612227051773024374 0ustar kismithkismith/* * Copyright (c) 2010-2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Swift/Controllers/Chat/ChatControllerBase.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Swift { ChatControllerBase::ChatControllerBase(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &toJID, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController, TimerFactory* timerFactory, EntityCapsProvider* entityCapsProvider, HistoryController* historyController, MUCRegistry* mucRegistry) : selfJID_(self), stanzaChannel_(stanzaChannel), iqRouter_(iqRouter), chatWindowFactory_(chatWindowFactory), toJID_(toJID), labelsEnabled_(false), presenceOracle_(presenceOracle), avatarManager_(avatarManager), useDelayForLatency_(useDelayForLatency), eventController_(eventController), timerFactory_(timerFactory), entityCapsProvider_(entityCapsProvider), historyController_(historyController), mucRegistry_(mucRegistry) { chatWindow_ = chatWindowFactory_->createChatWindow(toJID, eventStream); chatWindow_->onAllMessagesRead.connect(boost::bind(&ChatControllerBase::handleAllMessagesRead, this)); chatWindow_->onSendMessageRequest.connect(boost::bind(&ChatControllerBase::handleSendMessageRequest, this, _1, _2)); chatWindow_->onLogCleared.connect(boost::bind(&ChatControllerBase::handleLogCleared, this)); entityCapsProvider_->onCapsChanged.connect(boost::bind(&ChatControllerBase::handleCapsChanged, this, _1)); setOnline(stanzaChannel->isAvailable() && iqRouter->isAvailable()); createDayChangeTimer(); } ChatControllerBase::~ChatControllerBase() { delete chatWindow_; } void ChatControllerBase::handleLogCleared() { cancelReplaces(); } void ChatControllerBase::handleCapsChanged(const JID& jid) { if (jid.compare(toJID_, JID::WithoutResource) == 0) { handleBareJIDCapsChanged(jid); } } void ChatControllerBase::createDayChangeTimer() { if (timerFactory_) { boost::posix_time::ptime now = boost::posix_time::second_clock::local_time(); boost::posix_time::ptime midnight(now.date() + boost::gregorian::days(1)); long millisecondsUntilMidnight = (midnight - now).total_milliseconds(); dateChangeTimer_ = boost::shared_ptr(timerFactory_->createTimer(millisecondsUntilMidnight)); dateChangeTimer_->onTick.connect(boost::bind(&ChatControllerBase::handleDayChangeTick, this)); dateChangeTimer_->start(); } } void ChatControllerBase::handleDayChangeTick() { dateChangeTimer_->stop(); boost::posix_time::ptime now = boost::posix_time::second_clock::local_time(); chatWindow_->addSystemMessage(str(format(QT_TRANSLATE_NOOP("", "The day is now %1%")) % std::string(boost::posix_time::to_iso_extended_string(now)).substr(0,10))); dayTicked(); createDayChangeTimer(); } void ChatControllerBase::setEnabled(bool enabled) { chatWindow_->setInputEnabled(enabled); } void ChatControllerBase::setOnline(bool online) { setEnabled(online); } JID ChatControllerBase::getBaseJID() { return JID(toJID_.toBare()); } void ChatControllerBase::setAvailableServerFeatures(boost::shared_ptr info) { if (iqRouter_->isAvailable() && info->hasFeature(DiscoInfo::SecurityLabelsCatalogFeature)) { GetSecurityLabelsCatalogRequest::ref request = GetSecurityLabelsCatalogRequest::create(getBaseJID(), iqRouter_); request->onResponse.connect(boost::bind(&ChatControllerBase::handleSecurityLabelsCatalogResponse, this, _1, _2)); request->send(); } else { chatWindow_->setSecurityLabelsEnabled(false); labelsEnabled_ = false; } } void ChatControllerBase::handleAllMessagesRead() { if (!unreadMessages_.empty()) { targetedUnreadMessages_.clear(); foreach (boost::shared_ptr stanzaEvent, unreadMessages_) { stanzaEvent->conclude(); } unreadMessages_.clear(); chatWindow_->setUnreadMessageCount(0); onUnreadCountChanged(); } } int ChatControllerBase::getUnreadCount() { return targetedUnreadMessages_.size(); } void ChatControllerBase::handleSendMessageRequest(const std::string &body, bool isCorrectionMessage) { if (!stanzaChannel_->isAvailable() || body.empty()) { return; } boost::shared_ptr message(new Message()); message->setTo(toJID_); message->setType(Swift::Message::Chat); message->setBody(body); if (labelsEnabled_) { if (!isCorrectionMessage) { lastLabel_ = chatWindow_->getSelectedSecurityLabel(); } SecurityLabelsCatalog::Item labelItem = lastLabel_; if (labelItem.getLabel()) { message->addPayload(labelItem.getLabel()); } } preSendMessageRequest(message); boost::posix_time::ptime now = boost::posix_time::microsec_clock::universal_time(); if (useDelayForLatency_) { message->addPayload(boost::make_shared(now, selfJID_)); } if (isCorrectionMessage) { message->addPayload(boost::shared_ptr (new Replace(lastSentMessageStanzaID_))); } message->setID(lastSentMessageStanzaID_ = idGenerator_.generateID()); stanzaChannel_->sendMessage(message); postSendMessage(message->getBody(), boost::dynamic_pointer_cast(message)); onActivity(message->getBody()); #ifdef SWIFT_EXPERIMENTAL_HISTORY logMessage(body, selfJID_, toJID_, now, false); #endif } void ChatControllerBase::handleSecurityLabelsCatalogResponse(boost::shared_ptr catalog, ErrorPayload::ref error) { if (catalog && !error) { if (catalog->getItems().size() == 0) { chatWindow_->setSecurityLabelsEnabled(false); labelsEnabled_ = false; } else { labelsEnabled_ = true; chatWindow_->setAvailableSecurityLabels(catalog->getItems()); chatWindow_->setSecurityLabelsEnabled(true); } } else { labelsEnabled_ = false; chatWindow_->setSecurityLabelsError(); } } void ChatControllerBase::showChatWindow() { chatWindow_->show(); } void ChatControllerBase::activateChatWindow() { chatWindow_->activate(); } std::string ChatControllerBase::addMessage(const std::string& message, const std::string& senderName, bool senderIsSelf, const boost::shared_ptr label, const std::string& avatarPath, const boost::posix_time::ptime& time) { if (boost::starts_with(message, "/me ")) { return chatWindow_->addAction(String::getSplittedAtFirst(message, ' ').second, senderName, senderIsSelf, label, avatarPath, time); } else { return chatWindow_->addMessage(message, senderName, senderIsSelf, label, avatarPath, time); } } void ChatControllerBase::replaceMessage(const std::string& message, const std::string& id, const boost::posix_time::ptime& time) { if (boost::starts_with(message, "/me ")) { chatWindow_->replaceWithAction(String::getSplittedAtFirst(message, ' ').second, id, time); } else { chatWindow_->replaceMessage(message, id, time); } } bool ChatControllerBase::isFromContact(const JID& from) { return from.toBare() == toJID_.toBare(); } void ChatControllerBase::handleIncomingMessage(boost::shared_ptr messageEvent) { preHandleIncomingMessage(messageEvent); if (messageEvent->isReadable() && !messageEvent->getConcluded()) { unreadMessages_.push_back(messageEvent); if (messageEvent->targetsMe()) { targetedUnreadMessages_.push_back(messageEvent); } } boost::shared_ptr message = messageEvent->getStanza(); std::string body = message->getBody(); if (message->isError()) { std::string errorMessage = str(format(QT_TRANSLATE_NOOP("", "Couldn't send message: %1%")) % getErrorMessage(message->getPayload())); chatWindow_->addErrorMessage(errorMessage); } else if (messageEvent->getStanza()->getPayload()) { handleMUCInvitation(messageEvent->getStanza()); return; } else if (messageEvent->getStanza()->getPayload() && messageEvent->getStanza()->getPayload()->getInvite()) { handleMediatedMUCInvitation(messageEvent->getStanza()); return; } else { if (!messageEvent->isReadable()) { return; } showChatWindow(); JID from = message->getFrom(); std::vector > delayPayloads = message->getPayloads(); for (size_t i = 0; useDelayForLatency_ && i < delayPayloads.size(); i++) { if (!delayPayloads[i]->getFrom()) { continue; } boost::posix_time::ptime now = boost::posix_time::microsec_clock::universal_time(); std::ostringstream s; s << "The following message took " << (now - delayPayloads[i]->getStamp()).total_milliseconds() / 1000.0 << " seconds to be delivered from " << delayPayloads[i]->getFrom()->toString() << "."; chatWindow_->addSystemMessage(std::string(s.str())); } boost::shared_ptr label = message->getPayload(); // Determine the timestamp boost::posix_time::ptime timeStamp = boost::posix_time::microsec_clock::universal_time(); boost::optional messageTimeStamp = getMessageTimestamp(message); if (messageTimeStamp) { timeStamp = *messageTimeStamp; } onActivity(body); boost::shared_ptr replace = message->getPayload(); if (replace) { std::string body = message->getBody(); // Should check if the user has a previous message std::map::iterator lastMessage; lastMessage = lastMessagesUIID_.find(from); if (lastMessage != lastMessagesUIID_.end()) { replaceMessage(body, lastMessagesUIID_[from], timeStamp); } } else { lastMessagesUIID_[from] = addMessage(body, senderDisplayNameFromMessage(from), isIncomingMessageFromMe(message), label, std::string(avatarManager_->getAvatarPath(from).string()), timeStamp); } logMessage(body, from, selfJID_, timeStamp, true); } chatWindow_->show(); chatWindow_->setUnreadMessageCount(unreadMessages_.size()); onUnreadCountChanged(); postHandleIncomingMessage(messageEvent); } std::string ChatControllerBase::getErrorMessage(boost::shared_ptr error) { std::string defaultMessage = QT_TRANSLATE_NOOP("", "Error sending message"); if (!error->getText().empty()) { return error->getText(); } else { switch (error->getCondition()) { case ErrorPayload::BadRequest: return QT_TRANSLATE_NOOP("", "Bad request"); case ErrorPayload::Conflict: return QT_TRANSLATE_NOOP("", "Conflict"); case ErrorPayload::FeatureNotImplemented: return QT_TRANSLATE_NOOP("", "This feature is not implemented"); case ErrorPayload::Forbidden: return QT_TRANSLATE_NOOP("", "Forbidden"); case ErrorPayload::Gone: return QT_TRANSLATE_NOOP("", "Recipient can no longer be contacted"); case ErrorPayload::InternalServerError: return QT_TRANSLATE_NOOP("", "Internal server error"); case ErrorPayload::ItemNotFound: return QT_TRANSLATE_NOOP("", "Item not found"); case ErrorPayload::JIDMalformed: return QT_TRANSLATE_NOOP("", "JID Malformed"); case ErrorPayload::NotAcceptable: return QT_TRANSLATE_NOOP("", "Message was rejected"); case ErrorPayload::NotAllowed: return QT_TRANSLATE_NOOP("", "Not allowed"); case ErrorPayload::NotAuthorized: return QT_TRANSLATE_NOOP("", "Not authorized"); case ErrorPayload::PaymentRequired: return QT_TRANSLATE_NOOP("", "Payment is required"); case ErrorPayload::RecipientUnavailable: return QT_TRANSLATE_NOOP("", "Recipient is unavailable"); case ErrorPayload::Redirect: return QT_TRANSLATE_NOOP("", "Redirect"); case ErrorPayload::RegistrationRequired: return QT_TRANSLATE_NOOP("", "Registration required"); case ErrorPayload::RemoteServerNotFound: return QT_TRANSLATE_NOOP("", "Recipient's server not found"); case ErrorPayload::RemoteServerTimeout: return QT_TRANSLATE_NOOP("", "Remote server timeout"); case ErrorPayload::ResourceConstraint: return QT_TRANSLATE_NOOP("", "The server is low on resources"); case ErrorPayload::ServiceUnavailable: return QT_TRANSLATE_NOOP("", "The service is unavailable"); case ErrorPayload::SubscriptionRequired: return QT_TRANSLATE_NOOP("", "A subscription is required"); case ErrorPayload::UndefinedCondition: return QT_TRANSLATE_NOOP("", "Undefined condition"); case ErrorPayload::UnexpectedRequest: return QT_TRANSLATE_NOOP("", "Unexpected request"); } } return defaultMessage; } void ChatControllerBase::handleGeneralMUCInvitation(MUCInviteEvent::ref event) { unreadMessages_.push_back(event); chatWindow_->show(); chatWindow_->setUnreadMessageCount(unreadMessages_.size()); onUnreadCountChanged(); chatWindow_->addMUCInvitation(senderDisplayNameFromMessage(event->getInviter()), event->getRoomJID(), event->getReason(), event->getPassword(), event->getDirect()); eventController_->handleIncomingEvent(event); } void ChatControllerBase::handleMUCInvitation(Message::ref message) { MUCInvitationPayload::ref invite = message->getPayload(); MUCInviteEvent::ref inviteEvent = boost::make_shared(toJID_, invite->getJID(), invite->getReason(), invite->getPassword(), true); handleGeneralMUCInvitation(inviteEvent); } void ChatControllerBase::handleMediatedMUCInvitation(Message::ref message) { MUCUserPayload::Invite invite = *message->getPayload()->getInvite(); JID from = message->getFrom(); std::string reason; if (!invite.reason.empty()) { reason = invite.reason; } std::string password; if (message->getPayload()->getPassword()) { password = *message->getPayload()->getPassword(); } MUCInviteEvent::ref inviteEvent = boost::make_shared(invite.from, from, reason, password, false); handleGeneralMUCInvitation(inviteEvent); } } swift-im-2.0+dev6/Swift/Controllers/Chat/ChatsManager.h0000644000175000017500000001343312227051773022655 0ustar kismithkismith/* * Copyright (c) 2010-2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include #include #include #include #include #include #include #include #include namespace Swift { class EventController; class ChatController; class ChatControllerBase; class MUCController; class MUCManager; class ChatWindowFactory; class JoinMUCWindow; class JoinMUCWindowFactory; class NickResolver; class PresenceOracle; class AvatarManager; class StanzaChannel; class IQRouter; class PresenceSender; class MUCBookmarkManager; class ChatListWindowFactory; class TimerFactory; class EntityCapsProvider; class DirectedPresenceSender; class MUCSearchWindowFactory; class ProfileSettingsProvider; class MUCSearchController; class FileTransferOverview; class FileTransferController; class XMPPRoster; class SettingsProvider; class WhiteboardManager; class HistoryController; class ChatsManager { public: ChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRouter, EventController* eventController, ChatWindowFactory* chatWindowFactory, JoinMUCWindowFactory* joinMUCWindowFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, PresenceSender* presenceSender, UIEventStream* uiEventStream, ChatListWindowFactory* chatListWindowFactory, bool useDelayForLatency, TimerFactory* timerFactory, MUCRegistry* mucRegistry, EntityCapsProvider* entityCapsProvider, MUCManager* mucManager, MUCSearchWindowFactory* mucSearchWindowFactory, ProfileSettingsProvider* profileSettings, FileTransferOverview* ftOverview, XMPPRoster* roster, bool eagleMode, SettingsProvider* settings, HistoryController* historyController_, WhiteboardManager* whiteboardManager); virtual ~ChatsManager(); void setAvatarManager(AvatarManager* avatarManager); void setOnline(bool enabled); void setServerDiscoInfo(boost::shared_ptr info); void handleIncomingMessage(boost::shared_ptr message); private: ChatListWindow::Chat createChatListChatItem(const JID& jid, const std::string& activity); void handleChatRequest(const std::string& contact); void handleJoinMUCRequest(const JID& muc, const boost::optional& password, const boost::optional& nick, bool addAutoJoin, bool createAsReservedIfNew); void handleSearchMUCRequest(); void handleMUCSelectedAfterSearch(const JID&); void rebindControllerJID(const JID& from, const JID& to); void handlePresenceChange(boost::shared_ptr newPresence); void handleUIEvent(boost::shared_ptr event); void handleMUCBookmarkAdded(const MUCBookmark& bookmark); void handleMUCBookmarkRemoved(const MUCBookmark& bookmark); void handleUserLeftMUC(MUCController* mucController); void handleBookmarksReady(); void handleChatActivity(const JID& jid, const std::string& activity, bool isMUC); void handleNewFileTransferController(FileTransferController*); void handleWhiteboardSessionRequest(const JID& contact, bool senderIsSelf); void handleWhiteboardStateChange(const JID& contact, const ChatWindow::WhiteboardSessionState state); void appendRecent(const ChatListWindow::Chat& chat); void prependRecent(const ChatListWindow::Chat& chat); void setupBookmarks(); void loadRecents(); void saveRecents(); void handleChatMadeRecent(); void handleMUCBookmarkActivated(const MUCBookmark&); void handleRecentActivated(const ChatListWindow::Chat&); void handleUnreadCountChanged(ChatControllerBase* controller); void handleAvatarChanged(const JID& jid); void handleClearRecentsRequested(); void handleJIDAddedToRoster(const JID&); void handleJIDRemovedFromRoster(const JID&); void handleJIDUpdatedInRoster(const JID&); void handleRosterCleared(); void handleSettingChanged(const std::string& settingPath); void markAllRecentsOffline(); void updatePresenceReceivingStateOnChatController(const JID&); ChatController* getChatControllerOrFindAnother(const JID &contact); ChatController* createNewChatController(const JID &contact); ChatController* getChatControllerOrCreate(const JID &contact); ChatController* getChatControllerIfExists(const JID &contact, bool rebindIfNeeded = true); private: std::map mucControllers_; std::map chatControllers_; EventController* eventController_; JID jid_; StanzaChannel* stanzaChannel_; IQRouter* iqRouter_; ChatWindowFactory* chatWindowFactory_; JoinMUCWindowFactory* joinMUCWindowFactory_; NickResolver* nickResolver_; PresenceOracle* presenceOracle_; AvatarManager* avatarManager_; PresenceSender* presenceSender_; UIEventStream* uiEventStream_; MUCBookmarkManager* mucBookmarkManager_; boost::shared_ptr serverDiscoInfo_; ChatListWindow* chatListWindow_; JoinMUCWindow* joinMUCWindow_; boost::bsignals::scoped_connection uiEventConnection_; bool useDelayForLatency_; TimerFactory* timerFactory_; MUCRegistry* mucRegistry_; EntityCapsProvider* entityCapsProvider_; MUCManager* mucManager; MUCSearchController* mucSearchController_; std::list recentChats_; ProfileSettingsProvider* profileSettings_; FileTransferOverview* ftOverview_; XMPPRoster* roster_; bool eagleMode_; bool userWantsReceipts_; SettingsProvider* settings_; HistoryController* historyController_; WhiteboardManager* whiteboardManager_; }; } swift-im-2.0+dev6/Swift/Controllers/Chat/ChatsManager.cpp0000644000175000017500000006632212227051773023215 0ustar kismithkismith/* * Copyright (c) 2010-2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Swift/Controllers/Chat/ChatsManager.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Swift { typedef std::pair JIDChatControllerPair; typedef std::pair JIDMUCControllerPair; #define RECENT_CHATS "recent_chats" ChatsManager::ChatsManager( JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRouter, EventController* eventController, ChatWindowFactory* chatWindowFactory, JoinMUCWindowFactory* joinMUCWindowFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, PresenceSender* presenceSender, UIEventStream* uiEventStream, ChatListWindowFactory* chatListWindowFactory, bool useDelayForLatency, TimerFactory* timerFactory, MUCRegistry* mucRegistry, EntityCapsProvider* entityCapsProvider, MUCManager* mucManager, MUCSearchWindowFactory* mucSearchWindowFactory, ProfileSettingsProvider* profileSettings, FileTransferOverview* ftOverview, XMPPRoster* roster, bool eagleMode, SettingsProvider* settings, HistoryController* historyController, WhiteboardManager* whiteboardManager) : jid_(jid), joinMUCWindowFactory_(joinMUCWindowFactory), useDelayForLatency_(useDelayForLatency), mucRegistry_(mucRegistry), entityCapsProvider_(entityCapsProvider), mucManager(mucManager), ftOverview_(ftOverview), roster_(roster), eagleMode_(eagleMode), settings_(settings), historyController_(historyController), whiteboardManager_(whiteboardManager) { timerFactory_ = timerFactory; eventController_ = eventController; stanzaChannel_ = stanzaChannel; iqRouter_ = iqRouter; chatWindowFactory_ = chatWindowFactory; nickResolver_ = nickResolver; presenceOracle_ = presenceOracle; avatarManager_ = NULL; serverDiscoInfo_ = boost::make_shared(); presenceSender_ = presenceSender; uiEventStream_ = uiEventStream; mucBookmarkManager_ = NULL; profileSettings_ = profileSettings; presenceOracle_->onPresenceChange.connect(boost::bind(&ChatsManager::handlePresenceChange, this, _1)); uiEventConnection_ = uiEventStream_->onUIEvent.connect(boost::bind(&ChatsManager::handleUIEvent, this, _1)); chatListWindow_ = chatListWindowFactory->createChatListWindow(uiEventStream_); chatListWindow_->onMUCBookmarkActivated.connect(boost::bind(&ChatsManager::handleMUCBookmarkActivated, this, _1)); chatListWindow_->onRecentActivated.connect(boost::bind(&ChatsManager::handleRecentActivated, this, _1)); chatListWindow_->onClearRecentsRequested.connect(boost::bind(&ChatsManager::handleClearRecentsRequested, this)); joinMUCWindow_ = NULL; mucSearchController_ = new MUCSearchController(jid_, mucSearchWindowFactory, iqRouter, profileSettings_); mucSearchController_->onMUCSelected.connect(boost::bind(&ChatsManager::handleMUCSelectedAfterSearch, this, _1)); ftOverview_->onNewFileTransferController.connect(boost::bind(&ChatsManager::handleNewFileTransferController, this, _1)); whiteboardManager_->onSessionRequest.connect(boost::bind(&ChatsManager::handleWhiteboardSessionRequest, this, _1, _2)); whiteboardManager_->onRequestAccepted.connect(boost::bind(&ChatsManager::handleWhiteboardStateChange, this, _1, ChatWindow::WhiteboardAccepted)); whiteboardManager_->onSessionTerminate.connect(boost::bind(&ChatsManager::handleWhiteboardStateChange, this, _1, ChatWindow::WhiteboardTerminated)); whiteboardManager_->onRequestRejected.connect(boost::bind(&ChatsManager::handleWhiteboardStateChange, this, _1, ChatWindow::WhiteboardRejected)); roster_->onJIDAdded.connect(boost::bind(&ChatsManager::handleJIDAddedToRoster, this, _1)); roster_->onJIDRemoved.connect(boost::bind(&ChatsManager::handleJIDRemovedFromRoster, this, _1)); roster_->onJIDUpdated.connect(boost::bind(&ChatsManager::handleJIDUpdatedInRoster, this, _1)); roster_->onRosterCleared.connect(boost::bind(&ChatsManager::handleRosterCleared, this)); settings_->onSettingChanged.connect(boost::bind(&ChatsManager::handleSettingChanged, this, _1)); userWantsReceipts_ = settings_->getSetting(SettingConstants::REQUEST_DELIVERYRECEIPTS); setupBookmarks(); loadRecents(); } ChatsManager::~ChatsManager() { settings_->onSettingChanged.disconnect(boost::bind(&ChatsManager::handleSettingChanged, this, _1)); roster_->onJIDAdded.disconnect(boost::bind(&ChatsManager::handleJIDAddedToRoster, this, _1)); roster_->onJIDRemoved.disconnect(boost::bind(&ChatsManager::handleJIDRemovedFromRoster, this, _1)); roster_->onJIDUpdated.disconnect(boost::bind(&ChatsManager::handleJIDUpdatedInRoster, this, _1)); roster_->onRosterCleared.disconnect(boost::bind(&ChatsManager::handleRosterCleared, this)); delete joinMUCWindow_; foreach (JIDChatControllerPair controllerPair, chatControllers_) { delete controllerPair.second; } foreach (JIDMUCControllerPair controllerPair, mucControllers_) { delete controllerPair.second; } delete mucBookmarkManager_; delete mucSearchController_; } void ChatsManager::saveRecents() { std::string recents; int i = 1; foreach (ChatListWindow::Chat chat, recentChats_) { std::vector activity; boost::split(activity, chat.activity, boost::is_any_of("\t\n")); if (activity.empty()) { /* Work around Boost bug https://svn.boost.org/trac/boost/ticket/4751 */ activity.push_back(""); } std::string recent = chat.jid.toString() + "\t" + (eagleMode_ ? "" : activity[0]) + "\t" + (chat.isMUC ? "true" : "false") + "\t" + chat.nick; recents += recent + "\n"; if (i++ > 25) { break; } } profileSettings_->storeString(RECENT_CHATS, recents); } void ChatsManager::handleClearRecentsRequested() { recentChats_.clear(); saveRecents(); handleUnreadCountChanged(NULL); } void ChatsManager::handleJIDAddedToRoster(const JID &jid) { updatePresenceReceivingStateOnChatController(jid); } void ChatsManager::handleJIDRemovedFromRoster(const JID &jid) { updatePresenceReceivingStateOnChatController(jid); } void ChatsManager::handleJIDUpdatedInRoster(const JID &jid) { updatePresenceReceivingStateOnChatController(jid); } void ChatsManager::handleRosterCleared() { /* Setting that all chat controllers aren't receiving presence anymore; including MUC 1-to-1 chats due to the assumtion that this handler is only called on log out. */ foreach(JIDChatControllerPair pair, chatControllers_) { pair.second->setContactIsReceivingPresence(false); } } void ChatsManager::updatePresenceReceivingStateOnChatController(const JID &jid) { ChatController* controller = getChatControllerIfExists(jid); if (controller) { if (!mucRegistry_->isMUC(jid.toBare())) { RosterItemPayload::Subscription subscription = roster_->getSubscriptionStateForJID(jid); controller->setContactIsReceivingPresence(subscription == RosterItemPayload::From || subscription == RosterItemPayload::Both); } else { controller->setContactIsReceivingPresence(true); } } } void ChatsManager::loadRecents() { std::string recentsString(profileSettings_->getStringSetting(RECENT_CHATS)); std::vector recents; boost::split(recents, recentsString, boost::is_any_of("\n")); int i = 0; foreach (std::string recentString, recents) { if (i++ > 30) { break; } std::vector recent; boost::split(recent, recentString, boost::is_any_of("\t")); if (recent.size() < 4) { continue; } JID jid(recent[0]); if (!jid.isValid()) { continue; } std::string activity(recent[1]); bool isMUC = recent[2] == "true"; std::string nick(recent[3]); StatusShow::Type type = StatusShow::None; boost::filesystem::path path; if (isMUC) { if (mucControllers_.find(jid.toBare()) != mucControllers_.end()) { type = StatusShow::Online; } } else { if (avatarManager_) { path = avatarManager_->getAvatarPath(jid); } Presence::ref presence = presenceOracle_->getHighestPriorityPresence(jid.toBare()); type = presence ? presence->getShow() : StatusShow::None; } ChatListWindow::Chat chat(jid, nickResolver_->jidToNick(jid), activity, 0, type, path, isMUC, nick); prependRecent(chat); } handleUnreadCountChanged(NULL); } void ChatsManager::setupBookmarks() { if (!mucBookmarkManager_) { mucBookmarkManager_ = new MUCBookmarkManager(iqRouter_); mucBookmarkManager_->onBookmarksReady.connect(boost::bind(&ChatsManager::handleBookmarksReady, this)); mucBookmarkManager_->onBookmarkAdded.connect(boost::bind(&ChatsManager::handleMUCBookmarkAdded, this, _1)); mucBookmarkManager_->onBookmarkRemoved.connect(boost::bind(&ChatsManager::handleMUCBookmarkRemoved, this, _1)); if (chatListWindow_) { chatListWindow_->setBookmarksEnabled(false); chatListWindow_->clearBookmarks(); } } } void ChatsManager::handleBookmarksReady() { if (chatListWindow_) { chatListWindow_->setBookmarksEnabled(true); } } void ChatsManager::handleMUCBookmarkAdded(const MUCBookmark& bookmark) { std::map::iterator it = mucControllers_.find(bookmark.getRoom()); if (it == mucControllers_.end() && bookmark.getAutojoin()) { handleJoinMUCRequest(bookmark.getRoom(), bookmark.getPassword(), bookmark.getNick(), false, false); } chatListWindow_->addMUCBookmark(bookmark); } void ChatsManager::handleMUCBookmarkRemoved(const MUCBookmark& bookmark) { chatListWindow_->removeMUCBookmark(bookmark); } ChatListWindow::Chat ChatsManager::createChatListChatItem(const JID& jid, const std::string& activity) { int unreadCount = 0; if (mucRegistry_->isMUC(jid)) { MUCController* controller = mucControllers_[jid.toBare()]; StatusShow::Type type = StatusShow::None; std::string nick = ""; if (controller) { unreadCount = controller->getUnreadCount(); if (controller->isJoined()) { type = StatusShow::Online; } nick = controller->getNick(); } return ChatListWindow::Chat(jid, jid.toString(), activity, unreadCount, type, boost::filesystem::path(), true, nick); } else { ChatController* controller = getChatControllerIfExists(jid, false); if (controller) { unreadCount = controller->getUnreadCount(); } JID bareishJID = mucRegistry_->isMUC(jid.toBare()) ? jid : jid.toBare(); Presence::ref presence = presenceOracle_->getHighestPriorityPresence(bareishJID); StatusShow::Type type = presence ? presence->getShow() : StatusShow::None; boost::filesystem::path avatarPath = avatarManager_ ? avatarManager_->getAvatarPath(bareishJID) : boost::filesystem::path(); return ChatListWindow::Chat(bareishJID, nickResolver_->jidToNick(bareishJID), activity, unreadCount, type, avatarPath, false); } } void ChatsManager::handleChatActivity(const JID& jid, const std::string& activity, bool isMUC) { if (mucRegistry_->isMUC(jid.toBare()) && !isMUC) { /* Don't include PMs in MUC rooms.*/ return; } ChatListWindow::Chat chat = createChatListChatItem(jid, activity); /* FIXME: handle nick changes */ appendRecent(chat); handleUnreadCountChanged(NULL); saveRecents(); } void ChatsManager::handleUnreadCountChanged(ChatControllerBase* controller) { int unreadTotal = 0; bool controllerIsMUC = dynamic_cast(controller); bool isPM = controller && !controllerIsMUC && mucRegistry_->isMUC(controller->getToJID().toBare()); foreach (ChatListWindow::Chat& chatItem, recentChats_) { bool match = false; if (controller) { /* Matching MUC item */ match |= chatItem.isMUC == controllerIsMUC && chatItem.jid.toBare() == controller->getToJID().toBare(); /* Matching PM */ match |= isPM && chatItem.jid == controller->getToJID(); /* Matching non-PM */ match |= !isPM && !controllerIsMUC && chatItem.jid.toBare() == controller->getToJID().toBare(); } if (match) { chatItem.setUnreadCount(controller->getUnreadCount()); } unreadTotal += chatItem.unreadCount; } chatListWindow_->setRecents(recentChats_); chatListWindow_->setUnreadCount(unreadTotal); } void ChatsManager::appendRecent(const ChatListWindow::Chat& chat) { recentChats_.erase(std::remove(recentChats_.begin(), recentChats_.end(), chat), recentChats_.end()); recentChats_.push_front(chat); } void ChatsManager::prependRecent(const ChatListWindow::Chat& chat) { recentChats_.erase(std::remove(recentChats_.begin(), recentChats_.end(), chat), recentChats_.end()); recentChats_.push_back(chat); } void ChatsManager::handleUserLeftMUC(MUCController* mucController) { std::map::iterator it; for (it = mucControllers_.begin(); it != mucControllers_.end(); ++it) { if ((*it).second == mucController) { foreach (ChatListWindow::Chat& chat, recentChats_) { if (chat.isMUC && chat.jid == (*it).first) { chat.statusType = StatusShow::None; chatListWindow_->setRecents(recentChats_); break; } } mucControllers_.erase(it); delete mucController; return; } } } void ChatsManager::handleSettingChanged(const std::string& settingPath) { if (settingPath == SettingConstants::REQUEST_DELIVERYRECEIPTS.getKey()) { userWantsReceipts_ = settings_->getSetting(SettingConstants::REQUEST_DELIVERYRECEIPTS); return; } } void ChatsManager::handleUIEvent(boost::shared_ptr event) { boost::shared_ptr chatEvent = boost::dynamic_pointer_cast(event); if (chatEvent) { handleChatRequest(chatEvent->getContact()); return; } boost::shared_ptr removeMUCBookmarkEvent = boost::dynamic_pointer_cast(event); if (removeMUCBookmarkEvent) { mucBookmarkManager_->removeBookmark(removeMUCBookmarkEvent->getBookmark()); return; } boost::shared_ptr addMUCBookmarkEvent = boost::dynamic_pointer_cast(event); if (addMUCBookmarkEvent) { mucBookmarkManager_->addBookmark(addMUCBookmarkEvent->getBookmark()); return; } boost::shared_ptr editMUCBookmarkEvent = boost::dynamic_pointer_cast(event); if (editMUCBookmarkEvent) { mucBookmarkManager_->replaceBookmark(editMUCBookmarkEvent->getOldBookmark(), editMUCBookmarkEvent->getNewBookmark()); } else if (JoinMUCUIEvent::ref joinEvent = boost::dynamic_pointer_cast(event)) { handleJoinMUCRequest(joinEvent->getJID(), joinEvent->getPassword(), joinEvent->getNick(), joinEvent->getShouldJoinAutomatically(), joinEvent->getCreateAsReservedRoomIfNew()); mucControllers_[joinEvent->getJID()]->activateChatWindow(); } else if (boost::shared_ptr joinEvent = boost::dynamic_pointer_cast(event)) { if (!joinMUCWindow_) { joinMUCWindow_ = joinMUCWindowFactory_->createJoinMUCWindow(uiEventStream_); joinMUCWindow_->onSearchMUC.connect(boost::bind(&ChatsManager::handleSearchMUCRequest, this)); } joinMUCWindow_->setMUC(joinEvent->getRoom()); joinMUCWindow_->setNick(nickResolver_->jidToNick(jid_)); joinMUCWindow_->show(); } } void ChatsManager::markAllRecentsOffline() { foreach (ChatListWindow::Chat& chat, recentChats_) { chat.setStatusType(StatusShow::None); } chatListWindow_->setRecents(recentChats_); } /** * If a resource goes offline, release bound chatdialog to that resource. */ void ChatsManager::handlePresenceChange(boost::shared_ptr newPresence) { if (mucRegistry_->isMUC(newPresence->getFrom().toBare())) return; foreach (ChatListWindow::Chat& chat, recentChats_) { if (newPresence->getFrom().toBare() == chat.jid.toBare() && !chat.isMUC) { Presence::ref presence = presenceOracle_->getHighestPriorityPresence(chat.jid.toBare()); chat.setStatusType(presence ? presence->getShow() : StatusShow::None); chatListWindow_->setRecents(recentChats_); break; } } //if (newPresence->getType() != Presence::Unavailable) return; JID fullJID(newPresence->getFrom()); std::map::iterator it = chatControllers_.find(fullJID); if (it == chatControllers_.end()) return; JID bareJID(fullJID.toBare()); //It doesn't make sense to have two unbound dialogs. if (chatControllers_.find(bareJID) != chatControllers_.end()) return; rebindControllerJID(fullJID, bareJID); } void ChatsManager::setAvatarManager(AvatarManager* avatarManager) { if (avatarManager_) { avatarManager_->onAvatarChanged.disconnect(boost::bind(&ChatsManager::handleAvatarChanged, this, _1)); } avatarManager_ = avatarManager; foreach (ChatListWindow::Chat& chat, recentChats_) { if (!chat.isMUC) { chat.setAvatarPath(avatarManager_->getAvatarPath(chat.jid)); } } avatarManager_->onAvatarChanged.connect(boost::bind(&ChatsManager::handleAvatarChanged, this, _1)); } void ChatsManager::handleAvatarChanged(const JID& jid) { foreach (ChatListWindow::Chat& chat, recentChats_) { if (!chat.isMUC && jid.toBare() == chat.jid.toBare()) { chat.setAvatarPath(avatarManager_->getAvatarPath(jid)); break; } } } void ChatsManager::setServerDiscoInfo(boost::shared_ptr info) { serverDiscoInfo_ = info; foreach (JIDChatControllerPair pair, chatControllers_) { pair.second->setAvailableServerFeatures(info); } foreach (JIDMUCControllerPair pair, mucControllers_) { pair.second->setAvailableServerFeatures(info); } } /** * This is to be called on connect/disconnect. */ void ChatsManager::setOnline(bool enabled) { foreach (JIDChatControllerPair controllerPair, chatControllers_) { controllerPair.second->setOnline(enabled); } foreach (JIDMUCControllerPair controllerPair, mucControllers_) { controllerPair.second->setOnline(enabled); if (enabled) { controllerPair.second->rejoin(); } } if (!enabled) { delete mucBookmarkManager_; mucBookmarkManager_ = NULL; chatListWindow_->setBookmarksEnabled(false); markAllRecentsOffline(); } else { setupBookmarks(); } } void ChatsManager::handleChatRequest(const std::string &contact) { ChatController* controller = getChatControllerOrFindAnother(JID(contact)); controller->activateChatWindow(); } ChatController* ChatsManager::getChatControllerOrFindAnother(const JID &contact) { ChatController* controller = getChatControllerIfExists(contact); if (!controller && !mucRegistry_->isMUC(contact.toBare())) { foreach (JIDChatControllerPair pair, chatControllers_) { if (pair.first.toBare() == contact.toBare()) { controller = pair.second; break; } } } return controller ? controller : createNewChatController(contact); } ChatController* ChatsManager::createNewChatController(const JID& contact) { assert(chatControllers_.find(contact) == chatControllers_.end()); ChatController* controller = new ChatController(jid_, stanzaChannel_, iqRouter_, chatWindowFactory_, contact, nickResolver_, presenceOracle_, avatarManager_, mucRegistry_->isMUC(contact.toBare()), useDelayForLatency_, uiEventStream_, eventController_, timerFactory_, entityCapsProvider_, userWantsReceipts_, settings_, historyController_, mucRegistry_); chatControllers_[contact] = controller; controller->setAvailableServerFeatures(serverDiscoInfo_); controller->onActivity.connect(boost::bind(&ChatsManager::handleChatActivity, this, contact, _1, false)); controller->onUnreadCountChanged.connect(boost::bind(&ChatsManager::handleUnreadCountChanged, this, controller)); updatePresenceReceivingStateOnChatController(contact); return controller; } ChatController* ChatsManager::getChatControllerOrCreate(const JID &contact) { ChatController* controller = getChatControllerIfExists(contact); return controller ? controller : createNewChatController(contact); } ChatController* ChatsManager::getChatControllerIfExists(const JID &contact, bool rebindIfNeeded) { if (chatControllers_.find(contact) == chatControllers_.end()) { if (mucRegistry_->isMUC(contact.toBare())) { return NULL; } //Need to look for an unbound window to bind first JID bare(contact.toBare()); if (chatControllers_.find(bare) != chatControllers_.end()) { rebindControllerJID(bare, contact); } else { foreach (JIDChatControllerPair pair, chatControllers_) { if (pair.first.toBare() == contact.toBare()) { if (rebindIfNeeded) { rebindControllerJID(pair.first, contact); return chatControllers_[contact]; } else { return pair.second; } } } return NULL; } } return chatControllers_[contact]; } void ChatsManager::rebindControllerJID(const JID& from, const JID& to) { chatControllers_[to] = chatControllers_[from]; chatControllers_.erase(from); chatControllers_[to]->setToJID(to); } void ChatsManager::handleJoinMUCRequest(const JID &mucJID, const boost::optional& password, const boost::optional& nickMaybe, bool addAutoJoin, bool createAsReservedIfNew) { if (!stanzaChannel_->isAvailable()) { /* This is potentially not the optimal solution, but it will avoid consistency issues.*/ return; } if (addAutoJoin) { MUCBookmark bookmark(mucJID, mucJID.getNode()); bookmark.setAutojoin(true); if (nickMaybe) { bookmark.setNick(*nickMaybe); } if (password) { bookmark.setPassword(*password); } mucBookmarkManager_->addBookmark(bookmark); } std::map::iterator it = mucControllers_.find(mucJID); if (it != mucControllers_.end()) { it->second->rejoin(); } else { std::string nick = (nickMaybe && !(*nickMaybe).empty()) ? nickMaybe.get() : nickResolver_->jidToNick(jid_); MUC::ref muc = mucManager->createMUC(mucJID); if (createAsReservedIfNew) { muc->setCreateAsReservedIfNew(); } MUCController* controller = new MUCController(jid_, muc, password, nick, stanzaChannel_, iqRouter_, chatWindowFactory_, presenceOracle_, avatarManager_, uiEventStream_, false, timerFactory_, eventController_, entityCapsProvider_, roster_, historyController_, mucRegistry_); mucControllers_[mucJID] = controller; controller->setAvailableServerFeatures(serverDiscoInfo_); controller->onUserLeft.connect(boost::bind(&ChatsManager::handleUserLeftMUC, this, controller)); controller->onUserJoined.connect(boost::bind(&ChatsManager::handleChatActivity, this, mucJID.toBare(), "", true)); controller->onActivity.connect(boost::bind(&ChatsManager::handleChatActivity, this, mucJID.toBare(), _1, true)); controller->onUnreadCountChanged.connect(boost::bind(&ChatsManager::handleUnreadCountChanged, this, controller)); handleChatActivity(mucJID.toBare(), "", true); } mucControllers_[mucJID]->showChatWindow(); } void ChatsManager::handleSearchMUCRequest() { mucSearchController_->openSearchWindow(); } void ChatsManager::handleIncomingMessage(boost::shared_ptr message) { JID jid = message->getFrom(); boost::shared_ptr event(new MessageEvent(message)); bool isInvite = message->getPayload(); bool isMediatedInvite = (message->getPayload() && message->getPayload()->getInvite()); if (isMediatedInvite) { jid = (*message->getPayload()->getInvite()).from; } if (!event->isReadable() && !message->getPayload() && !message->getPayload() && !message->getPayload() && !isInvite && !isMediatedInvite && !message->hasSubject()) { return; } // Try to deliver it to a MUC if (message->getType() == Message::Groupchat || message->getType() == Message::Error /*|| (isInvite && message->getType() == Message::Normal)*/) { std::map::iterator i = mucControllers_.find(jid.toBare()); if (i != mucControllers_.end()) { i->second->handleIncomingMessage(event); return; } else if (message->getType() == Message::Groupchat) { //FIXME: Error handling - groupchat messages from an unknown muc. return; } } //if not a mucroom if (!event->isReadable() && !isInvite && !isMediatedInvite) { /* Only route such messages if a window exists, don't open new windows for them.*/ ChatController* controller = getChatControllerIfExists(jid); if (controller) { controller->handleIncomingMessage(event); } } else { getChatControllerOrCreate(jid)->handleIncomingMessage(event); } } void ChatsManager::handleMUCSelectedAfterSearch(const JID& muc) { if (joinMUCWindow_) { joinMUCWindow_->setMUC(muc.toString()); } } void ChatsManager::handleMUCBookmarkActivated(const MUCBookmark& mucBookmark) { uiEventStream_->send(boost::make_shared(mucBookmark.getRoom(), mucBookmark.getPassword(), mucBookmark.getNick())); } void ChatsManager::handleNewFileTransferController(FileTransferController* ftc) { ChatController* chatController = getChatControllerOrCreate(ftc->getOtherParty()); chatController->handleNewFileTransferController(ftc); chatController->activateChatWindow(); } void ChatsManager::handleWhiteboardSessionRequest(const JID& contact, bool senderIsSelf) { ChatController* chatController = getChatControllerOrCreate(contact); chatController->handleWhiteboardSessionRequest(senderIsSelf); chatController->activateChatWindow(); } void ChatsManager::handleWhiteboardStateChange(const JID& contact, const ChatWindow::WhiteboardSessionState state) { ChatController* chatController = getChatControllerOrCreate(contact); chatController->handleWhiteboardStateChange(state); chatController->activateChatWindow(); if (state == ChatWindow::WhiteboardAccepted) { boost::filesystem::path path; JID bareJID = contact.toBare(); if (avatarManager_) { path = avatarManager_->getAvatarPath(bareJID); } ChatListWindow::Chat chat(bareJID, nickResolver_->jidToNick(bareJID), "", 0, StatusShow::None, path, false); chatListWindow_->addWhiteboardSession(chat); } else { chatListWindow_->removeWhiteboardSession(contact.toBare()); } } void ChatsManager::handleRecentActivated(const ChatListWindow::Chat& chat) { if (chat.isMUC) { /* FIXME: This means that recents requiring passwords will just flat-out not work */ uiEventStream_->send(boost::make_shared(chat.jid, boost::optional(), chat.nick)); } else { uiEventStream_->send(boost::make_shared(chat.jid)); } } } swift-im-2.0+dev6/Swift/Controllers/SettingConstants.h0000644000175000017500000000201612227051773022746 0ustar kismithkismith/* * Copyright (c) 2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include namespace Swift { class SettingConstants { public: static const SettingsProvider::Setting IDLE_GOES_OFFLINE; static const SettingsProvider::Setting IDLE_TIMEOUT; static const SettingsProvider::Setting SHOW_NOTIFICATIONS; static const SettingsProvider::Setting REQUEST_DELIVERYRECEIPTS; static const SettingsProvider::Setting FORGET_PASSWORDS; static const SettingsProvider::Setting REMEMBER_RECENT_CHATS; static const SettingsProvider::Setting LAST_LOGIN_JID; static const SettingsProvider::Setting LOGIN_AUTOMATICALLY; static const SettingsProvider::Setting SHOW_OFFLINE; static const SettingsProvider::Setting EXPANDED_ROSTER_GROUPS; static const SettingsProvider::Setting PLAY_SOUNDS; }; } swift-im-2.0+dev6/Swift/Controllers/MainController.cpp0000644000175000017500000010730712227051773022730 0ustar kismithkismith/* * Copyright (c) 2010-2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "Swiften/Network/TimerFactory.h" #include "Swift/Controllers/BuildVersion.h" #include "Swiften/Client/Storages.h" #include "Swiften/VCards/VCardManager.h" #include "Swift/Controllers/Chat/UserSearchController.h" #include "Swift/Controllers/Chat/ChatsManager.h" #include "Swift/Controllers/XMPPEvents/EventController.h" #include "Swift/Controllers/EventWindowController.h" #include "Swift/Controllers/UIInterfaces/LoginWindow.h" #include "Swift/Controllers/UIInterfaces/LoginWindowFactory.h" #include "Swift/Controllers/UIInterfaces/MainWindow.h" #include "Swift/Controllers/Chat/MUCController.h" #include "Swiften/Client/NickResolver.h" #include "Swift/Controllers/Roster/RosterController.h" #include "Swift/Controllers/SoundEventController.h" #include "Swift/Controllers/SoundPlayer.h" #include "Swift/Controllers/StatusTracker.h" #include "Swift/Controllers/SystemTray.h" #include "Swift/Controllers/SystemTrayController.h" #include "Swift/Controllers/XMLConsoleController.h" #include #include #include "Swift/Controllers/FileTransferListController.h" #include "Swift/Controllers/UIEvents/UIEventStream.h" #include "Swift/Controllers/PresenceNotifier.h" #include "Swift/Controllers/EventNotifier.h" #include "Swift/Controllers/Storages/StoragesFactory.h" #include "Swift/Controllers/WhiteboardManager.h" #include "SwifTools/Dock/Dock.h" #include "SwifTools/Notifier/TogglableNotifier.h" #include "Swiften/Base/foreach.h" #include "Swiften/Client/Client.h" #include "Swiften/Presence/PresenceSender.h" #include "Swiften/Elements/ChatState.h" #include "Swiften/Elements/Presence.h" #include "Swiften/Elements/VCardUpdate.h" #include "Swift/Controllers/Settings/SettingsProvider.h" #include "Swiften/Elements/DiscoInfo.h" #include "Swiften/Disco/CapsInfoGenerator.h" #include "Swiften/Disco/GetDiscoInfoRequest.h" #include "Swiften/Disco/ClientDiscoManager.h" #include "Swiften/VCards/GetVCardRequest.h" #include "Swiften/StringCodecs/SHA1.h" #include "Swiften/StringCodecs/Hexify.h" #include "Swift/Controllers/UIEvents/RequestChatUIEvent.h" #include "Swift/Controllers/UIEvents/JoinMUCUIEvent.h" #include "Swift/Controllers/Storages/CertificateStorageFactory.h" #include "Swift/Controllers/Storages/CertificateStorageTrustChecker.h" #include "Swiften/Network/NetworkFactories.h" #include #include #include #include "Swift/Controllers/AdHocManager.h" #include #include #include #include #include #include namespace Swift { static const std::string CLIENT_NAME = "Swift"; static const std::string CLIENT_NODE = "http://swift.im"; MainController::MainController( EventLoop* eventLoop, NetworkFactories* networkFactories, UIFactory* uiFactories, SettingsProvider* settings, SystemTray* systemTray, SoundPlayer* soundPlayer, StoragesFactory* storagesFactory, CertificateStorageFactory* certificateStorageFactory, Dock* dock, Notifier* notifier, URIHandler* uriHandler, IdleDetector* idleDetector, bool useDelayForLatency) : eventLoop_(eventLoop), networkFactories_(networkFactories), uiFactory_(uiFactories), storagesFactory_(storagesFactory), certificateStorageFactory_(certificateStorageFactory), settings_(settings), uriHandler_(uriHandler), idleDetector_(idleDetector), loginWindow_(NULL) , useDelayForLatency_(useDelayForLatency), ftOverview_(NULL) { storages_ = NULL; certificateStorage_ = NULL; statusTracker_ = NULL; presenceNotifier_ = NULL; eventNotifier_ = NULL; rosterController_ = NULL; chatsManager_ = NULL; historyController_ = NULL; historyViewController_ = NULL; eventWindowController_ = NULL; profileController_ = NULL; contactEditController_ = NULL; userSearchControllerChat_ = NULL; userSearchControllerAdd_ = NULL; whiteboardManager_ = NULL; adHocManager_ = NULL; quitRequested_ = false; clientInitialized_ = false; offlineRequested_ = false; timeBeforeNextReconnect_ = -1; dock_ = dock; uiEventStream_ = new UIEventStream(); notifier_ = new TogglableNotifier(notifier); notifier_->setPersistentEnabled(settings_->getSetting(SettingConstants::SHOW_NOTIFICATIONS)); eventController_ = new EventController(); eventController_->onEventQueueLengthChange.connect(boost::bind(&MainController::handleEventQueueLengthChange, this, _1)); systemTrayController_ = new SystemTrayController(eventController_, systemTray); loginWindow_ = uiFactory_->createLoginWindow(uiEventStream_); loginWindow_->setShowNotificationToggle(!notifier->isExternallyConfigured()); soundEventController_ = new SoundEventController(eventController_, soundPlayer, settings); xmppURIController_ = new XMPPURIController(uriHandler_, uiEventStream_); std::string selectedLoginJID = settings_->getSetting(SettingConstants::LAST_LOGIN_JID); bool loginAutomatically = settings_->getSetting(SettingConstants::LOGIN_AUTOMATICALLY); std::string cachedPassword; std::string cachedCertificate; ClientOptions cachedOptions; bool eagle = settings_->getSetting(SettingConstants::FORGET_PASSWORDS); if (!eagle) { foreach (std::string profile, settings->getAvailableProfiles()) { ProfileSettingsProvider profileSettings(profile, settings); std::string password = profileSettings.getStringSetting("pass"); std::string certificate = profileSettings.getStringSetting("certificate"); std::string jid = profileSettings.getStringSetting("jid"); ClientOptions clientOptions = parseClientOptions(profileSettings.getStringSetting("options")); loginWindow_->addAvailableAccount(jid, password, certificate, clientOptions); if (jid == selectedLoginJID) { cachedPassword = password; cachedCertificate = certificate; cachedOptions = clientOptions; } } loginWindow_->selectUser(selectedLoginJID); loginWindow_->setLoginAutomatically(loginAutomatically); } loginWindow_->onLoginRequest.connect(boost::bind(&MainController::handleLoginRequest, this, _1, _2, _3, _4, _5, _6, _7)); loginWindow_->onPurgeSavedLoginRequest.connect(boost::bind(&MainController::handlePurgeSavedLoginRequest, this, _1)); loginWindow_->onCancelLoginRequest.connect(boost::bind(&MainController::handleCancelLoginRequest, this)); loginWindow_->onQuitRequest.connect(boost::bind(&MainController::handleQuitRequest, this)); idleDetector_->setIdleTimeSeconds(settings->getSetting(SettingConstants::IDLE_TIMEOUT)); idleDetector_->onIdleChanged.connect(boost::bind(&MainController::handleInputIdleChanged, this, _1)); xmlConsoleController_ = new XMLConsoleController(uiEventStream_, uiFactory_); fileTransferListController_ = new FileTransferListController(uiEventStream_, uiFactory_); settings_->onSettingChanged.connect(boost::bind(&MainController::handleSettingChanged, this, _1)); if (loginAutomatically) { profileSettings_ = new ProfileSettingsProvider(selectedLoginJID, settings_); /* FIXME: deal with autologin with a cert*/ handleLoginRequest(selectedLoginJID, cachedPassword, cachedCertificate, CertificateWithKey::ref(), cachedOptions, true, true); } else { profileSettings_ = NULL; } } MainController::~MainController() { idleDetector_->onIdleChanged.disconnect(boost::bind(&MainController::handleInputIdleChanged, this, _1)); purgeCachedCredentials(); //setManagersOffline(); eventController_->disconnectAll(); resetClient(); delete fileTransferListController_; delete xmlConsoleController_; delete xmppURIController_; delete soundEventController_; delete systemTrayController_; delete eventController_; delete notifier_; delete uiEventStream_; } void MainController::purgeCachedCredentials() { safeClear(password_); } void MainController::resetClient() { purgeCachedCredentials(); resetCurrentError(); resetPendingReconnects(); vCardPhotoHash_.clear(); delete contactEditController_; contactEditController_ = NULL; delete profileController_; profileController_ = NULL; delete eventWindowController_; eventWindowController_ = NULL; delete chatsManager_; chatsManager_ = NULL; #ifdef SWIFT_EXPERIMENTAL_HISTORY delete historyViewController_; historyViewController_ = NULL; delete historyController_; historyController_ = NULL; #endif delete ftOverview_; ftOverview_ = NULL; delete rosterController_; rosterController_ = NULL; delete eventNotifier_; eventNotifier_ = NULL; delete presenceNotifier_; presenceNotifier_ = NULL; delete certificateStorage_; certificateStorage_ = NULL; delete storages_; storages_ = NULL; delete statusTracker_; statusTracker_ = NULL; delete profileSettings_; profileSettings_ = NULL; delete userSearchControllerChat_; userSearchControllerChat_ = NULL; delete userSearchControllerAdd_; userSearchControllerAdd_ = NULL; delete adHocManager_; adHocManager_ = NULL; delete whiteboardManager_; whiteboardManager_ = NULL; clientInitialized_ = false; } void MainController::handleSettingChanged(const std::string& settingPath) { if (settingPath == SettingConstants::SHOW_NOTIFICATIONS.getKey()) { notifier_->setPersistentEnabled(settings_->getSetting(SettingConstants::SHOW_NOTIFICATIONS)); } } void MainController::resetPendingReconnects() { timeBeforeNextReconnect_ = -1; if (reconnectTimer_) { reconnectTimer_->stop(); reconnectTimer_.reset(); } resetCurrentError(); } void MainController::resetCurrentError() { if (lastDisconnectError_) { lastDisconnectError_->conclude(); lastDisconnectError_ = boost::shared_ptr(); } } void MainController::handleConnected() { boundJID_ = client_->getJID(); resetCurrentError(); resetPendingReconnects(); if (settings_->getSetting(SettingConstants::FORGET_PASSWORDS)) { purgeCachedCredentials(); } bool freshLogin = rosterController_ == NULL; myStatusLooksOnline_ = true; if (freshLogin) { profileController_ = new ProfileController(client_->getVCardManager(), uiFactory_, uiEventStream_); srand(time(NULL)); int randomPort = 10000 + rand() % 10000; client_->getFileTransferManager()->startListeningOnPort(randomPort); ftOverview_ = new FileTransferOverview(client_->getFileTransferManager()); fileTransferListController_->setFileTransferOverview(ftOverview_); rosterController_ = new RosterController(jid_, client_->getRoster(), client_->getAvatarManager(), uiFactory_, client_->getNickManager(), client_->getNickResolver(), client_->getPresenceOracle(), client_->getSubscriptionManager(), eventController_, uiEventStream_, client_->getIQRouter(), settings_, client_->getEntityCapsProvider(), ftOverview_); rosterController_->onChangeStatusRequest.connect(boost::bind(&MainController::handleChangeStatusRequest, this, _1, _2)); rosterController_->onSignOutRequest.connect(boost::bind(&MainController::signOut, this)); rosterController_->getWindow()->onShowCertificateRequest.connect(boost::bind(&MainController::handleShowCertificateRequest, this)); contactEditController_ = new ContactEditController(rosterController_, client_->getVCardManager(), uiFactory_, uiEventStream_); whiteboardManager_ = new WhiteboardManager(uiFactory_, uiEventStream_, client_->getNickResolver(), client_->getWhiteboardSessionManager()); /* Doing this early as an ordering fix. Various things later will * want to have the user's nick available and this means it will * be before they receive stanzas that need it (e.g. bookmarks).*/ client_->getVCardManager()->requestOwnVCard(); #ifdef SWIFT_EXPERIMENTAL_HISTORY historyController_ = new HistoryController(storages_->getHistoryStorage()); historyViewController_ = new HistoryViewController(jid_, uiEventStream_, historyController_, client_->getNickResolver(), client_->getAvatarManager(), client_->getPresenceOracle(), uiFactory_); chatsManager_ = new ChatsManager(jid_, client_->getStanzaChannel(), client_->getIQRouter(), eventController_, uiFactory_, uiFactory_, client_->getNickResolver(), client_->getPresenceOracle(), client_->getPresenceSender(), uiEventStream_, uiFactory_, useDelayForLatency_, networkFactories_->getTimerFactory(), client_->getMUCRegistry(), client_->getEntityCapsProvider(), client_->getMUCManager(), uiFactory_, profileSettings_, ftOverview_, client_->getRoster(), !settings_->getSetting(SettingConstants::REMEMBER_RECENT_CHATS), settings_, historyController_, whiteboardManager_); #else chatsManager_ = new ChatsManager(jid_, client_->getStanzaChannel(), client_->getIQRouter(), eventController_, uiFactory_, uiFactory_, client_->getNickResolver(), client_->getPresenceOracle(), client_->getPresenceSender(), uiEventStream_, uiFactory_, useDelayForLatency_, networkFactories_->getTimerFactory(), client_->getMUCRegistry(), client_->getEntityCapsProvider(), client_->getMUCManager(), uiFactory_, profileSettings_, ftOverview_, client_->getRoster(), !settings_->getSetting(SettingConstants::REMEMBER_RECENT_CHATS), settings_, NULL, whiteboardManager_); #endif client_->onMessageReceived.connect(boost::bind(&ChatsManager::handleIncomingMessage, chatsManager_, _1)); chatsManager_->setAvatarManager(client_->getAvatarManager()); eventWindowController_ = new EventWindowController(eventController_, uiFactory_); loginWindow_->morphInto(rosterController_->getWindow()); DiscoInfo discoInfo; discoInfo.addIdentity(DiscoInfo::Identity(CLIENT_NAME, "client", "pc")); discoInfo.addFeature(DiscoInfo::ChatStatesFeature); discoInfo.addFeature(DiscoInfo::SecurityLabelsFeature); discoInfo.addFeature(DiscoInfo::MessageCorrectionFeature); #ifdef SWIFT_EXPERIMENTAL_FT discoInfo.addFeature(DiscoInfo::JingleFeature); discoInfo.addFeature(DiscoInfo::JingleFTFeature); discoInfo.addFeature(DiscoInfo::JingleTransportsIBBFeature); discoInfo.addFeature(DiscoInfo::JingleTransportsS5BFeature); #endif #ifdef SWIFT_EXPERIMENTAL_WB discoInfo.addFeature(DiscoInfo::WhiteboardFeature); #endif discoInfo.addFeature(DiscoInfo::MessageDeliveryReceiptsFeature); client_->getDiscoManager()->setCapsNode(CLIENT_NODE); client_->getDiscoManager()->setDiscoInfo(discoInfo); userSearchControllerChat_ = new UserSearchController(UserSearchController::StartChat, jid_, uiEventStream_, client_->getVCardManager(), uiFactory_, client_->getIQRouter(), rosterController_); userSearchControllerAdd_ = new UserSearchController(UserSearchController::AddContact, jid_, uiEventStream_, client_->getVCardManager(), uiFactory_, client_->getIQRouter(), rosterController_); adHocManager_ = new AdHocManager(JID(boundJID_.getDomain()), uiFactory_, client_->getIQRouter(), uiEventStream_, rosterController_->getWindow()); } loginWindow_->setIsLoggingIn(false); client_->requestRoster(); GetDiscoInfoRequest::ref discoInfoRequest = GetDiscoInfoRequest::create(JID(boundJID_.getDomain()), client_->getIQRouter()); discoInfoRequest->onResponse.connect(boost::bind(&MainController::handleServerDiscoInfoResponse, this, _1, _2)); discoInfoRequest->send(); client_->getVCardManager()->requestOwnVCard(); rosterController_->setEnabled(true); rosterController_->getWindow()->setStreamEncryptionStatus(client_->isStreamEncrypted()); profileController_->setAvailable(true); contactEditController_->setAvailable(true); /* Send presence later to catch all the incoming presences. */ sendPresence(statusTracker_->getNextPresence()); /* Enable chats last of all, so rejoining MUCs has the right sent presence */ chatsManager_->setOnline(true); } void MainController::handleEventQueueLengthChange(int count) { dock_->setNumberOfPendingMessages(count); } void MainController::reconnectAfterError() { if (reconnectTimer_) { reconnectTimer_->stop(); } performLoginFromCachedCredentials(); } void MainController::handleChangeStatusRequest(StatusShow::Type show, const std::string &statusText) { boost::shared_ptr presence(new Presence()); if (show == StatusShow::None) { // Note: this is misleading, None doesn't mean unavailable on the wire. presence->setType(Presence::Unavailable); resetPendingReconnects(); myStatusLooksOnline_ = false; offlineRequested_ = true; } else { offlineRequested_ = false; presence->setShow(show); } presence->setStatus(statusText); statusTracker_->setRequestedPresence(presence); if (presence->getType() != Presence::Unavailable) { profileSettings_->storeInt("lastShow", presence->getShow()); profileSettings_->storeString("lastStatus", presence->getStatus()); } if (presence->getType() != Presence::Unavailable && !client_->isAvailable()) { performLoginFromCachedCredentials(); } else { sendPresence(presence); } } void MainController::sendPresence(boost::shared_ptr presence) { rosterController_->getWindow()->setMyStatusType(presence->getShow()); rosterController_->getWindow()->setMyStatusText(presence->getStatus()); systemTrayController_->setMyStatusType(presence->getShow()); notifier_->setTemporarilyDisabled(presence->getShow() == StatusShow::DND); // Add information and send if (!vCardPhotoHash_.empty()) { presence->updatePayload(boost::make_shared(vCardPhotoHash_)); } client_->getPresenceSender()->sendPresence(presence); if (presence->getType() == Presence::Unavailable) { logout(); } } void MainController::handleInputIdleChanged(bool idle) { if (!statusTracker_) { //Haven't logged in yet. return; } if (settings_->getSetting(SettingConstants::IDLE_GOES_OFFLINE)) { if (idle) { logout(); } } else { if (idle) { if (statusTracker_->goAutoAway()) { if (client_ && client_->isAvailable()) { sendPresence(statusTracker_->getNextPresence()); } } } else { if (statusTracker_->goAutoUnAway()) { if (client_ && client_->isAvailable()) { sendPresence(statusTracker_->getNextPresence()); } } } } } void MainController::handleShowCertificateRequest() { std::vector chain = client_->getStanzaChannel()->getPeerCertificateChain(); rosterController_->getWindow()->openCertificateDialog(chain); } void MainController::handleLoginRequest(const std::string &username, const std::string &password, const std::string& certificatePath, CertificateWithKey::ref certificate, const ClientOptions& options, bool remember, bool loginAutomatically) { jid_ = JID(username); if (!jid_.isValid() || jid_.getNode().empty()) { loginWindow_->setMessage(QT_TRANSLATE_NOOP("", "User address invalid. User address should be of the form 'alice@wonderland.lit'")); loginWindow_->setIsLoggingIn(false); } else { loginWindow_->setMessage(""); loginWindow_->setIsLoggingIn(true); profileSettings_ = new ProfileSettingsProvider(username, settings_); if (!settings_->getSetting(SettingConstants::FORGET_PASSWORDS)) { profileSettings_->storeString("jid", username); profileSettings_->storeString("certificate", certificatePath); profileSettings_->storeString("pass", (remember || loginAutomatically) ? password : ""); std::string optionString = serializeClientOptions(options); profileSettings_->storeString("options", optionString); settings_->storeSetting(SettingConstants::LAST_LOGIN_JID, username); settings_->storeSetting(SettingConstants::LOGIN_AUTOMATICALLY, loginAutomatically); loginWindow_->addAvailableAccount(profileSettings_->getStringSetting("jid"), profileSettings_->getStringSetting("pass"), profileSettings_->getStringSetting("certificate"), options); } password_ = password; certificate_ = certificate; clientOptions_ = options; performLoginFromCachedCredentials(); } } void MainController::handlePurgeSavedLoginRequest(const std::string& username) { settings_->removeProfile(username); loginWindow_->removeAvailableAccount(username); } void MainController::performLoginFromCachedCredentials() { if (settings_->getSetting(SettingConstants::FORGET_PASSWORDS) && password_.empty()) { /* Then we can't try to login again. */ return; } /* If we logged in with a bare JID, and we have a full bound JID, re-login with the * bound JID to try and keep dynamically assigned resources */ JID clientJID = jid_; if (boundJID_.isValid() && jid_.isBare() && boundJID_.toBare() == jid_) { clientJID = boundJID_; } if (!statusTracker_) { statusTracker_ = new StatusTracker(); } if (!clientInitialized_) { storages_ = storagesFactory_->createStorages(jid_.toBare()); certificateStorage_ = certificateStorageFactory_->createCertificateStorage(jid_.toBare()); certificateTrustChecker_ = new CertificateStorageTrustChecker(certificateStorage_); client_ = boost::make_shared(clientJID, createSafeByteArray(password_.c_str()), networkFactories_, storages_); clientInitialized_ = true; client_->setCertificateTrustChecker(certificateTrustChecker_); client_->onDataRead.connect(boost::bind(&XMLConsoleController::handleDataRead, xmlConsoleController_, _1)); client_->onDataWritten.connect(boost::bind(&XMLConsoleController::handleDataWritten, xmlConsoleController_, _1)); client_->onDisconnected.connect(boost::bind(&MainController::handleDisconnected, this, _1)); client_->onConnected.connect(boost::bind(&MainController::handleConnected, this)); client_->setSoftwareVersion(CLIENT_NAME, buildVersion); client_->getVCardManager()->onVCardChanged.connect(boost::bind(&MainController::handleVCardReceived, this, _1, _2)); presenceNotifier_ = new PresenceNotifier(client_->getStanzaChannel(), notifier_, client_->getMUCRegistry(), client_->getAvatarManager(), client_->getNickResolver(), client_->getPresenceOracle(), networkFactories_->getTimerFactory()); presenceNotifier_->onNotificationActivated.connect(boost::bind(&MainController::handleNotificationClicked, this, _1)); eventNotifier_ = new EventNotifier(eventController_, notifier_, client_->getAvatarManager(), client_->getNickResolver()); eventNotifier_->onNotificationActivated.connect(boost::bind(&MainController::handleNotificationClicked, this, _1)); if (certificate_ && !certificate_->isNull()) { client_->setCertificate(certificate_); } boost::shared_ptr presence(new Presence()); presence->setShow(static_cast(profileSettings_->getIntSetting("lastShow", StatusShow::Online))); presence->setStatus(profileSettings_->getStringSetting("lastStatus")); statusTracker_->setRequestedPresence(presence); } else { /* In case we're in the middle of another login, make sure they don't overlap */ client_->disconnect(); } systemTrayController_->setConnecting(); if (rosterController_) { rosterController_->getWindow()->setConnecting(); } ClientOptions clientOptions = clientOptions_; bool eagle = settings_->getSetting(SettingConstants::FORGET_PASSWORDS); clientOptions.forgetPassword = eagle; clientOptions.useTLS = eagle ? ClientOptions::RequireTLS : ClientOptions::UseTLSWhenAvailable; client_->connect(clientOptions); } void MainController::handleDisconnected(const boost::optional& error) { if (rosterController_) { rosterController_->getWindow()->setStreamEncryptionStatus(false); } if (settings_->getSetting(SettingConstants::FORGET_PASSWORDS)) { purgeCachedCredentials(); } if (quitRequested_) { resetClient(); loginWindow_->quit(); } else if (error) { std::string message; std::string certificateErrorMessage; bool forceSignout = false; switch(error->getType()) { case ClientError::UnknownError: message = QT_TRANSLATE_NOOP("", "Unknown Error"); break; case ClientError::DomainNameResolveError: message = QT_TRANSLATE_NOOP("", "Unable to find server"); break; case ClientError::ConnectionError: message = QT_TRANSLATE_NOOP("", "Error connecting to server"); break; case ClientError::ConnectionReadError: message = QT_TRANSLATE_NOOP("", "Error while receiving server data"); break; case ClientError::ConnectionWriteError: message = QT_TRANSLATE_NOOP("", "Error while sending data to the server"); break; case ClientError::XMLError: message = QT_TRANSLATE_NOOP("", "Error parsing server data"); break; case ClientError::AuthenticationFailedError: message = QT_TRANSLATE_NOOP("", "Login/password invalid"); break; case ClientError::CompressionFailedError: message = QT_TRANSLATE_NOOP("", "Error while compressing stream"); break; case ClientError::ServerVerificationFailedError: message = QT_TRANSLATE_NOOP("", "Server verification failed"); break; case ClientError::NoSupportedAuthMechanismsError: message = QT_TRANSLATE_NOOP("", "Authentication mechanisms not supported"); break; case ClientError::UnexpectedElementError: message = QT_TRANSLATE_NOOP("", "Unexpected response"); break; case ClientError::ResourceBindError: message = QT_TRANSLATE_NOOP("", "Error binding resource"); break; case ClientError::SessionStartError: message = QT_TRANSLATE_NOOP("", "Error starting session"); break; case ClientError::StreamError: message = QT_TRANSLATE_NOOP("", "Stream error"); break; case ClientError::TLSError: message = QT_TRANSLATE_NOOP("", "Encryption error"); break; case ClientError::ClientCertificateLoadError: message = QT_TRANSLATE_NOOP("", "Error loading certificate (Invalid password?)"); break; case ClientError::ClientCertificateError: message = QT_TRANSLATE_NOOP("", "Certificate not authorized"); break; case ClientError::CertificateCardRemoved: message = QT_TRANSLATE_NOOP("", "Certificate card removed"); forceSignout = true; break; case ClientError::UnknownCertificateError: certificateErrorMessage = QT_TRANSLATE_NOOP("", "Unknown certificate"); break; case ClientError::CertificateExpiredError: certificateErrorMessage = QT_TRANSLATE_NOOP("", "Certificate has expired"); break; case ClientError::CertificateNotYetValidError: certificateErrorMessage = QT_TRANSLATE_NOOP("", "Certificate is not yet valid"); break; case ClientError::CertificateSelfSignedError: certificateErrorMessage = QT_TRANSLATE_NOOP("", "Certificate is self-signed"); break; case ClientError::CertificateRejectedError: certificateErrorMessage = QT_TRANSLATE_NOOP("", "Certificate has been rejected"); break; case ClientError::CertificateUntrustedError: certificateErrorMessage = QT_TRANSLATE_NOOP("", "Certificate is not trusted"); break; case ClientError::InvalidCertificatePurposeError: certificateErrorMessage = QT_TRANSLATE_NOOP("", "Certificate cannot be used for encrypting your connection"); break; case ClientError::CertificatePathLengthExceededError: certificateErrorMessage = QT_TRANSLATE_NOOP("", "Certificate path length constraint exceeded"); break; case ClientError::InvalidCertificateSignatureError: certificateErrorMessage = QT_TRANSLATE_NOOP("", "Invalid certificate signature"); break; case ClientError::InvalidCAError: certificateErrorMessage = QT_TRANSLATE_NOOP("", "Invalid Certificate Authority"); break; case ClientError::InvalidServerIdentityError: certificateErrorMessage = QT_TRANSLATE_NOOP("", "Certificate does not match the host identity"); break; case ClientError::RevokedError: certificateErrorMessage = QT_TRANSLATE_NOOP("", "Certificate has been revoked"); break; case ClientError::RevocationCheckFailedError: certificateErrorMessage = QT_TRANSLATE_NOOP("", "Unable to determine certificate revocation state"); break; } bool forceReconnectAfterCertificateTrust = false; if (!certificateErrorMessage.empty()) { std::vector certificates = certificateTrustChecker_->getLastCertificateChain(); if (!certificates.empty() && loginWindow_->askUserToTrustCertificatePermanently(certificateErrorMessage, certificates)) { certificateStorage_->addCertificate(certificates[0]); forceReconnectAfterCertificateTrust = true; } else { message = QT_TRANSLATE_NOOP("", "Certificate error"); } } if (forceReconnectAfterCertificateTrust && settings_->getSetting(SettingConstants::FORGET_PASSWORDS)) { forceReconnectAfterCertificateTrust = false; forceSignout = true; message = QT_TRANSLATE_NOOP("", "Re-enter credentials and retry"); } if (forceReconnectAfterCertificateTrust) { performLoginFromCachedCredentials(); } else if (forceSignout || !rosterController_) { //hasn't been logged in yet or permanent error signOut(); loginWindow_->setMessage(message); loginWindow_->setIsLoggingIn(false); } else { logout(); if (settings_->getSetting(SettingConstants::FORGET_PASSWORDS)) { message = str(format(QT_TRANSLATE_NOOP("", "Disconnected from %1%: %2%. To reconnect, Sign Out and provide your password again.")) % jid_.getDomain() % message); } else { if (!offlineRequested_) { setReconnectTimer(); } if (lastDisconnectError_) { message = str(format(QT_TRANSLATE_NOOP("", "Reconnect to %1% failed: %2%. Will retry in %3% seconds.")) % jid_.getDomain() % message % boost::lexical_cast(timeBeforeNextReconnect_)); lastDisconnectError_->conclude(); } else { message = str(format(QT_TRANSLATE_NOOP("", "Disconnected from %1%: %2%.")) % jid_.getDomain() % message); } lastDisconnectError_ = boost::make_shared(JID(jid_.getDomain()), message); eventController_->handleIncomingEvent(lastDisconnectError_); } } } else if (!rosterController_) { //hasn't been logged in yet loginWindow_->setIsLoggingIn(false); } } void MainController::setReconnectTimer() { if (timeBeforeNextReconnect_ < 0) { timeBeforeNextReconnect_ = 1; } else { timeBeforeNextReconnect_ = timeBeforeNextReconnect_ >= 150 ? 300 : timeBeforeNextReconnect_ * 2; // Randomly selected by roll of a die, as required by 3920bis } if (reconnectTimer_) { reconnectTimer_->stop(); } reconnectTimer_ = networkFactories_->getTimerFactory()->createTimer(timeBeforeNextReconnect_ * 1000); reconnectTimer_->onTick.connect(boost::bind(&MainController::reconnectAfterError, this)); reconnectTimer_->start(); } void MainController::handleCancelLoginRequest() { signOut(); } void MainController::signOut() { if (settings_->getSetting(SettingConstants::FORGET_PASSWORDS)) { purgeCachedCredentials(); } eventController_->clear(); logout(); loginWindow_->loggedOut(); resetClient(); } void MainController::logout() { if (settings_->getSetting(SettingConstants::FORGET_PASSWORDS)) { purgeCachedCredentials(); } systemTrayController_->setMyStatusType(StatusShow::None); if (clientInitialized_ /*&& client_->isAvailable()*/) { client_->disconnect(); } if (rosterController_ && myStatusLooksOnline_) { rosterController_->getWindow()->setMyStatusType(StatusShow::None); rosterController_->getWindow()->setMyStatusText(""); myStatusLooksOnline_ = false; } setManagersOffline(); } void MainController::setManagersOffline() { if (chatsManager_) { chatsManager_->setOnline(false); } if (rosterController_) { rosterController_->setEnabled(false); } if (profileController_) { profileController_->setAvailable(false); } if (contactEditController_) { contactEditController_->setAvailable(false); } } void MainController::handleServerDiscoInfoResponse(boost::shared_ptr info, ErrorPayload::ref error) { if (!error) { chatsManager_->setServerDiscoInfo(info); adHocManager_->setServerDiscoInfo(info); } } void MainController::handleVCardReceived(const JID& jid, VCard::ref vCard) { if (!jid.equals(jid_, JID::WithoutResource) || !vCard || vCard->getPhoto().empty()) { return; } std::string hash = Hexify::hexify(SHA1::getHash(vCard->getPhoto())); if (hash != vCardPhotoHash_) { vCardPhotoHash_ = hash; if (client_ && client_->isAvailable()) { sendPresence(statusTracker_->getNextPresence()); } } } void MainController::handleNotificationClicked(const JID& jid) { assert(chatsManager_); if (clientInitialized_) { if (client_->getMUCRegistry()->isMUC(jid)) { uiEventStream_->send(boost::make_shared(jid)); } else { uiEventStream_->send(boost::shared_ptr(new RequestChatUIEvent(jid))); } } } void MainController::handleQuitRequest() { if (client_ && client_->isActive()) { quitRequested_ = true; client_->disconnect(); } else { resetClient(); loginWindow_->quit(); } } #define SERIALIZE_BOOL(option) result += options.option ? "1" : "0"; result += ","; #define SERIALIZE_INT(option) result += boost::lexical_cast(options.option); result += ","; #define SERIALIZE_STRING(option) result += Base64::encode(createByteArray(options.option)); result += ","; #define SERIALIZE_SAFE_STRING(option) result += safeByteArrayToString(Base64::encode(options.option)); result += ","; #define SERIALIZE_URL(option) SERIALIZE_STRING(option.toString()) std::string MainController::serializeClientOptions(const ClientOptions& options) { std::string result; SERIALIZE_BOOL(useStreamCompression); switch (options.useTLS) { case ClientOptions::NeverUseTLS: result += "1";break; case ClientOptions::UseTLSWhenAvailable: result += "2";break; case ClientOptions::RequireTLS: result += "3";break; } result += ","; SERIALIZE_BOOL(allowPLAINWithoutTLS); SERIALIZE_BOOL(useStreamResumption); SERIALIZE_BOOL(useAcks); SERIALIZE_STRING(manualHostname); SERIALIZE_INT(manualPort); switch (options.proxyType) { case ClientOptions::NoProxy: result += "1";break; case ClientOptions::SystemConfiguredProxy: result += "2";break; case ClientOptions::SOCKS5Proxy: result += "3";break; case ClientOptions::HTTPConnectProxy: result += "4";break; } result += ","; SERIALIZE_STRING(manualProxyHostname); SERIALIZE_INT(manualProxyPort); SERIALIZE_URL(boshURL); SERIALIZE_URL(boshHTTPConnectProxyURL); SERIALIZE_SAFE_STRING(boshHTTPConnectProxyAuthID); SERIALIZE_SAFE_STRING(boshHTTPConnectProxyAuthPassword); return result; } #define CHECK_PARSE_LENGTH if (i >= segments.size()) {return result;} #define PARSE_INT_RAW(defaultValue) CHECK_PARSE_LENGTH intVal = defaultValue; try {intVal = boost::lexical_cast(segments[i]);} catch(const boost::bad_lexical_cast&) {};i++; #define PARSE_STRING_RAW CHECK_PARSE_LENGTH stringVal = byteArrayToString(Base64::decode(segments[i]));i++; #define PARSE_BOOL(option, defaultValue) PARSE_INT_RAW(defaultValue); result.option = (intVal == 1); #define PARSE_INT(option, defaultValue) PARSE_INT_RAW(defaultValue); result.option = intVal; #define PARSE_STRING(option) PARSE_STRING_RAW; result.option = stringVal; #define PARSE_SAFE_STRING(option) PARSE_STRING_RAW; result.option = SafeString(createSafeByteArray(stringVal)); #define PARSE_URL(option) {PARSE_STRING_RAW; result.option = URL::fromString(stringVal);} ClientOptions MainController::parseClientOptions(const std::string& optionString) { ClientOptions result; size_t i = 0; int intVal = 0; std::string stringVal; std::vector segments = String::split(optionString, ','); PARSE_BOOL(useStreamCompression, 1); PARSE_INT_RAW(-1); switch (intVal) { case 1: result.useTLS = ClientOptions::NeverUseTLS;break; case 2: result.useTLS = ClientOptions::UseTLSWhenAvailable;break; case 3: result.useTLS = ClientOptions::RequireTLS;break; default:; } PARSE_BOOL(allowPLAINWithoutTLS, 0); PARSE_BOOL(useStreamResumption, 0); PARSE_BOOL(useAcks, 1); PARSE_STRING(manualHostname); PARSE_INT(manualPort, -1); PARSE_INT_RAW(-1); switch (intVal) { case 1: result.proxyType = ClientOptions::NoProxy;break; case 2: result.proxyType = ClientOptions::SystemConfiguredProxy;break; case 3: result.proxyType = ClientOptions::SOCKS5Proxy;break; case 4: result.proxyType = ClientOptions::HTTPConnectProxy;break; } PARSE_STRING(manualProxyHostname); PARSE_INT(manualProxyPort, -1); PARSE_URL(boshURL); PARSE_URL(boshHTTPConnectProxyURL); PARSE_SAFE_STRING(boshHTTPConnectProxyAuthID); PARSE_SAFE_STRING(boshHTTPConnectProxyAuthPassword); return result; } } swift-im-2.0+dev6/Swift/Controllers/ApplicationInfo.h0000644000175000017500000000045312227051773022516 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #define SWIFT_APPLICATION_NAME "Swift" #define SWIFT_ORGANIZATION_NAME "Swift" #define SWIFT_ORGANIZATION_DOMAIN "swift.im" swift-im-2.0+dev6/Swift/Controllers/DummySystemTray.h0000644000175000017500000000063112227051773022575 0ustar kismithkismith/* * 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 "Swift/Controllers/SystemTray.h" namespace Swift { class DummySystemTray : public SystemTray { public: void setUnreadMessages(bool some) {}; void setStatusType(StatusShow::Type type) {}; void setConnecting() {} }; } swift-im-2.0+dev6/Swift/Controllers/FileTransferListController.cpp0000644000175000017500000000322012227051773025251 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include "Swift/Controllers/FileTransferListController.h" #include #include "Swift/Controllers/UIInterfaces/FileTransferListWidgetFactory.h" #include "Swift/Controllers/UIEvents/RequestFileTransferListUIEvent.h" namespace Swift { FileTransferListController::FileTransferListController(UIEventStream* uiEventStream, FileTransferListWidgetFactory* fileTransferListWidgetFactory) : fileTransferListWidgetFactory(fileTransferListWidgetFactory), fileTransferListWidget(NULL), fileTransferOverview(0) { uiEventStream->onUIEvent.connect(boost::bind(&FileTransferListController::handleUIEvent, this, _1)); } FileTransferListController::~FileTransferListController() { delete fileTransferListWidget; } void FileTransferListController::setFileTransferOverview(FileTransferOverview *overview) { fileTransferOverview = overview; if (fileTransferListWidget) { fileTransferListWidget->setFileTransferOverview(fileTransferOverview); } } void FileTransferListController::handleUIEvent(boost::shared_ptr rawEvent) { boost::shared_ptr event = boost::dynamic_pointer_cast(rawEvent); if (event != NULL) { if (fileTransferListWidget == NULL) { fileTransferListWidget = fileTransferListWidgetFactory->createFileTransferListWidget(); if (fileTransferOverview) { fileTransferListWidget->setFileTransferOverview(fileTransferOverview); } } fileTransferListWidget->show(); fileTransferListWidget->activate(); } } } swift-im-2.0+dev6/Swift/Controllers/SystemTray.h0000644000175000017500000000067012227051773021564 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include "Swiften/Elements/StatusShow.h" namespace Swift { class SystemTray { public: virtual ~SystemTray(){}; virtual void setUnreadMessages(bool some) = 0; virtual void setStatusType(StatusShow::Type type) = 0; virtual void setConnecting() = 0; }; } swift-im-2.0+dev6/Swift/Controllers/WhiteboardManager.h0000644000175000017500000000375412227051774023032 0ustar kismithkismith/* * Copyright (c) 2012 Mateusz Piękos * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include #include #include #include #include #include #include namespace Swift { class WhiteboardSessionManager; class NickResolver; class WhiteboardManager { public: WhiteboardManager(WhiteboardWindowFactory* whiteboardWindowFactory, UIEventStream* uiEventStream, NickResolver* nickResolver, WhiteboardSessionManager* whiteboardSessionManager); ~WhiteboardManager(); WhiteboardWindow* createNewWhiteboardWindow(const JID& contact, WhiteboardSession::ref session); public: boost::signal< void (const JID&, bool senderIsSelf)> onSessionRequest; boost::signal< void (const JID&)> onSessionTerminate; boost::signal< void (const JID&)> onRequestAccepted; boost::signal< void (const JID&)> onRequestRejected; private: void handleUIEvent(boost::shared_ptr event); void handleSessionTerminate(const JID& contact); void handleSessionCancel(const JID& contact); void handleSessionAccept(const JID& contact); void handleRequestReject(const JID& contact); void handleIncomingSession(IncomingWhiteboardSession::ref session); void acceptSession(const JID& from); void requestSession(const JID& contact); void cancelSession(const JID& from); WhiteboardWindow* findWhiteboardWindow(const JID& contact); private: std::map whiteboardWindows_; WhiteboardWindowFactory* whiteboardWindowFactory_; UIEventStream* uiEventStream_; NickResolver* nickResolver_; boost::bsignals::scoped_connection uiEventConnection_; WhiteboardSessionManager* whiteboardSessionManager_; }; } swift-im-2.0+dev6/Swift/Controllers/XMPPEvents/0000755000175000017500000000000012227051774021236 5ustar kismithkismithswift-im-2.0+dev6/Swift/Controllers/XMPPEvents/StanzaEvent.h0000644000175000017500000000156312227051774023656 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include "Swiften/Base/boost_bsignals.h" namespace Swift { class StanzaEvent { public: StanzaEvent() : time_(boost::posix_time::microsec_clock::universal_time()) {concluded_ = false;}; virtual ~StanzaEvent() {}; void conclude() {concluded_ = true; onConclusion();}; /** Do not call this directly from outside the class. * If you connect to this signal, you *must* disconnect from it manually. */ boost::signal onConclusion; bool getConcluded() {return concluded_;}; boost::posix_time::ptime getTime() {return time_;} private: bool concluded_; boost::posix_time::ptime time_; }; } swift-im-2.0+dev6/Swift/Controllers/XMPPEvents/SubscriptionRequestEvent.h0000644000175000017500000000172712227051774026455 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include "Swiften/Base/boost_bsignals.h" #include #include "Swift/Controllers/XMPPEvents/StanzaEvent.h" #include #include "Swiften/JID/JID.h" namespace Swift { class SubscriptionRequestEvent : public StanzaEvent { public: SubscriptionRequestEvent(const JID& jid, const std::string& reason) : jid_(jid), reason_(reason){}; virtual ~SubscriptionRequestEvent(){}; const JID& getJID() const {return jid_;}; const std::string& getReason() const {return reason_;}; boost::signal onAccept; boost::signal onDecline; void accept() { onAccept(); conclude(); }; void decline() { onDecline(); conclude(); }; void defer() { conclude(); } private: JID jid_; std::string reason_; }; } swift-im-2.0+dev6/Swift/Controllers/XMPPEvents/ErrorEvent.h0000644000175000017500000000127512227051774023507 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include "Swiften/Base/boost_bsignals.h" #include #include "Swift/Controllers/XMPPEvents/StanzaEvent.h" #include #include "Swiften/JID/JID.h" namespace Swift { class ErrorEvent : public StanzaEvent { public: ErrorEvent(const JID& jid, const std::string& text) : jid_(jid), text_(text){}; virtual ~ErrorEvent(){}; const JID& getJID() const {return jid_;}; const std::string& getText() const {return text_;}; private: JID jid_; std::string text_; }; } swift-im-2.0+dev6/Swift/Controllers/XMPPEvents/EventController.cpp0000644000175000017500000000537712227051774025103 0ustar kismithkismith/* * Copyright (c) 2010-2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include #include #include namespace Swift { EventController::EventController() { } EventController::~EventController() { foreach(boost::shared_ptr event, events_) { event->onConclusion.disconnect(boost::bind(&EventController::handleEventConcluded, this, event)); } } void EventController::handleIncomingEvent(boost::shared_ptr sourceEvent) { boost::shared_ptr messageEvent = boost::dynamic_pointer_cast(sourceEvent); boost::shared_ptr subscriptionEvent = boost::dynamic_pointer_cast(sourceEvent); boost::shared_ptr errorEvent = boost::dynamic_pointer_cast(sourceEvent); boost::shared_ptr mucInviteEvent = boost::dynamic_pointer_cast(sourceEvent); /* If it's a duplicate subscription request, remove the previous request first */ if (subscriptionEvent) { EventList existingEvents(events_); foreach(boost::shared_ptr existingEvent, existingEvents) { boost::shared_ptr existingSubscriptionEvent = boost::dynamic_pointer_cast(existingEvent); if (existingSubscriptionEvent) { if (existingSubscriptionEvent->getJID() == subscriptionEvent->getJID()) { existingEvent->conclude(); } } } } if ((messageEvent && messageEvent->isReadable()) || subscriptionEvent || errorEvent || mucInviteEvent) { events_.push_back(sourceEvent); sourceEvent->onConclusion.connect(boost::bind(&EventController::handleEventConcluded, this, sourceEvent)); onEventQueueLengthChange(events_.size()); onEventQueueEventAdded(sourceEvent); if (sourceEvent->getConcluded()) { handleEventConcluded(sourceEvent); } } } void EventController::handleEventConcluded(boost::shared_ptr event) { event->onConclusion.disconnect(boost::bind(&EventController::handleEventConcluded, this, event)); events_.erase(std::remove(events_.begin(), events_.end(), event), events_.end()); onEventQueueLengthChange(events_.size()); } void EventController::disconnectAll() { onEventQueueLengthChange.disconnect_all_slots(); onEventQueueEventAdded.disconnect_all_slots(); } void EventController::clear() { events_.clear(); onEventQueueLengthChange(0); } } swift-im-2.0+dev6/Swift/Controllers/XMPPEvents/MUCInviteEvent.h0000644000175000017500000000162712227051774024222 0ustar kismithkismith/* * Copyright (c) 2012 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once namespace Swift { class MUCInviteEvent : public StanzaEvent { public: typedef boost::shared_ptr ref; public: MUCInviteEvent(const JID& inviter, const JID& roomJID, const std::string& reason, const std::string& password, bool direct) : inviter_(inviter), roomJID_(roomJID), reason_(reason), password_(password), direct_(direct) {} const JID& getInviter() const { return inviter_; } const JID& getRoomJID() const { return roomJID_; } const std::string& getReason() const { return reason_; } const std::string& getPassword() const { return password_; } bool getDirect() const { return direct_; } private: JID inviter_; JID roomJID_; std::string reason_; std::string password_; bool direct_; }; } swift-im-2.0+dev6/Swift/Controllers/XMPPEvents/EventController.h0000644000175000017500000000177112227051774024542 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #ifndef SWIFTEN_EventController_H #define SWIFTEN_EventController_H #include "Swiften/Base/boost_bsignals.h" #include #include #include "Swift/Controllers/XMPPEvents/StanzaEvent.h" #include "Swift/Controllers/XMPPEvents/MessageEvent.h" namespace Swift { typedef std::vector > EventList; class EventController { public: EventController(); ~EventController(); void handleIncomingEvent(boost::shared_ptr sourceEvent); boost::signal onEventQueueLengthChange; boost::signal)> onEventQueueEventAdded; const EventList& getEvents() const {return events_;} void disconnectAll(); void clear(); private: void handleEventConcluded(boost::shared_ptr event); EventList events_; }; } #endif swift-im-2.0+dev6/Swift/Controllers/XMPPEvents/MessageEvent.h0000644000175000017500000000170712227051774024002 0ustar kismithkismith/* * Copyright (c) 2010-2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include namespace Swift { class MessageEvent : public StanzaEvent { public: typedef boost::shared_ptr ref; MessageEvent(boost::shared_ptr stanza) : stanza_(stanza), targetsMe_(true) {}; boost::shared_ptr getStanza() {return stanza_;} bool isReadable() { return getStanza()->isError() || !getStanza()->getBody().empty(); } void read() { assert (isReadable()); conclude(); } void setTargetsMe(bool targetsMe) { targetsMe_ = targetsMe; } bool targetsMe() const { return targetsMe_; } private: boost::shared_ptr stanza_; bool targetsMe_; }; } swift-im-2.0+dev6/Swift/Controllers/Settings/0000755000175000017500000000000012227051773021064 5ustar kismithkismithswift-im-2.0+dev6/Swift/Controllers/Settings/SettingsProvider.h0000644000175000017500000000377212227051773024561 0ustar kismithkismith/* * Copyright (c) 2010-2012 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include namespace Swift { class SettingsProvider { public: template class Setting { public: Setting(const std::string& key, const T& defaultValue) : key(key), defaultValue(defaultValue) { } const std::string& getKey() const { return key; } const T& getDefaultValue() const { return defaultValue; } private: std::string key; T defaultValue; }; public: virtual ~SettingsProvider() {} virtual std::string getSetting(const Setting& setting) = 0; virtual void storeSetting(const Setting& setting, const std::string& value) = 0; virtual bool getSetting(const Setting& setting) = 0; virtual void storeSetting(const Setting& setting, const bool& value) = 0; virtual int getSetting(const Setting& setting) = 0; virtual void storeSetting(const Setting& setting, const int& value) = 0; virtual std::vector getAvailableProfiles() = 0; virtual void createProfile(const std::string& profile) = 0; virtual void removeProfile(const std::string& profile) = 0; /** A final setting is one that this settings provider says may not be overriden by lower priority profiles. * e.g. An Administrator-set configuration to disallow saving user passwords could not be overridden by the user. */ template bool getIsSettingFinal(const Setting& setting) { return getIsSettingFinal(setting.getKey()); } virtual bool hasSetting(const std::string& key) = 0; friend class SettingsProviderHierachy; protected: virtual bool getIsSettingFinal(const std::string& settingPath) = 0; public: /** * Emitted when a setting is changed. */ boost::signal onSettingChanged; }; } swift-im-2.0+dev6/Swift/Controllers/Settings/UnitTest/0000755000175000017500000000000012227051773022643 5ustar kismithkismithswift-im-2.0+dev6/Swift/Controllers/Settings/UnitTest/SettingsProviderHierachyTest.cpp0000644000175000017500000000464012227051773031203 0ustar kismithkismith/* * Copyright (c) 2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include using namespace Swift; using namespace std; class SettingsProviderHierachyTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(SettingsProviderHierachyTest); CPPUNIT_TEST(testEmpty); CPPUNIT_TEST(testTop); CPPUNIT_TEST(testBottom); CPPUNIT_TEST(testBoth); CPPUNIT_TEST(testTopDefault); CPPUNIT_TEST(testBottomOverrides); CPPUNIT_TEST(testFinal); CPPUNIT_TEST_SUITE_END(); public: SettingsProviderHierachyTest() : setting1("somekey", 42) {}; void setUp() { bottom = new DummySettingsProvider(); top = new DummySettingsProvider(); testling = new SettingsProviderHierachy(); testling->addProviderToTopOfStack(bottom); testling->addProviderToTopOfStack(top); } void tearDown() { delete testling; delete top; delete bottom; } void testEmpty() { CPPUNIT_ASSERT_EQUAL(42, testling->getSetting(setting1)); } void testTop() { top->storeSetting(setting1, 37); CPPUNIT_ASSERT_EQUAL(37, testling->getSetting(setting1)); } void testBottom() { bottom->storeSetting(setting1, 17); CPPUNIT_ASSERT_EQUAL(17, testling->getSetting(setting1)); } void testBoth() { bottom->storeSetting(setting1, 17); top->storeSetting(setting1, 37); CPPUNIT_ASSERT_EQUAL(37, testling->getSetting(setting1)); } void testTopDefault() { bottom->storeSetting(setting1, 17); top->storeSetting(setting1, 42); CPPUNIT_ASSERT_EQUAL(42, testling->getSetting(setting1)); } void testBottomOverrides() { bottom->storeSetting(setting1, 17); bottom->setFinal(setting1.getKey()); top->storeSetting(setting1, 5); CPPUNIT_ASSERT_EQUAL(17, testling->getSetting(setting1)); } void testFinal() { bottom->storeSetting(setting1, 17); bottom->setFinal(setting1.getKey()); testling->storeSetting(setting1, 5); CPPUNIT_ASSERT_EQUAL(17, testling->getSetting(setting1)); } private: SettingsProviderHierachy* testling; DummySettingsProvider* bottom; DummySettingsProvider* top; SettingsProvider::Setting setting1; }; CPPUNIT_TEST_SUITE_REGISTRATION(SettingsProviderHierachyTest); swift-im-2.0+dev6/Swift/Controllers/Settings/SettingsProviderHierachy.cpp0000644000175000017500000000654212227051773026567 0ustar kismithkismith/* * Copyright (c) 2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include namespace Swift { SettingsProviderHierachy::~SettingsProviderHierachy() { } bool SettingsProviderHierachy::hasSetting(const std::string& key) { foreach (SettingsProvider* provider, providers_) { if (provider->hasSetting(key)) { return true; } } return false; } std::string SettingsProviderHierachy::getSetting(const Setting& setting) { std::string value = setting.getDefaultValue(); foreach (SettingsProvider* provider, providers_) { std::string providerSetting = provider->getSetting(setting); if (provider->hasSetting(setting.getKey())) { value = providerSetting; } if (provider->getIsSettingFinal(setting.getKey())) { return value; } } return value; } void SettingsProviderHierachy::storeSetting(const Setting& setting, const std::string& settingValue) { if (!getIsSettingFinal(setting.getKey())) { getWritableProvider()->storeSetting(setting, settingValue); } } bool SettingsProviderHierachy::getSetting(const Setting& setting) { bool value = setting.getDefaultValue(); foreach (SettingsProvider* provider, providers_) { bool providerSetting = provider->getSetting(setting); if (provider->hasSetting(setting.getKey())) { value = providerSetting; if (provider->getIsSettingFinal(setting.getKey())) { return providerSetting; } } } return value; } void SettingsProviderHierachy::storeSetting(const Setting& setting, const bool& settingValue) { if (!getIsSettingFinal(setting.getKey())) { getWritableProvider()->storeSetting(setting, settingValue); } } int SettingsProviderHierachy::getSetting(const Setting& setting) { int value = setting.getDefaultValue(); foreach (SettingsProvider* provider, providers_) { int providerSetting = provider->getSetting(setting); if (provider->hasSetting(setting.getKey())) { value = providerSetting; if (provider->getIsSettingFinal(setting.getKey())) { return providerSetting; } } } return value; } void SettingsProviderHierachy::storeSetting(const Setting& setting, const int& settingValue) { if (!getIsSettingFinal(setting.getKey())) { getWritableProvider()->storeSetting(setting, settingValue); } } std::vector SettingsProviderHierachy::getAvailableProfiles() { /* Always pull profiles from the topmost */ return getWritableProvider()->getAvailableProfiles(); } void SettingsProviderHierachy::createProfile(const std::string& profile) { return getWritableProvider()->createProfile(profile); } void SettingsProviderHierachy::removeProfile(const std::string& profile) { return getWritableProvider()->removeProfile(profile); } bool SettingsProviderHierachy::getIsSettingFinal(const std::string& settingPath) { bool isFinal = false; foreach (SettingsProvider* provider, providers_) { isFinal |= provider->getIsSettingFinal(settingPath); } return isFinal; } SettingsProvider* SettingsProviderHierachy::getWritableProvider() { return providers_.back(); } void SettingsProviderHierachy::addProviderToTopOfStack(SettingsProvider* provider) { providers_.push_back(provider); provider->onSettingChanged.connect(onSettingChanged); } } swift-im-2.0+dev6/Swift/Controllers/Settings/DummySettingsProvider.h0000644000175000017500000000425112227051773025566 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include namespace Swift { class DummySettingsProvider : public SettingsProvider { public: virtual ~DummySettingsProvider() {} virtual std::string getSetting(const Setting& setting) { return stringValues.find(setting.getKey()) != stringValues.end() ? stringValues[setting.getKey()] : setting.getDefaultValue(); }; virtual void storeSetting(const Setting& setting, const std::string& value) { stringValues[setting.getKey()] = value; onSettingChanged(setting.getKey()); }; virtual bool getSetting(const Setting& setting) { return boolValues.find(setting.getKey()) != boolValues.end() ? boolValues[setting.getKey()] : setting.getDefaultValue(); }; virtual void storeSetting(const Setting& setting, const bool& value) { boolValues[setting.getKey()] = value; onSettingChanged(setting.getKey()); }; virtual int getSetting(const Setting& setting) { return intValues.find(setting.getKey()) != intValues.end() ? intValues[setting.getKey()] : setting.getDefaultValue(); }; virtual void storeSetting(const Setting& setting, const int& value) { intValues[setting.getKey()] = value; onSettingChanged(setting.getKey()); }; virtual std::vector getAvailableProfiles() {return std::vector();} virtual void createProfile(const std::string& ) {} virtual void removeProfile(const std::string& ) {} virtual bool getIsSettingFinal(const std::string& settingPath) {return finals.count(settingPath);} void setFinal(const std::string& settingPath) { finals.insert(settingPath); } virtual bool hasSetting(const std::string& key) { return stringValues.find(key) != stringValues.end() || intValues.find(key) != intValues.end() || boolValues.find(key) != boolValues.end(); } private: std::map stringValues; std::map intValues; std::map boolValues; std::set finals; }; } swift-im-2.0+dev6/Swift/Controllers/Settings/XMLSettingsProvider.cpp0000644000175000017500000000670512227051773025474 0ustar kismithkismith/* * Copyright (c) 2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include #include #include #include #include #include namespace Swift { XMLSettingsProvider::XMLSettingsProvider(const std::string& xmlConfig) : level_(0) { if (!xmlConfig.empty()) { PlatformXMLParserFactory factory; XMLParser* parser = factory.createXMLParser(this); if (parser->parse(xmlConfig)) { SWIFT_LOG(debug) << "Found and parsed system config" << std::endl; } else { SWIFT_LOG(debug) << "Found invalid system config" << std::endl; } delete parser; } else { SWIFT_LOG(debug) << "No system config found" << std::endl; } } XMLSettingsProvider::~XMLSettingsProvider() { } bool XMLSettingsProvider::hasSetting(const std::string& key) { return (values_.find(key) != values_.end()); } std::string XMLSettingsProvider::getSetting(const Setting& setting) { if (values_.find(setting.getKey()) != values_.end()) { std::string value = values_[setting.getKey()]; return value; } return setting.getDefaultValue(); } void XMLSettingsProvider::storeSetting(const Setting& /*settingPath*/, const std::string& /*settingValue*/) { assert(false); } bool XMLSettingsProvider::getSetting(const Setting& setting) { if (values_.find(setting.getKey()) != values_.end()) { std::string value = values_[setting.getKey()]; return boost::iequals(value, "true") || value == "1"; } return setting.getDefaultValue(); } void XMLSettingsProvider::storeSetting(const Setting& /*settingPath*/, const bool& /*settingValue*/) { assert(false); } int XMLSettingsProvider::getSetting(const Setting& setting) { if (values_.find(setting.getKey()) != values_.end()) { std::string value = values_[setting.getKey()]; try { return value.empty() ? setting.getDefaultValue() : boost::lexical_cast(value);; } catch(boost::bad_lexical_cast &) {} } return setting.getDefaultValue(); } void XMLSettingsProvider::storeSetting(const Setting& /*settingPath*/, const int& /*settingValue*/) { assert(false); } std::vector XMLSettingsProvider::getAvailableProfiles() { assert(false); return std::vector(); } void XMLSettingsProvider::createProfile(const std::string& /*profile*/) { assert(false); } void XMLSettingsProvider::removeProfile(const std::string& /*profile*/) { assert(false); } bool XMLSettingsProvider::getIsSettingFinal(const std::string& settingPath) { return finals_.count(settingPath); } void XMLSettingsProvider::handleStartElement(const std::string& element, const std::string& /*ns*/, const AttributeMap& attributes) { level_++; if (level_ == SettingLevel) { if (attributes.getBoolAttribute("final", false)) { finals_.insert(element); } currentElement_ = element; currentText_ = ""; } } void XMLSettingsProvider::handleEndElement(const std::string& /*element*/, const std::string& /*ns*/) { if (level_ == SettingLevel) { values_[currentElement_] = currentText_; SWIFT_LOG(debug) << "Setting value of " << currentElement_ << " to " << currentText_ << std::endl; } level_--; } void XMLSettingsProvider::handleCharacterData(const std::string& data) { if (level_ >= SettingLevel) { currentText_ += data; } } } swift-im-2.0+dev6/Swift/Controllers/Settings/XMLSettingsProvider.h0000644000175000017500000000341712227051773025136 0ustar kismithkismith/* * Copyright (c) 2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include #include #include namespace Swift { class XMLSettingsProvider : public SettingsProvider, public XMLParserClient { public: XMLSettingsProvider(const std::string& xmlConfig); virtual ~XMLSettingsProvider(); virtual std::string getSetting(const Setting& setting); virtual void storeSetting(const Setting& setting, const std::string& value); virtual bool getSetting(const Setting& setting); virtual void storeSetting(const Setting& setting, const bool& value); virtual int getSetting(const Setting& setting); virtual void storeSetting(const Setting& setting, const int& value); virtual std::vector getAvailableProfiles(); virtual void createProfile(const std::string& profile); virtual void removeProfile(const std::string& profile); virtual bool hasSetting(const std::string& key); virtual void handleStartElement(const std::string& element, const std::string& ns, const AttributeMap& attributes); virtual void handleEndElement(const std::string& element, const std::string& ns); virtual void handleCharacterData(const std::string& data); protected: virtual bool getIsSettingFinal(const std::string& settingPath); private: std::map values_; /* Settings that are final*/ std::set finals_; enum Level { TopLevel = 0, SettingLevel = 2 }; int level_; std::string currentElement_; std::string currentText_; }; } swift-im-2.0+dev6/Swift/Controllers/Settings/SettingsProviderHierachy.h0000644000175000017500000000313712227051773026231 0ustar kismithkismith/* * Copyright (c) 2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include namespace Swift { class SettingsProviderHierachy : public SettingsProvider { public: virtual ~SettingsProviderHierachy(); virtual std::string getSetting(const Setting& setting); virtual void storeSetting(const Setting& setting, const std::string& value); virtual bool getSetting(const Setting& setting); virtual void storeSetting(const Setting& setting, const bool& value); virtual int getSetting(const Setting& setting); virtual void storeSetting(const Setting& setting, const int& value); virtual std::vector getAvailableProfiles(); virtual void createProfile(const std::string& profile); virtual void removeProfile(const std::string& profile); virtual bool hasSetting(const std::string& key); protected: virtual bool getIsSettingFinal(const std::string& settingPath); public: /** * Adds a provider less significant than any already added. * This means that if an existing provider has a setting, this provider won't be asked. * Any settings will be pushed into the topmost (least significant) provider. * Does not take ownership of provider. */ void addProviderToTopOfStack(SettingsProvider* provider); private: SettingsProvider* getWritableProvider(); private: /* Start/Left is most significant (lowest), left overrides right.*/ std::vector providers_; }; } swift-im-2.0+dev6/Swift/Controllers/PresenceNotifier.h0000644000175000017500000000324512227051773022705 0ustar kismithkismith/* * 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 #include #include "Swiften/Base/boost_bsignals.h" #include "Swiften/Elements/Presence.h" #include "Swiften/JID/JID.h" #include "SwifTools/Notifier/Notifier.h" #include "Swiften/Avatars/AvatarManager.h" #include "Swiften/Network/Timer.h" namespace Swift { class TimerFactory; class StanzaChannel; class MUCRegistry; class NickResolver; class PresenceOracle; class PresenceNotifier { public: PresenceNotifier(StanzaChannel* stanzaChannel, Notifier* notifier, const MUCRegistry* mucRegistry, AvatarManager* avatarManager, NickResolver* nickResolver, const PresenceOracle* presenceOracle, TimerFactory* timerFactory); ~PresenceNotifier(); void setInitialQuietPeriodMS(int ms); boost::signal onNotificationActivated; private: void handlePresenceReceived(boost::shared_ptr); void handleStanzaChannelAvailableChanged(bool); void handleNotificationActivated(JID jid); void handleTimerTick(); std::string getStatusType(const JID&) const; std::string getStatusMessage(const JID&) const; private: void showNotification(const JID& jid, Notifier::Type type); private: StanzaChannel* stanzaChannel; Notifier* notifier; const MUCRegistry* mucRegistry; AvatarManager* avatarManager; NickResolver* nickResolver; const PresenceOracle* presenceOracle; TimerFactory* timerFactory; boost::shared_ptr timer; bool justInitialized; bool inQuietPeriod; std::set availableUsers; }; } swift-im-2.0+dev6/Swift/Controllers/EventNotifier.h0000644000175000017500000000201712227051773022216 0ustar kismithkismith/* * 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 #include "SwifTools/Notifier/Notifier.h" #include "Swiften/Base/boost_bsignals.h" #include "Swift/Controllers/XMPPEvents/StanzaEvent.h" #include "Swiften/JID/JID.h" namespace Swift { class EventController; class Notifier; class AvatarManager; class NickResolver; class JID; class UIEventStream; class SettingsProvider; class EventNotifier { public: EventNotifier(EventController* eventController, Notifier* notifier, AvatarManager* avatarManager, NickResolver* nickResolver); ~EventNotifier(); boost::signal onNotificationActivated; private: void handleEventAdded(boost::shared_ptr); void handleNotificationActivated(JID jid); private: EventController* eventController; Notifier* notifier; AvatarManager* avatarManager; NickResolver* nickResolver; }; } swift-im-2.0+dev6/Swift/Controllers/CertificateMemoryStorageFactory.h0000644000175000017500000000130112227051773025720 0ustar kismithkismith/* * 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 #include namespace Swift { class CertificateFactory; class CertificateMemoryStorageFactory : public CertificateStorageFactory { public: CertificateMemoryStorageFactory() { } virtual CertificateStorage* createCertificateStorage(const JID&) const { return new CertificateMemoryStorage(); } private: boost::filesystem::path basePath; CertificateFactory* certificateFactory; }; } swift-im-2.0+dev6/Swift/Controllers/WhiteboardManager.cpp0000644000175000017500000001247212227051774023362 0ustar kismithkismith/* * Copyright (c) 2012 Mateusz Piękos * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include #include #include #include #include #include #include #include "Swiften/Client/NickResolver.h" #include #include namespace Swift { typedef std::pair JIDWhiteboardWindowPair; WhiteboardManager::WhiteboardManager(WhiteboardWindowFactory* whiteboardWindowFactory, UIEventStream* uiEventStream, NickResolver* nickResolver, WhiteboardSessionManager* whiteboardSessionManager) : whiteboardWindowFactory_(whiteboardWindowFactory), uiEventStream_(uiEventStream), nickResolver_(nickResolver), whiteboardSessionManager_(whiteboardSessionManager) { #ifdef SWIFT_EXPERIMENTAL_WB whiteboardSessionManager_->onSessionRequest.connect(boost::bind(&WhiteboardManager::handleIncomingSession, this, _1)); #endif uiEventConnection_ = uiEventStream_->onUIEvent.connect(boost::bind(&WhiteboardManager::handleUIEvent, this, _1)); } WhiteboardManager::~WhiteboardManager() { foreach (JIDWhiteboardWindowPair whiteboardWindowPair, whiteboardWindows_) { delete whiteboardWindowPair.second; } } WhiteboardWindow* WhiteboardManager::createNewWhiteboardWindow(const JID& contact, WhiteboardSession::ref session) { WhiteboardWindow *window = whiteboardWindowFactory_->createWhiteboardWindow(session); window->setName(nickResolver_->jidToNick(contact)); whiteboardWindows_[contact.toBare()] = window; return window; } WhiteboardWindow* WhiteboardManager::findWhiteboardWindow(const JID& contact) { if (whiteboardWindows_.find(contact.toBare()) == whiteboardWindows_.end()) { return NULL; } return whiteboardWindows_[contact.toBare()]; } void WhiteboardManager::handleUIEvent(boost::shared_ptr event) { boost::shared_ptr requestWhiteboardEvent = boost::dynamic_pointer_cast(event); if (requestWhiteboardEvent) { requestSession(requestWhiteboardEvent->getContact()); } boost::shared_ptr sessionAcceptEvent = boost::dynamic_pointer_cast(event); if (sessionAcceptEvent) { acceptSession(sessionAcceptEvent->getContact()); } boost::shared_ptr sessionCancelEvent = boost::dynamic_pointer_cast(event); if (sessionCancelEvent) { cancelSession(sessionCancelEvent->getContact()); } boost::shared_ptr showWindowEvent = boost::dynamic_pointer_cast(event); if (showWindowEvent) { WhiteboardWindow* window = findWhiteboardWindow(showWindowEvent->getContact()); if (window != NULL) { window->activateWindow(); } } } void WhiteboardManager::acceptSession(const JID& from) { IncomingWhiteboardSession::ref session = boost::dynamic_pointer_cast(whiteboardSessionManager_->getSession(from)); WhiteboardWindow* window = findWhiteboardWindow(from); if (session && window) { session->accept(); window->show(); } } void WhiteboardManager::requestSession(const JID& contact) { WhiteboardSession::ref session = whiteboardSessionManager_->requestSession(contact); session->onSessionTerminated.connect(boost::bind(&WhiteboardManager::handleSessionTerminate, this, _1)); session->onRequestAccepted.connect(boost::bind(&WhiteboardManager::handleSessionAccept, this, _1)); session->onRequestRejected.connect(boost::bind(&WhiteboardManager::handleRequestReject, this, _1)); WhiteboardWindow* window = findWhiteboardWindow(contact); if (window == NULL) { createNewWhiteboardWindow(contact, session); } else { window->setSession(session); } onSessionRequest(session->getTo(), true); } void WhiteboardManager::cancelSession(const JID& from) { WhiteboardSession::ref session = whiteboardSessionManager_->getSession(from); if (session) { session->cancel(); } } void WhiteboardManager::handleIncomingSession(IncomingWhiteboardSession::ref session) { session->onSessionTerminated.connect(boost::bind(&WhiteboardManager::handleSessionTerminate, this, _1)); session->onRequestAccepted.connect(boost::bind(&WhiteboardManager::handleSessionAccept, this, _1)); WhiteboardWindow* window = findWhiteboardWindow(session->getTo()); if (window == NULL) { createNewWhiteboardWindow(session->getTo(), session); } else { window->setSession(session); } onSessionRequest(session->getTo(), false); } void WhiteboardManager::handleSessionTerminate(const JID& contact) { onSessionTerminate(contact); } void WhiteboardManager::handleSessionCancel(const JID& contact) { onSessionTerminate(contact); } void WhiteboardManager::handleSessionAccept(const JID& contact) { WhiteboardWindow* window = findWhiteboardWindow(contact); window->show(); onRequestAccepted(contact); } void WhiteboardManager::handleRequestReject(const JID& contact) { onRequestRejected(contact); } } swift-im-2.0+dev6/Swift/Controllers/FileTransferListController.h0000644000175000017500000000155612227051773024730 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include "Swift/Controllers/UIEvents/UIEventStream.h" namespace Swift { class FileTransferListWidgetFactory; class FileTransferListWidget; class FileTransferOverview; class FileTransferListController { public: FileTransferListController(UIEventStream* uiEventStream, FileTransferListWidgetFactory* fileTransferListWidgetFactory); ~FileTransferListController(); void setFileTransferOverview(FileTransferOverview* overview); private: void handleUIEvent(boost::shared_ptr event); private: FileTransferListWidgetFactory* fileTransferListWidgetFactory; FileTransferListWidget* fileTransferListWidget; FileTransferOverview* fileTransferOverview; }; } swift-im-2.0+dev6/Swift/Controllers/ProfileSettingsProvider.h0000644000175000017500000000204512227051773024272 0ustar kismithkismith/* * Copyright (c) 2010-2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include #include namespace Swift { class ProfileSettingsProvider { public: ProfileSettingsProvider(const std::string& profile, SettingsProvider* provider); virtual ~ProfileSettingsProvider(); virtual std::string getStringSetting(const std::string &settingPath); virtual void storeString(const std::string &settingPath, const std::string &settingValue); virtual int getIntSetting(const std::string& settingPath, int defaultValue); virtual void storeInt(const std::string& settingPath, int settingValue); /** See @SettingsProvider::getIsSettingFinal for discussion of what this means.*/ virtual bool getIsSettingFinal(const std::string& settingPath); private: std::string profileSettingPath(const std::string &settingPath); SettingsProvider* provider_; std::string profile_; }; } swift-im-2.0+dev6/Swift/Controllers/ConnectionSettings.h0000644000175000017500000000134412227051773023257 0ustar kismithkismith/* * Copyright (c) 2012 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include struct ConnectionSettings { enum Method { Automatic, Manual, BOSH }; enum ProxyType { None, System, SOCKS5, HTTPConnect }; Method method; struct { bool useManualServer; std::string manualServerHostname; int manualServerPort; ProxyType proxyType; bool useManualProxy; std::string manualProxyHostname; int manualProxyPort; } manualSettings; struct { std::string boshURI; bool useManualProxy; std::string manualProxyHostname; int manualProxyPort; } boshSettings; }; swift-im-2.0+dev6/Swift/Controllers/FileTransfer/0000755000175000017500000000000012227051773021650 5ustar kismithkismithswift-im-2.0+dev6/Swift/Controllers/FileTransfer/FileTransferOverview.h0000644000175000017500000000154712227051773026143 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include "Swift/Controllers/FileTransfer/FileTransferController.h" #include namespace Swift { class ChatsManager; class FileTransferManager; class FileTransferOverview { public: FileTransferOverview(FileTransferManager*); ~FileTransferOverview(); void sendFile(const JID&, const std::string&); const std::vector& getFileTransfers() const; boost::signal onNewFileTransferController; private: void handleIncomingFileTransfer(IncomingFileTransfer::ref transfer); private: std::vector fileTransfers; FileTransferManager *fileTransferManager; }; } swift-im-2.0+dev6/Swift/Controllers/FileTransfer/FileTransferOverview.cpp0000644000175000017500000000256012227051773026472 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include "FileTransferOverview.h" #include #include #include #include namespace Swift { FileTransferOverview::FileTransferOverview(FileTransferManager* ftm) : fileTransferManager(ftm) { fileTransferManager->onIncomingFileTransfer.connect(boost::bind(&FileTransferOverview::handleIncomingFileTransfer, this, _1)); } FileTransferOverview::~FileTransferOverview() { } void FileTransferOverview::sendFile(const JID& jid, const std::string& filename) { if (boost::filesystem::exists(filename) && boost::filesystem::file_size(filename) > 0) { FileTransferController* controller = new FileTransferController(jid, filename, fileTransferManager); fileTransfers.push_back(controller); onNewFileTransferController(controller); } } void FileTransferOverview::handleIncomingFileTransfer(IncomingFileTransfer::ref transfer) { FileTransferController* controller = new FileTransferController(transfer); fileTransfers.push_back(controller); onNewFileTransferController(controller); } const std::vector& FileTransferOverview::getFileTransfers() const { return fileTransfers; } } swift-im-2.0+dev6/Swift/Controllers/FileTransfer/FileTransferController.h0000644000175000017500000000365112227051773026456 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include #include #include #include #include #include #include #include namespace Swift { class FileTransferManager; class ChatWindow; class FileTransferController { public: /** * For outgoing file transfers. It'll create a file transfer via FileTransferManager as soon as the descriptive information is available. */ FileTransferController(const JID&, const std::string&, FileTransferManager*); /** * For incoming file transfers. */ FileTransferController(IncomingFileTransfer::ref transfer); ~FileTransferController(); std::string setChatWindow(ChatWindow*, std::string nickname); void setReceipient(const JID& otherParty); void start(std::string& description); void accept(std::string& file); void cancel(); const JID &getOtherParty() const; bool isIncoming() const; FileTransfer::State getState() const; int getProgress() const; boost::uintmax_t getSize() const; boost::signal onStateChage; boost::signal onProgressChange; private: void handleFileTransferStateChange(FileTransfer::State); void handleProgressPercentageChange(int percentage); private: bool sending; JID otherParty; std::string filename; FileTransfer::ref transfer; boost::shared_ptr fileReadStream; boost::shared_ptr fileWriteStream; FileTransferManager* ftManager; FileTransferProgressInfo* ftProgressInfo; ChatWindow* chatWindow; std::string uiID; FileTransfer::State currentState; }; } swift-im-2.0+dev6/Swift/Controllers/FileTransfer/FileTransferProgressInfo.cpp0000644000175000017500000000157512227051773027311 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include "FileTransferProgressInfo.h" #include namespace Swift { FileTransferProgressInfo::FileTransferProgressInfo(boost::uintmax_t completeBytes) : completeBytes(completeBytes), completedBytes(0), percentage(0) { onProgressPercentage(0); } void FileTransferProgressInfo::setBytesProcessed(int processedBytes) { int oldPercentage = int(double(completedBytes) / double(completeBytes) * 100.0); completedBytes += processedBytes; int newPercentage = int(double(completedBytes) / double(completeBytes) * 100.0); if (oldPercentage != newPercentage) { onProgressPercentage(newPercentage); } percentage = newPercentage; } int FileTransferProgressInfo::getPercentage() const { return percentage; } } swift-im-2.0+dev6/Swift/Controllers/FileTransfer/FileTransferController.cpp0000644000175000017500000001274112227051773027011 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include "FileTransferController.h" #include "Swiften/FileTransfer/OutgoingJingleFileTransfer.h" #include "Swiften/FileTransfer/FileTransferManager.h" #include #include #include #include "Swift/Controllers/UIInterfaces/ChatWindow.h" #include #include #include namespace Swift { FileTransferController::FileTransferController(const JID& receipient, const std::string& filename, FileTransferManager* fileTransferManager) : sending(true), otherParty(receipient), filename(filename), ftManager(fileTransferManager), ftProgressInfo(0), chatWindow(0), currentState(FileTransfer::State::WaitingForStart) { } FileTransferController::FileTransferController(IncomingFileTransfer::ref transfer) : sending(false), otherParty(transfer->getSender()), filename(transfer->filename), transfer(transfer), ftManager(0), ftProgressInfo(0), chatWindow(0), currentState(FileTransfer::State::WaitingForStart) { } FileTransferController::~FileTransferController() { delete ftProgressInfo; } const JID &FileTransferController::getOtherParty() const { return otherParty; } std::string FileTransferController::setChatWindow(ChatWindow* wnd, std::string nickname) { chatWindow = wnd; if (sending) { uiID = wnd->addFileTransfer(QT_TRANSLATE_NOOP("", "me"), true, filename, boost::filesystem::file_size(boost::filesystem::path(filename))); } else { uiID = wnd->addFileTransfer(nickname, false, filename, transfer->fileSizeInBytes); } return uiID; } void FileTransferController::setReceipient(const JID& receipient) { this->otherParty = receipient; } bool FileTransferController::isIncoming() const { return !sending; } FileTransfer::State FileTransferController::getState() const { return currentState; } int FileTransferController::getProgress() const { return ftProgressInfo ? ftProgressInfo->getPercentage() : 0; } boost::uintmax_t FileTransferController::getSize() const { if (transfer) { return transfer->fileSizeInBytes; } else { return 0; } } void FileTransferController::start(std::string& description) { SWIFT_LOG(debug) << "FileTransferController::start" << std::endl; fileReadStream = boost::make_shared(boost::filesystem::path(filename)); OutgoingFileTransfer::ref outgoingTransfer = ftManager->createOutgoingFileTransfer(otherParty, boost::filesystem::path(filename), description, fileReadStream); if (outgoingTransfer) { ftProgressInfo = new FileTransferProgressInfo(outgoingTransfer->fileSizeInBytes); ftProgressInfo->onProgressPercentage.connect(boost::bind(&FileTransferController::handleProgressPercentageChange, this, _1)); outgoingTransfer->onStateChange.connect(boost::bind(&FileTransferController::handleFileTransferStateChange, this, _1)); outgoingTransfer->onProcessedBytes.connect(boost::bind(&FileTransferProgressInfo::setBytesProcessed, ftProgressInfo, _1)); outgoingTransfer->start(); transfer = outgoingTransfer; } else { std::cerr << "File transfer not supported!" << std::endl; } } void FileTransferController::accept(std::string& file) { SWIFT_LOG(debug) << "FileTransferController::accept" << std::endl; IncomingFileTransfer::ref incomingTransfer = boost::dynamic_pointer_cast(transfer); if (incomingTransfer) { fileWriteStream = boost::make_shared(boost::filesystem::path(file)); ftProgressInfo = new FileTransferProgressInfo(transfer->fileSizeInBytes); ftProgressInfo->onProgressPercentage.connect(boost::bind(&FileTransferController::handleProgressPercentageChange, this, _1)); transfer->onStateChange.connect(boost::bind(&FileTransferController::handleFileTransferStateChange, this, _1)); transfer->onProcessedBytes.connect(boost::bind(&FileTransferProgressInfo::setBytesProcessed, ftProgressInfo, _1)); incomingTransfer->accept(fileWriteStream); } else { std::cerr << "Expected an incoming transfer in this situation!" << std::endl; } } void FileTransferController::cancel() { if (transfer) { transfer->cancel(); } else { chatWindow->setFileTransferStatus(uiID, ChatWindow::Canceled); } } void FileTransferController::handleFileTransferStateChange(FileTransfer::State state) { currentState = state; onStateChage(); switch(state.state) { case FileTransfer::State::Negotiating: chatWindow->setFileTransferStatus(uiID, ChatWindow::Negotiating); return; case FileTransfer::State::Transferring: chatWindow->setFileTransferStatus(uiID, ChatWindow::Transferring); return; case FileTransfer::State::Canceled: chatWindow->setFileTransferStatus(uiID, ChatWindow::Canceled); return; case FileTransfer::State::Finished: chatWindow->setFileTransferStatus(uiID, ChatWindow::Finished); if (fileWriteStream) { fileWriteStream->close(); } return; case FileTransfer::State::Failed: chatWindow->setFileTransferStatus(uiID, ChatWindow::FTFailed); return; case FileTransfer::State::WaitingForAccept: chatWindow->setFileTransferStatus(uiID, ChatWindow::WaitingForAccept); return; case FileTransfer::State::WaitingForStart: return; } std::cerr << "Unhandled FileTransfer::State!" << std::endl; } void FileTransferController::handleProgressPercentageChange(int percentage) { onProgressChange(); chatWindow->setFileTransferProgress(uiID, percentage); } } swift-im-2.0+dev6/Swift/Controllers/FileTransfer/FileTransferProgressInfo.h0000644000175000017500000000112712227051773026747 0ustar kismithkismith/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #pragma once #include #include namespace Swift { class FileTransferProgressInfo { public: FileTransferProgressInfo(boost::uintmax_t completeBytes); public: void setBytesProcessed(int processedBytes); int getPercentage() const; boost::signal onProgressPercentage; private: boost::uintmax_t completeBytes; boost::uintmax_t completedBytes; int percentage; }; } swift-im-2.0+dev6/Swift/Controllers/EventNotifier.cpp0000644000175000017500000000744312227051773022561 0ustar kismithkismith/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Swift/Controllers/EventNotifier.h" #include #include #include #include #include #include "Swift/Controllers/XMPPEvents/EventController.h" #include "SwifTools/Notifier/Notifier.h" #include "Swiften/Avatars/AvatarManager.h" #include "Swiften/Client/NickResolver.h" #include "Swiften/JID/JID.h" #include "Swift/Controllers/XMPPEvents/MessageEvent.h" #include "Swift/Controllers/XMPPEvents/SubscriptionRequestEvent.h" #include "Swift/Controllers/XMPPEvents/ErrorEvent.h" #include "Swift/Controllers/XMPPEvents/MUCInviteEvent.h" #include "Swift/Controllers/Settings/SettingsProvider.h" namespace Swift { EventNotifier::EventNotifier(EventController* eventController, Notifier* notifier, AvatarManager* avatarManager, NickResolver* nickResolver) : eventController(eventController), notifier(notifier), avatarManager(avatarManager), nickResolver(nickResolver) { eventController->onEventQueueEventAdded.connect(boost::bind(&EventNotifier::handleEventAdded, this, _1)); } EventNotifier::~EventNotifier() { notifier->purgeCallbacks(); eventController->onEventQueueEventAdded.disconnect(boost::bind(&EventNotifier::handleEventAdded, this, _1)); } void EventNotifier::handleEventAdded(boost::shared_ptr event) { if (event->getConcluded()) { return; } if (boost::shared_ptr messageEvent = boost::dynamic_pointer_cast(event)) { JID jid = messageEvent->getStanza()->getFrom(); std::string title = nickResolver->jidToNick(jid); if (!messageEvent->getStanza()->isError() && !messageEvent->getStanza()->getBody().empty()) { JID activationJID = jid; if (messageEvent->getStanza()->getType() == Message::Groupchat) { activationJID = jid.toBare(); } std::string messageText = messageEvent->getStanza()->getBody(); if (boost::starts_with(messageText, "/me ")) { messageText = "*" + String::getSplittedAtFirst(messageText, ' ').second + "*"; } notifier->showMessage(Notifier::IncomingMessage, title, messageText, avatarManager->getAvatarPath(jid), boost::bind(&EventNotifier::handleNotificationActivated, this, activationJID)); } } else if(boost::shared_ptr subscriptionEvent = boost::dynamic_pointer_cast(event)) { JID jid = subscriptionEvent->getJID(); std::string title = jid; std::string message = str(format(QT_TRANSLATE_NOOP("", "%1% wants to add you to his/her contact list")) % nickResolver->jidToNick(jid)); notifier->showMessage(Notifier::SystemMessage, title, message, boost::filesystem::path(), boost::function()); } else if(boost::shared_ptr errorEvent = boost::dynamic_pointer_cast(event)) { notifier->showMessage(Notifier::SystemMessage, QT_TRANSLATE_NOOP("", "Error"), errorEvent->getText(), boost::filesystem::path(), boost::function()); } else if (boost::shared_ptr mucInviteEvent = boost::dynamic_pointer_cast(event)) { std::string title = mucInviteEvent->getInviter(); std::string message = str(format(QT_TRANSLATE_NOOP("", "%1% has invited you to enter the %2% room")) % nickResolver->jidToNick(mucInviteEvent->getInviter()) % mucInviteEvent->getRoomJID()); // FIXME: not show avatar or greyed out avatar for mediated invites notifier->showMessage(Notifier::SystemMessage, title, message, avatarManager->getAvatarPath(mucInviteEvent->getInviter()), boost::bind(&EventNotifier::handleNotificationActivated, this, mucInviteEvent->getInviter())); } } void EventNotifier::handleNotificationActivated(JID jid) { onNotificationActivated(jid); } } swift-im-2.0+dev6/Swift/Controllers/StatusUtil.h0000644000175000017500000000050212227051773021553 0ustar kismithkismith/* * 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 #include namespace Swift { std::string statusShowTypeToFriendlyName(StatusShow::Type type); } swift-im-2.0+dev6/Swift/Controllers/SoundPlayer.h0000644000175000017500000000053012227051773021700 0ustar kismithkismith/* * Copyright (c) 2010 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once namespace Swift { class SoundPlayer { public: virtual ~SoundPlayer() {}; enum SoundEffect{MessageReceived}; virtual void playSound(SoundEffect sound) = 0; }; } swift-im-2.0+dev6/Swift/Controllers/DummySoundPlayer.h0000644000175000017500000000052412227051773022717 0ustar kismithkismith/* * 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 "Swift/Controllers/SoundPlayer.h" namespace Swift { class DummySoundPlayer : public SoundPlayer { public: void playSound(SoundEffect sound) {}; }; } swift-im-2.0+dev6/Swift/Translations/0000755000175000017500000000000012227051774017440 5ustar kismithkismithswift-im-2.0+dev6/Swift/Translations/swift_he.ts0000644000175000017500000033577212227051774021641 0ustar kismithkismith UTF-8 Starting chat with %1% in chatroom %2% מתחיל כעת שיחה עם %1% בתוך חדר שיחה %2% Starting chat with %1% - %2% מתחיל כעת שיחה עם %1% - %2% This chat doesn't support delivery receipts. שיחה זו לא תומכת קבלות משלוח. This chat may not support delivery receipts. You might not receive delivery receipts for the messages you sent. ייתכן ושיחה זו לא תומכת בקבלות משלוח. אתה עשויה שלא לקבל קבלות משלוח עבור ההודעות שנשלחות מן הקצה שלך. me אני %1% has gone offline ‫%1% במצב לא מקוון כעת %1% has become available ‫%1% במצב זמין כעת %1% has gone away ‫%1% במצב נעדר כעת %1% is now busy %1% עסוק/ה כעת The day is now %1% היום כעת הוא %1% Couldn't send message: %1% לא ניתן היה לשלוח הודעה: %1% Error sending message שגיאה בשליחת הודעה Bad request בקשה שגויה בקשה רעה Conflict התנגשות This feature is not implemented תכונה זו אינה מיושמת Forbidden אסור Recipient can no longer be contacted לא ניתן לתקשר עוד עם נמען Internal server error שגיאת שרת פנימית Item not found פריט לא נמצא JID Malformed כתובת JID פגומה Message was rejected הודעה נדחתה Not allowed לא מותר Not authorized לא מורשה Payment is required נדרש תשלום Recipient is unavailable נמען אינו זמין Redirect הכוונה חוזרת Registration required נדרשת הרשמה Recipient's server not found שרת נמען לא נמצא Remote server timeout תם זמן שרת מרוחק The server is low on resources רדוד השרת הינו דל במשאבים The service is unavailable השירות אינו זמין A subscription is required נדרש מינוי נדרשת הרשמה Undefined condition תנאי מצב לא מוגדר Unexpected request בקשה לא צפויה Room %1% is not responding. This operation may never complete. חדר %1% אינו מגיב. פעולה זו עשויה שלא להסתיים. Unable to enter this room לא ניתן להיכנס אל חדר זה Unable to enter this room as %1%, retrying as %2% לא ניתן להיכנס אל חדר זה תחת השם %1%, מנסה כעת להיכנס בשם %2% No nickname specified לא צוין שם כינוי A password needed נדרשת מילת מעבר Only members may enter רק חברים מורשים להיכנס You are banned from the room נאסרת מן חדר זה The room is full החדר מלא The room does not exist החדר לא קיים Couldn't join room: %1%. לא היה ניתן להצטרף אל חדר: %1%. You have entered room %1% as %2%. נכנסת אל החדר %1% בכינוי %2%. %1% has entered the room as a %2%. ‫%1% נכנס/ה אל החדר בתפקיד %2%. %1% has entered the room. ‫%1% נכנס/ה אל החדר. moderator אחראי participant משתתף visitor מבקר The room subject is now: %1% נושא החדר כעת הוא: %1% %1% is now a %2% ‫%1% מצוי/ה כעת במצב %2% Moderators אחראים Participants משתתפים Visitors מבקרים Occupants נוכחים Trying to enter room %1% מנסה כעת להיכנס אל חדר %1% %1% has left the room%2% %1% עזב/ה את החדר%2% You have been kicked out of the room נבעטת החוצה מן החדר You have been banned from the room נאסרת מן החדר You are no longer a member of the room and have been removed אינך עוד במעמד של חבר בחדר זה והוסרת The room has been destroyed החדר הוחרב %1% has left the room ‫%1% עזב/ה את החדר Room configuration failed: %1%. תצורת חדר נכשלה: %1%. Occupant role change failed: %1%. שינוי תפקיד נוכח נכשל: %1%. You have left the room עזבת את החדר The correct room password is needed הסיסמה המדויקת נחוצה and וגם %1% have entered the room ‫%1% נכנסו אל החדר %1% has entered the room ‫%1% נכנס/ה אל החדר %1% have left the room ‫%1% עזבו את החדר %1% have entered then left the room ‫%1% נכנסו אל החדר ואז עזבו %1% has entered then left the room ‫%1% נכנס/ה אל החדר ואז עזב/ה %1% have left then returned to the room ‫%1% עזבו ואז חזרו אל החדר %1% has left then returned to the room ‫%1% עזב/ה ואז חזר/ה אל החדר %1% wants to add you to his/her contact list ‫%1% רוצה להוסיפך אל רשימת הקשרים שלו/שלה Error שגיאה %1% has invited you to enter the %2% room %1% הזמינך להיכנס אל החדר %2% User address invalid. User address should be of the form 'alice@wonderland.lit' כתובת משתמש שגויה. על כתובת משתמש להיות בצורה זו 'alice@wonderland.lit' Unknown Error שגיאה לא מוכרת Unable to find server לא ניתן למצוא שרת Error connecting to server שגיאה בעת התחברות אל שרת Error while receiving server data שגיאה במהלך קבלת נתונים של שרת Error while sending data to the server שגיאה במהלך שליחת נתונים אל השרת Error parsing server data שגיאה בניתוח נתונים של שרת Login/password invalid credentials נתוני התחברות שגויים Error while compressing stream שגיאה במהלך דחיסת זרם Server verification failed אימות שרת נכשל Authentication mechanisms not supported מנגנון אימות לא נתמך Unexpected response מענה לא צפוי Error binding resource שגיאה בכריכת משאב Error starting session שגיאה בהתחלת סשן Stream error שגיאת זרם Encryption error שגיאת הצפנה Error loading certificate (Invalid password?) בגיאה בטעינת תעודה (סיסמה שגויה?) Certificate not authorized תעודה לא מורשית Certificate card removed כרטיס תעודה הוסר Unknown certificate תעודה לא מוכרת Certificate has expired תעודה פקעה Certificate is not yet valid תעודה אינה תקפה עדיין Certificate is self-signed התעודה הינה חתומה באופן עצמי Certificate has been rejected תעודה נדחתה Certificate is not trusted התעודה אינה מהימנה Certificate cannot be used for encrypting your connection לא ניתן להשתמש בתעודה עבור הצפנת חיבורך Certificate path length constraint exceeded הגבלת אורך נתיב תעודה נחצתה Invalid certificate signature חתימת תעודה שגויה Invalid Certificate Authority רשות תעודה שגויה Certificate does not match the host identity תעודה לא תואמת את זהות המארח Certificate has been revoked נשללה תעודה נפסלה Unable to determine certificate revocation state לא ניתן לקבוע מצב פסילות של תעודה Certificate error שגיאת תעודה Re-enter credentials and retry הזן נתוני התחברות מחדש ונסה שוב Disconnected from %1%: %2%. To reconnect, Sign Out and provide your password again. מנותק מן ‫%1%: ‫%2%. כדי להתחבר שוב, התנתק וספק את סיסמתך פעם נוספת. Reconnect to %1% failed: %2%. Will retry in %3% seconds. מתחבר מחדש כעת אל %1% נכשל: %2%. ניסיון נוסף בעוד %3% שניות. Disconnected from %1%: %2%. מנותק מן ‫%1%: ‫%2%. Contacts קשרים Server %1% rejected contact list change to item '%2%' שרת %1% דחה שינוי רשימת קשרים לפריט '%2%' Available זמין Away נעדר Busy עסוק Offline לא מקוון There was an error publishing your profile data ארעה שגיאה בפרסום נתוני דיוקנך %1% (%2%) %1% and %2% others (%3%) %1% וגם %2% אחרים (%3%) %1%, %2% (%3%) TLS Client Certificate Selection מבחר תעודת לקוח TLS Select a certificate to use for authentication בחר תעודה לשימוש עבור אימות CloseButton Close Tab סגור כרטיסייה MAC_APPLICATION_MENU Services שירותים Hide %1 הסתר %1 Hide Others הסתר אחרים Show All הצג הכל Preferences... העדפות... Quit %1 יציאה מן %1 About %1 אודות ‫%1 QApplication QT_LAYOUT_DIRECTION Translate this to LTR for left-to-right or RTL for right-to-left languages RTL QDialogButtonBox &Yes &כן &No &לא &OK &אישור OK אישור &Cancel &ביטול Cancel ביטול QLineEdit Select All בחר הכל &Undo &ביטול ביצוע &בטל &Redo &ביצוע חוזר בצע &שוב Cu&t &גזור &Copy הע&תק &Paste ה&דבק Delete מחק QMessageBox Show Details... הצג פרטים... Hide Details... הסתר פרטים... QObject No rooms found לא נמצאו חדרים %1 would like to add you to their contact list. ‫%1 רוצה להוסיפך אל רשימת הקשרים שלהם. %1 would like to add you to their contact list, saying '%2' ‫%1 רוצה להוסיפך אל רשימת הקשרים שלהם, באומרם '%2' %1 has invited you to enter the %2 room. %1 הזמינך להיכנס אל החדר %2. You've been invited to enter the %1 room. הוזמנת להיכנס אל החדר %1. Reason: %1 סיבה: %1 This person may not have really sent this invitation! אפשרי שאישיות זו לא באמת שלחה את הזמנה זו! Direction כיוון Other Party צד אחר State מצב Progress התקדמות Size גודל Incoming נכנסת Outgoing יוצאת Waiting for start ממתין כעת להתחלה Waiting for other side to accept ממתין כעת לצד השני לקבל Negotiating מסדיר כעת Transferring מעביר כעת Finished הוגמרה Failed נכשלה Canceled בוטלה Connection Options אפשרויות חיבור QScrollBar Scroll here גלול לכאן Top ראש שיא Bottom תחת תחתית Page up עמוד מעלה Page down עמוד מטה Scroll up גלול מעלה Scroll down גלול מטה QTextControl Select All בחר הכל &Copy הע&תק &Undo &בטל &Redo בצע &שוב Cu&t &גזור &Paste ה&דבק Delete מחק QWebPage Copy Link העתק קישור Copy העתק Copy Image העתק תמונה Scroll here גלול לכאן Top שיא Bottom תחתית Page up עמוד מעלה Page down עמוד מטה Scroll up גלול מעלה Scroll down גלול מטה QWizard < &Back < &קודם &Finish &סיום &Help &עזרה Go Back חזרה Continue המשך Commit בצע Done סיים Quit יציאה Help עזרה Cancel ביטול &Next &הבא &Next > &הבא > QtAffiliationEditor Edit Affiliations עריכת ערוך שיוכים Affiliation: שיוך: Owner בעלים Administrator מנהל Member חבר Outcast (Banned) מוחרם (נאסר) Add User הוסף משתמש Remove User הסר משתמש QtBookmarkDetailWindow Edit Bookmark Details עריכת פרטי סימנייה Bookmark Name: שם סימנייה: Room Address: כתובת חדר: Your Nickname: שם כינוייך: Room password: סיסמת חדר: Enter automatically כנס אוטומטית Join automatically הצטרפות אוטומטית QtCertificateViewerDialog Certificate Viewer מציג תעודה QtConnectionSettings Connection Options אפשרויות חיבור Connection Method: שיטת חיבור: Automatic אטומטי Manual ידנית BOSH Secure connection: חיבור מאובטח: Never כלל לא Encrypt when possible הצפן במידת האפשר הצפן כאשר אפשר Always encrypt הצפן תמיד Allow Compression התר דחיסה Allow sending password over insecure connection התר שליחת סיסמתך על פני חיבור לא מאובטח Manually select server בחר שרת באופן ידני Hostname: שם מארח: Port: פורט: Connection Proxy חיבור פרוקסי Proxy type: טיפוס פרוקסי: None ללא Use system-configured proxy השתמש בהגדרות פרוקסי מערכת SOCKS5 HTTP Connect Override system-configured proxy עקוף הגדרות פרוקסי מערכת BOSH URI: כתובת BOSH: Manually select HTTP proxy באופן ידני בחר ידנית פרוקסי HTTP QtHistoryWindow History היסטוריה Search: חיפוש: Next הבא Previous הקודם QtJoinMUCWindow Enter Room כנס אל חדר Room Address: כתובת חדר: Your Nickname: שם כינוייך: Room Password: סיסמת חדר: Automatically configure newly created rooms הגדר אוטומטית חדרים חדשים שנוצרים Room: חדר: Search ... חיפוש ... Nickname: כינוי: Enter automatically in future כנס אוטומטית בעתיד QtMUCSearchWindow Search Room חיפוש חדר Service: שירות: Cancel ביטול OK אישור List rooms מנה חדרים רשימת חדרים QtUserSearchFieldsPage Nickname: שם כינוי: First name: שם פרטי: Last name: שם משפחה: E-Mail: דוא״ל: Fetching search fields מאחזר כעת שדות חיפוש QtUserSearchFirstPage Add a user הוספת משתמש Add another user to your contact list. If you know their address you can add them directly, or you can search for them. הוסף משתמש אחר אל רשימת הקשרים שלך. אם כתובתם ידועה לך ביכולתך להוסיפה ישירות, לחלופין ביכולתך לחפש עבורם. I know their address: כתובתם ידועה לי: I'd like to search my server ברצוני לחפש בשרת שלי I'd like to search another server: ברצוני לחפש בשרת אחר: QtUserSearchResultsPage No results. אין תוצאות. QtUserSearchWizard Find User מציאת משתמש Swift::ChatListModel Bookmarked Rooms חדרים מסומנים Recent Chats שיחות אחרונות Opened Whiteboards לוחות לבנים פתוחים Swift::QtAboutWidget About %1 אודות ‫%1 Version %1 גרסא %1 Built with Qt %1 הודר באמצעות Qt ‫%1 Running with Qt %1 מורץ בעזרת Qt ‫%1 Using the English translation by %1 ממשק תורגם לשפה העברית באדיבות %1 View License הצג רישיון Swift::QtAdHocCommandWindow Cancel ביטול Back חזרה Next קדימה Complete הושלם Error: %1 שגיאה: %1 Warning: %1 אזהרה: %1 Error executing command שגיאה בהרצת פקודה Swift::QtAffiliationEditor Add User הוסף משתמש Added User's Address: כתובת משתמש שהוספה: Swift::QtAvatarWidget No picture אין תמונה Select picture ... בחר תמונה ... Clear picture טהר תמונה Select picture בחר תמונה Image Files (*.png *.jpg *.jpeg *.gif) קבצי תמונה ‪(*.png *.jpg *.jpeg *.gif) Image Files (*.png *.jpg *.gif) קבצי תמונה ‭(*.png *.jpg *.gif)‬ Error שגיאה The selected picture is in an unrecognized format הפורמט של התמונה הנבחרת אינו מוכר Swift::QtBookmarkDetailWindow Bookmark not valid סימנייה לא תקפה You must specify a valid room address (e.g. someroom@rooms.example.com). עליך לציין כתובת חדר תקפה (למשל myroom@chats.example.com). Swift::QtCertificateViewerDialog General כללי Valid From בתוקף מן Valid To בתוקף עד Serial Number מספר סידורי Version גרסא Subject נושא Organization ארגון Common Name שם כללי Locality מקומיות Organizational Unit יחידה ארגונית Country ארץ State מחוז Alternate Subject Names שמות נושא חילופיים E-mail Address כתובת דוא״ל DNS Name שם DNS Issuer מנפיק Swift::QtChatListWindow Add New Bookmark הוסף סימנייה חדשה Edit Bookmark ערוך סימנייה Remove Bookmark הסר סימנייה Clear recents טהר אחרונות Swift::QtChatView Clear log טהר רשומת יומן You are about to clear the contents of your chat log. בחרת לטהר את התכנים של יומן השיחה שלך. Are you sure? האם דעתך גמורה? %1 edited ערוכה %1 ערוך Waiting for other side to accept the transfer. ממתין כעת לקצה האחר לקבל את ההעברה. Cancel ביטול Negotiating... מסדיר כעת... Transfer has been canceled! העברה בוטלה! Transfer completed successfully. העברה הושלמה בהצלחה. Transfer failed. העברה נכשלה. Started whiteboard chat שיחת לוח לבן הותחלה Show whiteboard הצג לוח לבן Whiteboard chat has been canceled שיחת לוח לבן בוטלה Whiteboard chat request has been rejected בקשה לשיחת לוח לבן נדחתה Return to room חזור אל חדר Swift::QtChatWindow Correcting תיקון This chat may not support message correction. If you send a correction anyway, it may appear as a duplicate message שיחה זו עשויה ש אפשרי ששיחה זו לא תומכת בתיקון הודעות. אם תשלח תיקון בכל זאת, זה עשוי להופיע כהודעה כפולה This chat does not support message correction. If you send a correction anyway, it will appear as a duplicate message שיחה זו לא תומכת בתיקון הודעות. אם תשלח תיקון בכל זאת, זה יופיע כהודעה כפולה This message has not been received by your server yet. הודעה זו לא התקבלה עדיין על ידי שרתך. This message may not have been transmitted. אפשרי שהודעה זו לא שודרה. The receipt for this message has been received. הקבלה עבור הודעה זו התקבלה. The receipt for this message has not yet been received. The recipient(s) might not have received this message. הקבלה עבור הודעה זו עדיין לא התקבלה. ייתכן שהקצה המרוחק לא קיבל את הודעה זו. Send file שלח קובץ Cancel ביטול Set Description הגדר תיאור Send שלח Receiving file מקבל כעת קובץ Accept הסכם Starting whiteboard chat מתחיל כעת שיחת לוח לבן %1 would like to start a whiteboard chat ‫%1 רוצה להתחיל שיחת לוח לבן File transfer description תיאור העברת קובץ Description: תיאור: Save File שמור קובץ Change subject… שנה נושא… Configure room… הגדר חדר… Edit affiliations… ערוך שיוכים… Destroy room החרב חדר Invite person to this room… הזמן אישיות אל חדר זה… Change room subject שנה נושא חדר New subject: נושא חדש: Confirm room destruction אמת חורבן חדר Are you sure you want to destroy the room? האם אתה בטוח שברצונך להרוס את החדר? This will destroy the room. פעולה זו תחריב את החדר. Accept Invite הסכם להזמנה Couldn't send message: %1 לא ניתן היה לשלוח הודעה: %1 Swift::QtContactEditWidget Name: שם: Groups: קבוצות: New Group: קבוצה חדשה: Swift::QtContactEditWindow Edit contact ערוך קשר Remove contact הסר קשר OK אישור Confirm contact deletion ודא מחיקת קשר Are you sure you want to delete this contact? האם אתה בטוח שברצונך למחוק את קשר זה? This will remove the contact '%1' from all groups they may be in. פעולה זו תסיר את הקשר '%1' מן כל הקבוצות בהן הם עשויים להיות. Swift::QtEventWindow Display Notice הצג התראה Swift::QtFileTransferListWidget Clear Finished Transfers טהר העברות גמורות File Transfer List רשימת העברת קובץ Swift::QtHistoryWindow History היסטוריה Swift::QtInviteToChatWindow Users to invite to this chat (one per line): משתמשים להזמנה אל שיחה זו (אחד בכל שורה): If you want to provide a reason for the invitation, enter it here ניתן להזינה כאן אם ברצונך לספק סיבה עבור ההזמנה, הזן אותה כאן Swift::QtJoinMUCWindow someroom@rooms.example.com Swift::QtLoginWindow User address: כתובת משתמש: User address - looks like someuser@someserver.com כתובת משתמש - נראית כמו someuser@someserver.com Example: alice@wonderland.lit דוגמא: alice@wonderland.lit Password: סיסמה: Click if you have a personal certificate used for login to the service. ברשותי תעודה אישית לשימוש עבור התחברותי אל השירות. Connect התחבר Remember Password? זכור סיסמה? Login Automatically? התחבר אוטומטית? &Swift &General &כללי &About %1 &אודות %1‫ &Show Debug Console &הצג מסוף ניפוי שגיאות Show &File Transfer Overview הצג סקירת העברת ק&ובץ &Play Sounds &נגן צלילים Display Pop-up &Notifications הצג התראות &קופצות &Quit י&ציאה Remove profile הסר דיוקן Remove the profile '%1'? להסיר את הדיוקן '%1'? Cancel ביטול Confirm terms of use אמת תנאי שימוש Select an authentication certificate בחר תעודת אימות P12 files (*.cert *.p12 *.pfx);;All files (*.*) קבצי P12 ‫(‪*.cert *.p12 *.pfx‬);;כל הקבצים (*.*) The certificate presented by the server is not valid. התעודה המוצגת על ידי השרת אינה תקפה. Would you like to permanently trust this certificate? This must only be done if you know it is correct. you know for certain it האם ברצונך לסמוך על תעודה זו לצמיתות? פעולה זו צריכה להיעשות אך ורק אם ידוע לך בוודאות שתעודה זו מדויקת. Subject: %1 נושא: %1 SHA-1 Fingerprint: %1 טביעת אצבע SHA-1: ‫%1 Swift::QtMUCConfigurationWindow Cancel ביטול OK אישור Swift::QtMUCSearchWindow Searching חיפוש Swift::QtMainWindow &Contacts &קשרים &Notices ה&תראות C&hats &שיחות &View &תצוגה &Show offline contacts הצג קשרים &לא מקוונים &Show Emoticons הצג &רגשונים &Actions &פעולות Edit &Profile… ערוך &דיוקן… Enter &Room… כנס אל &חדר… &View History… &הצג היסטוריה… &Add Contact… הוסף &קשר… &Edit Selected Contact… &ערוך קשר נבחר… Start &Chat… התחל &שיחה… Run Server Command הרץ פקודת שרת &Request Delivery Receipts משלוח &בקש קבלות מסירה Collecting commands... אוסף כעת פקודות... &Chats &שיחות No Available Commands אין פקודות זמינות Edit &Profile עריכת &פרופיל Enter &Room כניסה אל &חדר &Add Contact &הוספת קשר &Edit Selected Contact &עריכת קשר נוכחי Start &Chat התחלת &שיחה &Sign Out &התנתק Swift::QtNameWidget Show Nickname הצג שם כינוי (No Nickname Set) (לא נקבע שם כינוי) Show Address הצג כתובת Edit Profile ערוך דיוקן Swift::QtOccupantListWidget No actions for this user לא קיימות אין פעולות עבור משתמש זה Kick user בעט משתמש Kick and ban user בעט ואסור משתמש Make moderator הפוך לאחראי Make participant הפוך למשתתף Remove voice שלול ביטוי Add to contacts הוסף אל קשרים Swift::QtProfileWindow Edit Profile ערוך דיוקן Nickname: שם כינוי: Save שמור Swift::QtRosterHeader Connection is secured חיבור הינו מאובטח Swift::QtRosterWidget Edit… ערוך… Remove הסר Send File שלח קובץ Start Whiteboard Chat התחל שיחת לוח לבן All Files (*);; כל הקבצים (*);; Rename שנה שם Rename group שנה שם קבוצה Enter a new name for group '%1': הזן שם חדש עבור קבוצה '%1': Swift::QtStatusWidget Connecting מתחבר כעת (No message) (אין הודעה) Swift::QtSubscriptionRequestWindow %1 would like to add you to their contact list. Would you like to add them to your contact list and share your status when you're online? If you choose to defer this choice, you will be asked again when you next login. %1 רוצה להוסיפך אל רשימת הקשרים שלהם. האם ברצונך להוסיפם אל רשימת הקשרים שלך ולשתף את מצב חיבורך כאשר הינך במצב מקוון? במידה ותיבחר האפשרות לדחות, אני אתשאל אותך שוב בהתחברותך הבאה. You have already replied to this request כבר השבת לבקשה זו OK אישור Yes כן No לא Defer עכב Swift::QtTreeWidget Edit עריכה Remove הסרה Rename שינוי שם Rename group שינוי שם קבוצה Enter a new name for group '%1': נא להזין שם חדש עבור הקבוצה '%1': Swift::QtUserSearchDetailsPage Please choose a name for the contact, and select the groups you want to add the contact to. נא להזין שם עבור הקשר, ולבחור את הקבוצות אליהן ברצונך להוסיף את הקשר. Swift::QtUserSearchFirstPage %1. If you know their address you can enter it directly, or you can search for them. %1. אם כתובתם ידועה לך ביכולתך להזינה ישירות, לחלופין ביכולתך לחפש עבורם. Add another user to your contact list הוסף משתמש אחר אל רשימת קשרים Chat to another user שוחח עם משתמש אחר Swift::QtUserSearchWindow Add Contact הוסף קשר Chat to User שיח שוחח עם משתמש alice@wonderland.lit How would you like to find the user to add? כיצד ברצונך למצוא את המשתמש להוספה? How would you like to find the user to chat to? כיצד ברצונך למצוא את המשתמש לשיחה? Error while searching שגיאה במהלך חיפוש This server doesn't support searching for users. שרת זה לא תומך בחיפוש עבור משתמשים. Swift::QtWebView Clear טהר Increase font size הגדל מידת גופן Decrease font size הקטן מידת גופן Swift::QtWhiteboardWindow Closing window is equivalent closing the session. Are you sure you want to do this? סגירת חלון הינה שקולה אל סגירת הסשן. האם אתה בטוח שברצונך לעשות זאת? Swift::QtXMLConsoleWidget Console מסוף Trace input/output חקור קלט/פלט Clear טהר Debug Console מסוף ניפוי שגיאות <!-- IN --> <!-- נכנסת --> <!-- OUT --> <!-- יוצאת --> TRANSLATION_INFO TRANSLATION_AUTHOR Isratine Citizen TRANSLATION_LICENSE This string contains the license under which this translation is licensed. We ask you to license the translation under the BSD license. Please read http://www.opensource.org/licenses/bsd-license.php, and if you agree to release your translation under this license, use the following (untranslated) text: 'This translation is licensed under the BSD License. See http://www.opensource.org/licenses/bsd-license.php' 'תרגום זה הינו רשוי תחת הרשיון BSD. למידע נוסף אצל http://www.opensource.org/licenses/bsd-license.php' 'This translation is licensed under the BSD License. See http://www.opensource.org/licenses/bsd-license.php' swift-im-2.0+dev6/Swift/Translations/swift_pl.ts0000644000175000017500000021033612227051774021644 0ustar kismithkismith UTF-8 Starting chat with %1% in chatroom %2% Rozpoczynanie rozmowy z %1% w pokoju %2% Starting chat with %1% - %2% Rozpoczynanie rozmowy z %1% - %2% me ja %1% has gone offline %1% rozłączył się %1% has become available %1% jest teraz dostępny %1% has gone away %1% jest teraz nieobecny %1% is now busy %1% jest teraz zajęty The day is now %1% Bieżąca data to %1% Error sending message Błąd przy wysyłaniu wiadomości Bad request Nieprawidłowe żądanie Conflict Konflikt This feature is not implemented Ta funkcjonalność nie została zaimplementowana Forbidden Zabronione Recipient can no longer be contacted Z odbiorcą nie można się już skontaktować Internal server error Wewnętrzny błąd serwera Item not found Nie znaleziono obiektu JID Malformed Nieprawidłowy JID Message was rejected Wiadomość została odrzucona Not allowed Niedozwolone Not authorized Brak autoryzacji Payment is required Wymagana opłata Recipient is unavailable Odbiorca jest niedostępny Redirect Przekierowanie Registration required Wymagana rejestracja Recipient's server not found Nie odnaleziono serwera adresata Remote server timeout Przekroczony czas zdalnego serwera The server is low on resources Brak zasobów na serwerze The service is unavailable Serwer jest niedostępny A subscription is required Wymagana subskrypcja Undefined condition Niezdefiniowany przypadek Unexpected request Niespodziewane żądanie Room %1% is not responding. This operation may never complete. Pokój %1% nie odpowiada. Ta operacja może się nigdy nie zakończyć. Unable to enter this room Nie można wejść do tego pokoju Unable to enter this room as %1%, retrying as %2% Nie udało się wejść do pokoju jako %1%, ponowna próba jako %2% No nickname specified Nie podano pseudonimu A password needed Wymagane podanie hasła Only members may enter Tylko członkowie mogą wejść do pokoju You are banned from the room Twój dostęp do pokoju został zablokowany The room is full Pokój jest pełny The room does not exist Pokój nie istnieje You have entered room %1% as %2%. Wszedłeś do pokoju %1% jako %2%. %1% has entered the room as a %2%. %1% wszedł do pokoju jako %2%. %1% has entered the room. %1% wszedł do pokoju. moderator moderator participant uczestnik visitor odwiedzający The room subject is now: %1% Aktualny temat pokoju: %1% %1% is now a %2% %1% ma teraz rolę '%2%' Moderators Moderatorzy Participants Uczestnicy Visitors Odwiedzający Occupants Użytkownicy Trying to enter room %1% Wchodzenie do pokoju %1% %1% has left the room %1% opuścił(a) pokój You have left the room Opuściłeś(aś) pokój and i %1% have entered the room %1% dołączyli(ły) do pokoju %1% has entered the room %1% dołączył(a) do pokoju %1% have left the room %1% opuścili(ły) pokój %1% have entered then left the room %1% weszli(ły) a następnie opuścili(ły) pokój %1% has entered then left the room %1% dołączył(a) a następnie opuścił(a) pokój %1% have left then returned to the room %1% opuścili(ły) a następnie wrócili(ły) do pokoju %1% has left then returned to the room %1% opuścił(a) a następnie wrócił(a) do pokoju %1% wants to add you to his/her contact list %1% chce dodać Cię do swojej listy kontaktów Error Błąd Unknown Error Nieznany błąd Unable to find server Nie można odnaleźć serwera Error connecting to server Błąd przy łączeniu z serwerem Error while receiving server data Błąd podczas odbierania danych z serwera Error while sending data to the server Błąd podczas odbierania danych z serwera Error parsing server data Błąd parsowania danych serwera Login/password invalid Nieprawidłowa nazwa użytkownika lub hasło Error while compressing stream Błąd podczas pakowania strumienia Server verification failed Weryfikacja serwera nie powiodła się Authentication mechanisms not supported Mechanizm uwierzytelniania nie jest obsługiwany Unexpected response Nieoczekiwana odpowiedź Error binding resource Błąd ustawiania zasobu Error starting session Błąd rozpoczynania sesji Stream error Błąd strumienia Encryption error Błąd szyfrowania Error loading certificate (Invalid password?) Błąd otwierania certyfikatu (nieprawidłowe hasło?) Certificate not authorized Certyfikat nieautoryzowany Unknown certificate Nieznany certyfikat Certificate has expired Minęła data ważności certyfikatu Certificate is not yet valid Certyfikat nie jest jeszcze ważny Certificate is self-signed Certyfikat wystawiony samodzielnie Certificate has been rejected Certyfikat został odrzucony Certificate is not trusted Certyfikat jest niezaufany Certificate cannot be used for encrypting your connection Certyfikat nie może być użyty do szyfrowania tego połączenia Certificate path length constraint exceeded Przekroczono ograniczenie długości ścieżki certyfikatu Invalid certificate signature Nieprawidłowy podpis certyfikatu Invalid Certificate Authority Nieprawidłowy urząd certyfikacji Certificate does not match the host identity Certyfikat nie pasuje to tożsamości serwera Certificate error Błędny certyfikat Reconnect to %1% failed: %2%. Will retry in %3% seconds. Ponowne połączenie z %1% nie udało się: %2%. Kolejna próba za %3% sekund(y). Disconnected from %1%: %2%. Rozłączono z %1%: %2%. Contacts Kontakty Server %1% rejected contact list change to item '%2%' Serwer %1% odrzucił zmianę elementu '%2%' listy kontaktów Available Dostępny Away Z dala od komputera Busy Zajęty Offline Rozłączony There was an error publishing your profile data Wystąpił błąd podczas publikowania twojego profilu CloseButton Close Tab Zamknij kartę MAC_APPLICATION_MENU Services Usługi Hide %1 Ukryj %1 Hide Others Ukryj pozostałe Show All Pokaż wszystko Preferences... Preferencje… Quit %1 Zakończ %1 About %1 %1… QApplication QT_LAYOUT_DIRECTION Translate this to LTR for left-to-right or RTL for right-to-left languages LTR QDialogButtonBox &Yes &Tak &No &Nie &OK &OK OK OK &Cancel &Anuluj Cancel Anuluj QLineEdit Select All Zaznacz wszystko &Undo &Cofnij &Redo &Przywróć Cu&t W&ytnij &Copy S&kopiuj &Paste &Wklej Delete Skasuj QMessageBox Show Details... Pokaż szczegóły... Hide Details... Ukryj szczegóły... QObject No rooms found Nie odnaleziono pokoi %1 would like to add you to their contact list. %1 chce Ciebie dodać do swojej listy kontaktów. %1 would like to add you to their contact list, saying '%2' %1 chce Ciebie dodać do swojej listy kontaktów mówiąc '%2' QScrollBar Scroll here Przewiń tutaj Top Do góry Bottom W dół Page up Strona do góry Page down Strona w dół Scroll up Przewiń do góry Scroll down Przewiń w dół QTextControl Select All Zaznacz wszystko &Copy S&kopiuj &Undo &Cofnij &Redo &Przywróć Cu&t W&ytnij &Paste &Wklej Delete Skasuj QWebPage Copy Link Skopiuj odsyłacz Copy Skopiuj Copy Image Skopiuj obrazek Scroll here Przewiń tutaj Top Do góry Bottom W dół Page up Strona do góry Page down Strona w dół Scroll up Przewiń do góry Scroll down Przewiń w dół QWizard < &Back < &Wstecz &Finish &Zakończ &Help &Pomoc Go Back Wróć Continue Kontynuuj Commit Dokonaj Done Zrobione Quit Zakończ Help Pomoc Cancel Anuluj &Next &Dalej &Next > &Dalej > QtBookmarkDetailWindow Edit Bookmark Details Edytuj szczegóły zakładki Bookmark Name: Nazwa zakładki: Room Address: Adres pokoju: Your Nickname: Twój pseudonim: Room password: Hasło pokoju: Join automatically Automatycznie otwieraj QtJoinMUCWindow Enter Room Wejdź do pokoju Room: Pokój: Search ... Szukaj ... Nickname: Pseudonim: Enter automatically in future W przyszłości wchodź automatycznie QtMUCSearchWindow Search Room Szukaj pokoju Service: Usługa: Cancel Anuluj List rooms Lista pokoi OK OK QtUserSearchFieldsPage Nickname: Pseudonim: First name: Imię: Last name: Nazwisko: E-Mail: E-mail: Fetching search fields Pobieranie formularza wyszukiwania QtUserSearchFirstPage Add a user Dodaj użytkownika Add another user to your contact list. If you know their address you can add them directly, or you can search for them. Dodaj nowego użytkownika do swojej listy kontaktów. Możesz od razu wpisać adres jeśli go znasz. Możesz też użyć wyszukiwarki. I know their address: Znam adres: I'd like to search my server Chcę poszukać na moim serwerze I'd like to search another server: Chcę poszukać na innym serwerze: QtUserSearchWizard Find User Znajdź użytkownika Swift::ChatListModel Bookmarked Rooms Zakładki pokoi Swift::QtAboutWidget About %1 O %1 Version %1 Wersja %1 Built with Qt %1 Zbudowane z Qt %1 Running with Qt %1 Uruchomione z Qt %1 Using the English translation by %1 Autor polskiego tłumaczenia: %1 View License Pokaż licencję Swift::QtAvatarWidget No picture Brak obrazu Select picture ... Wybierz obraz ... Clear picture Usuń obraz Select picture Wybierz obraz Image Files (*.png *.jpg *.gif) Pliki obrazów (*.png *.jpg *.gif) Error Błąd The selected picture is in an unrecognized format Nie można rozpoznać formatu wybranego obrazu Swift::QtBookmarkDetailWindow Bookmark not valid Nieprawidłowa zakładka You must specify a valid room address (e.g. someroom@rooms.example.com). Musisz podać prawidłowy adres pokoju (np. jakiśpokój@pokoje.example.com). Swift::QtChatListWindow Add New Bookmark Dodaj nową zakładkę Edit Bookmark Edytuj zakładkę Remove Bookmark Usuń zakładkę Swift::QtChatView Clear log Wyczyść zapis rozmowy You are about to clear the contents of your chat log. Zapis rozmowy zostanie wyczyszczony. Are you sure? Czy na pewno? Swift::QtChatWindow This message has not been received by your server yet. Ta wiadomość nie dotarła jeszcze do twojego serwera. This message may not have been transmitted. Tej wiadomości być może nie udało się przesłać. Couldn't send message: %1 Nie udało się wysłać wiadomości: %1 Swift::QtContactEditWidget Name: Nazwa: Groups: Grupy: New Group: Nowa grupa: Swift::QtContactEditWindow Edit contact Edytuj kontakt Remove contact Usuń kontakt OK OK Confirm contact deletion Potwierdź usunięcie kontaktu Are you sure you want to delete this contact? Czy na pewno chcesz usunąć ten kontakt? This will remove the contact '%1' from all groups they may be in. Ta operacja usunie kontakt '%1' z wszystkich grup, w których się znajduje. Swift::QtEventWindow Display Notice Wyświetl zdarzenie Swift::QtJoinMUCWindow someroom@rooms.example.com .lit seems more safe than przyklad.pl (translated example.com), because I have no idea what this website may contain tomorrow jakiś-pokój@pokoje.kraina-czarów.lit Swift::QtLoginWindow User address: Adres użytkownika: User address - looks like someuser@someserver.com Adres użytkownika - w postaci ktoś@jakiśserwer.com Example: alice@wonderland.lit Przykład: alicja@kraina.czarów.lit Password: Hasło: Click if you have a personal certificate used for login to the service. Kliknij jeśli masz osobisty certyfikat do logowania do tej usługi. Connect Połącz Remember Password? Zapamiętaj hasło Login Automatically? Zaloguj automatycznie &Swift &Swift &General &Ogólne &About %1 &O %1 &Show Debug Console &Pokaż konsolę &Play Sounds O&dtwarzaj dźwięki Display Pop-up &Notifications &Wyświetlaj wyskakujące powiadomienia &Quit Za&kończ Remove profile Usuń profil Remove the profile '%1'? Usunąć profil '%1'? Cancel Anuluj Select an authentication certificate Wybierz certyfikat uwierzytelniania The certificate presented by the server is not valid. Certyfikat serwera jest nieprawidłowy. Would you like to permanently trust this certificate? This must only be done if you know it is correct. Czy chcesz na stałe zaufać temu certyfikatowi. Zrób to tylko jeśli masz pewność, że jest on poprawny. Subject: %1 Temat: %1 SHA-1 Fingerprint: %1 Odcisk palca SHA-1: %1 Swift::QtMUCSearchWindow Searching Wyszukiwanie Swift::QtMainWindow &Contacts &Kontakty &Notices &Zdarzenia C&hats &Rozmowy &View &Widok &Show offline contacts &Pokazuj rozłączone kontakty &Actions &Akcje Edit &Profile… Edytuj &profil… Enter &Room… &Wejdź do pokoju… &Add Contact… &Dodaj kontakt… &Edit Selected Contact… &Edytuj wybrany kontakt… Start &Chat… &Rozpocznij rozmowę &Sign Out W&yloguj się Swift::QtNameWidget Show Nickname Pokazuj pseudonim (No Nickname Set) (brak pseudonimu) Show Address Pokazuj adres Edit Profile Edytuj profil Swift::QtProfileWindow Edit Profile Edytuj profil Nickname: Pseudonim: Save Zapisz Swift::QtStatusWidget Connecting Łączenie (No message) (Brak opisu) Swift::QtSubscriptionRequestWindow You have already replied to this request Odpowiedź na to pytanie została już udzielona %1 would like to add you to their contact list. Would you like to add them to your contact list and share your status when you're online? If you choose to defer this choice, you will be asked again when you next login. %1 chce dodać Cię do swojej listy kontaktów. Czy chcesz dodać tą osobę do swojej listy i udostępniać swój status? Możesz odłożyć tę decyzję na później - pytanie pojawi się ponownie gdy zalogujesz się następnym razem. OK OK Yes Tak No Nie Defer Później Swift::QtTreeWidget Edit Edytuj Remove Usuń Rename Zmień nazwę Rename group Zmień nazwę grupy Enter a new name for group '%1': Podaj nową nazwę dla grupy '%1': Swift::QtUserSearchDetailsPage Please choose a name for the contact, and select the groups you want to add the contact to. Podaj nazwę dla kontaktu i wybierz grupy, do których chcesz go dodać. Swift::QtUserSearchFirstPage %1. If you know their address you can enter it directly, or you can search for them. %1. Możesz od razu wpisać adres jeśli go znasz. Możesz też użyć wyszukiwarki. Add another user to your contact list Dodaj nowego użytkownika do swojej listy kontaktów Chat to another user Porozmawiaj z innym użytkownikiem Swift::QtUserSearchWindow Add Contact Dodaj kontakt Chat to User Porozmawiaj z użytkownikiem alice@wonderland.lit alicja@kraina-czarow.lit How would you like to find the user to add? Jak chcesz wyszukać użytkownika do dodania? How would you like to find the user to chat to? Jak chcesz wyszukać użytkownika do porozmawiania? Error while searching Błąd podczas wyszukiwania This server doesn't support searching for users. Ten serwer nie obsługuje wyszukiwania użytkowników. Swift::QtWebView Clear Wyczyść Swift::QtXMLConsoleWidget Console Konsola Trace input/output Śledź wejście/wyjście Clear Wyczyść Debug Console Konsola debugowania <!-- IN --> <!-- PRZYCHODZĄCE --> <!-- OUT --> <!-- WYCHODZĄCE --> TRANSLATION_INFO TRANSLATION_AUTHOR Maciej Niedzielski TRANSLATION_LICENSE This string contains the license under which this translation is licensed. We ask you to license the translation under the BSD license. Please read http://www.opensource.org/licenses/bsd-license.php, and if you agree to release your translation under this license, use the following (untranslated) text: 'This translation is licensed under the BSD License. See http://www.opensource.org/licenses/bsd-license.php' This translation is licensed under the BSD License. See http://www.opensource.org/licenses/bsd-license.php swift-im-2.0+dev6/Swift/Translations/swift_ca.ts0000644000175000017500000034506712227051774021626 0ustar kismithkismith UTF-8 Starting chat with %1% in chatroom %2% Començant conversa amb %1% a la sala %2% Starting chat with %1% - %2% Començant conversa amb %1% - %2% This chat doesn't support delivery receipts. Aquesta conversa no és compatible amb confirmacions de lliurament. This chat may not support delivery receipts. You might not receive delivery receipts for the messages you sent. Aquesta conversa potser no sigui compatible amb confirmacions de lliurament. Potser no rebis les confirmacions de lliurament dels missatges que enviïs. me Jo %1% has gone offline %1% s'ha desconnectat %1% has become available %1% es troba disponible %1% has gone away %1% s'ha absentat %1% is now busy TMPFIX genero: o/a? sinonimo? masculino? %1% està ocupat/da The day is now %1% El día es ara %1% Couldn't send message: %1% No s'ha pogut enviar el missatge: %1% Error sending message Error enviant missatge Bad request Sol·licitud incorrecta Conflict Conflicte This feature is not implemented Aquesta característica no es troba implementada Forbidden Prohibit Recipient can no longer be contacted Ja no et pot contactar amb el destinatari Internal server error Error intern del servidor Item not found Element no trobat JID Malformed JID Malformat Message was rejected El missatge ha sigut rebutjat Not allowed No permès Not authorized No autoritzat Payment is required Pagament requerit Recipient is unavailable Destinatari no disponible Redirect Redirecció Registration required Registre requerit Recipient's server not found No s'ha trobat el servidor del destinatari Remote server timeout Temps d'espera del servidor remot esgotat The server is low on resources El servidor te pocs recursos disponibles The service is unavailable El servei no es troba disponible A subscription is required Es requereix una subscripció Undefined condition Condició no definida Unexpected request Sol·licitud inesperada Room %1% is not responding. This operation may never complete. La sala %1% no respon. És possible que aquesta operació no es completi mai. Unable to enter this room No es pot entrar a aquesta sala Unable to enter this room as %1%, retrying as %2% No es pot entrar a aquesta sala com a %1%, provant de nou com a %2% No nickname specified No s'ha especificat un nick A password needed Es necessita contrasenya Only members may enter Només els membres poden entrar You are banned from the room bloquejat? Estàs vetat de la sala The room is full La sala es troba plena The room does not exist La sala no existeix Couldn't join room: %1%. No s'ha pogut entrar a la sala: %1%. You have entered room %1% as %2%. Has entrat a la sala %1% com a %2%. %1% has entered the room as a %2%. %1% ha entrat a la sala com a %2%. %1% has entered the room. %1% ha entrat a la sala. moderator moderador participant participant visitor visitant The room subject is now: %1% El tema de la sala és ara: %1% %1% is now a %2% %1% ara es un %2% Moderators Moderadors Participants Participants Visitors Visitants Occupants TMPFIX, used where? Ocupants Trying to enter room %1% Intentant entrar a la sala %1% %1% has left the room%2% %1% ha sortit de la sala %2% You have been kicked out of the room Has sigut expulsat de la sala You have been banned from the room Has sigut vetat de la sala You are no longer a member of the room and have been removed Ja no ets un membre de la sala i has sigut expulsat The room has been destroyed La sala ha estat destruïda %1% has left the room %1% ha sortit de la sala Room configuration failed: %1%. La configuració de la sala ha fallat: %1%. Occupant role change failed: %1%. El canvi de rol de l'ocupant ha fallat: %1%. You have left the room Has sortit de la sala The correct room password is needed Es necessita la contrasenya de sala correcta and i %1% have entered the room %1% han entrat a la sala %1% has entered the room %1% ha entrat a la sala %1% have left the room %1% han sortit de la sala %1% have entered then left the room %1% han entrat i sortit de la sala %1% has entered then left the room %1% ha entrat i sortit de la sala %1% have left then returned to the room %1% han sortit i tornat a la sala %1% has left then returned to the room %1% ha sortit i tornat a la sala %1% wants to add you to his/her contact list %1% vol afegir-te a la seva llista de contactes Error Error %1% has invited you to enter the %2% room %1% t'ha convidat a entrar a la sala %2% User address invalid. User address should be of the form 'alice@wonderland.lit' Adreça d'usuari no vàlida. L'adreça d'usuari ha de ser de la forma 'alicia@paismeravelles.lit' Unknown Error Error Desconegut Unable to find server No es pot trobar el servidor Error connecting to server Error connectant al servidor Error while receiving server data Error al rebre dades del servidor Error while sending data to the server Error enviant dades al servidor Error parsing server data Error analitzant dades del servidor Login/password invalid Usuari/contrasenya no vàlids Error while compressing stream Error comprimint flux de dades Server verification failed La verificació del servidor ha fallat Authentication mechanisms not supported Mecanisme d'autenticació no soportat Unexpected response Resposta inesperada Error binding resource Error vinculant recurs Error starting session Error iniciant sessió Stream error Error de flux de dades Encryption error Error d'encriptatge Error loading certificate (Invalid password?) Error carregant certificat (Contrasenya no vàlida?) Certificate not authorized Certificat no autoritzat Certificate card removed Targeta de certificat eliminada Unknown certificate Certificat desconegut Certificate has expired El certificat ha caducat Certificate is not yet valid El certificat encara no es vàlid Certificate is self-signed TMPFIX, signatura personal?? El certificat es auto-signat Certificate has been rejected El certificat ha sigut rebutjat Certificate is not trusted El certificat no es de confiança Certificate cannot be used for encrypting your connection El certificat no put set utilitzat per encriptar la teva connexió Certificate path length constraint exceeded TMPFIX S'ha excedit la restricció de la longitud de camí del certificat Invalid certificate signature Signatura de certificat no vàlida Invalid Certificate Authority Entitat Certificadora no Vàlida Certificate does not match the host identity El certificat no coincideix amb la identitat del servidor Certificate has been revoked El certificat ha estat revocat Unable to determine certificate revocation state No es pot determinar l'estat de revocació del certificat Certificate error Error de certificat Re-enter credentials and retry Torna a introduir les credencials i prova de nou Disconnected from %1%: %2%. To reconnect, Sign Out and provide your password again. Desconnectat de %1%: %2%. Per tornar a connectar, desconnecta't i proporciona la teva contrasenya de nou. Reconnect to %1% failed: %2%. Will retry in %3% seconds. La reconnexió a %1% ha fallat: %2%. S'intentarà de nou d'aquí a %3% segons. Disconnected from %1%: %2%. Desconnectat de %1%: %2%. Contacts Contactes Server %1% rejected contact list change to item '%2%' El servidor %1% ha rebutjat el canvi a l'element '%2%' de la llista de contactes Available Disponible Away Ausent Busy Ocupat Offline Desconnectat There was an error publishing your profile data Hi ha hagut un error publicant les dades del teu perfil %1% (%2%) %1% (%2%) %1% and %2% others (%3%) %1% i %2% més (%3%) %1%, %2% (%3%) %1%, %2% (%3%) TLS Client Certificate Selection Selecció de certificat de client TLS Select a certificate to use for authentication Selecciona un certificat per utilitzar com a autenticació ChatListModel Bookmarked Rooms Sales en Marcadors CloseButton Close Tab Tancar Pestanya MAC_APPLICATION_MENU Services Serveis Hide %1 Ocultar %1 Hide Others Ocultar Altres Show All Mostrar Tot Preferences... Preferències... Quit %1 Sortir de %1 About %1 Sobre %1 QApplication QT_LAYOUT_DIRECTION Translate this to LTR for left-to-right or RTL for right-to-left languages LTR QDialogButtonBox &Yes &Si &No &No &OK D'&acord OK D'acord &Cancel &Cancel·lar Cancel Cancel·lar QLineEdit Select All Sel·leccionar Tot &Undo &Desfer &Redo &Refer Cu&t Re&tallar &Copy &Copiar &Paste &Enganxar Delete Eliminar QMessageBox Show Details... Mostrar Detalls... Hide Details... Ocultar Detalls... QObject No rooms found No s'han trobat sales %1 would like to add you to their contact list. %1 vol afegir-te a la seva llista de contactes. %1 would like to add you to their contact list, saying '%2' %1 vol afegir-te a la seva llista de contactes, dient '%2' %1 has invited you to enter the %2 room. %1 t'ha convidat a entrar a la sala %2. You've been invited to enter the %1 room. T'han convidat a entrar a la sala %1. Reason: %1 Motiu: %1 This person may not have really sent this invitation! És possible que aquesta persona no hagi enviat realment aquesta invitació! Direction Sentit? Direcció Other Party Sounds weird... "El teu contacte" o similar? L'altra part State Estat Progress Progrés Size Mida Incoming Entrant Outgoing Sortint Waiting for start Esperant que comenci Waiting for other side to accept Esperant a que l'altra banda accepti Negotiating Negociant Transferring Transferint Finished Finalitzat Failed Fallit Canceled Cancel·lat Connection Options Opcions de connexió QScrollBar Scroll here Desplaçar aquí Top Part superior Bottom Part inferior Page up Retrocedir pàgina Page down Avançar pàgina Scroll up Desplaçar a dalt Scroll down Desplaçar a baix QTextControl Select All Sel·leccionar Tot &Copy &Copiar &Undo &Desfer &Redo &Refer Cu&t Re&tallar &Paste &Enganxar Delete Eliminar QWebPage Copy Link Copiar Enllaç Copy Copiar Copy Image Copiar Imatge Scroll here Desplaçar aquí Top Part superior Bottom Part inferior Page up Retrocedir pàgina Page down Avançar pàgina Scroll up Desplaçar a dalt Scroll down Desplaçar a baix QWizard < &Back < &Enrrere &Finish &Finalitzar &Help A&juda Go Back Retrocedir Continue Continuar Commit Publicar Done Fet Quit Sortir Help Ajuda Cancel Cancel·lar &Next &Següent &Next > &Seguent > QtAboutWidget About %1 Sobre %1 Version %1 Versió %1 Built with Qt %1 Compil·lat amb Qt %1 Running with Qt %1 Executant-se amb Qt %1 Using the English translation by %1 Utilitzant la traducció catalana per %1 View License Veure Llicencia QtAffiliationEditor Edit Affiliations Editar afiliacions Affiliation: Afiliació: Owner Propietari Administrator Administrador Member Membre Outcast (Banned) Marginat (Vetat) Add User Afegir usuari Remove User Eliminar usuari QtBookmarkDetailWindow Edit Bookmark Details Editar Detalls de Marcador Bookmark Name: Nom del Marcador: Room Address: Adreça de la sala: Your Nickname: El teu nick: Room password: Contrasenya de la sala: Enter automatically Entrar automàticament Join automatically Entrar automàticament QtCertificateViewerDialog Certificate Viewer Visualitzador de certificats QtChatListWindow Add New Bookmark Afegir Nou Marcador Edit Bookmark Editar Marcador Remove Bookmark Eliminar Marcador QtConnectionSettings Connection Options Opcions de connexió Connection Method: Mètode de connexió: Automatic Automàtic Manual Manual BOSH BOSH Secure connection: Connexió segura: Never Mai Encrypt when possible Encriptar quan sigui possible Always encrypt Encriptar sempre Allow Compression Permetre compressió Allow sending password over insecure connection Permetre enviar contrasenya sobre connexió insegura Manually select server Seleccionar servidor manualment Hostname: Nom de servidor: Port: Port: Connection Proxy Proxy de connexió Proxy type: Tipus de proxy: None Cap Use system-configured proxy Utilitzar el proxy del sistema SOCKS5 SOCKS5 HTTP Connect HTTP Connect Override system-configured proxy Substituir el proxy del sistema BOSH URI: URI de BOSH: Manually select HTTP proxy Seleccionar proxy HTTP manualment QtEventWindow Display Notice Mostrar Avís QtHistoryWindow History Historial Search: Cercar: Next Següent Previous Anterior QtJoinMUCWindow Enter Room Entrar a la sala Room Address: Adreça de la sala: Your Nickname: El teu nick: Room Password: Contrasenya de la sala: Automatically configure newly created rooms Configurar automàticament sales de nova creació Room: Sala: Search ... Cercar ... Nickname: Nick: Enter automatically in future Entrar automàticament en el futur QtMUCSearchWindow Search Room Cercar Sala Service: Servei: Cancel Cancel·lar OK D'acord List rooms Llistar sales Searching Buscant QtSubscriptionRequestWindow %1 would like to add you to their contact list. Would you like to add them to your contact list and share your status when you're online? If you choose to defer this choice, you will be asked again when you next login. %1 vol afegir-te a la seva llista de contactes. Vols afegir-ho a la teva llista de contactes i compartir el teu estat quan et connectis? Si esculls ajornar aquesta elecció, se't preguntarà de nou la propera vegada que et connectis. You have already replied to this request Ja has respost a aquesta sol·licitud OK D'acord Yes Si No No Defer Ajornar QtUserSearchDetailsPage Please choose a name for the contact, and select the groups you want to add the contact to. Si us plau, escull el nom que li vols donar al contacte, i sel·lecciona els grups als quals vols afegir-ho. QtUserSearchFieldsPage Nickname: Nick: First name: Nom: Last name: Cognom: E-Mail: Correu electrònic: Fetching search fields Obtenint camps de cerca QtUserSearchFirstPage Add a user Afegir un usuari Add another user to your contact list. If you know their address you can add them directly, or you can search for them. Afegir a un altre usuari a la teva llista de contactes. Si coneixes la seva adreça, pots introduir-la directament, o pots buscar a l'usuari. I know their address: Conec la seva adreça: I'd like to search my server Vull buscar al meu servidor I'd like to search another server: Vull buscar a un altre servidor: %1. If you know their address you can enter it directly, or you can search for them. %1. Si coneixes la seva adreça, pots introduir-la directament, o pots buscar a l'usuari. Add another user to your contact list Afegir a un altre usuari a la teva llista de contactes Chat to another user Conversar amb un altre usuari QtUserSearchResultsPage No results. No hi ha resultats. QtUserSearchWindow Add Contact Afegir Contacte Chat to User Conversar amb Usuari alice@wonderland.lit alicia@paismeravelles.lit How would you like to find the user to add? Com vols buscar a l'usuari al que afegir? How would you like to find the user to chat to? Com vols buscar a l'usuari amb el qual conversar? Error while searching Error durant la cerca This server doesn't support searching for users. Aquest servidor no suporta cerca d'usuaris. QtUserSearchWizard Find User Buscar Usuari Swift::ChatListModel Bookmarked Rooms TMPFIX? Sales en Marcadors Recent Chats Converses recents Opened Whiteboards Pissarres obertes Swift::QtAboutWidget About %1 Sobre %1 Version %1 Versió %1 Built with Qt %1 Compil·lat amb Qt %1 Running with Qt %1 Executant-se amb Qt %1 Using the English translation by %1 Utilitzant la traducció catalana per %1 View License Veure Llicencia Swift::QtAdHocCommandWindow Cancel Cancel·lar Back Retrocedir Next Següent Complete Complet Error: %1 Error: %1 Warning: %1 Avís: %1 Error executing command Error executant l'ordre Swift::QtAffiliationEditor Add User Afegir usuari Added User's Address: Adreça de l'usuari afegit: Swift::QtAvatarWidget No picture Sense imatge Select picture ... Escollir imatge ... Clear picture Esborrar imatge Select picture Escollir imatge Image Files (*.png *.jpg *.jpeg *.gif) Fitxers d'imatge (*.png *.jpg *.jpeg *.gif) Image Files (*.png *.jpg *.gif) Arxius d'imatge (*.png *.jpg *.gif) Error Error The selected picture is in an unrecognized format La imatge sel·leccionada es troba en un format no reconegut Swift::QtBookmarkDetailWindow Bookmark not valid Marcador no vàlid You must specify a valid room address (e.g. someroom@rooms.example.com). Has d'especificar un adreça de sala vàlida (ex. algunasala@sales.exemple.com). You must specify a valid room address (e.g. myroom@chats.example.com). Has d'especificar un adreça de sala vàlida (ex. lamevasala@chats.exemple.com). Swift::QtCertificateViewerDialog General General Valid From Vàlid des de Valid To Vàlid fins a Serial Number Número de sèrie Version Versió Subject Subjecte Organization Organització Common Name Nom comú Locality Localitat Organizational Unit Unitat organitzativa Country País State Estat Alternate Subject Names Noms de subjecte alternatius E-mail Address Adreça de correu electrònic DNS Name Nom DNS Issuer Emissor Swift::QtChatListWindow Add New Bookmark Afegir Nou Marcador Edit Bookmark Editar Marcador Remove Bookmark Eliminar Marcador Clear recents Esborrar recents Swift::QtChatView Clear log Esborrar text You are about to clear the contents of your chat log. Estas a punt d'esborrar el contingut d'aquesta conversa. Are you sure? TMPFIX, genero? Estas segur? %1 edited %1 editat Waiting for other side to accept the transfer. Esperant a que l'altra banda accepti la transferència. Cancel Cancel·lar Negotiating... Negociant... Transfer has been canceled! La transferència ha estat cancel·lada! Transfer completed successfully. La transferència s'ha completat amb èxit. Transfer failed. La transferència ha fallat. Started whiteboard chat S'ha començat una conversa de pissarra Show whiteboard Mostrar pissarra Whiteboard chat has been canceled La conversa de pissarra ha estat cancel·lada Whiteboard chat request has been rejected La sol·licitud de conversa de pissarra ha estat rebutjada Return to room Tornar a la sala Swift::QtChatWindow Correcting Corregint This chat may not support message correction. If you send a correction anyway, it may appear as a duplicate message Aquesta conversa potser no sigui compatible amb la correcció de missatges. Si envies una correcció de tota manera, és possible que aparegui com un missatge duplicat This chat does not support message correction. If you send a correction anyway, it will appear as a duplicate message Aquesta conversa no es compatible amb la correcció de missatges. Si envies una correcció de tota manera, es mostrarà com un missatge duplicat This message has not been received by your server yet. Aquest missatge no ha sigut rebut pel teu servidor encara. This message may not have been transmitted. Es possible que aquest missatge no s'hagi transmès. The receipt for this message has been received. S'ha rebut la confirmació per aquest missatge. The receipt for this message has not yet been received. The recipient(s) might not have received this message. Plural pain Encara no s'ha rebut la confirmació per aquest missatge. És possible que els destinataris no l'hagin rebut. Send file Enviar fitxer Cancel Cancel·lar Set Description Establir descripció Send Enviar Receiving file Rebent fitxer Accept Acceptar Starting whiteboard chat Començant conversa de pissarra %1 would like to start a whiteboard chat %1 vol començar una conversa de pissarra File transfer description Descripció de la transferència de fitxer Description: Descripció: Save File Guardar fitxer Change subject… Canviar el tema... Configure room… Configurar sala... Edit affiliations… Editar afiliacions... Destroy room Destruir la sala Invite person to this room… Convidar a una persona a aquesta sala... Change room subject Canviar el tema de la sala New subject: Nou tema: Confirm room destruction Confirmar destrucció de la sala Are you sure you want to destroy the room? Estàs segur de que vols destruir la sala? This will destroy the room. Això destruirà la sala. Accept Invite Acceptar invitació Couldn't send message: %1 No s'ha pogut enviar el missatge: %1 Swift::QtContactEditWidget Name: Nom: Groups: Grups: New Group: Nou Grup: Swift::QtContactEditWindow Edit contact Editar contacte Remove contact Eliminar contacte OK D'acord Confirm contact deletion Confirmar eliminació del contacte Are you sure you want to delete this contact? Segur que vols eliminar aquest contacte? This will remove the contact '%1' from all groups they may be in. Això eliminarà el contacte '%1' de tots els grups als que estigui. Swift::QtEventWindow Display Notice Mostrar Avís Swift::QtFileTransferListWidget Clear Finished Transfers Esborrar transferències finalitzades File Transfer List Llista de transferència de fitxers Swift::QtHistoryWindow History Historial Swift::QtInviteToChatWindow Users to invite to this chat (one per line): conversa o sala? Usuaris per convidar a aquesta conversa (un per línia): If you want to provide a reason for the invitation, enter it here Si vols proporcionar un motiu per la invitació, el pots introduir aquí Swift::QtJoinMUCWindow someroom@rooms.example.com algunasala@sales.exemple.com Swift::QtLoginWindow User address: Adreça d'usuari: User address - looks like someuser@someserver.com Adreça d'usuari - Semblant a usuari@algunservidor.com Example: alice@wonderland.lit Exemple: alicia@paismeravelles.lit Password: Contrasenya: Click if you have a personal certificate used for login to the service. Fes click si tens un certificat personal per connectar al servei. Connect Connectar Remember Password? Recordar Contrasenya? Login Automatically? Connectar Automàticament? &Swift &Swift &General TMPFIX, used where? &General &About %1 &Sobre %1 &Show Debug Console &Mostrar Consola de Depuració Show &File Transfer Overview VERY long. Alternative "Mostrar llista de transferències" Mostrar vista general de transferència de &fitxers &Play Sounds &Reproduir Sons Display Pop-up &Notifications Mostrar &Notificacions Emergents &Quit &Sortir Remove profile Eliminar perfil Remove the profile '%1'? Eliminar el perfil '%1'? Cancel Cancel·lar Confirm terms of use Confirmar condicions d'ús Select an authentication certificate Selecciona un certificat d'autenticació P12 files (*.cert *.p12 *.pfx);;All files (*.*) Fitxers P12 (*.cert *.p12 *.pfx);;Tots els fitxers (*.*) The certificate presented by the server is not valid. El certificat presentat pel servidor no es vàlid. Would you like to permanently trust this certificate? This must only be done if you know it is correct. Vols confiar en aquest certificat de forma permanent? Això s'ha de fer només si saps que es correcte. Subject: %1 Subjecte: %1 SHA-1 Fingerprint: %1 Empremta digital SHA-1: %1 Swift::QtMUCConfigurationWindow Cancel Cancel·lar OK D'acord Swift::QtMUCSearchWindow Searching Buscant Swift::QtMainWindow &Contacts &Contactes &Notices Av&isos C&hats TMPFIX? Kev said "conversations" context C&onverses &View &Veure &Show offline contacts &Mostrar contactes desconnectats &Show Emoticons &Mostrar emoticones &Actions &Accions Edit &Profile… Editar &Perfil… Enter &Room… Entrar a &Sala… &View History… &Veure historial... &Add Contact… &Afegir Contacte… &Edit Selected Contact… &Editar Contacte Sel·leccionat… Start &Chat… Començar &Conversa... Run Server Command Executar ordre de servidor &Sign Out &Desconnectar &Request Delivery Receipts &Demanar confirmació de lliurament Collecting commands... Recopilant ordres... &Chats C&onverses No Available Commands No hi ha ordres disponibles Notices TMPFIX, used? avisos2 Swift::QtNameWidget Show Nickname Mostrar Nick (No Nickname Set) (Sense Nick Definit) Show Address Mostrar Adreça Edit Profile Editar Perfil Swift::QtOccupantListWidget No actions for this user No hi ha accions per aquest usuari Kick user Expulsar usuari Kick and ban user Expulsar i vetar usuari Make moderator Fer moderador Make participant Fer participant Remove voice Treure la veu Add to contacts Afegir a contactes Swift::QtProfileWindow Edit Profile Editar Perfil Nickname: Nick: Save Guardar Swift::QtRosterHeader Connection is secured La connexió és segura Swift::QtRosterWidget Edit… Editar... Remove Eliminar Send File Enviar fitxer Start Whiteboard Chat Començar conversa de pissarra All Files (*);; Tots els fitxers (*);; Rename Canviar el nom Rename group Canviar el nom al grup Enter a new name for group '%1': Introdueix un nom nou pel grup '%1': Swift::QtStatusWidget Connecting Connectant (No message) (Sense missatge) Swift::QtSubscriptionRequestWindow %1 would like to add you to their contact list. Would you like to add them to your contact list and share your status when you're online? If you choose to defer this choice, you will be asked again when you next login. %1 vol afegir-te a la seva llista de contactes. Vols afegir-ho a la teva llista de contactes i compartir el teu estat quan et connectis? Si esculls ajornar aquesta elecció, se't preguntarà de nou la propera vegada que et connectis. You have already replied to this request TMPFIX, used where? Ja has respost a aquesta sol·licitud OK D'acord Yes Si No No Defer TMPFIX: deixar per despres? Ajornar Swift::QtTreeWidget Edit Editar Remove Eliminar Rename Renombrar Rename group Renombrar grup Enter a new name for group '%1': Introdueix un nom nou pel grup %1: New name for %1 Nou nom per %1 Swift::QtUserSearchDetailsPage Please choose a name for the contact, and select the groups you want to add the contact to. Somewhat free translation. TMPFIX? Si us plau, escull el nom que li vols donar al contacte, i sel·lecciona els grups als quals vols afegir-ho. Swift::QtUserSearchFirstPage %1. If you know their address you can enter it directly, or you can search for them. %1. Si coneixes la seva adreça, pots introduir-la directament, o pots buscar a l'usuari. Add another user to your contact list Afegir a un altre usuari a la teva llista de contactes Chat to another user Conversar amb un altre usuari Swift::QtUserSearchWindow Add Contact Afegir Contacte Chat to User Conversar amb Usuari alice@wonderland.lit alicia@paismeravelles.lit How would you like to find the user to add? Com vols buscar a l'usuari al que afegir? How would you like to find the user to chat to? Com vols buscar a l'usuari amb el qual conversar? Error while searching Error durant la cerca This server doesn't support searching for users. Aquest servidor no suporta cerca d'usuaris. Swift::QtWebView Clear Esborrar text Increase font size Augmentar la mida de la lletra Decrease font size Reduir la mida de la lletra Swift::QtWhiteboardWindow Closing window is equivalent closing the session. Are you sure you want to do this? Tancar la finestra es equivalent a tancar la sessió. Estàs segur de que vols fer això? Swift::QtXMLConsoleWidget Console Consola Trace input/output Monitoritzar entrada/sortida Clear Esborrar Debug Console Consola de Depuració <!-- IN --> <!-- ENTRANT --> <!-- OUT --> <!-- SORTINT --> TRANSLATION_INFO TRANSLATION_AUTHOR JanKusanagi TRANSLATION_LICENSE This string contains the license under which this translation is licensed. We ask you to license the translation under the BSD license. Please read http://www.opensource.org/licenses/bsd-license.php, and if you agree to release your translation under this license, use the following (untranslated) text: 'This translation is licensed under the BSD License. See http://www.opensource.org/licenses/bsd-license.php' This translation is licensed under the BSD License. See http://www.opensource.org/licenses/bsd-license.php TRANSLATION_LICENSE Should be the following (untranslated) text: 'This translation is licensed under the BSD License. See http://www.opensource.org/licenses/bsd-license.php' This translation is licensed under the BSD License. See http://www.opensource.org/licenses/bsd-license.php swift-im-2.0+dev6/Swift/Translations/swift_en.ts0000644000175000017500000000011612227051774021624 0ustar kismithkismith swift-im-2.0+dev6/Swift/Translations/swift_fr.ts0000644000175000017500000021105412227051774021636 0ustar kismithkismith UTF-8 Starting chat with %1% in chatroom %2% Démarrer une discussion avec %1% dans le salon %2% Starting chat with %1% - %2% Démarrer une discussion avec %1% - %2% me moi %1% has gone offline %1% est hors ligne %1% has become available %1% est devenu disponible %1% has gone away %1% est parti %1% is now busy %1% est maintenant occupé The day is now %1% Nous sommes maintenant le %1% Error sending message Erreur d'envoi du message Bad request Requête invalide Conflict Conflit This feature is not implemented Cette fonctionnalité n'est pas implementée Forbidden Interdit Recipient can no longer be contacted Le destinataire ne peut plus être contacté Internal server error Erreur interne du serveur Item not found Objet non trouvé JID Malformed JID mal formaté Message was rejected Message a été rejeté Not allowed Non permis Not authorized Non autorisé Payment is required Paiement est requis Recipient is unavailable Le destinataire est indisponible Redirect Rediriger Registration required Inscription requise Recipient's server not found Serveur du destinataire non trouvé Remote server timeout Pas de réponse du serveur distant The server is low on resources Le serveur est faible en ressources The service is unavailable Le service est indisponible A subscription is required Un abonnement est requis Undefined condition Condition non définie Unexpected request Requête inattendue Room %1% is not responding. This operation may never complete. Salon %1% ne réponds pas. Cette opération peut ne jamais se terminer. Unable to enter this room Impossible de joindre ce salon Unable to enter this room as %1%, retrying as %2% Impossible de joindre ce salon en tant que %1%, réessaye en tant que %2% No nickname specified Aucun pseudo spécifié A password needed Un mot de passe est nécessaire Only members may enter Seuls les membres peuvent joindre You are banned from the room Vous êtes bannis du salon The room is full Le salon est plein The room does not exist Le salon n'existe pas You have entered room %1% as %2%. Vous avez rejoint le salon %1% en tant que %2%. %1% has entered the room as a %2%. %1% a rejoint le salon en tant que %2%. %1% has entered the room. %1% a rejoint le salon. moderator modérateur participant participant visitor visiteur The room subject is now: %1% Le sujet du salon est maintenant: %1% %1% is now a %2% %1% est maintenant un %2% Moderators Modérateurs Participants Participants Visitors Visiteurs Occupants Occupants Trying to enter room %1% Tente de joindre le salon %1% %1% has left the room %1% a quitté le salon You have left the room Vous avez quitté le salon and et %1% have entered the room %1% ont rejoint le salon %1% has entered the room %1% a rejoint le salon %1% have left the room %1% ont quitté le salon %1% have entered then left the room %1% ont rejoint et quitté le salon %1% has entered then left the room %1% a rejoint et quitté le salon %1% have left then returned to the room %1% ont quitté et rejoint le salon %1% has left then returned to the room %1% a quitté et rejoint le salon %1% wants to add you to his/her contact list %1% souhaite vous ajouter à sa liste de contacts Error Erreur Unknown Error Erreur inconnue Unable to find server Impossible de trouver le serveur Error connecting to server Erreur de connexion au serveur Error while receiving server data Erreur lors de la réception de données du serveur Error while sending data to the server Erreur lors de l'envoi de données vers le serveur Error parsing server data Erreur de traîtement des données du serveur Login/password invalid Identifiant/mot de passe invalide Error while compressing stream Erreur lors de la compression de flux Server verification failed Vérification du serveur a échoué Authentication mechanisms not supported Méthodes d'authentification non prises en charge Unexpected response Réponse inattendue Error binding resource Erreur de liaison de ressource Error starting session Erreur de démarrage de session Stream error Erreur de flux Encryption error Erreur de cryptage Error loading certificate (Invalid password?) Erreur de chargement du certificat (mot de passe invalide?) Certificate not authorized Certificat non autorisé Unknown certificate Certificat inconnu Certificate has expired Le certificat a expiré Certificate is not yet valid Le certificat n'est pas encore valide Certificate is self-signed Le certificat est auto-signé Certificate has been rejected Le certificat a été rejeté Certificate is not trusted Le certificat n'est pas approuvé Certificate cannot be used for encrypting your connection Le certificat ne peut pas être utilisé pour crypter votre connexion Certificate path length constraint exceeded Contrainte de longueur du chemin d'accès de certificat dépassée Invalid certificate signature Signature de certificat invalide Invalid Certificate Authority Autorité de Certification invalide Certificate does not match the host identity Le certificat ne correspond pas à l'identité hôte Certificate error Erreur de certificat Reconnect to %1% failed: %2%. Will retry in %3% seconds. Reconnexion à %1% a echoué: %2%. Va réessayer dans %3% secondes. Disconnected from %1%: %2%. Déconnecté de %1%: %2%. Contacts Contacts Server %1% rejected contact list change to item '%2%' Serveur %1% a rejeté le changement de liste de contacts en objet '%2%' Available Disponible Away Absent Busy Occupé Offline Hors ligne There was an error publishing your profile data Il y a eu une erreur de publication de vos données de profil CloseButton Close Tab Fermer l'onglet MAC_APPLICATION_MENU Services Services Hide %1 Cacher %1 Hide Others Cacher les autres Show All Tout Voir Preferences... Préférences... Quit %1 Quitter %1 About %1 A propos de %1 QApplication QT_LAYOUT_DIRECTION Translate this to LTR for left-to-right or RTL for right-to-left languages LTR QDialogButtonBox &Yes &Oui &No &Non &OK &OK OK OK &Cancel &Annuler Cancel Annuler QLineEdit Select All Tout sélectionner &Undo &Annuler &Redo &Rétablir Cu&t Coupe&r &Copy &Copier &Paste &Coller Delete Supprimer QMessageBox Show Details... Afficher les Détails... Hide Details... Cacher les Détails... QObject No rooms found Aucuns salons trouvés %1 would like to add you to their contact list. %1 souhaite vous ajouter à-sa liste de contacts. %1 would like to add you to their contact list, saying '%2' %1 souhaite vous ajouter à sa liste de contacts, dit '%2' QScrollBar Scroll here Défiler ici Top Haut Bottom Bas Page up Haut de page Page down Bas de page Scroll up Défiler vers le haut Scroll down Défiler vers le bas QTextControl Select All Tout Sélectionner &Copy &Copier &Undo &Annuler &Redo &Rétablir Cu&t &Couper &Paste &Coller Delete Supprimer QWebPage Copy Link Copier le lien Copy Copier Copy Image Copier l'image Scroll here Déplacer ici Top Haut Bottom Bas Page up Haut de page Page down Bas de page Scroll up Défiler vers le haut Scroll down Défiler vers le bas QWizard < &Back < &Précédent &Finish &Terminer &Help &Aide Go Back Revenir Continue Continuer Commit Publier Done Fait Quit Quitter Help Aide Cancel Annuler &Next &Suivant &Next > &Suivant > QtBookmarkDetailWindow Edit Bookmark Details Editer les Details du Signet Bookmark Name: Non du Signet: Room Address: Adresse du Salon: Your Nickname: Votre Pseudo: Room password: Mot de passe du salon: Join automatically Joindre automatiquement QtJoinMUCWindow Enter Room Joindre le Salon Room: Salon: Search ... Rechercher ... Nickname: Pseudo: Enter automatically in future Joindre automatiquement par la suite QtMUCSearchWindow Search Room Rechercher un Salon Service: Service: Cancel Annuler OK OK List rooms Liste des salons QtUserSearchFieldsPage Nickname: Pseudo: First name: Prénom: Last name: Nom: E-Mail: E-Mail: Fetching search fields Récupération des champs de recherche QtUserSearchFirstPage Add a user Ajouter un utilisateur Add another user to your contact list. If you know their address you can add them directly, or you can search for them. Ajouter un autre utilisateur à votre liste de contacts. Si vous connaissez leur adresse vous pouvez les ajouter directement, ou vous pouvez les rechercher. I know their address: Je connais leur adresse: I'd like to search my server Je souhaite rechercher sur mon serveur I'd like to search another server: Je souhaite rechercher sur un autre serveur: QtUserSearchWizard Find User Trouver un utilisateur Swift::ChatListModel Bookmarked Rooms Signets de Salons Swift::QtAboutWidget About %1 A propos de %1 Version %1 Version %1 Built with Qt %1 Compilé avec Qt %1 Running with Qt %1 Exécute avec Qt %1 Using the English translation by %1 Utilise la traduction Française de %1 View License Voir la Licence Swift::QtAvatarWidget No picture Aucune image Select picture ... Sélectionner l'image ... Clear picture Effacer l'image Select picture Sélectionner l'image Image Files (*.png *.jpg *.gif) Fichiers d'images (*.png *.jpg *.gif) Error Erreur The selected picture is in an unrecognized format L'image sélectionnée est dans un format non-reconnu Swift::QtBookmarkDetailWindow Bookmark not valid Signet non valide You must specify a valid room address (e.g. someroom@rooms.example.com). Vous devez spécifier une adresse de salon valide (ex. myroom@chats.example.com). Swift::QtChatListWindow Add New Bookmark Ajouter un Nouveau Signet Edit Bookmark Editer le Signet Remove Bookmark Supprimer le Signet Swift::QtChatView Clear log Effacer l'historique You are about to clear the contents of your chat log. Vous êtes sur le point d'effacer le contenu de votre historique de discussion. Are you sure? Etes vous sûr? Swift::QtChatWindow This message has not been received by your server yet. Ce message n'a pas encore été reçu par votre serveur. This message may not have been transmitted. Ce message peut ne pas avoir été transmis. Couldn't send message: %1 N'a pas pu envoyer ce message: %1 Swift::QtContactEditWidget Name: Nom: Groups: Groupes: New Group: Nouveau Groupe: Swift::QtContactEditWindow Edit contact Editer le contact Remove contact Supprimer le contact OK OK Confirm contact deletion Confirmer la suppression du contact Are you sure you want to delete this contact? Etes-vous sûr de vouloir supprimer ce contact? This will remove the contact '%1' from all groups they may be in. Cela supprimera le contact '%1' de tous les groupes dans lesquels il peut être. Swift::QtEventWindow Display Notice Afficher l'Avis Swift::QtJoinMUCWindow someroom@rooms.example.com someroom@rooms.example.com Swift::QtLoginWindow User address: Adresse de l'utilisateur: User address - looks like someuser@someserver.com Adresse de l'utilisateur - ressemble à someuser@someserver.com Example: alice@wonderland.lit Exemple: alice@wonderland.lit Password: Mot de passe: Click if you have a personal certificate used for login to the service. Cliquez si vous avez un certificat personnel utilisé pour la connexion au service. Connect Se connecter Remember Password? Mémoriser le Mot de passe? Login Automatically? Connexion automatique? &Swift &Swift &General &Général &About %1 &A propos de %1 &Show Debug Console &Voir la Console de Débogage &Play Sounds &Jouer les Sons Display Pop-up &Notifications Afficher les Pop-up de &Notifications &Quit &Quitter Remove profile Supprimer le profil Remove the profile '%1'? Supprimer le profil '%1'? Cancel Annuler Select an authentication certificate Sélectionnez un certificat d'authentification The certificate presented by the server is not valid. Le certificat presenté par le serveur n'est pas valide. Would you like to permanently trust this certificate? This must only be done if you know it is correct. Souhaitez-vous autoriser définitivement ce certificat? Cela doit être effectué uniquement si vous savez qu'il est valide. Subject: %1 Sujet: %1 SHA-1 Fingerprint: %1 SHA-1 Fingerprint: %1 Swift::QtMUCSearchWindow Searching Recherche Swift::QtMainWindow &Contacts &Contacts &Notices &Avis C&hats D&iscussions &View &Vue &Show offline contacts &Voir les contacts hors ligne &Actions &Actions Edit &Profile… Editer le &Profil… Enter &Room… Joindre un &Salon… &Add Contact… &Ajouter un Contact… &Edit Selected Contact… &Editer le Contact Sélectionné… Start &Chat… Démarrer une &Discussion &Sign Out &Se déconnecter Swift::QtNameWidget Show Nickname Voir le Pseudo (No Nickname Set) (Aucun Pseudo renseigné) Show Address Voir l'Addresse Edit Profile Editer le Profil Swift::QtProfileWindow Edit Profile Editer le Profil Nickname: Pseudo: Save Enregistrer Swift::QtStatusWidget Connecting Connexion (No message) (Aucun message) Swift::QtSubscriptionRequestWindow %1 would like to add you to their contact list. Would you like to add them to your contact list and share your status when you're online? If you choose to defer this choice, you will be asked again when you next login. %1 souhaite vous ajouter à sa liste de contacts. Souhaitez-vous l' ajouter à votre liste de contacts et partager votre status lorsque vous êtes en ligne? Si vous choisissez de refuser, il vous sera demandé à nouveau lors de votre prochaine connexion. You have already replied to this request Vous avez déja répondu à cette requête OK OK Yes Oui No Non Defer Refuser Swift::QtTreeWidget Edit Editer Remove Supprimer Rename Renommer Rename group Renommer le groupe Enter a new name for group '%1': Entrer un nouveau nom pour le groupe '%1': Swift::QtUserSearchDetailsPage Please choose a name for the contact, and select the groups you want to add the contact to. Veuillez choisir un nom pour le contact, et sélectionner les groupes dans lesquels vous voulez ajouter le contact. Swift::QtUserSearchFirstPage %1. If you know their address you can enter it directly, or you can search for them. %1. Si vous connaissez leur adresse vous pouvez la saisir directement, ou vous pouvez les rechercher. Add another user to your contact list Ajouter un autre utilisateur à votre liste de contacts Chat to another user Discuter avec un autre utilisateur Swift::QtUserSearchWindow Add Contact Ajouter un Contact Chat to User Discuter avec un utilisateur alice@wonderland.lit alice@wonderland.lit How would you like to find the user to add? Comment voulez-vous trouver l'utilisateur à ajouter? How would you like to find the user to chat to? Comment voulez-vous trouver l'utilisateur avec qui discuter? Error while searching Erreur lors de la recherche This server doesn't support searching for users. Ce serveur ne supporte pas la recherche d'utilisateurs. Swift::QtWebView Clear Effacer Swift::QtXMLConsoleWidget Console Console Trace input/output Tracer les entrées/sorties Clear Effacer Debug Console Console de débogage <!-- IN --> <!-- ENTREE --> <!-- OUT --> <!-- SORTIE --> TRANSLATION_INFO TRANSLATION_AUTHOR Cédric DUBOULOZ TRANSLATION_LICENSE This string contains the license under which this translation is licensed. We ask you to license the translation under the BSD license. Please read http://www.opensource.org/licenses/bsd-license.php, and if you agree to release your translation under this license, use the following (untranslated) text: 'This translation is licensed under the BSD License. See http://www.opensource.org/licenses/bsd-license.php' 'This translation is licensed under the BSD License. See http://www.opensource.org/licenses/bsd-license.php' swift-im-2.0+dev6/Swift/Translations/swift_es.ts0000644000175000017500000034471712227051774021653 0ustar kismithkismith UTF-8 Starting chat with %1% in chatroom %2% Comenzando conversación con %1% en la sala %2% Starting chat with %1% - %2% Comenzando conversación con %1% - %2% This chat doesn't support delivery receipts. Esta conversación no es compatible con confirmaciones de entrega. This chat may not support delivery receipts. You might not receive delivery receipts for the messages you sent. Esta conversación quizá no sea compatible con confirmaciones de entrega. Tal vez no recibas las confirmaciones de entrega de los mensajes que envies. me Yo %1% has gone offline %1% se ha desconectado %1% has become available %1% se encuentra disponible %1% has gone away %1% se ha ausentado %1% is now busy TMPFIX genero: o/a? sinonimo? masculino? %1% está ocupado/a The day is now %1% El día es ahora %1% Couldn't send message: %1% No se ha podido enviar el mensaje: %1% Error sending message Error enviando mensaje Bad request Solicitud incorrecta Conflict Conflicto This feature is not implemented Esta característica no está implementada Forbidden Prohibido Recipient can no longer be contacted El destinatario ya no puede ser contactado Internal server error Error interno del servidor Item not found Elemento no encontrado JID Malformed JID Malformado Message was rejected El mensaje ha sido rechazado Not allowed No permitido Not authorized No autorizado Payment is required Pago requerido Recipient is unavailable Destinatario no disponible Redirect Redirección Registration required Registro requerido Recipient's server not found No se ha encontrado el servidor del destinatario Remote server timeout Tiempo de espera del servidor remoto agotado The server is low on resources El servidor tiene pocos recursos disponibles The service is unavailable El servicio no está disponible A subscription is required Se requiere una suscripción Undefined condition Condición no definida Unexpected request Solicitud inesperada Room %1% is not responding. This operation may never complete. La sala %1% no responde. Es posible que esta operación no se complete nunca. Unable to enter this room No se puede entrar en esta sala Unable to enter this room as %1%, retrying as %2% No se puede entrar en esta sala como %1%, reintentando como %2% No nickname specified No se ha especificado nick A password needed Se necesita contraseña Only members may enter Solo los miembros pueden entrar You are banned from the room bloqueado? Estás vetado de la sala The room is full La sala está llena The room does not exist La sala no existe Couldn't join room: %1%. No se ha podido entrar a la sala: %1%. You have entered room %1% as %2%. Has entrado a la sala %1% como %2%. %1% has entered the room as a %2%. %1% ha entrado en la sala como %2%. %1% has entered the room. %1% ha entrado a la sala. moderator moderador participant participante visitor visitante The room subject is now: %1% El tema de la sala es ahora: %1% %1% is now a %2% %1% ahora es un %2% Moderators Moderadores Participants Participantes Visitors Visitantes Occupants TMPFIX, used where? Ocupantes Trying to enter room %1% Intentando entrar a la sala %1% %1% has left the room%2% %1% ha salido de la sala %2% You have been kicked out of the room Has sido expulsado de la sala You have been banned from the room Has sido vetado de la sala You are no longer a member of the room and have been removed Ya no eres un miembro de la sala y has sido expulsado The room has been destroyed La sala ha sido destruida %1% has left the room %1% ha salido de la sala Room configuration failed: %1%. La configuración de la sala ha fallado: %1%. Occupant role change failed: %1%. El cambio de rol del ocupante ha fallado: %1%. You have left the room Has salido de la sala The correct room password is needed Se necesita la contraseña de sala correcta and y %1% have entered the room %1% han entrado a la sala %1% has entered the room %1% ha entrado a la sala %1% have left the room %1% han salido de la sala %1% have entered then left the room %1% han entrado y salido de la sala %1% has entered then left the room %1% ha entrado y salido de la sala %1% have left then returned to the room %1% han salido y vuelto a la sala %1% has left then returned to the room %1% ha salido y vuelto a la sala %1% wants to add you to his/her contact list %1% quiere añadirte a su lista de contactos Error Error %1% has invited you to enter the %2% room %1% te ha invitado a entrar a la sala %2% User address invalid. User address should be of the form 'alice@wonderland.lit' Dirección de usuario no válida. La dirección de usuario ha de ser de la forma 'alicia@paismaravillas.lit' Unknown Error Error Desconocido Unable to find server No se puede encontrar el servidor Error connecting to server Error conectando al servidor Error while receiving server data Error al recibir datos del servidor Error while sending data to the server Error al enviar datos al servidor Error parsing server data Error analizando datos del servidor Login/password invalid Usuario/contraseña no válidos Error while compressing stream Error comprimiendo flujo de datos Server verification failed La verificación del servidor ha fallado Authentication mechanisms not supported Mecanismo de autenticación no soportado Unexpected response Respuesta inesperada Error binding resource Error vinculando recurso Error starting session Error iniciando sesión Stream error Error de flujo de datos Encryption error Error de cifrado Error loading certificate (Invalid password?) Error cargando certificado (¿Contraseña no válida?) Certificate not authorized Certificado no autorizado Certificate card removed Tarjeta de certificado eliminada Unknown certificate Certificado desconocido Certificate has expired El certificado ha caducado Certificate is not yet valid El certificado aún no es válido Certificate is self-signed TMPFIX, firma personal? El certificado es auto-firmado Certificate has been rejected El certificado ha sido rechazado Certificate is not trusted El certificado no es de confianza Certificate cannot be used for encrypting your connection El certificado no puede usarse para cifrar tu conexión Certificate path length constraint exceeded TMPFIX Se ha excedido la restricción de longitud de ruta del certificado Invalid certificate signature Firma de certificado no válida Invalid Certificate Authority Entidad Certificadora no Válida Certificate does not match the host identity El certificado no coincide con la identidad del servidor Certificate has been revoked El certificado ha sido revocado Unable to determine certificate revocation state No se puede determinar el estado de revocación del certificado Certificate error Error de certificado Re-enter credentials and retry Vuelve a introducir las credenciales y prueba de nuevo Disconnected from %1%: %2%. To reconnect, Sign Out and provide your password again. Desconectado de %1%: %2%. Para reconectar, desconéctate y proporciona tu contraseña de nuevo. Reconnect to %1% failed: %2%. Will retry in %3% seconds. La reconexión a %1% ha fallado: %2%. Se intentará de nuevo en %3% segundos. Disconnected from %1%: %2%. Desconectado de %1%: %2%. Contacts Contactos Server %1% rejected contact list change to item '%2%' El servidor %1% ha rechazado el cambio al elemento '%2%' de la lista de contactos Available Disponible Away Ausente Busy Ocupado Offline Desconectado There was an error publishing your profile data Ha habido un error publicando los datos de tu perfil %1% (%2%) %1% (%2%) %1% and %2% others (%3%) %1% y %2% más (%3%) %1%, %2% (%3%) %1%, %2% (%3%) TLS Client Certificate Selection Selección de certificado de cliente TLS Select a certificate to use for authentication Selecciona un certificado para usar como autenticación ChatListModel Bookmarked Rooms Salas en Marcadores CloseButton Close Tab Cerrar Pestaña MAC_APPLICATION_MENU Services Servicios Hide %1 Ocultar %1 Hide Others Ocultar Otros Show All Mostrar Todo Preferences... Preferencias... Quit %1 Salir de %1 About %1 Acerca de %1 QApplication QT_LAYOUT_DIRECTION Translate this to LTR for left-to-right or RTL for right-to-left languages LTR QDialogButtonBox &Yes &Sí &No &No &OK &Aceptar OK Aceptar &Cancel &Cancelar Cancel Cancelar QLineEdit Select All Seleccionar Todo &Undo &Deshacer &Redo &Rehacer Cu&t C&ortar &Copy &Copiar &Paste &Pegar Delete Eliminar QMessageBox Show Details... Mostrar Detalles... Hide Details... Ocultar Detalles... QObject No rooms found No se han encontrado salas %1 would like to add you to their contact list. %1 quiere añadirte a su lista de contactos. %1 would like to add you to their contact list, saying '%2' %1 quiere añadirte a su lista de contactos, diciendo '%2' %1 has invited you to enter the %2 room. %1 te ha invitado a entrar a la sala %2. You've been invited to enter the %1 room. Te han invitado a entrar a la sala %1. Reason: %1 Motivo: %1 This person may not have really sent this invitation! ¡Es posible que esta persona no haya enviado realmente esta invitación! Direction Sentido? Dirección Other Party Sounds weird... "Tu contacto" o similar? La otra parte State Estado Progress Progreso Size Tamaño Incoming Entrante Outgoing Saliente Waiting for start Esperando que comience Waiting for other side to accept Esperando a que el otro lado acepte Negotiating Negociando Transferring Transfiriendo Finished Finalizado Failed Fallido Canceled Cancelado Connection Options Opciones de conexión QScrollBar Scroll here Desplazar aquí Top Arriba Bottom Abajo Page up Retroceder página Page down Avanzar página Scroll up Desplazar arriba Scroll down Desplazar abajo QTextControl Select All Seleccionar Todo &Copy &Copiar &Undo &Deshacer &Redo &Rehacer Cu&t C&ortar &Paste &Pegar Delete Eliminar QWebPage Copy Link Copiar Enlace Copy Copiar Copy Image Copiar Imagen Scroll here Desplazar aquí Top Arriba Bottom Abajo Page up Retroceder página Page down Avanzar página Scroll up Desplazar arriba Scroll down Desplazar abajo QWizard < &Back < &Atrás &Finish &Finalizar &Help A&yuda Go Back Retroceder Continue Continuar Commit Publicar Done Hecho Quit Salir Help Ayuda Cancel Cancelar &Next &Siguiente &Next > &Siguiente > QtAboutWidget About %1 Acerca de %1 Version %1 Versión %1 Built with Qt %1 Compilado con Qt %1 Running with Qt %1 Ejecutándose con Qt %1 Using the English translation by %1 Usando la traducción española por %1 View License Ver Licencia QtAffiliationEditor Edit Affiliations Editar afiliaciones Affiliation: Afiliación: Owner Propietario Administrator Administrador Member Miembro Outcast (Banned) Marginado (Vetado) Add User Añadir usuario Remove User Eliminar usuario QtBookmarkDetailWindow Edit Bookmark Details Editar Detalles de Marcador Bookmark Name: Nombre del Marcador: Room Address: Dirección de la sala: Your Nickname: Tu nick: Room password: Contraseña de la sala: Enter automatically Entrar automáticamente Join automatically Entrar automáticamente QtCertificateViewerDialog Certificate Viewer Visor de certificados QtChatListWindow Add New Bookmark Añadir Nuevo Marcador Edit Bookmark Editar Marcador Remove Bookmark Eliminar Marcador QtConnectionSettings Connection Options Opciones de conexión Connection Method: Método de conexión: Automatic Automático Manual Manual BOSH BOSH Secure connection: Conexión segura: Never Nunca Encrypt when possible Cifrar cuando sea posible Always encrypt Cifrar siempre Allow Compression Permitir compresión Allow sending password over insecure connection Permitir enviar contraseña sobre conexión insegura Manually select server Seleccionar servidor manualmente Hostname: Nombre de servidor: Port: Puerto: Connection Proxy Proxy de conexión Proxy type: Tipo de proxy: None Ninguno Use system-configured proxy Usar el proxy del sistema SOCKS5 SOCKS5 HTTP Connect HTTP Connect Override system-configured proxy Reemplazar el proxy del sistema BOSH URI: URI de BOSH: Manually select HTTP proxy Seleccionar proxy HTTP manualmente QtEventWindow Display Notice Mostrar Aviso QtHistoryWindow History Historial Search: Buscar: Next Siguiente Previous Anterior QtJoinMUCWindow Enter Room Entrar a la sala Room Address: Dirección de la sala: Your Nickname: Tu nick: Room Password: Contraseña de la sala: Automatically configure newly created rooms Configurar automáticamente salas de nueva creación Room: Sala: Search ... Buscar ... Nickname: Nick: Enter automatically in future Entrar automáticamente en el futuro QtMUCSearchWindow Search Room Buscar Sala Service: Servicio: Cancel Cancelar OK Aceptar List rooms Listar salas Searching Buscando QtSubscriptionRequestWindow %1 would like to add you to their contact list. Would you like to add them to your contact list and share your status when you're online? If you choose to defer this choice, you will be asked again when you next login. %1 quiere añadirte a su lista de contactos. ¿Quieres añadirle a tu lista de contactos y compartir tu estado cuando te conectes? Si escoges posponer esta elección, se te preguntará de nuevo la próxima vez que te conectes. You have already replied to this request Ya has respondido a esta solicitud OK Aceptar Yes No No Defer Posponer QtUserSearchDetailsPage Please choose a name for the contact, and select the groups you want to add the contact to. Por favor, escoge el nombre que le quieres dar al contacto, y selecciona los grupos a los que quieres añadirlo. QtUserSearchFieldsPage Nickname: Nick: First name: Nombre: Last name: Apellido: E-Mail: Correo electrónico: Fetching search fields Obteniendo campos de búsqueda QtUserSearchFirstPage Add a user Añadir un usuario Add another user to your contact list. If you know their address you can add them directly, or you can search for them. Añadir a otro usuario a tu lista de contactos. Si conoces su dirección, puedes introducirla directamente, o puedes buscar al usuario. I know their address: Conozco su dirección: I'd like to search my server Quiero buscar en mi servidor I'd like to search another server: Quiero buscar en otro servidor: %1. If you know their address you can enter it directly, or you can search for them. %1. Si conoces su dirección, puedes introducirla directamente, o puedes buscar al usuario. Add another user to your contact list Añadir a otro usuario a tu lista de contactos Chat to another user Conversar con otro usuario QtUserSearchResultsPage No results. No hay resultados. QtUserSearchWindow Add Contact Añadir Contacto Chat to User Conversar con Usuario alice@wonderland.lit alicia@paismaravillas.lit How would you like to find the user to add? ¿Cómo quieres buscar al usuario a añadir? How would you like to find the user to chat to? ¿Cómo quieres buscar al usuario con el que conversar? Error while searching Error durante la búsqueda This server doesn't support searching for users. Este servidor no soporta búsqueda de usuarios. QtUserSearchWizard Find User Buscar Usuario Swift::ChatListModel Bookmarked Rooms TMPFIX? "de marcadores"? Salas en Marcadores Recent Chats Conversaciones recientes Opened Whiteboards Pizarras abiertas Swift::QtAboutWidget About %1 Acerca de %1 Version %1 Versión %1 Built with Qt %1 Compilado con Qt %1 Running with Qt %1 Ejecutándose con Qt %1 Using the English translation by %1 Usando la traducción española por %1 View License Ver Licencia Swift::QtAdHocCommandWindow Cancel Cancelar Back Retroceder Next Siguiente Complete Completado Error: %1 Error: %1 Warning: %1 Advertencia: %1 Error executing command Error ejecutando el comando Swift::QtAffiliationEditor Add User Añadir usuario Added User's Address: Dirección del usuario añadido: Swift::QtAvatarWidget No picture Sin imagen Select picture ... Seleccionar imagen ... Clear picture Borrar imagen Select picture Seleccionar imagen Image Files (*.png *.jpg *.jpeg *.gif) Archivos de imagen (*.png *.jpg *.jpeg *.gif) Image Files (*.png *.jpg *.gif) Archivos de imagen (*.png *.jpg *.gif) Error Error The selected picture is in an unrecognized format La imagen seleccionada se encuentra en un formato no reconocido Swift::QtBookmarkDetailWindow Bookmark not valid Marcador no válido You must specify a valid room address (e.g. someroom@rooms.example.com). Debes especificar una dirección de sala vàlida (ej. algunasala@salas.ejemplo.com). You must specify a valid room address (e.g. myroom@chats.example.com). Debes especificar una dirección de sala vàlida (ej. misala@chats.ejemplo.com). Swift::QtCertificateViewerDialog General General Valid From Válido desde Valid To Válido hasta Serial Number Número de serie Version Versión Subject Sujeto Organization Organización Common Name Nombre común Locality Localidad Organizational Unit Unidad organizativa Country País State Estado Alternate Subject Names Nombres de sujeto alternativos E-mail Address Dirección de correo electrónico DNS Name Nombre DNS Issuer Emisor Swift::QtChatListWindow Add New Bookmark Añadir Nuevo Marcador Edit Bookmark Editar Marcador Remove Bookmark Eliminar Marcador Clear recents Borrar recientes Swift::QtChatView Clear log Borrar texto You are about to clear the contents of your chat log. Estás a punto de borrar el contenido de esta conversación. Are you sure? TMPFIX, genero? ¿Estás seguro? %1 edited %1 editado Waiting for other side to accept the transfer. Esperando a que el otro lado acepte la transferencia. Cancel Cancelar Negotiating... Negociando... Transfer has been canceled! ¡La transferencia ha sido cancelada! Transfer completed successfully. La transferencia se ha completado con éxito. Transfer failed. La transferencia ha fallado. Started whiteboard chat Se ha comenzado una conversación de pizarra Show whiteboard Mostrar pizarra Whiteboard chat has been canceled La conversación de pizarra ha sido cancelada Whiteboard chat request has been rejected La solicitud de conversación de pizarra ha sido rechazada Return to room Volver a la sala Swift::QtChatWindow Correcting Corrigiendo This chat may not support message correction. If you send a correction anyway, it may appear as a duplicate message Esta conversación quizá no sea compatible con la corrección de mensajes. Si envías una corrección de todos modos, es posbile que aparezca como un mensaje duplicado This chat does not support message correction. If you send a correction anyway, it will appear as a duplicate message Esta conversación no es compatible con la corrección de mensajes. Si envías una corrección de todos modos, aparecerá como un mensaje duplicado This message has not been received by your server yet. Este mensaje no ha sido recibido por tu servidor todavía. This message may not have been transmitted. Es posible que este mensaje no se haya transmitido. The receipt for this message has been received. Se ha recibido la confirmación para este mensaje. The receipt for this message has not yet been received. The recipient(s) might not have received this message. Plural pain Aún no se ha recibido la confirmación para este mensaje. Es posible que los destinatarios no lo hayan recibido. Send file Enviar archivo Cancel Cancelar Set Description Establecer descripción Send Enviar Receiving file Recibiendo archivo Accept Aceptar Starting whiteboard chat Comenzando conversación de pizarra %1 would like to start a whiteboard chat %1 quiere comenzar una conversación de pizarra File transfer description Descripción de la transferencia de archivo Description: Descripción: Save File Guardar archivo Change subject… Cambiar el tema... Configure room… Configurar sala... Edit affiliations… Editar afiliaciones... Destroy room Destruir la sala Invite person to this room… Invitar a una persona a esta sala... Change room subject Cambiar el tema de la sala New subject: Nuevo tema: Confirm room destruction Confirmar destrucción de la sala Are you sure you want to destroy the room? ¿Estás seguro de que quieres destruir la sala? This will destroy the room. Esto destruirá la sala. Accept Invite Aceptar invitación Couldn't send message: %1 No se ha podido enviar el mensaje: %1 Swift::QtContactEditWidget Name: Nombre: Groups: Grupos: New Group: Nuevo Grupo: Swift::QtContactEditWindow Edit contact Editar contacto Remove contact Eliminar contacto OK Aceptar Confirm contact deletion Confirmar eliminación del contacto Are you sure you want to delete this contact? ¿Seguro que quieres eliminar este contacto? This will remove the contact '%1' from all groups they may be in. Esto eliminará el contacto '%1' de todos los grupos en los que esté. Swift::QtEventWindow Display Notice Mostrar Aviso Swift::QtFileTransferListWidget Clear Finished Transfers Borrar transferencias finalizadas File Transfer List Lista de transferencia de archivos Swift::QtHistoryWindow History Historial Swift::QtInviteToChatWindow Users to invite to this chat (one per line): conversación o sala? Usuarios a invitar a esta conversación (uno por línea): If you want to provide a reason for the invitation, enter it here Si quieres proporcionar un motivo para la invitación, introdúcelo aquí Swift::QtJoinMUCWindow someroom@rooms.example.com algunasala@salas.ejemplo.com Swift::QtLoginWindow User address: Dirección de usuario: User address - looks like someuser@someserver.com Dirección de usuario - Similar a usuario@algunservidor.com Example: alice@wonderland.lit Ejemplo: alicia@paismaravillas.lit Password: Contraseña: Click if you have a personal certificate used for login to the service. Haz click si tienes un certificado personal para conectar al servicio. Connect Conectar Remember Password? ¿Recordar Contraseña? Login Automatically? ¿Conectar Automáticamente? &Swift &Swift &General TMPFIX, used where? &General &About %1 &Acerca de %1 &Show Debug Console &Mostrar Consola de Depuración Show &File Transfer Overview VERY long. Alternative "Mostrar lista de transferencias" Mostrar vista general de transferencia de &archivos &Play Sounds &Reproducir Sonidos Display Pop-up &Notifications Mostrar &Notificaciones Emergentes &Quit &Salir Remove profile Eliminar perfil Remove the profile '%1'? ¿Eliminar el perfil '%1'? Cancel Cancelar Confirm terms of use Confirmar condiciones de uso Select an authentication certificate Selecciona un certificado de autenticación P12 files (*.cert *.p12 *.pfx);;All files (*.*) Archivos P12 (*.cert *.p12 *.pfx);;Todos los archivos (*.*) The certificate presented by the server is not valid. El certificado presentado por el servidor no es válido. Would you like to permanently trust this certificate? This must only be done if you know it is correct. ¿Quieres confiar en este certificado de forma permanente? Esto solo debe hacerse si sabes que es correcto. Subject: %1 Sujeto: %1 SHA-1 Fingerprint: %1 Huella digital SHA-1: %1 Swift::QtMUCConfigurationWindow Cancel Cancelar OK Aceptar Swift::QtMUCSearchWindow Searching Buscando Swift::QtMainWindow &Contacts &Contactos &Notices Av&isos C&hats TMPFIX? Kev said "conversations" context C&onversaciones &View &Ver &Show offline contacts &Mostrar contactos desconectados &Show Emoticons &Mostrar emoticonos &Actions &Acciones Edit &Profile… Editar &Perfil… Enter &Room… Entrar a &Sala… &View History… &Ver historial... &Add Contact… &Añadir Contacto… &Edit Selected Contact… &Editar Contacto Seleccionado… Start &Chat… Comenzar &Conversación... Run Server Command Ejecutar comando de servidor &Sign Out &Desconectar &Request Delivery Receipts &Pedir confirmación de entrega Collecting commands... Recopilando comandos... &Chats C&onversaciones No Available Commands No hay comandos disponibles Notices TMPFIX, used? avisos2 Swift::QtNameWidget Show Nickname Mostrar Nick (No Nickname Set) (Sin Nick Definido) Show Address Mostrar Dirección Edit Profile Editar Perfil Swift::QtOccupantListWidget No actions for this user No hay acciones para este usuario Kick user Expulsar usuario Kick and ban user Expulsar y vetar usuario Make moderator Hacer moderador Make participant Hacer participante Remove voice Quitar la voz Add to contacts Añadir a contactos Swift::QtProfileWindow Edit Profile Editar Perfil Nickname: Nick: Save Guardar Swift::QtRosterHeader Connection is secured La conexión es segura Swift::QtRosterWidget Edit… Editar... Remove Eliminar Send File Enviar archivo Start Whiteboard Chat Comenzar conversación de pizarra All Files (*);; Todos los archivos (*);; Rename Renombrar Rename group Renombrar grupo Enter a new name for group '%1': Introduce un nombre nuevo para el grupo '%1': Swift::QtStatusWidget Connecting Conectando (No message) (Sin mensaje) Swift::QtSubscriptionRequestWindow %1 would like to add you to their contact list. Would you like to add them to your contact list and share your status when you're online? If you choose to defer this choice, you will be asked again when you next login. %1 quiere añadirte a su lista de contactos. ¿Quieres añadirle a tu lista de contactos y compartir tu estado cuando te conectes? Si escoges posponer esta elección, se te preguntará de nuevo la próxima vez que te conectes. You have already replied to this request TMPFIX, used where? Ya has respondido a esta solicitud OK Aceptar Yes No No Defer Posponer Swift::QtTreeWidget Edit Editar Remove Eliminar Rename Renombrar Rename group Renombrar grupo Enter a new name for group '%1': Introduce un nombre nuevo para el grupo %1: New name for %1 Nuevo nombre para %1 Swift::QtUserSearchDetailsPage Please choose a name for the contact, and select the groups you want to add the contact to. Somewhat free translation. TMPFIX? Por favor, escoge el nombre que le quieres dar al contacto, y selecciona los grupos a los que quieres añadirlo. Swift::QtUserSearchFirstPage %1. If you know their address you can enter it directly, or you can search for them. %1. Si conoces su dirección, puedes introducirla directamente, o puedes buscar al usuario. Add another user to your contact list Añadir a otro usuario a tu lista de contactos Chat to another user Conversar con otro usuario Swift::QtUserSearchWindow Add Contact Añadir Contacto Chat to User Conversar con Usuario alice@wonderland.lit alicia@paismaravillas.lit How would you like to find the user to add? ¿Cómo quieres buscar al usuario a añadir? How would you like to find the user to chat to? ¿Cómo quieres buscar al usuario con el que conversar? Error while searching Error durante la búsqueda This server doesn't support searching for users. Este servidor no soporta búsqueda de usuarios. Swift::QtWebView Clear Borrar texto Increase font size Incrementar el tamaño de la letra Decrease font size Reducir el tamaño de la letra Swift::QtWhiteboardWindow Closing window is equivalent closing the session. Are you sure you want to do this? Cerrar la ventana es equivalente a cerrar la sesión. ¿Estás seguro de que quieres hacer esto? Swift::QtXMLConsoleWidget Console Consola Trace input/output Monitorizar entrada/salida Clear Borrar Debug Console Consola de Depuración <!-- IN --> <!-- ENTRANTE --> <!-- OUT --> <!-- SALIENTE --> TRANSLATION_INFO TRANSLATION_AUTHOR JanKusanagi TRANSLATION_LICENSE This string contains the license under which this translation is licensed. We ask you to license the translation under the BSD license. Please read http://www.opensource.org/licenses/bsd-license.php, and if you agree to release your translation under this license, use the following (untranslated) text: 'This translation is licensed under the BSD License. See http://www.opensource.org/licenses/bsd-license.php' This translation is licensed under the BSD License. See http://www.opensource.org/licenses/bsd-license.php TRANSLATION_LICENSE Should be the following (untranslated) text: 'This translation is licensed under the BSD License. See http://www.opensource.org/licenses/bsd-license.php' This translation is licensed under the BSD License. See http://www.opensource.org/licenses/bsd-license.php swift-im-2.0+dev6/Swift/Translations/swift_nl.ts0000644000175000017500000023661412227051774021651 0ustar kismithkismith UTF-8 Starting chat with %1% in chatroom %2% Conversatie begonnen met %1% in kamer %2% Starting chat with %1% - %2% Conversatie begonnen met %1% - %2% me ik %1% has gone offline %1% is offline gegaan %1% has become available %1% is nu beschikbaar %1% has gone away %1% is nu afwezig %1% is now busy %1% is nu bezet The day is now %1% De huidige dag is nu %1% Error sending message Fout tijdens het versturen van bericht Bad request Ongeldig verzoek Conflict Conflict This feature is not implemented Deze functie is niet geïmplementeerd Forbidden Verboden Recipient can no longer be contacted Begunstigde is onbereikbaar Internal server error Interne serverfout Item not found Element niet gevonden JID Malformed JID misvormd Message was rejected Bericht geweigerd Not allowed Niet toegelaten Not authorized Niet toegestaan Payment is required Betaling is vereist Recipient is unavailable Begunstigde is niet beschikbaar Redirect Omleiding Registration required Registratie vereist Recipient's server not found Server begunstigde niet gevonden Remote server timeout Server time-out The server is low on resources De server heeft een tekort aan bronnen The service is unavailable De server is niet beschikbaar A subscription is required Inschrijving is vereist Undefined condition Onbekende fout Unexpected request Onverwacht verzoek Room %1% is not responding. This operation may never complete. Kamer %1% antwoordt niet. Deze operatie kan mogelijk nooit voltooien. No nickname specified Geen roepnaam gespecifieerd A password needed Wachtwoord vereist You are banned from the room U bent verbannen uit de kamer The room is full De kamer is vol The room does not exist Deze kamer bestaat niet Unable to enter this room Kan deze kamer niet betreden Unable to enter this room as %1%, retrying as %2% Kan deze kamer niet betreden als %1%; als %2% opniew aan het proberen Only members may enter Enkel leden mogen deze kamer betreden You have entered room %1% as %2%. U heeft de kamer %1% als %2% betreden. %1% has entered the room as a %2%. %1% heeft de kamer betreden als %2%. %1% has entered the room. %1% heeft de kamer betreden. moderator moderator participant deelnemer visitor bezoeker The room subject is now: %1% Het onderwerp van deze kamer is nu: %1% %1% is now a %2% %1% is nu een %2% Moderators Moderators Participants Deelnemers Visitors Bezoekers Occupants Bewoners Trying to enter room %1% Aan het proberen om de kamer %1% te betreden %1% have left then returned to the room %1% hebben de kamer verlaten en terug betreden %1% has left then returned to the room %1% heeft de kamer verlaten en terug betreden %1% has left the room %1% heeft de kamer verlaten You have left the room U heeft de kamer verlaten and en %1% have entered the room %1% hebben de kamer betreden %1% has entered the room %1% heeft de kamer betreden %1% have entered then left the room %1% hebben de kamer betreden en terug verlaten %1% has entered then left the room %1% heeft de kamer betreden en terug verlaten %1% have left the room %1% hebben de kamer verlaten Unknown Error Onbekende fout Unable to find server Kan server niet vinden Error connecting to server Fout tijdens het verbinden met de server Error while receiving server data Fout tijdens het ontvangen van gegevens Error while sending data to the server Fout tijdens het verzenden van gegevens Error parsing server data Fout tijdens het ontleden van gegevens Login/password invalid Gebruikersnaam/wachtwoord ongeldig Error while compressing stream Fout tijdens het comprimeren Server verification failed Serververificatie gefaald Authentication mechanisms not supported Authenticatiemechanismen niet ondersteund Unexpected response Onverwacht antwoord Error binding resource Fout tijdens het binden van de resource Error starting session Fout bij het starten van de sessie Stream error Stroomfout Encryption error Beveiligingsfout Error loading certificate (Invalid password?) Fout bij het laden van het certificaat (Ongeldig wachtwoord?) Certificate not authorized Certificaat heeft geen toestemming Unknown certificate Onbekend certificaat Certificate has expired Certificaat is verstreken Certificate is not yet valid Certificaat is nog niet geldig Certificate is self-signed Certificaat is zelfondertekend Certificate has been rejected Certificaat werd afgewezen Certificate is not trusted Certificaat wordt niet vertrouwd Certificate cannot be used for encrypting your connection Certificaat kan niet gebruikt worden om verbinding te beveiligen Certificate path length constraint exceeded Padlengte certificaat overschreden Invalid certificate signature Certificaat heeft ongeldige handtekening Invalid Certificate Authority Ongeldige certificeringsinstantie Certificate does not match the host identity Certificaat komt niet overeen met identiteit Certificate error Certificaatfout Reconnect to %1% failed: %2%. Will retry in %3% seconds. Herverbinding met %1% mislukt: %2%. Zal opnieuw proberen binnen %3% seconden. Disconnected from %1%: %2%. Verbinding met %1% verbroken: %2%. Contacts Contacten Server %1% rejected contact list change to item '%2%' Server %1% heeft de aanpassing van contact '%2%' geweigerd Available Beschikbaar Away Afwezig Busy Bezet Offline Offline Error Fout %1% wants to add you to his/her contact list %1% wil u aan zijn/haar contactenlijst toevoegen There was an error publishing your profile data Er is een fout opgetreden bij het publiceren van uw profiel %1% (%2%) %1% (%2%) %1% and %2% others (%3%) %1% en %2% anderen (%3%) %1%, %2% (%3%) %1%, %2% (%3%) User address invalid. User address should be of the form 'alice@wonderland.lit' Gebruikersadres ongeldig. Gebruikersadres moet van de vorm 'alice@wonderland.lit' zijn This chat doesn't support delivery receipts. Deze conversatie ondersteunt geen ontvangstbevestigingen. This chat may not support delivery receipts. You might not receive delivery receipts for the messages you sent. Deze conversatie ondersteunt mogelijks geen ontvangstbevestigingen. Couldn't send message: %1% Kon boodschap niet verzenden: %1% The correct room password is needed Het correcte wachtwoord voor de kamer is vereist Couldn't join room: %1%. Kon kamer niet betreden: %1%. %1% has left the room%2% %1% heeft de kamer %2% verlaten You have been kicked out of the room U bent uit de kamer geschopt You have been banned from the room U bent uit de kamer verbannen You are no longer a member of the room and have been removed U bent niet langer een lid van de kamer, en werd uit de kamer verwijderd The room has been destroyed De kamer werd vernietigd Room configuration failed: %1%. Configuratie van kamer gefaald: %1%. Occupant role change failed: %1%. Deelnemersrol veranderd: %1%. %1% has invited you to enter the %2% room %1% heeft U uitgenodigd om de kamer %2% te betreden Certificate card removed Certificaatskaart verwijderd Certificate has been revoked Certificaat werd ingetrokken Unable to determine certificate revocation state Kan ingetrokken toestand van certificaat niet conttroleren Re-enter credentials and retry Voer gebruikersinformatie terug in en probeer opnieuw Disconnected from %1%: %2%. To reconnect, Sign Out and provide your password again. Verbinding met %1% verbroken: %2%. Om opnieuw te verbinden, meld terug af en voer het wachtwoord opnieuw in. TLS Client Certificate Selection Keuze TLS Klantcertificaat Select a certificate to use for authentication Kies een certificaat om te gebruiken voor authenticatie CloseButton Close Tab Sluit tab MAC_APPLICATION_MENU Services Voorzieningen Hide %1 Verberg %1 Hide Others Verberg andere Show All Toon alles Preferences... Voorkeuren... Quit %1 Stop %1 About %1 Over %1 QApplication QT_LAYOUT_DIRECTION Translate this to LTR for left-to-right or RTL for right-to-left languages LTR QDialogButtonBox &Yes &Ja &No &Nee &OK &OK OK OK &Cancel &Annuleren Cancel Annuleren QLineEdit Select All Selecteer alle Delete Verwijder &Undo &Herstel &Redo &Opniew Cu&t &Knip &Copy &Kopieer &Paste &Plak QMessageBox Show Details... Toon details... Hide Details... Verberg details... QObject No rooms found Geen kamers gevonden %1 would like to add you to their contact list. %1 wil u aan zijn contactenlijst toevoegen. %1 would like to add you to their contact list, saying '%2' %1 wil u aan zijn contactenlijst toevoegen, met als boodschap '%2' %1 has invited you to enter the %2 room. %1 heeft U uitgenodigd om de kamer %2 te betreden. You've been invited to enter the %1 room. U bent uitgenodigd om de kamer %1 te betreden. Reason: %1 Reden: %1 This person may not have really sent this invitation! Deze persoon heeft mogelijks deze uitnodiging niet echt verstuurd! Direction Richting Other Party Andere Partij State Toestand Progress Voortgang Size Grootte Incoming Inkomend Outgoing Uitgaand Waiting for start Aan het wachten om te starten Waiting for other side to accept Aan het wachten op de andere kant om te aanvaarden Negotiating Aan het onderhandelen Transferring Aan het overbrengen Finished Voltooid Failed Gefaald Canceled Geannuleerd Connection Options Verbindingsopties QScrollBar Scroll here Ga naar hier Top Bovenste Bottom Onderste Page up Pagina omhoog Page down Pagina omlaag Scroll up Rol omhoog Scroll down Rol omlaag QTextControl Select All Selecteer alle &Copy &Kopieer &Undo &Herstel &Redo &Opniew Cu&t &Knip &Paste &Plak Delete Verwijder QWebPage Copy Link Kopieer koppeling Copy Kopieer Copy Image Kopieer afbeelding Scroll here Ga naar hier Top Bovenste Bottom Onderste Page up Pagina omhoog Page down Pagina omlaag Scroll up Rol omhoog Scroll down Rol omlaag QWizard < &Back < &Terug &Finish &Beëindigen &Help &Help Go Back Terug Continue Verder Commit Bevestig Done Klaar Quit Afsluiten Help Help Cancel Annuleren &Next &Volgende &Next > &Volgende > QtAffiliationEditor Affiliation: Lidmaatschap: Owner Eigenaar Administrator Beheerder Member Lid Outcast (Banned) Verstoteling Add User Voeg gebruiker toe Remove User Verwijder gebruiker Edit Affiliations Editeer Lidmaatschappen QtBookmarkDetailWindow Edit Bookmark Details Bladwijzerdetails aanpassen Bookmark Name: Naam bladwijzer: Your Nickname: Uw roepnaam: Room password: Wachtwoord kamer: Join automatically Automatisch betreden Room Address: Adres kamer: Enter automatically Automatisch betreden QtCertificateViewerDialog Certificate Viewer Certificaat QtConnectionSettings Connection Method: Verbindingsmethode: Automatic Automatisch Manual Manueel BOSH BOSH Secure connection: Beveilig verbinding: Never Nooit Encrypt when possible Beveilig wanneer mogelijk Always encrypt Altijd beveiligen Allow Compression Comprimeer wanneer mogelijk Allow sending password over insecure connection Sta toe om paswoord over onbeveiligde verbinding te sturen Manually select server Manuele serverselectie Hostname: Hostnaam: Port: Poort: Connection Proxy Verbindingsproxy Proxy type: Proxy type: None Geen Use system-configured proxy Gebruik systeemgeconfigureerde proxy SOCKS5 SOCKS5 HTTP Connect HTTP Connect Override system-configured proxy Override systemgeconfigureerde proxy BOSH URI: BOSH URI: Manually select HTTP proxy Selecteer HTTP proxy manueel Connection Options Verbindingsopties QtHistoryWindow Search: Zoek: Next Volgende Previous Vorige History Geschiedenis QtJoinMUCWindow Room: Kamer: Search ... Zoek ... Nickname: Roepnaam: Enter Room Kamer betreden Enter automatically in future Automatisch betreden Room Address: Adres kamer: Your Nickname: Uw roepnaam: Room Password: Wachtwoord kamer: Automatically configure newly created rooms Configureer niewe kamer automatisch QtMUCSearchWindow Search Room Zoek kamer Service: Dienst: Cancel Annuleren List rooms Kamers ophalen OK OK QtProfileWindow Edit Profile Bewerk profiel Save Bewaar QtUserSearchFieldsPage Nickname: Roepnaam: First name: Voornaam: Last name: Familienaam: E-Mail: E-Mail: Fetching search fields Gegevens aan het ophalen QtUserSearchFirstPage Add a user Gebruiker toevoegen Add another user to your contact list. If you know their address you can add them directly, or you can search for them. Voeg een andere gebruiker aan je contactlijst toe. Indien u zijn/haar JID kent kan u hem/haar onmiddelijk toevoegen; anders kan u hem/haar zoeken. I'd like to search my server Ik wil mijn server doorzoeken I'd like to search another server: Ik wil een andere server doorzoeken: I know their address: Ik ken het adres: QtUserSearchResultsPage No results. Geen resultaten. QtUserSearchWizard Find User Zoek gebruiker QtVCardPhotoAndNameFields Form Formatted Name Nickname Prefix Given Name Middle Name Last Name Suffix QtVCardWidget Form Add Field Swift::ChatListModel Bookmarked Rooms Bladwijzers Recent Chats Recente conversaties Opened Whiteboards Geopenede tekentafels Swift::QtAboutWidget About %1 Over %1 Version %1 Versie %1 Built with Qt %1 Gebouwd met Qt %1 Running with Qt %1 Actief met Qt %1 Using the English translation by %1 Nederlandse vertaling door %1 View License Bekijk Licentie Swift::QtAdHocCommandWindow Cancel Annuleren Back Terug Next Volgende Complete Voltooien Error: %1 Fout: %1 Warning: %1 Waarschuwing: %1 Error executing command Fout bij het uitvoeren van de opdracht Swift::QtAffiliationEditor Add User Voeg gebruiker toe Added User's Address: Adres toegevoegde gebruiker: Swift::QtAvatarWidget No picture Geen afbeelding Select picture ... Selecteer afbeelding ... Clear picture Verwijder afbeelding Select picture Selecteer afbeelding Image Files (*.png *.jpg *.gif) Afbeeldingen (*.png *.jpg *.gif) Error Fout The selected picture is in an unrecognized format Het formaat van de geselecteerde afbeelding werd niet herkend Image Files (*.png *.jpg *.jpeg *.gif) Beeldbestanden (*.png *.jpg *.jpeg *.gif) Swift::QtBookmarkDetailWindow Bookmark not valid Bladwijzer ongeldig You must specify a valid room address (e.g. someroom@rooms.example.com). Specifieer een geldige kamer (bv. eenkamer@kamers.voorbeeld.com). Swift::QtCertificateViewerDialog General Algemeen Valid From Geldig van Valid To Geldig tot Serial Number Serienummer Version Versie Subject Onderwerp Organization Organisatie Common Name Naam Locality Localiteit Organizational Unit Organisatie-eenheid Country Land State Staat Alternate Subject Names Alternatieve onderwerpsnaam E-mail Address E-mail adres DNS Name DNS naam Issuer Verstrekker Swift::QtChatListWindow Add New Bookmark Voeg bladwijzer toe Edit Bookmark Bewerk bladwijzer Remove Bookmark Verwijder bladwijzer Clear recents Wis recente conversaties Swift::QtChatView Clear log Wis inhoud You are about to clear the contents of your chat log. De inhoud van dit venster zal gewist worden. Are you sure? Bent u zeker? %1 edited %1 geëditeerd Waiting for other side to accept the transfer. Aan het wachten op de andere kant om te aanvaarden. Cancel Annuleren Negotiating... Aan het onderhandelen... Transfer has been canceled! Overdracht geannuleerd! Transfer completed successfully. Overdracht succesvol beëindigd. Transfer failed. Overdracht gefaald. Started whiteboard chat Tekentafel gestart Show whiteboard Toon tekentafel Whiteboard chat has been canceled Tekentafel werd geannuleerd Whiteboard chat request has been rejected Tekentafeverzoekl werd verworpen Return to room Keer terug naar kamer Swift::QtChatWindow This message has not been received by your server yet. Deze boodschap werd nog niet door uw server ontvangen. This message may not have been transmitted. Dit bericht kan mogelijk niet verzonden zijn. Couldn't send message: %1 Kon boodschap niet verzenden: %1 Correcting Corrigeren This chat may not support message correction. If you send a correction anyway, it may appear as a duplicate message Deze conversatie ondersteunt mogelijks geen verbeteringen. Verbeteringen kunnen als duplicate boodschappen aankomen This chat does not support message correction. If you send a correction anyway, it will appear as a duplicate message Deze conversatie ondersteunt geen verbeteringen. Verbeteringen zullen als duplicate boodschappen aankomen The receipt for this message has been received. Bevestiging ontvangen. The receipt for this message has not yet been received. The recipient(s) might not have received this message. Bevestiging nog niet ontvangen. De ontvanger kan mogelijks dit bericht nog niet ontvangen hebben. Send file Verzend bestand Cancel Annuleren Set Description Verander beschrijving Send Verzenden Receiving file Bestand aan het ontvangen Accept Accepteren Starting whiteboard chat Tekentafel aan het starten File transfer description Beschrijving bestand Description: Beschrijving: Save File Bestand opslaan Change subject… Verander onderwerp... Configure room… Configureer kamer... Edit affiliations… Verander lidmaatschap... Destroy room Vernietig kamer Invite person to this room… Nodig persoon uit voor deze kamer... Change room subject Verander onderwerp New subject: Niew onderwerp: Confirm room destruction Bevestig vernietiging kamer Are you sure you want to destroy the room? Bent U zeker dat U deze kamer wil vernietigen? This will destroy the room. Dit zal de kamer vernietigeng. Accept Invite Accepteer uitnodiging %1 would like to start a whiteboard chat %1 wil een tekentafel starten Swift::QtContactEditWidget Name: Naam: Groups: Groepen: New Group: Nieuwe groep: Swift::QtContactEditWindow Edit contact Contact bewerken Remove contact Contact verwijderen OK OK Confirm contact deletion Bevestig verwijderen van contact Are you sure you want to delete this contact? Bent u zeker dat u diet contact wil verwijderen? This will remove the contact '%1' from all groups they may be in. Dit zal het contact '%1' uit alle groepen verwijderen. Swift::QtEventWindow Display Notice Toon bericht Swift::QtFileTransferListWidget Clear Finished Transfers Wis voltooide overdrachten File Transfer List Lijst Bestandsoverdrachten Swift::QtHistoryWindow History Geschiedenis Swift::QtInviteToChatWindow Users to invite to this chat (one per line): Uit te nodigen gebruikers (één per lijn): If you want to provide a reason for the invitation, enter it here Uitnodigingsreden (Optioneel) Swift::QtJoinMUCWindow someroom@rooms.example.com eenkamer@kamers.voorbeeld.com Swift::QtLoginWindow User address: Gebruikersadres: User address - looks like someuser@someserver.com Gebruikersadres (van de vorm 'iemand@ergens.com') Example: alice@wonderland.lit Voorbeeld: alice@wonderland.lit Password: Wachtwoord: Click if you have a personal certificate used for login to the service. Klik hier indien u een persoonlijk certificaat gekregen heeft om in te loggen. Connect Verbinden Remember Password? Wachtwoord onthouden? Login Automatically? Automatisch inloggen? &Swift &Swift &General &Algemeen &About %1 &Over %1 &Show Debug Console Toon &debug console &Play Sounds &Geluid aan Display Pop-up &Notifications Toon &schermboodschappen &Quit &Afsluiten Remove profile Verwijder profiel Remove the profile '%1'? Profiel '%1' verwijderen? Cancel Annuleren Select an authentication certificate Selecteer een authenticatiecertificaat The certificate presented by the server is not valid. Het certificaat aangeboden door de server is ongeldig. Would you like to permanently trust this certificate? This must only be done if you know it is correct. Wilt u dit certificaat permanent vertrouwen? Dit mag enkel gedaan worden als u zeker bent dat het certificaat juist is. Subject: %1 Onderwerp: %1 SHA-1 Fingerprint: %1 SHA-1 vingerafdruk: %1 Show &File Transfer Overview Toon overzicht &Bestandsoverdrachten Confirm terms of use Bevestig gebruikersovereenkomst Swift::QtMUCConfigurationWindow Cancel Annuleren OK OK Swift::QtMUCSearchWindow Searching Aan het zoeken Swift::QtMainWindow &Contacts &Contacten &Notices &Berichten C&hats C&onversaties &View &Beeld &Actions &Acties &Show offline contacts &Toon offline contacten &Add Contact… Contact &toevoegen… &Edit Selected Contact… Geselecteerde contact &bewerken… Start &Chat… &Conversatie starten... &Sign Out &Afmelden Edit &Profile… Bewerk &profiel… Enter &Room… &Kamer betreden… Run Server Command Voer opdracht op server uit Collecting commands... Opdrachten aan het verzamelen... No Available Commands Geen beschikbare opdrachten &Show Emoticons &Toon emoticons &View History… Toon &Geschiedenis... &Request Delivery Receipts Vraag &ontvangstbevestigingen &Chats &Conversaties Swift::QtNameWidget (No Nickname Set) (Geen roepnaam ingesteld) Show Nickname Toon roepnaam Show Address Toon gebruikersadres Edit Profile Bewerk profiel Swift::QtOccupantListWidget No actions for this user Geen acties voor deze gebruiker Kick user Schop gebruiker Kick and ban user Schop en verban gebruiker Make moderator Maak moderator Make participant Maak deelnemer Remove voice Verwijder stem Add to contacts Voeg toe aan contacten Swift::QtProfileWindow Edit Profile Bewerk profiel Nickname: Roepnaam: Save Bewaar Swift::QtRosterHeader Connection is secured Verbinding is beveiligd Swift::QtRosterWidget Edit… Bewerk... Remove Verwijder Send File Verzend bestand Start Whiteboard Chat Start tekentafel All Files (*);; Alle bestanden (*);; Rename Naam wijzigen Rename group Groepsnaam wijzigen Enter a new name for group '%1': Geef een niewe naam voor groep '%1': Swift::QtStatusWidget Connecting Aan het verbinden (No message) (Geen boodschap) Swift::QtSubscriptionRequestWindow You have already replied to this request Dit verzoek is reeds beantwoord OK OK Yes Ja No Neen Defer Uitstellen %1 would like to add you to their contact list. Would you like to add them to your contact list and share your status when you're online? If you choose to defer this choice, you will be asked again when you next login. %1 wil u aan zijn/haar contactenlijst toevoegen. Wilt u dit contact toevoegen aan uw contactenlijst, en uw aanwezigheid delen wanneer u aanwezig bent? Als u deze keuze uitstelt, zal deze vraag opnieuw gesteld worden wanneer u zich opnieuw aanmeldt. Swift::QtTreeWidget Edit Bewerk Remove Verwijder Rename Naam wijzigen Rename group Groepsnaam wijzigen Enter a new name for group '%1': Geef een niewe naam voor groep '%1': Swift::QtUserSearchDetailsPage Please choose a name for the contact, and select the groups you want to add the contact to. Kies een naam voor het contact, en selecteer de groepen waar het contact moet toegevoegd worden. Swift::QtUserSearchFirstPage Chat to another user Start conversatie met andere gebruiker %1. If you know their address you can enter it directly, or you can search for them. %1. Indien u het adres kent kan u dit rechtstreeks invoeren, anders kan u het adres opzoeken. Add another user to your contact list Voeg een andere gebruiker toe aan uw contactenlijst Swift::QtUserSearchWindow Add Contact Contact toevoegen Chat to User Start conversatie met gebruiker How would you like to find the user to add? Hoe wilt u de gebruiker toevoegen? How would you like to find the user to chat to? Hoe wilt u de gebruiker contacteren? Error while searching Fout tijdens zoeken This server doesn't support searching for users. Deze server ondersteunt het zoeken van gebruikers niet. alice@wonderland.lit alice@wonderland.lit Swift::QtVCardAddressField Form Street Region Country Land Postal Code City Address Extension PO Box Preferred Home Work Postal Parcel Domestic Delivery International Delivery Delivery Type Swift::QtVCardAddressLabelField Form Address Label Preferred Home Work Postal Parcel Domestic Delivery International Delivery Delivery Type Swift::QtVCardBirthdayField Form Birthday dd.MM.yyyy X Swift::QtVCardDescriptionField Form Description Swift::QtVCardEMailField Form Type: Internet X.400 E-Mail Preferred Home Work Swift::QtVCardJIDField Form JID Swift::QtVCardOrganisationField Form Organisation Name Remove Unit Add Unit Organisation Units Swift::QtVCardRoleField Form Role Swift::QtVCardTelephoneField Form Telephone Preferred Home Work Voice Cell Pager ISDN Video Voice Messaging Service Fax Modem Personal Communication Service Bulletin Board System Swift::QtVCardTitleField Form Title Swift::QtVCardURLField Form URL Swift::QtWebView Clear Wissen Increase font size Vergroot lettertype Decrease font size Verklein lettertype Swift::QtWhiteboardWindow Closing window is equivalent closing the session. Are you sure you want to do this? Het venster sluiten zal deze sessie beëindigen. Bent U zeker dat U dit wil doen? Swift::QtXMLConsoleWidget Console Console Trace input/output Toon invoer/uitvoer Clear Wissen Debug Console Debug console <!-- IN --> <!-- IN --> <!-- OUT --> <!-- UIT --> TRANSLATION_INFO TRANSLATION_AUTHOR Remko Tronçon TRANSLATION_LICENSE This string contains the license under which this translation is licensed. We ask you to license the translation under the BSD license. Please read http://www.opensource.org/licenses/bsd-license.php, and if you agree to release your translation under this license, use the following (untranslated) text: 'This translation is licensed under the BSD License. See http://www.opensource.org/licenses/bsd-license.php' This translation is licensed under the GNU General Public License v3. See Documentation/Licenses/GPLv3.txt for more information swift-im-2.0+dev6/Swift/Translations/swift_hu.ts0000644000175000017500000012761512227051774021654 0ustar kismithkismith UTF-8 Starting chat with %1% in chatroom %2% Beszélgetés kezdése %1% partnerrel %2% szobában Starting chat with %1% - %2% Beszélgetés kezdése %1% %2% me én %1% has gone offline %1% kijelentkezett %1% has become available %1% bejelentkezett %1% has gone away %1% eltávozott %1% is now busy %1% mostantól elfoglalt The day is now %1% Ma %1% van Error sending message Hiba az üzenet küldésekor Bad request Hibás kérés Conflict Probléma This feature is not implemented A funkció még nincs megvalósítva Forbidden Elutasítva Recipient can no longer be contacted A címzettel már nem lehet felvenni a kapcsolatot Internal server error Belső szerver hiba Item not found Nem található JID Malformed Hibás JID Message was rejected Az üzenet visszautasítva Not allowed Nem engedélyezett Not authorized Nem engedélyezett Payment is required Fizetés szükséges Recipient is unavailable A partner nem elérhető Redirect Átirányítás Registration required Regisztráció szükséges Recipient's server not found A partner kiszolgálója nem található Remote server timeout Időtúllépés The server is low on resources A szerver túlterhelt The service is unavailable A szolgáltatás nem elérhető A subscription is required Feliratkozás szükséges Undefined condition Ismeretlen állapot Unexpected request Ismeretlen kérelem Room %1% is not responding. This operation may never complete. A szoba: %1% nem válaszol. Ez a művelet soha nem lesz befejezve. Unable to enter this room Nem lehet belépni ebbe a szobába Unable to enter this room as %1%, retrying as %2% Nem sikerült belépni ebbe a szobába %1% néven, újrapróbálkozás %2% néven No nickname specified Nincs becenév beállítva A password needed Jelszó szükséges Only members may enter Csak tagok léphetnek be You are banned from the room Kitiltottak a szobából The room is full A szoba megtelt The room does not exist A szoba nem létezik You have entered room %1% as %2%. Belépétél a(z) %1% szobába %2% néven. %1% has entered the room as a %2%. %1% belépett a szobába %2% néven. %1% has entered the room. %1% belépett a szobába. moderator moderátor participant résztvevő visitor látogató The room subject is now: %1% A szoba témája: %1% %1% is now a %2% %1% most %2% Moderators Moderátorok Participants Résztvevők Visitors Látogatók Occupants Jelenlévők Trying to enter room %1% Belépés a szobába: %1% %1% has left the room %1% kilépett You have left the room Kiléptél a szobából and és %1% have entered the room %1% belépett a szobába %1% has entered the room %1% belépett a szobába %1% have left the room %1% kilépett %1% have entered then left the room %1% belépett, majd kilépett a szobából %1% has entered then left the room %1% belépett, majd kilépett a szobából %1% have left then returned to the room %1% elhagyta, majd vissza lépett a szobába %1% has left then returned to the room %1% elhagyta, majd visszalépett a szobába %1% wants to add you to his/her contact list %1% szeretne felvenni a partnerlistájára Error Hiba Unknown Error Ismeretlen hiba Unable to find server Nem találom a kiszolgálót Error connecting to server Hiba a szerverhez való kapcsolódáskor Error while receiving server data Hiba adatfogadás közben a szervertől Error while sending data to the server Hiba adatküldés közben a szerverthez Error parsing server data Hiba a szerver adat feldolgozásakor Login/password invalid Azonosító/Jelszó hibás Error while compressing stream Hiba az adat tömörítése közben Server verification failed A szerver ellenőrzés meghíusúlt Authentication mechanisms not supported Az azonosítási mechanizmus nem támogatott Unexpected response Nem várt válasz Error binding resource Hiba a forrás lekötésekor Error starting session Hiba a munkafolyamat megkezdésekor Stream error Folyamat hiba Encryption error Titkosítási hiba Error loading certificate (Invalid password?) Hiba a tanusítvány betöltésekor (Hibás jelszó?) Certificate not authorized A tanusítvány nem engedélyezett Unknown certificate Ismeretlen tanusítvány Certificate has expired A tanusítvány lejárt Certificate is not yet valid A tanusítvány nem valós Certificate is self-signed A tanusítvány saját kezüleg lett aláírva Certificate has been rejected A tanusítvány visszautasítva Certificate is not trusted A tanusítvány nem megbízható Certificate cannot be used for encrypting your connection A tanusítvány nem használható a kapcsolat titkosítására Certificate path length constraint exceeded A tanusítvány úthossza nem megfelelő Invalid certificate signature Hibás tanusítvány aláírás Invalid Certificate Authority Érvénytelen hitelesítésszlgáltató Certificate does not match the host identity A tanusítvány nem egyezik a befogadóval Certificate error Tanusítvány hiba Reconnect to %1% failed: %2%. Will retry in %3% seconds. Újracsatlakozás %1% -hoz/-hez maghíusúlt: %2%. Újrapróbálkozás %3% másodperc múlva. Disconnected from %1%: %2%. %1% szétkapcsolódva: %2%. Contacts Partnerek Server %1% rejected contact list change to item '%2%' A szerver %1% visszautasította a partnerlista módosítását erre '%2%' Available Elérhető Away Távol Busy Elfoglalt Offline Kijelentkezett There was an error publishing your profile data Valamilyen hiba lépett fel a profil adatok mentésekor CloseButton Close Tab Fül bezárása MAC_APPLICATION_MENU Services Szolgáltatások Hide %1 %1 elrejtése Hide Others Többi elrejtése Show All Összes megjelenítése Preferences... Beállítások... Quit %1 Kilépés %1 About %1 A %1 prógramról QApplication QT_LAYOUT_DIRECTION Translate this to LTR for left-to-right or RTL for right-to-left languages Fordítsd le ezt BJ a balról-jobbra vagy JB jobbról-balra író nyelvekhez QDialogButtonBox &Yes &Igen &No &Nem &OK &Oké OK Oké &Cancel &Mégsem Cancel Mégsem QLineEdit Select All Összes kiválasztása &Undo &Visszavonás &Redo Új&ra Cu&t K&ivágás &Copy Másolás (&C) &Paste &Beillesztés Delete Törlés QMessageBox Show Details... Részletek... Hide Details... Részletek elrejtése... QObject %1 would like to add you to their contact list. %1 szeretne felvenni a parterlistájára. %1 would like to add you to their contact list, saying '%2' %1 szeretne felvenni a parterlistájára, üzenete: %2 No rooms found Nincsenek szobák QScrollBar Scroll here Görgetés ide Top Felső Bottom Alsó Page up Lapozás felfele Page down Lapozás lefele Scroll up Felfele görgetés Scroll down Lefelé görgetés QTextControl Select All Összes Összes kiválasztása &Copy &Másolás &Undo &Visszavonás &Redo Új&ra Cu&t K&ivágás &Paste &Beillesztés Delete Törlés QWebPage Copy Link Link másolása Copy Másolás Copy Image Kép másolása Scroll here Görgetés ide Top Felső Bottom Alsó Page up Lapozás felfele Page down Lapozás lefele Scroll up Felfele görgetés Scroll down Lefelé görgetés QWizard < &Back < &Vissza &Finish &Befejezés &Help &Súgó Go Back Vissza Continue Folytatás Commit Megoszt Done Rendben Quit Kilépés Help Segítség Cancel Mégsem &Next &Következő &Next > &Következő > QtBookmarkDetailWindow Edit Bookmark Details Könyvjelző tulajdonságai Bookmark Name: Könyvjelző neve: Room Address: Szoba címe: Your Nickname: Becenved: Room password: Szoba jelszava: Join automatically Csatlakozás automatikusan QtJoinMUCWindow Enter Room Belépés a szobába Room: Szoba: Search ... Keresés ... Nickname: Becenév: Enter automatically in future Belépés automatikusan QtMUCSearchWindow Search Room Szoba keresése Service: Szolgáltatás: Cancel Mégsem OK Oké List rooms Szobák QtUserSearchFieldsPage Nickname: Becenév: First name: Vezetéknév: Last name: Keresztnév: E-Mail: E-mail: Fetching search fields Kereső mezők betöltése QtUserSearchFirstPage Add a user Felhasználó hozzáadása Add another user to your contact list. If you know their address you can add them directly, or you can search for them. Másik felhasználó hozzáadása a partnerlistához. Hogyha tudod a címét, hozzáadhatod közvetlenül, vagy rákereshetsz a nevére is. I know their address: Ismert címe: I'd like to search my server Keresés a szerveren I'd like to search another server: Keresés másik szerveren: QtUserSearchWizard Find User Felhasználó keresése Swift::ChatListModel Bookmarked Rooms Könyvjelzőzött szobák Swift::QtAboutWidget About %1 A %1 prógramról Version %1 %1 verziója Built with Qt %1 Qt %1 verzióval forgatva Running with Qt %1 Qt %1 verzióval fut Using the English translation by %1 Magyar fordítás %1 View License Szerződés megtekintése Swift::QtAvatarWidget No picture Nincs kép Select picture ... Válassz képet... Clear picture Kép törlése Select picture Kép választása Image Files (*.png *.jpg *.gif) Képfájlok (*.png *.jpg *.gif) Error Hiba The selected picture is in an unrecognized format A választott kép ismeretlen formáutmú Swift::QtBookmarkDetailWindow Bookmark not valid A könyvjelző hibás You must specify a valid room address (e.g. someroom@rooms.example.com). Valós szoba címet kell megadnod (pl. szobaneve@rooms.szerver.hu). Swift::QtChatListWindow Add New Bookmark Új könyvjelző Edit Bookmark Könyvjelsző szerkesztése Remove Bookmark Könyvjelző törlése Swift::QtChatView Clear log Eőzmények törlése You are about to clear the contents of your chat log. Törölni szeretnéd a beszélgetési előzményeket. Are you sure? Biztos? Swift::QtChatWindow This message has not been received by your server yet. Az üzenetet nem fogadta még a szerver. This message may not have been transmitted. Az üzenet nem lett elküldve. Couldn't send message: %1 Nem sikerült elküldeni az üzenetet: %1 Swift::QtContactEditWidget Name: Név: Groups: Csoportok: New Group: Új csoport: Swift::QtContactEditWindow Edit contact Kapcsolat szerkesztése Remove contact Kapcsolat törlése OK Oké Confirm contact deletion Erősísd meg a partner törlését Are you sure you want to delete this contact? Valóba törölni szeretnéd a partnert? This will remove the contact '%1' from all groups they may be in. Törölni fogja a partnert '%1' az összes csoportból, amelyben benne van. Swift::QtEventWindow Display Notice Értesítés megjelenítése Swift::QtJoinMUCWindow someroom@rooms.example.com valamiszoba@rooms.szerver.hu Swift::QtLoginWindow User address: Cím: User address - looks like someuser@someserver.com Cím - Így néz ki: nev@kiszolgalo.hu Example: alice@wonderland.lit Példáúl: aliz@csodaorszag.hu Password: Jelszó: Click if you have a personal certificate used for login to the service. Klikk, hogyha saját aláírásoddal akarsz belépni a szerverre. Connect Kapcsolódás Remember Password? Jelszó mentése? Login Automatically? Automatikus bejelentkezés? &Swift &Swift &General &Általános &About %1 %1 &Készítői &Show Debug Console &Fejlesztői Konzol &Play Sounds &Hangok bekapcsolása Display Pop-up &Notifications Felugró ablakok &engedélyezése &Quit &Kilépés Remove profile Profil törlése Remove the profile '%1'? %1 profil tötlése? Cancel Mégsem Select an authentication certificate Válassz egy azonosító aláírást The certificate presented by the server is not valid. Az aláírás hibás. Would you like to permanently trust this certificate? This must only be done if you know it is correct. Valóban megbízol véglegesen az aláírásban? Ha helyes az aláírás, akkor ez alapértelmezett. Subject: %1 Tárgy: %1 SHA-1 Fingerprint: %1 SHA-1 Lenyomat: %1 Swift::QtMUCSearchWindow Searching Keresés Swift::QtMainWindow &Contacts &Partnerek &Notices &Értesítések C&hats &Beszélgetések &View &Nézet &Show offline contacts &Kijelentkezett partnerek mutatása &Actions &Műveletek Edit &Profile… &Profil szerkesztése… Enter &Room… Belépés &szobába… &Add Contact… &Partner felvétele… &Edit Selected Contact… &Kiválasztott partner szerkesztése… Start &Chat… Bes&zélgetés kezdeményezése &Sign Out Kijelentkezé&s Swift::QtNameWidget Show Nickname Becenév mutatása (No Nickname Set) (Nincs megadva becenév) Show Address Cím megjelenítése Edit Profile Profil szerkesztése Swift::QtProfileWindow Edit Profile Profil szerkesztése Nickname: Becenév: Save Mentés Swift::QtStatusWidget Connecting Kapcsolódás (No message) (Nincs üzenet) Swift::QtSubscriptionRequestWindow %1 would like to add you to their contact list. Would you like to add them to your contact list and share your status when you're online? If you choose to defer this choice, you will be asked again when you next login. %1 szeretne téged felvenni a partnerei köze. Szeretnéd felvenni őt a partner listádra és megosztani vele az állapotod? Hogyha az elutasítást választod, következő belépéskor ismét megjelenik ez a kérdés. You have already replied to this request Már reagáltál erre a kérésre OK Oké Yes Igen No Nem Defer Elutasít Swift::QtTreeWidget Edit Szerkesztés Remove Törlés Rename Átnevezés Rename group Csoport átnevezése Enter a new name for group '%1': %1 új neve: Swift::QtUserSearchDetailsPage Please choose a name for the contact, and select the groups you want to add the contact to. Kérlek, válassz egy nevet a partnerhez, és válaszd ki a csoportot, amelyikhez hozzá szeretnéd adni. Swift::QtUserSearchFirstPage %1. If you know their address you can enter it directly, or you can search for them. %1. Hogyha tudod a címét, akkor megadhatod azt, vagy kereshetsz is a nevére. Add another user to your contact list Másik felhasználó hozzáadása a listához Chat to another user Beszélgetés kezdeményezése egy másik partnerrel Swift::QtUserSearchWindow Add Contact Partner felvétele Chat to User Beszélgetés kezdeményezése alice@wonderland.lit aliz@csodaorszag.hu How would you like to find the user to add? Hogy szeretnéd megkeresni a felhasználót a felvételhez? How would you like to find the user to chat to? Hogy szeretnéd megkeresni a felhasználót a beszélgetéshez? Error while searching Hiba a keresés közben This server doesn't support searching for users. A szerver nem támogatja a felhasználókeresést. Swift::QtWebView Clear Tisztít Swift::QtXMLConsoleWidget Console Konzol Trace input/output Bemenet/Kimenet követése Clear Ürítés Debug Console Debug konzol <!-- IN --> <!-- BE --> <!-- OUT --> <!-- KI--> TRANSLATION_INFO TRANSLATION_AUTHOR Szontágh Ferenc <szontagh.ferenc@exahost.eu> TRANSLATION_LICENSE Should be the following (untranslated) text: 'This translation is licensed under the BSD License. See http://www.opensource.org/licenses/bsd-license.php' Should be the following (untranslated) text: 'This translation is licensed under the BSD License. See http://www.opensource.org/licenses/bsd-license.php' TRANSLATION_LICENSE This string contains the license under which this translation is licensed. We ask you to license the translation under the BSD license. Please read http://www.opensource.org/licenses/bsd-license.php, and if you agree to release your translation under this license, use the following (untranslated) text: 'This translation is licensed under the BSD License. See http://www.opensource.org/licenses/bsd-license.php' This translation is licensed under the BSD License. See http://www.opensource.org/licenses/bsd-license.php swift-im-2.0+dev6/Swift/Translations/swift_de.ts0000644000175000017500000022326312227051774021624 0ustar kismithkismith UTF-8 Starting chat with %1% in chatroom %2% Beginne ein Gespräche mit %1% im Chatraum %2% Starting chat with %1% - %2% Beginne ein Gespräch mit %1% - %2% me Ich %1% has gone offline %1% ist offline gegangen %1% has become available %1% ist online gekommen %1% has gone away %1% ist nicht mehr am Rechner %1% is now busy %1% ist beschäftigt The day is now %1% Wir haben jetzt den Tag %1% Couldn't send message: %1% Konnte die Nachricht nicht senden: %1% Error sending message Fehler beim Senden der Nachricht Bad request Fehlerhafter Aufruf Conflict Konflikt This feature is not implemented Diese Eigenschaft ist nicht implementiert Forbidden Verboten Recipient can no longer be contacted Der Empfänger ist nicht länger verfügbar Internal server error Interner Server Fehler Item not found Element nicht gefunden JID Malformed Jabber ID ist falsch formatiert Message was rejected Nachricht wurde zurückgewiesen Not allowed Nicht erlaubt Not authorized Nicht authorisiert Payment is required Bezahlung ist nötig Recipient is unavailable Empfänger nicht verfügbar Redirect Weiterleitung Registration required Registrierung nötig Recipient's server not found Server des Empfängers konnte nicht gefunden werden Remote server timeout Zeitüberschreitung beim entfernten Server The server is low on resources Der Server hat nur noch wenige Resourcen zur Verfügung The service is unavailable Dieser Service ist nicht verfügbar A subscription is required Ein Abonnement ist nötig Undefined condition Nicht definierter Zustand Unexpected request Unerwarteter Aufruf Room %1% is not responding. This operation may never complete. Der Chatraum %1% antwortet nicht. Diese Aktion wird wohl nie enden. Unable to enter this room Es ist nicht möglich diesem Chatraum beizutreten Unable to enter this room as %1%, retrying as %2% Es ist nicht möglich diesem Chatraum als %1% beizutreten, versuche es als %2% No nickname specified Nickname nicht angegeben A password needed Ein Passwort ist nötig Only members may enter Es dürfen nur Mitglieder eintreten You are banned from the room Du wurdest aus diesem Chatraum gebannt The room is full Der Chatraum ist voll The room does not exist Dieser Chatraum existiert nicht Couldn't join room: %1%. Konnte dem Raum nicht beitreten: %1%. You have entered room %1% as %2%. Du hast den Chatraum %1% als %2% betreten. %1% has entered the room as a %2%. %1% hat den Chatraum als ein %2% betreten. %1% has entered the room. %1% hat den Chatraum betreten. moderator Moderator participant Teilnehmer visitor Gast The room subject is now: %1% Das Thema des Chatraumes ist nun: %1% %1% is now a %2% %1% ist nun ein %2% Moderators Moderatoren Participants Teilnehmer Visitors Gäste Occupants Besitzer Trying to enter room %1% Versuche in den Chatraum %1% einzutreten %1% has left the room %1% hat den Chatraum verlassen You have left the room Du hast den Chatraum verlassen and und %1% have entered the room %1% haben den Chatraum betreten %1% has entered the room %1% hat den Chatraum betreten %1% have left the room %1% haben den Chatraum verlassen %1% have entered then left the room %1% habe den Chatraum betreten und dann wieder verlassen %1% has entered then left the room %1% hat den Chatraum betreten und dann wieder verlassen %1% have left then returned to the room %1% haben den Chatraum verlassen und dann wieder betreten %1% has left then returned to the room %1% hat den Chatraum verlassen und dann wieder betreten %1% wants to add you to his/her contact list %1% möchte dich zu seiner/ihrer Kontaktliste hinzufügen Error Fehler User address invalid. User address should be of the form 'alice@wonderland.lit' Die Benutzerkennung ist falsch. Eine Benutzerkennung sollte wie z.B. 'alice@wonderland.lit' ausschauen Unknown Error unbekannter Fehler Unable to find server Der Server kann nicht gefunden werden Error connecting to server Fehler beim Aufbau einer Verbindung zum Server Error while receiving server data Fehler beim Empfangen der Serverdaten Error while sending data to the server Fehler beim Senden der Daten zum Server Error parsing server data Fehler beim Einlesen der Serverdaten Login/password invalid Login oder Passwort ungültig Error while compressing stream Fehler beim Komprimieren des Datenstreams Server verification failed Überprüfung des Servers fehlgeschlagen Authentication mechanisms not supported Authentifizierungsmethoden werden nicht unterstützt Unexpected response Unerwartete Antwort Error binding resource Fehler beim Verknüpfen der Resourcen Error starting session Fehler beim Starten der Sitzung Stream error Stream Fehler Encryption error Verschlüsselungsfehler Error loading certificate (Invalid password?) Fehler beim Laden des Zertifikates (falsches Passwort?) Certificate not authorized Zertifikat nicht authorisiert Unknown certificate Unbekanntes Zertifikat Certificate has expired Zertifikat ist abgelaufen Certificate is not yet valid Zertifikat noch nicht gültig Certificate is self-signed Zertifikat ist selbst signiert Certificate has been rejected Zertifikat wurde zurückgewiesen Certificate is not trusted Zertifikat wird nicht vertraut Certificate cannot be used for encrypting your connection Zertifikat kann nicht zum Verschlüsseln der Verbindung genutzt werden Certificate path length constraint exceeded Zertifikatpfadlängenbeschränkung überschritten Invalid certificate signature Ungültige Zertifikatssignatur Invalid Certificate Authority Ungültige Zertifizierungsstelle Certificate does not match the host identity Zertifikat stimmt nicht mit der Hostidentität überein Certificate error Zertifikatsfehler Disconnected from %1%: %2%. To reconnect, Sign Out and provide your password again. Verbindung zu %1% wurde getrennt: %2%. Bitte ausloggen und nochmal einloggen um die Verbindung wieder aufzubauen. Reconnect to %1% failed: %2%. Will retry in %3% seconds. Erneutes Verbinden zu %1% fehlgeschlagen: %2%. Es wird in %3% Sekunden erneut versucht zu verbinden. Disconnected from %1%: %2%. Verbindung zu %1% getrennt: %2%. Contacts Kontakte Server %1% rejected contact list change to item '%2%' Server %1% hat die Kontaktlistenänderung, für Element '%2%', zurückgewiesen Available Verfügbar Away Abwesend Busy Beschäftigt Offline Offline There was an error publishing your profile data Es ist ein Fehler während der Veröffentlichung deiner Profildaten aufgetreten %1% (%2%) %1% (%2%) %1% and %2% others (%3%) %1% und %2% andere (%3%) %1%, %2% (%3%) %1%, %2% (%3%) CloseButton Close Tab Tab schließen MAC_APPLICATION_MENU Services Dienste Hide %1 Verstecke %1 Hide Others Verstecke andere Show All Zeige alle Preferences... Einstellungen... Quit %1 Beende %1 About %1 Über %1 QApplication QT_LAYOUT_DIRECTION Translate this to LTR for left-to-right or RTL for right-to-left languages LTR QDialogButtonBox &Yes &Ja &No &Nein &OK &Ok OK Ok &Cancel &Abbrechen Cancel Abbrechen QLineEdit Select All Alle auswählen &Undo &Rückgängig &Redo &Wiederherstellen Cu&t &Ausschneiden &Copy &Kopieren &Paste &Einfügen Delete Löschen QMessageBox Show Details... Details anzeigen... Hide Details... Details verstecken... QObject No rooms found Keine Chaträume gefunden %1 would like to add you to their contact list. %1 möchte dich zu seiner/ihrer Kontaktliste hinzufügen. %1 would like to add you to their contact list, saying '%2' %1 möchte dich zu seiner/ihrer Kontaktliste hinzufügen, sein/ihr Kommentar dazu war: '%2' Systray Infobereich No system tray Kein Infobereich (Systemtray) verfügbar QScrollBar Scroll here Hier scrollen Top Oben Bottom Unten Page up Seite hoch Page down Seite runter Scroll up raufscrollen Scroll down runterscrollen QTextControl Select All Alles auswählen &Copy &Kopieren &Undo &Rückgängig &Redo &Wiederherstellen Cu&t &Ausschneiden &Paste &Einfügen Delete Löschen QWebPage Copy Link Verknüpfung kopieren Copy Kopieren Copy Image Bild kopieren Scroll here Hier scrollen Top Oben Bottom Unten Page up Seite hoch Page down Seite runter Scroll up hochscrollen Scroll down runterscrollen QWizard < &Back < &Zurück &Finish &Abschließen &Help &Hilfe Go Back Zurück Continue Weiter Commit Festlegen Done Fertig Quit Beenden Help Hilfe Cancel Abbrechen &Next &Nächstes &Next > &Nächstes > QtBookmarkDetailWindow Edit Bookmark Details Lesezeichendetails editieren Bookmark Name: Lesezeichenname: Room Address: Chatraumadresse: Your Nickname: Dein Nickname: Room password: Chatraum Passwort: Join automatically Automatisch betreten QtJoinMUCWindow Enter Room Chatraum betreten Room: Chatraum: Search ... Suchen ... Nickname: Nickname: Enter automatically in future In Zukunft automatisch eintreten QtMUCSearchWindow Search Room Chatraum suchen Service: Service: Cancel Abbrechen List rooms Chaträume auflisten OK Ok QtUserSearchFieldsPage Nickname: Nickname: First name: Vorname: Last name: Nachname: E-Mail: E-Mail: Fetching search fields Suchfelder abrufen QtUserSearchFirstPage Add a user Benutzer hinzufügen Add another user to your contact list. If you know their address you can add them directly, or you can search for them. Füge einen anderen Benutzer zu deiner Kontaktliste hinzu. Wenn du dessen Adresse weißt, kannst du sie direkt eingeben, oder du kannst nach ihm suchen. I know their address: Ich kennen seine Adresse: I'd like to search my server Ich will meinen Server nach ihm durchsuchen I'd like to search another server: Ich will einen anderen Server nach ihm durchsuchen: QtUserSearchWizard Find User Benutzer finden Swift::ChatListModel Bookmarked Rooms Chatraum-Lesezeichen Recent Chats Kürzliche Gespräche Swift::QtAboutWidget About %1 Über %1 Version %1 Version %1 Built with Qt %1 Anhand von Qt %1 erstellt Running with Qt %1 Läuft unter Qt %1 Using the English translation by %1 Die, zur Zeit genutzte, deutsche Übersetzung stammt von %1 View License Lizenz anzeigen Swift::QtAdHocCommandWindow Cancel Abbrechen Back Zurück Next Weiter Complete Fertig Error: %1 Fehler: %1 Warning: %1 Warnung: %1 Error executing command Fehler beim Ausführen des Kommandos Swift::QtAvatarWidget No picture Kein Bild Select picture ... Bild auswählen ... Clear picture Bild verwerfen Select picture Bild auswählen Image Files (*.png *.jpg *.gif) Bilddateien (*.png *.jpg *.gif) Error Fehler The selected picture is in an unrecognized format Das ausgewählte Bild ist von einem unbekannten Format Swift::QtBookmarkDetailWindow Bookmark not valid Lesezeichen nicht gültig You must specify a valid room address (e.g. someroom@rooms.example.com). Du musst eine gültige Chatraumadresse angeben (z.B. meinraum@chatraeume.beispiel.com). Swift::QtChatListWindow Add New Bookmark Neues Lesezeichen hinzufügen Edit Bookmark Lesezeichen editieren Remove Bookmark Lesezeichen entfernen Swift::QtChatView Clear log Aufzeichnung löschen You are about to clear the contents of your chat log. Du bist dabei die Gesprächsaufzeichung zu löschen. Are you sure? Bist du dir sicher? %1 edited %1 editiert Swift::QtChatWindow Correcting verbessern This message has not been received by your server yet. Die Nachricht wurde noch nicht von deinem Server empfangen. This message may not have been transmitted. Die Nachricht scheint nicht versandt worden zu sein. Couldn't send message: %1 Konnte die Nachricht nicht senden: %1 Swift::QtContactEditWidget Name: Name: Groups: Gruppen: New Group: Neue Gruppe: Swift::QtContactEditWindow Edit contact Kontakt editieren Remove contact Kontakt entfernen OK Ok Confirm contact deletion Das Löschen des Kontakts bestätigen Are you sure you want to delete this contact? Bist du dir sicher, dass du diesen Kontakt löschen willst? This will remove the contact '%1' from all groups they may be in. Dies wird den Kontakt '%1' aus allen Gruppen, in denen er sich befindet, entfernen. Swift::QtEventWindow Display Notice Hinweis anzeigen Swift::QtJoinMUCWindow someroom@rooms.example.com meinraum@chatraeume.beispiel.com Swift::QtLoginWindow User address: Benutzeradresse: User address - looks like someuser@someserver.com Benutzeradresse - schaut ungefähr aus wie irgendeinbenutzer@irgendeinserver.de Example: alice@wonderland.lit Beispiel: alice@wunderland.lit Password: Passwort: Click if you have a personal certificate used for login to the service. Wenn du ein persönliches Zertifikat zum Einloggen hast, drücke hier. Connect Verbinden Remember Password? Passwort behalten? Login Automatically? Automatisch einloggen? &Swift &Swift &General All&gemein &About %1 &Über %1 &Show Debug Console &Debug-Konsole anzeigen &Play Sounds &Klänge abspielen Display Pop-up &Notifications Hi&nweisdialoge anzeigen &Quit &Beenden Remove profile Profil entfernen Remove the profile '%1'? Das Profil '%1' entfernen? Cancel Abbrechen Select an authentication certificate Wähle ein Authentifizierungszertifikat The certificate presented by the server is not valid. Das Serverzertifikat ist nicht gültig. Would you like to permanently trust this certificate? This must only be done if you know it is correct. Willst du diesem Zertifikat permanent Vertrauen? Das darf nur der Fall sein, wenn du genau weißt, dass es korrekt ist. Subject: %1 Betreff: %1 SHA-1 Fingerprint: %1 SHA-1 Fingerabdruck: %1 Swift::QtMUCSearchWindow Searching Suche Swift::QtMainWindow &Contacts &Kontakte &Notices H&inweise C&hats C&hats &View A&nsicht &Show offline contacts &Offline-Kontakte anzeigen &Actions &Aktionen Edit &Profile… &Profil editieren… Enter &Room… Chat&raum betreten… &Add Contact… Kont&akt hinzufügen… &Edit Selected Contact… Ausgewählten Kontakt &editieren… Start &Chat… Gesprä&ch beginnen Run Server Command Server Kommando ausführen &Sign Out A&bmelden Collecting commands... Verfügbare Kommandos werden abgerufen... No Available Commands Keine Kommandos verfügbar Swift::QtNameWidget Show Nickname Nicknamen anzeigen (No Nickname Set) (Kein Nickname eingestellt) Show Address Adresse anzeigen Edit Profile Profil editieren Swift::QtProfileWindow Edit Profile Profil editieren Nickname: Nickname: Save Speichern Swift::QtStatusWidget Connecting Verbinden (No message) (Keine Nachricht) Swift::QtSubscriptionRequestWindow You have already replied to this request Du hast auf diese Anfrage bereits geantwortet %1 would like to add you to their contact list. Would you like to add them to your contact list and share your status when you're online? If you choose to defer this choice, you will be asked again when you next login. %1 möchte dich zu seiner/ihrer Kontaktliste hinzufügen. Willst du ihn/sie auch zu deiner Kontaktliste hinzufügen und deinen Status ihm/ihr mitteilen wenn du online bist? Wenn du dich jetzt noch nicht festlegen willst, wirst du beim nächsten Einloggen nochmals danach gefragt. OK OK Yes Ja No Nein Defer Später Swift::QtTreeWidget Edit Editieren Remove Entfernen Rename Umbenennen Rename group Guppe umbenennen Enter a new name for group '%1': Gibt einen neuen Namen für die Gruppe '%1' ein: Swift::QtUserSearchDetailsPage Please choose a name for the contact, and select the groups you want to add the contact to. Bitte wähle einen Namen für den Kontakt und wähle die Gruppen aus zu denen der Kontakt hinzugefügt werden soll. Swift::QtUserSearchFirstPage %1. If you know their address you can enter it directly, or you can search for them. %1. Wenn du seine Adresse kennst, kannst du sie direkt eingeben, ansonsten kannst du auch danach suchen. Add another user to your contact list Füge einen anderen Benutzer zu deinen Kontakten hinzu Chat to another user Mit einem anderen Benutzer chatten Swift::QtUserSearchWindow Add Contact Kontakt hinzufügen Chat to User Mit einem Benutzer chatten alice@wonderland.lit alice@wonderland.lit How would you like to find the user to add? Wie willst du nach dem Benutzer suchen, den du hinzufügen möchtest? How would you like to find the user to chat to? Wie willst du nach dem Benutzer suchen, mit dem du sprechen willst? Error while searching Während der Suche ist ein Fehler aufgetreten This server doesn't support searching for users. Dieser Server untersützt das Suchen nach Benutzern nicht. Swift::QtWebView Clear Leeren Increase font size Schriftgröße erhöhen Decrease font size Schriftgröße verringern Swift::QtXMLConsoleWidget Console Konsole Trace input/output Eingabe/Ausgabe mit schneiden Clear Leeren Debug Console Debug-Konsole <!-- IN --> <!-- EINTREFFEND --> <!-- OUT --> <!-- AUSGEHEND --> TRANSLATION_INFO TRANSLATION_AUTHOR Thilo Cestonaro TRANSLATION_LICENSE This string contains the license under which this translation is licensed. We ask you to license the translation under the BSD license. Please read http://www.opensource.org/licenses/bsd-license.php, and if you agree to release your translation under this license, use the following (untranslated) text: 'This translation is licensed under the BSD License. See http://www.opensource.org/licenses/bsd-license.php' This translation is licensed under the BSD License. See http://www.opensource.org/licenses/bsd-license.php swift-im-2.0+dev6/Swift/Translations/swift_sk.ts0000644000175000017500000035204612227051774021653 0ustar kismithkismith UTF-8 Starting chat with %1% in chatroom %2% Začína sa rozhovor s %1% v miestnosti %2% Starting chat with %1% - %2% Začína sa rozhovor s %1% - %2% This chat doesn't support delivery receipts. Tento rozhovor nepodporuje doručenky. This chat may not support delivery receipts. You might not receive delivery receipts for the messages you sent. Tento rozhovor nemusí podporovať doručenky. Je možné, že k odoslaným správam nedostanete potvrdenie o doručení. me ja %1% has gone offline %1% je odteraz offline %1% has become available %1% je odteraz tu %1% has gone away %1% je odteraz preč %1% is now busy %1% teraz nemá čas The day is now %1% Teraz je %1% Couldn't send message: %1% Správu sa nepodarilo odoslať: %1% Error sending message Chyba pri posielaní správy Bad request Nesprávna požiadavka Conflict Konflikt This feature is not implemented Táto vlastnosť nie je implementovaná Forbidden Zakázané Recipient can no longer be contacted Príjemcu už nie je možné kontaktovať Internal server error Vnútorná chyba serveru Item not found Položka nenájdená JID Malformed Nesprávne utvorené JID Message was rejected Správa bola odmietnutá Not allowed Nepovolené Not authorized Neautorizované Payment is required Vyžadovaná platba Recipient is unavailable Príjemca je nedostupný Redirect Presmerovanie Registration required Vyžadovaná registrácia Recipient's server not found Server príjemcu nenájdený Remote server timeout správny význam? Vypršal čas pre pripojenie k vzdialenému serveru The server is low on resources Server má nedostatok zdrojov The service is unavailable Služba nie je dostupná A subscription is required Vyžadované prihlásenie k odberu Undefined condition Nedefinovaná podmienka Unexpected request Neočakávaná požiadavka Room %1% is not responding. This operation may never complete. Miestnosť %1% neodpovedá. Táto operácia nemusí nikdy skončiť. Unable to enter this room Nepodarilo sa vstúpiť do miestnosti Unable to enter this room as %1%, retrying as %2% Nepodarilo sa vstúpiť do miestnosti ako %1%, znovu opakované ako %2% No nickname specified Nezadaná prezývka A password needed Je vyžadované heslo A password is needed Je vyžadované heslo Only members may enter Vstúpiť môžu iba členovia You are banned from the room Máte zakázané vstúpiť do miestnosti The room is full Miestosť je plná The room does not exist Miestosť neexistuje Couldn't join room: %1%. Nepodarilo sa vstúpiť do miestnosti: %1%. You have entered room %1% as %2%. Vstúpili ste do miestnosti %1% ako %2%. %1% has entered the room as a %2%. %1% vstúpil do miestnosti ako %2%. %1% has entered the room. %1% vstúpil do miestnosti. moderator moderátor participant účastník visitor návštevník The room subject is now: %1% Predmet miestnosti je: %1% %1% is now a %2% %1% je teraz %2% Moderators Moderátori Participants Účastníci Visitors Návštevníci Occupants lepšie synonymum k NoRole? Ostatní návštevníci Trying to enter room %1% Pokúšam sa vstúpiť do miestnosti %1% %1% has left the room%2% %1% opustil miestnosť%2% You have been kicked out of the room Boli ste vyhodení z miestnosti You have been banned from the room Bol vám zakázaný prístup do miestnosti You are no longer a member of the room and have been removed Už nieste členom miestnosti a vaše členstvo vám bol odobrané The room has been destroyed Miestnosť bola odstránená %1% has left the room %1% opustil/-a miestnosť You have left the room Opustili ste miestnosť The correct room password is needed Je potrebné správne heslo miestnosti and a %1% have entered the room %1% vstúpili do miestnosti %1% has entered the room %1% vstúpil/-a do miestnosti %1% have left the room %1% opustili miestnosť %1% have entered then left the room %1% vstúpili a potom opustili miestnosť %1% has entered then left the room %1% vstúpil/-a a potom opustil/a miestnosť %1% have left then returned to the room %1% opustili miestnosť a potom sa vrátili %1% has left then returned to the room %1% opustil/-a miestnosť a potom sa vrátil/-a Room configuration failed: %1%. Nepodarilo sa nastaviť miestnosť: %1%. Occupant role change failed: %1%. Nepodarila sa zmeniť rola pre návštevníka: %1%. %1% wants to add you to his/her contact list %1% si vás chce pridať do zoznamu kontaktov Error Chyba %1% has invited you to enter the %2% room %1% vás pozval/-a vstúpiť do miestnosti %2% User address invalid. User address should be of the form 'alice@wonderland.lit' Adresa osoby je neplatná. Adresa osoby by mala byť v tvare „alica@krajina-zazrakov.lit.“ Unknown Error Neznáma chyba Unable to find server Nepodarilo sa nájsť server Error connecting to server Chyba pri pripojení k serveru Error while receiving server data Chyba pri prijímaní dát zo serveru Error while sending data to the server Chyba pri posielaní dát na server Error parsing server data Chyba pri spracovaní dát zo serveru Login/password invalid Prihlasovacie meno alebo heslo nie je platné Error while compressing stream Chyba pri komprimovaní prúdu Server verification failed Zlyhalo overenie serveru Authentication mechanisms not supported Overovacie mechanizmy nie sú podporované Unexpected response Neočakávaná odpoveď Error binding resource Chyba pri spájaní zdroju Error starting session Chyba pri spúštaní sedenia Stream error Chyba prúdu Encryption error Chyba šifrovania Error loading certificate (Invalid password?) Chyba pri načítaní certifikátu (nesprávne heslo?) Certificate not authorized Neautorizovaný certifikát Certificate card removed Karta so certifikátom odstránená Unknown certificate Neznámy certifikát Certificate has expired Certifikát expiroval Certificate is not yet valid Certifikát zatiaľ nie je platný Certificate is self-signed Certifikát je podpísaný samým sebou Certificate has been rejected Certifikát bol odmietnutý Certificate is not trusted Certifikát nie je dôveryhodný Certificate cannot be used for encrypting your connection Certifikát nemôže byť použitý pre šifrovanie vášho spojenia Certificate path length constraint exceeded Prekročená dĺžka cesty certifikátu Invalid certificate signature Neplatný podpis certifikátu Invalid Certificate Authority Neplatná certifikačná autorita Certificate does not match the host identity Certifikát sa nezhoduje s identitou hostiteľa Certificate has been revoked Certifikát bol odvolaný Unable to determine certificate revocation state Nepodarilo sa zistiť stav odvolania certifikátu Certificate error Chyba certifikátu Re-enter credentials and retry Znovu zadajte prístupové údaje a zopakujte akciu Disconnected from %1%: %2%. To reconnect, Sign Out and provide your password again. Odpojené od %1%: %2%. Pre znovu pripojenie sa odhláste a zadajte znovu svoje heslo. Reconnect to %1% failed: %2%. Will retry in %3% seconds. Opätovné pripojenie ku %1% zlyhalo: %2%. Ďalší pokus o %3% sekúnd. Disconnected from %1%: %2%. Odpojené od %1%: %2%. Contacts Kontakty Server %1% rejected contact list change to item '%2%' Server %1% odmietol zmenu položky „%2%“ v zozname kontaktov Available Som tu Away Som preč Busy Nemám čas Offline Offline There was an error publishing your profile data Pri zverejnení údajov vášho profilu nastala chyba %1% (%2%) %1% (%2%) %1% and %2% others (%3%) %1% a %2% ostatní (%3%) %1%, %2% (%3%) %1%, %2% (%3%) TLS Client Certificate Selection Výber TLS klientského certifikátu Select a certificate to use for authentication Vyberte certifikát, ktorý chcete použiť pre overenie CloseButton Close Tab Zavrieť kartu MAC_APPLICATION_MENU Services Služby Hide %1 Skryť %1 Hide Others Skryť ostatné Show All Zobraziť všetky Preferences... Nastavenia… Quit %1 Ukončiť %1 About %1 O programe %1 QApplication QT_LAYOUT_DIRECTION Translate this to LTR for left-to-right or RTL for right-to-left languages LTR QDialogButtonBox &Yes Án&o &No &Nie &OK &Ok OK Ok &Cancel &Zrušiť Cancel Zrušiť QLineEdit Select All Vybrať všetko &Undo &Späť &Redo &Opakovať Cu&t Vyst&rihnúť &Copy &Kopírovať &Paste &Vložiť Delete Zmazať QMessageBox Show Details... Zobraziť detaily… Hide Details... Skryť detaily… QObject No rooms found Nenašli sa žiadne miestnosti %1 would like to add you to their contact list. %1 si vás chce pridať do zoznamu kontaktov. %1 would like to add you to their contact list, saying '%2' %1 si vás chce pridať do zoznamu kontaktov: „%2“ %1 has invited you to enter the %2 room. %1 vás pozval vstúpiť do miestnosti %2. Systray Oznamovacia oblasť No system tray Nie v oznamovacej oblasti You've been invited to enter the %1 room. Dostali ste pozvánku na vstup do miestnosti %1. Reason: %1 Dôvod: %1 This person may not have really sent this invitation! Táto osoba nemusela skutočne poslať túto pozvánku! Direction Smer Other Party Protistrana State Stav Progress Priebeh Size Veľkosť Incoming Prichádzajúci Outgoing Odchádzajúci Waiting for start Čakanie na spustenie Waiting for other side to accept Čakanie na prijatie protistranou Negotiating Vyjednávanie Transferring Prenášanie Finished Dokončené Failed Zlyhané Canceled Zrušené Connection Options Možnosti pripojenia QScrollBar Scroll here Posunúť sem Top Začiatok Bottom Koniec Page up O stránku hore Page down O stránku dole Scroll up Posunúť hore Scroll down Posunúť dole QTextControl Select All Vybrať všetko &Copy &Kopírovať &Undo &Späť &Redo &Opakovať Cu&t Vyst&rihnúť &Paste &Vložiť Delete Zmazať QWebPage Copy Link Kopírovať odkaz Copy Kopírovať Copy Image Kopírovať obrázok Scroll here Posunúť sem Top Začiatok Bottom Koniec Page up O stránku hore Page down O stránku dole Scroll up Posunúť hore Scroll down Posunúť dole QWizard < &Back < &Naspäť &Finish &Dokončiť &Help &Pomocník Go Back Prejsť naspäť Continue Pokračovať Commit správny význam? Odoslať Done Hotovo Quit Ukončiť Help Pomoc Cancel Zrušiť &Next Ď&alej &Next > Ď&alej > QtAffiliationEditor Dialog Dialog Edit Affiliations Upraviť členstvo Affiliation: Členstvo: Owner Majiteľ Administrator Správca Member Člen Outcast (Banned) Vyvrheľ (zakázaný) Add User Pridať osobu Remove User Odstrániť osobu QtBookmarkDetailWindow Edit Bookmark Details Upraviť detaily záložky Bookmark Name: Názov záložky: Room Address: Adresa miestnosti: Your Nickname: Vaša prezývka: Room password: Heslo pre miestnosť: Join automatically Vstúpiť automaticky Enter automatically Vstúpiť automaticky QtCertificateViewerDialog Certificate Viewer Prehliadač certifikátu QtConnectionSettings Dialog Dialóg Connection Options Možnosti pripojenia Connection Method: Spôsob pripojenia: Automatic Automatické Manual Manuálne BOSH BOSH Secure connection: Zabezpečenie spojenia: Never Bez zabezpečenia Encrypt when possible Ak je možné, šifrovať Always encrypt Vždy šifrovať Allow Compression Povoliť kompresiu Allow sending password over insecure connection Povoliť odosielanie hesla cez nezabezpečené spojenie Manually select server Manuálny výber servera Hostname: Názov hostiteľa: Port: Port: Connection Proxy Proxy server pre spojenie Proxy type: Typ proxy servera: None Žiadny Use system-configured proxy Podľa nastavenia systému SOCKS5 SOCKS5 HTTP Connect HTTP Connect Override system-configured proxy Prepísať systémové nastavenia proxy servera BOSH URI: BOSH URI: Manually select HTTP proxy Ručné zadanie HTTP proxy servera QtHistoryWindow Form Formulár History História Search: Vyhľadať: Next Ďalej Previous Naspäť QtJoinMUCWindow Enter Room Vstúpiť do miestnosti Room: Miestnosť: Search ... Vyhľadať… Nickname: Prezývka: Enter automatically in future V budúcnosti vstúpiť automaticky Password: Heslo: Automatically configure newly created rooms Nové miestnosti nastaviť automaticky Room Address: Adresa miestnosti: Your Nickname: Vaša prezývka: Room Password: Heslo miestnosti: QtMUCSearchWindow Search Room Vyhľadať miestnosť Service: Služba: Cancel Zrušiť OK Ok List rooms Zoznam miestností QtUserSearchFieldsPage Nickname: Prezývka: First name: Meno: Last name: Priezvisko: E-Mail: E-mail: Fetching search fields Prebieha načítanie polí pre vyhľadávanie QtUserSearchFirstPage Add a user Pridať osobu Add another user to your contact list. If you know their address you can add them directly, or you can search for them. inak pomenované ako inde Pridať ďalšiu osobu do vášho zoznamu kontaktov. Ak viete jej adresu môžete ju pridať priamo, inak ju môžete vyhľadať. I know their address: Viem jej adresu: I'd like to search my server Prehľadať môj server I'd like to search another server: Prehľadať iný server: QtUserSearchResultsPage No results. Žiadne výsledky. QtUserSearchWizard Find User Vyhľadať osobu Swift::ChatListModel Bookmarked Rooms kostrbatý preklad Záložky miestností Recent Chats Nedávne rozhovory Opened Whiteboards Otvorené tabule Swift::FileTransferController me ja Swift::QtAboutWidget About %1 O programe %1 Version %1 Verzia %1 Built with Qt %1 Zostavené s Qt %1 Running with Qt %1 Beží s Qt %1 Using the English translation by %1 Do slovenčiny preložil %1 View License Zobraziť licenciu Swift::QtAdHocCommandWindow Cancel Zrušiť Back Naspäť Next Ďalej Complete Hotovo Error: %1 Chyba: %1 Warning: %1 Upozornenie: %1 Error executing command Chyba pri vykonávaní príkazu Swift::QtAffiliationEditor Add User Pridať osobu Added User's Address: Pridaná adresa osoby: Swift::QtAvatarWidget No picture Bez obrázku Select picture ... Vybrať obrázok… Clear picture Vymazať obrázok Select picture Vybrať obrázok Image Files (*.png *.jpg *.jpeg *.gif) Súbory s obrázkami (*.png *.jpg *.jpeg *.gif) Image Files (*.png *.jpg *.gif) Súbory s obrázkami (*.png *.jpg *.gif) Error Chyba The selected picture is in an unrecognized format Nepodarilo sa rozpoznať formát vybraného obrázku Swift::QtBookmarkDetailWindow Bookmark not valid Neplatná záložka You must specify a valid room address (e.g. someroom@rooms.example.com). Musíte uviesť platnú adresu miestnosti (napríklad miestnost@miestnosti.priklad.sk). You must specify a valid room address (e.g. myroom@chats.example.com). Musíte uviesť platnú adresu miestnosti (napríklad miestnost@rozhovory.priklad.sk). Swift::QtCertificateViewerDialog General Všeobecné Valid From Platné od Valid To Platné do Serial Number Sériové číslo Version Verzia Subject Subjekt Organization Organizácia Common Name Bežný názov Locality Lokalita Organizational Unit Organizačná jednotka Country Štát State Stav Alternate Subject Names Alternatívne mená subjektu E-mail Address E-mailová adresa DNS Name Meno DNS Issuer Vydavateľ Swift::QtChatListWindow Add New Bookmark Pridať novú záložku Edit Bookmark Upraviť záložku Remove Bookmark Odstrániť záložku Clear recents Vymazať nedávne Swift::QtChatView Clear log Vymazať záznam You are about to clear the contents of your chat log. Práve idete vymazať záznam vášho rozhovoru. Are you sure? Ste si istý? %1 edited %1 upravené Waiting for other side to accept the transfer. Čakanie na prijatie prenosu druhou stranou. Cancel Zrušiť Negotiating... Vyjednávanie… Transfer has been canceled! Prenos bol zrušený! Transfer completed successfully. Prenos bol úspešne dokončený. Transfer failed. Prenos zlyhal. Started whiteboard chat Spustený rozhovor s využitím tabule Show whiteboard Zobraziť tabuľu Whiteboard chat has been canceled Rozhovor s využitím tabule bol zrušený Whiteboard chat request has been rejected Požiadavka na rozhovor s využitím tabule bola odmietnutá Return to room Vrátiť sa do miestnosti Swift::QtChatWindow This message has not been received by your server yet. Táto správa zatiaľ nebola prijatá vašim serverom. This message may not have been transmitted. Táto správa ešte nemusela byť prenesená. Couldn't send message: %1 Správu sa nepodarilo odoslať: %1 Actions Akcie Correcting Prebieha opravovanie This chat may not support message correction. If you send a correction anyway, it may appear as a duplicate message Tento rozhovor nemusí podporovať opravu správ. Keď aj tak pošlete opravu, môže vyzerať ako duplicitná správa This chat does not support message correction. If you send a correction anyway, it will appear as a duplicate message Tento rozhovor nepodporuje opravu správ. Keď aj tak pošlete opravu, bude vyzerať ako duplicitná správa The receipt for this message has been received. Pre túto správu bola prijatá doručenka. The receipt for this message has not yet been received. The receipient(s) might not have received this message. Doručenka pre túto správu zatiaľ neprišla. Príjemca nemusel prijať túto správu. The receipt for this message has not yet been received. The recipient(s) might not have received this message. Doručenka pre túto správu zatiaľ neprišla. Príjemca nemusel obdržať túto správu. Send file) Odoslať súbor Send file Odoslať súbor Cancel Zrušiť Set Description Nastaviť popis Send Odoslať Receiving file Prijímanie súboru Accept Prijať Starting whiteboard chat Prebieha spúšťanie rozhovoru s využitím tabule %1 would like to start a whiteboard chat %1 chce začať rozhovor s využitím tabule would like to start whiteboard chat chce začať rozhovor s využitím tabule File transfer description Popis prenosu súboru Description: Popis: Save File Uložiť súbor Change subject… Zmeniť predmet… Configure room… Nastaviť miestnosť… Edit affiliations… Upraviť členstvo… Invite person to this room… Pozvať používateľa do tejto miestnosti… Change subject Zmeniť predmet Configure room Nastaviť miestnosť Edit affiliations Upraviť členstvo Destroy room Nájsť vhodnejšie slovo k odstrániť – zničiť? Odstrániť miestnosť Invite person to this room Pozvať osobu do tejto mietnosti Change room subject Zmeniť predmet miestnosti New subject: Nový predmet: Confirm room destruction Potvrdenie odstránenia miestnosti Are you sure you want to destroy the room? Naozaj chcete odstrániť miestnosť? This will destroy the room. Týmto odstrániťe miestnosť. Enter person's address Zadajte adresu osoby Address: Adresa: Accept Invite Prijať pozvánku Swift::QtContactEditWidget Name: Meno: Groups: Skupiny: New Group: Nová skupina: Swift::QtContactEditWindow Edit contact Upraviť kontakt Remove contact Odstrániť kontakt OK Ok Confirm contact deletion Potvrdenie odstránenia kontaktu Are you sure you want to delete this contact? Naozaj chcete odstrániť tento kontakt? This will remove the contact '%1' from all groups they may be in. Kontakt „%1“ bude odstránený zo všetkých skupín, v ktorých sa nachádza. Swift::QtEventWindow Display Notice Zobraziť udalosť Swift::QtFileTransferListWidget Clear Finished Transfers Vymazať dokončené prenosy File Transfer List Zoznam prenosu súborov Swift::QtHistoryWindow History História Swift::QtInviteToChatWindow Users to invite to this chat (one per line): Zoznam osôb, ktoré chcete pozvať do tohto rozhovoru (po jednej na riadku): If you want to provide a reason for the invitation, enter it here Ak chcete zadať dôvod pre pozvánku, napíšte ho sem Swift::QtJoinMUCWindow someroom@rooms.example.com miestnost@miestnosti.priklad.sk Swift::QtLoginWindow User address: Adresa osoby: User address - looks like someuser@someserver.com Adresa osoby - vyzerá ako niekto@nejakyserver.sk Example: alice@wonderland.lit lepsi priklad Príklad: alica@krajina-zazrakov.lit Password: Heslo: Click if you have a personal certificate used for login to the service. Kliknite ak máte osobný certifikát použiteľný pre prihlásenie k službe. Connect Prihlásiť Remember Password? Zapamätať si heslo Login Automatically? Prihlásiť automaticky &Swift &Swift &General &Všeobecné &About %1 O &programe %1 &Show Debug Console &Zobraziť ladiacu konzolu &Play Sounds &Prehrávať zvuky Display Pop-up &Notifications notifikácia sa lepšie hodí Zobrazovať &oznámenia &Quit U&končiť Remove profile Odstrániť profil Remove the profile '%1'? Odstrániť profil „%1“? Cancel Zrušiť Confirm terms of use Potvrdenie podmienok použitia Select an authentication certificate Vyberte certifikát pre overenie P12 files (*.cert *.p12 *.pfx);;All files (*.*) Súbory P12 (*.cert *.p12 *.pfx);;Všetky súbory (*.*) The certificate presented by the server is not valid. správny význam? Certifikát, ktorý poskytol server nie je platný. Would you like to permanently trust this certificate? This must only be done if you know it is correct. Chcete natrvalo dôverovať tomuto certifikátu? Toto stačí spraviť iba raz, ak viete, že je správny. Subject: %1 Predmet: %1 SHA-1 Fingerprint: %1 Odtlačok SHA-1: %1 Show &File Transfer Overview Zobraziť prehľad prenosu &súborov Swift::QtMUCConfigurationWindow Cancel Zrušiť OK Ok Swift::QtMUCSearchWindow Searching Prebieha vyhľadávanie Swift::QtMainWindow &Contacts &Kontakty &Notices &Udalosti C&hats &Rozhovory &View &Zobraziť &Show offline contacts Zobraziť &offline kontakty &Compact Roster &Kompaktný zoznam kontaktov &Show Emoticons &Zobraziť emotikony &Actions &Akcie Edit &Profile… Upraviť &profil… Enter &Room… Vstúpiť do &miestnosti… &View History… &Zobraziť históriu… &Add Contact… Pridať &kontakt… &Edit Selected Contact… &Upraviť vybraný kontakt… Start &Chat… Začať &rozhovor… &Sign Out &Odhlásiť sa Notices &Udalosti Run Server Command Spustiť príkaz na serveri &Request Delivery Receipts Vyžadovať &doručenky Collecting commands... Prebieha získavanie príkazov… &Chats &Rozhovory No Available Commands Nie sú dostupné žiadne príkazy Swift::QtNameWidget Show Nickname Zobraziť prezývku (No Nickname Set) (Prezývka nenastavená) Show Address Zobraziť adresu Edit Profile Upraviť profil Swift::QtOccupantListWidget No actions for this user Pre osobu nie sú dostupné žiadne akcie Kick user Vykopnúť osobu Kick and ban user Vykopnúť osobu a zakázať prístup Make moderator Spraviť moderátora Make participant Spraviť účastníka Remove voice Odstrániť hlas Add to contacts Pridať do kontaktov Add contact Pridať kontakt Swift::QtProfileWindow Edit Profile Upraviť profil Nickname: Prezývka: Save Uložiť Swift::QtRosterHeader Connection is secured Spojenie je zabezpečené Swift::QtRosterWidget Edit Upraviť Edit… Upraviť… Remove Odstrániť Send File Odoslať súbor Start Whiteboard Chat Začať rozhovor s využitím tabule All Files (*);; Všetky súbory (*);; Rename Premenovať Rename group Premenovať skupinu Enter a new name for group '%1': Zadajte nové meno pre skupinu „%1“: Swift::QtStatusWidget Connecting Prebieha pripájanie (No message) (bez správy) Swift::QtSubscriptionRequestWindow You have already replied to this request Na túto žiadosť ste už reagovali OK Ok Yes Áno No Nie Defer Odložiť %1 would like to add you to their contact list. Would you like to add them to your contact list and share your status when you're online? If you choose to defer this choice, you will be asked again when you next login. formulácia %1 si vás chce pridať do zoznamu kontaktov. Chcete si ho pridať do vášho zoznamu kontaktov a keď budete pripojení, zdielať s ním stav? Keď sa rozhodnete odložiť výber, po nasledujúcom prihlásení sa môžete znovu rozhodnúť. Swift::QtSwift Confirm terms of use Potvrdenie podmienok použitia Do you agree to the terms of use? Súhlasiťe s podmienkami použitia? Swift::QtTreeWidget Edit Upraviť Remove Odstrániť Rename Premenovať Rename group Premenovať skupinu Enter a new name for group '%1': Zadajte nové meno pre skupinu „%1“: New name for %1 Nové meno pre %1 Swift::QtUserSearchDetailsPage Please choose a name for the contact, and select the groups you want to add the contact to. Zvoľte meno pre kontakt a vyberte skupiny do ktorých chcete kontakt zaradiť. Swift::QtUserSearchFirstPage %1. If you know their address you can enter it directly, or you can search for them. %1. Keď viete jeho adresu, môžete ju zadať priamo, alebo ho môžete vyhľadať. Add another user to your contact list Pridať osobu do vášho zoznamu kontaktov Chat to another user Rozhovor s ďalšou osobou Swift::QtUserSearchWindow Add Contact Pridať kontakt Chat to User Rozhovor s osobou alice@wonderland.lit alica@krajina-zazrakov.lit How would you like to find the user to add? kostrbaté Ako vyhľadať osobu, ktorú pridať: How would you like to find the user to chat to? Ako vyhľadať osobu s ktorou začať rozhovor: Error while searching Chyba pri vyhľadávaní This server doesn't support searching for users. Tento server nepodporuje vyhľadávanie osôb. Swift::QtWebView Clear Vymazať Increase font size Zväčšiť písmo Decrease font size Zmenšiť písmo Swift::QtWhiteboardWindow Closing window is equivalent closing the session. Are you sure you want to do this? Zatvorením okna ukončíte sedenie. Naozaj chcete toto spraviť? Swift::QtXMLConsoleWidget Console Konzola Trace input/output Sledovať vstup a výstup Clear Vymazať Debug Console Ladiaca konzola <!-- IN --> <!-- OUT --> TRANSLATION_INFO TRANSLATION_AUTHOR Pavol Babinčák TRANSLATION_LICENSE This string contains the license under which this translation is licensed. We ask you to license the translation under the BSD license. Please read http://www.opensource.org/licenses/bsd-license.php, and if you agree to release your translation under this license, use the following (untranslated) text: 'This translation is licensed under the BSD License. See http://www.opensource.org/licenses/bsd-license.php' This translation is licensed under the BSD License. See http://www.opensource.org/licenses/bsd-license.php TRANSLATION_LICENSE Should be the following (untranslated) text: 'This translation is licensed under the BSD License. See http://www.opensource.org/licenses/bsd-license.php' Tento preklad je uvolnený pod BSD licenciou. Viac na http://www.opensource.org/licenses/bsd-license.php swift-im-2.0+dev6/Swift/Translations/swift_sv.ts0000644000175000017500000021054612227051774021664 0ustar kismithkismith UTF-8 Starting chat with %1% in chatroom %2% Startar chat med %1% i chatrum %2% Starting chat with %1% - %2% Startar chat med %1% - %2% me jag %1% has gone offline %1% har gått offline %1% has become available %1% har blivit tillgänglig %1% has gone away %1% har gått iväg %1% is now busy %1% är nu upptagen The day is now %1% Dagen är nu %1% Error sending message Fel när meddelande skickades Bad request Ogiltig begäran Conflict Konflikt This feature is not implemented Denna funktion är inte implementerad Forbidden Coult be förbjudet, unclear when out of context Förbjuden Recipient can no longer be contacted Mottagaren kan inte längre kontaktas Internal server error Internt serverfel Item not found Objektet hittades inte JID Malformed I'm not entirely sure what JID is, but if it's Jabber ID this should be correct Felaktig JID Message was rejected Meddelandet avvisades Not allowed Could also be "inte tillåten" depending on the context Inte tillåtet Not authorized Inte behörig Payment is required Betalning krävs Recipient is unavailable Mottagaren är inte tillgänglig Redirect Very much depending on context, might be a bit off dirigera om Registration required Registrering krävs Recipient's server not found Mottagarens server hittades inte Remote server timeout Quite poor translation, I can't recall there beeing any good swedish term for "remote server" Timeout mot fjärrserver The server is low on resources Servern har ont om resurser The service is unavailable Tjänsten är inte tillgänglig A subscription is required En prenumeration krävs Undefined condition Odefinierad villkor Unexpected request Oväntad begäran Room %1% is not responding. This operation may never complete. Rum %1% svarar inte. Processen blir kanske aldrig färdig. Unable to enter this room Det går inte att ansluta till detta rum Unable to enter this room as %1%, retrying as %2% Det går inte att ansluta till detta rum som %1%, försöker igen som %2% No nickname specified Inget smeknamn valt A password needed Lösenord krävs Only members may enter Bara medlemmar kan ansluta You are banned from the room Du är bannad från detta rum The room is full Rummet är fullt The room does not exist Rummet existerar inte You have entered room %1% as %2%. Du har anslutit till rum %1% som %2%. %1% has entered the room as a %2%. %1% har anslutit till rummet som en %2%. %1% has entered the room. %1% har anslutit till rummet. moderator moderator participant deltagare visitor besökare The room subject is now: %1% Rummets ämne är nu: %1% %1% is now a %2% %1% är nu en %2% Moderators Moderatorer Participants Deltagare Visitors Besökare Occupants Passagerare Trying to enter room %1% Försöker ansluta till rum %1% %1% has left the room %1% har lämnat rummet You have left the room Du har lämnat rummet and och %1% have entered the room %1% har anslutit till rummet %1% has entered the room %1% har anslutit till rummet %1% have left the room %1% har lämnat rummet %1% have entered then left the room %1% har anslutit till och sedan lämnat rummet %1% has entered then left the room %1% har anslutit till och sedan lämnat rummet %1% have left then returned to the room %1% har lämnat och sedan anslutit till rummet %1% has left then returned to the room %1% har lämnat och sedan anslutit till rummet %1% wants to add you to his/her contact list %1% vill lägga till dig till sin kontaktlista Error Fel Unknown Error Okänt Fel Unable to find server Kan inte hitta server Error connecting to server Fel vid anslutning till server Error while receiving server data Fel vid mottagande av serverdata Error while sending data to the server Fel vid skickande av data till servern Error parsing server data Fel vid tolkning av serverdata Login/password invalid Användarnamn/lösenord felaktigt Error while compressing stream Strömmen is a poor translation, can't find any better though Fel vid komprimering av strömmen Server verification failed Verifikation mot servern misslyckades Authentication mechanisms not supported Autentiseringsmekanismen stöds inte Unexpected response Oväntat svar Error binding resource Fel vid låsandet av resurs Error starting session Fel vid sessionsstart Stream error Strömmingssfel Encryption error Krypteringsfel Error loading certificate (Invalid password?) Fel vid inläsning av certifikat (Felaktigt lösenord?) Certificate not authorized Authorized is not exactly the samt as godkänt, but it'll probably be in this context Certifikat inte godkänt Unknown certificate Okänt certifikat Certificate has expired Certifikat har upphört att gälla Certificate is not yet valid Certifikatet är ännu inte giltigt Certificate is self-signed Certifikatet är självsignerat Certificate has been rejected Certifikatet har blivit avvisat Certificate is not trusted Certifikatet är inte betrott Certificate cannot be used for encrypting your connection Certifikatet kan inte användas för att kryptera din anslutning Certificate path length constraint exceeded Certifikatets sökvägslängd för lång Invalid certificate signature Ogiltig certifikatsignatur Invalid Certificate Authority Ogiltig certifieringsmyndighet Certificate does not match the host identity Certifikatet matchar inte mottagarens identitet Certificate error Certifikatfel Reconnect to %1% failed: %2%. Will retry in %3% seconds. Återanslutning till %1% misslyckades: %2%. Försöker igen om %3% sekunder. Disconnected from %1%: %2%. Frånkopplad från %1%: %2%. Contacts Kontakter Server %1% rejected contact list change to item '%2%' Server %1% avvisade ändringen av kontaktlistan på punkt '%2%' Available Tillgänglig Away Borta Busy Upptagen Offline Offline There was an error publishing your profile data Fel vid publicering av profildata CloseButton Close Tab Stäng flik MAC_APPLICATION_MENU Services Tjänster Hide %1 Göm %1 Hide Others Göm Andra Show All Visa Alla Preferences... Inställningar... Quit %1 Avsluta %1 About %1 Om %1 QApplication QT_LAYOUT_DIRECTION Translate this to LTR for left-to-right or RTL for right-to-left languages LTR QDialogButtonBox &Yes &Ja &No &Nej &OK &OK OK OK &Cancel &Avbryt Cancel Avbryt QLineEdit Select All Markera alla &Undo &Ångra &Redo &Gör om Cu&t Kli&pp &Copy &Kopiera &Paste Klistra &in Delete Ta bort QMessageBox Show Details... Visa detaljer... Hide Details... Göm detaljer... QObject No rooms found Inga rum hittade %1 would like to add you to their contact list. %1 will lägga till dig till sin kontaktlista. %1 would like to add you to their contact list, saying '%2' %1 will lägga till dig till sin kontaktlista, säger '%2' QScrollBar Scroll here Scrolla här Top Topp Bottom Botten Page up Page up Page down Page down Scroll up Skrolla upp Scroll down Skrolla ner QTextControl Select All Markera alla &Copy &Kopiera &Undo &Ångra &Redo &Gör om Cu&t Kli&pp &Paste Klistra &in Delete Ta bort QWebPage Copy Link Kopiera länk Copy Kopiera Copy Image Kopiera bild Scroll here Scrolla här Top Topp Bottom Botten Page up Page up Page down Page down Scroll up Skrolla upp Scroll down Skrolla ner QWizard < &Back < &Tillbaka &Finish &Avsluta &Help &Hjälp Go Back Tillbaka Continue Fortsätt Commit Could be, depending on context Anförtro Done Färdig Quit Avsluta Help Hjälp Cancel Avbryt &Next &Nästa &Next > &Nästa > QtBookmarkDetailWindow Edit Bookmark Details Ändra bokmärkesdetaljer Bookmark Name: Bokmärkesnamn: Room Address: Adress till rummet: Your Nickname: Ditt smeknamn: Room password: Rummets lösenord: Join automatically Anslut automatiskt QtJoinMUCWindow Enter Room Anslut till rum Room: Rum: Search ... Sök ... Nickname: Smeknamn: Enter automatically in future Anslut automatiskt framöver QtMUCSearchWindow Search Room Sök rum Service: Tjänst: Cancel Avbryt OK OK List rooms Lista rum QtUserSearchFieldsPage Nickname: Smeknamn: First name: Förnamn: Last name: Efternamn: E-Mail: E-mail: Fetching search fields Hämtar sökfält QtUserSearchFirstPage Add a user Lägg till användare Add another user to your contact list. If you know their address you can add them directly, or you can search for them. Lägg till en annan användare till din kontaktlista. Om du vet deras adress kan du lägga till de direkt, annars kan du söka efter dem. I know their address: Jag vet adressen: I'd like to search my server Jag vill söka på min server I'd like to search another server: Jag vill söka på annan server: QtUserSearchWizard Find User Finn användare Swift::ChatListModel Bookmarked Rooms Bokmärkta rum Swift::QtAboutWidget About %1 Om %1 Version %1 Version %1 Built with Qt %1 Byggt med QT %1 Running with Qt %1 Körs med Qt %1 Using the English translation by %1 Använder den svenska översättningen av %1 View License Se licens Swift::QtAvatarWidget No picture Ingen bild Select picture ... Välj bild ... Clear picture Töm bild Select picture Välj bild Image Files (*.png *.jpg *.gif) Bildfil (*.png *.jpg *.gif) Error Fel The selected picture is in an unrecognized format Den valda bilden är i ett okänt format Swift::QtBookmarkDetailWindow Bookmark not valid Bokmärke felaktigt You must specify a valid room address (e.g. someroom@rooms.example.com). Du måste specificera en korrekt rumsadress (t.ex. rum@rum.exempel.com). Swift::QtChatListWindow Add New Bookmark Lägg till bokmärke Edit Bookmark Ändra bokmärke Remove Bookmark Ta bort bokmärke Swift::QtChatView Clear log Tom logg You are about to clear the contents of your chat log. Du är på väg att tömma innehållet i din chatlogg. Are you sure? Är du säker? Swift::QtChatWindow This message has not been received by your server yet. Meddelandet har inte mottagits av servern än. This message may not have been transmitted. Meddelandet kanske inte har blivit sänt. Couldn't send message: %1 Kunde inte sända meddelande: %1 Swift::QtContactEditWidget Name: Namn: Groups: Grupper: New Group: Ny grupp: Swift::QtContactEditWindow Edit contact Ändra kontakt Remove contact Ta bort kontakt OK OK Confirm contact deletion Bekräfta borttagande av kontakt Are you sure you want to delete this contact? Är du säker på att du vill ta bort denna kontakt? This will remove the contact '%1' from all groups they may be in. Detta kommer ta bort kontakten '%1' från alla grupper den är i. Swift::QtEventWindow Display Notice Visa meddelande Swift::QtJoinMUCWindow someroom@rooms.example.com rum@rum.exempel.com Swift::QtLoginWindow User address: Användaradress: User address - looks like someuser@someserver.com Användaradress - ser ut som användare@server.com Example: alice@wonderland.lit Exempel: alice@underlandet.lit Password: Lösenord: Click if you have a personal certificate used for login to the service. Tryck om du har personligt certifikat för att logga in till tjänsten. Connect Anslut Remember Password? Kom ihåg lösenord? Login Automatically? Logga in automatiskt? &Swift &Swift &General &Generellt &About %1 &Om %1 &Show Debug Console &Visa felsökningskonsol &Play Sounds &Spela upp ljud Display Pop-up &Notifications &Visa popup-meddelanden &Quit &Avsluta Remove profile Ta bort profil Remove the profile '%1'? Ta bort profilen '%1'? Cancel Avbryt Select an authentication certificate Välj ett verifikationscertifikat The certificate presented by the server is not valid. Certifikatet angivet av servern är inte giltigt. Would you like to permanently trust this certificate? This must only be done if you know it is correct. Vill du permanent lita på detta certifikat? Detta bör bara göras om du vet att det är korrekt. Subject: %1 Ämne: %1 SHA-1 Fingerprint: %1 SHA-1 Fingeravtryck: %1 Swift::QtMUCSearchWindow Searching Söker Swift::QtMainWindow &Contacts &Kontakter &Notices &Meddelanden C&hats &Chattar &View &Visa &Show offline contacts &Visa offlinekontakter &Actions &Åtgärder Edit &Profile… &Redigera profil… Enter &Room… &Anslut till rum… &Add Contact… &Lägg till kontakt… &Edit Selected Contact… &Redigera markerad kontakt… Start &Chat… Starta &chat &Sign Out &Logga ut Swift::QtNameWidget Show Nickname Visa smeknamn (No Nickname Set) (Inget smeknamn valt) Show Address Visa adress Edit Profile Redigera profil Swift::QtProfileWindow Edit Profile Redigera profil Nickname: Smeknamn: Save Spara Swift::QtStatusWidget Connecting Ansluter (No message) (Inget meddelande) Swift::QtSubscriptionRequestWindow %1 would like to add you to their contact list. Would you like to add them to your contact list and share your status when you're online? If you choose to defer this choice, you will be asked again when you next login. %1 will lägga till dig till sin kontaktlista. Vill du lägga till honom/henne till din kontaktlista och dela din status när du är online? Om du väljer att vänta med svaret kommer du frågas igen nästa inloggning. You have already replied to this request Du har redan svarat på denna förfrågan OK OK Yes Ja No Nej Defer Inte nu Swift::QtTreeWidget Edit Redigera Remove Ta bort Rename Byt namn Rename group Byt namn på grupp Enter a new name for group '%1': Välj ett nytt namn för grupp '%1': Swift::QtUserSearchDetailsPage Please choose a name for the contact, and select the groups you want to add the contact to. Vänligen välj ett namn på kontakten och en grupp du vill lägga till kontakten i. Swift::QtUserSearchFirstPage %1. If you know their address you can enter it directly, or you can search for them. %1. Om du vet deras adress kan du lägga in den direkt, eller så kan du söka efter honom/henne. Add another user to your contact list Lägg till användare till din kontaktlista Chat to another user Chatta med användare Swift::QtUserSearchWindow Add Contact Lägg till kontakt Chat to User Chatta med användare alice@wonderland.lit alice@underlandet.lit How would you like to find the user to add? Hur vill du hitta användaren du vill lägga till? How would you like to find the user to chat to? Hur vill du hitta användaren du vill chatta med? Error while searching Fel vis sökning This server doesn't support searching for users. Denna server stödjer inte att söka efter användare. Swift::QtWebView Clear Töm Swift::QtXMLConsoleWidget Console Konsol Trace input/output Spåra inmatning/utmatning Clear Töm Debug Console Felsökningskonsol <!-- IN --> <!-- IN--> <!-- OUT --> <!-- UT--> TRANSLATION_INFO TRANSLATION_AUTHOR Lars Johansson TRANSLATION_LICENSE This string contains the license under which this translation is licensed. We ask you to license the translation under the BSD license. Please read http://www.opensource.org/licenses/bsd-license.php, and if you agree to release your translation under this license, use the following (untranslated) text: 'This translation is licensed under the BSD License. See http://www.opensource.org/licenses/bsd-license.php' This translation is licensed under the BSD License. See http://www.opensource.org/licenses/bsd-license.php swift-im-2.0+dev6/Swift/Translations/swift_ru.ts0000644000175000017500000035150512227051774021663 0ustar kismithkismith UTF-8 Starting chat with %1% in chatroom %2% Начат чат с %1% в комнате %2% Starting chat with %1% - %2% Начат чат с %1% - %2% This chat doesn't support delivery receipts. Этот чат не поддерживает отчёты о доставке. This chat may not support delivery receipts. You might not receive delivery receipts for the messages you sent. Этот чат не поддерживает отчёты о доставке. Вы не будете получать уведомления о доставке отправленных Вами сообщений. me я %1% has gone offline %1% теперь отключён %1% has become available %1% снова доступен %1% has gone away %1% теперь 'отсутствую' %1% is now busy %1% теперь 'не беспокоить' The day is now %1% Сегодня %1% Couldn't send message: %1% Ошибка отправки сообщения: %1% Error sending message Ошибка отправки сообщения Bad request Неверный запрос Conflict Конфликт This feature is not implemented Эта функция не реализована Forbidden Запрещено Recipient can no longer be contacted wtf Получатель недоступен Internal server error Внутренняя ошибка сервера Item not found Элемент не найден JID Malformed Некорректный JID Message was rejected Сообщение отклонено Not allowed Не разрешено Not authorized Не авторизован Payment is required Требуется оплата Recipient is unavailable Получатель недоступен Redirect Перенаправление Registration required Требуется регистрация Recipient's server not found Сервер получателя не найден Remote server timeout Таймаут сервера The server is low on resources Серверу не хватает ресурсов The service is unavailable Сервис недоступен A subscription is required Требуется подписка Undefined condition Неопределённое условие Unexpected request Неожиданный запрос Room %1% is not responding. This operation may never complete. Комната %1% не отвечает. Эта операция может не завершиться. Unable to enter this room Не удалось войти в комнату Unable to enter this room as %1%, retrying as %2% Не удалось войти в комнату как %1%, попытка войти как %2% No nickname specified Ник не указан A password needed Нужен пароль Only members may enter Вход только для зарегистрированных участников You are banned from the room Вы забанены в этой комнате The room is full Комната полная The room does not exist Комната не существует Couldn't join room: %1%. Ошибка входа в комнату: %1%. You have entered room %1% as %2%. Вы вошли в комнату %1% как %2%. %1% has entered the room as a %2%. %1% входит как %2%. %1% has entered the room. %1% входит. moderator модератор participant участник visitor гость The room subject is now: %1% Тема конференции: %1% %1% is now a %2% %1% теперь %2% Moderators Модераторы Participants Участники Visitors Гости Occupants wtf Изгои Trying to enter room %1% Попытка войти в комнату %1% %1% has left the room%2% %1% вышел из комнаты%2% You have been kicked out of the room Вас выгнали из комнаты You have been banned from the room Вас забанили в этой комнате You are no longer a member of the room and have been removed Вы больше не зарегистрированный пользователь комнаты и были удалены The room has been destroyed Комната была уничтожена %1% has left the room %1% вышел Room configuration failed: %1%. Ошибка настройки комнаты: %1%. Occupant role change failed: %1%. Ошибка изменения роли: %1%. You have left the room Вы вышли из комнаты The correct room password is needed Необходим правильный пароль к комнате and и %1% have entered the room %1% вошли в комнату %1% has entered the room %1% вошёл в комнату %1% have left the room %1% вышли из комнаты %1% have entered then left the room %1% вошли и вышли %1% has entered then left the room %1% вошёл и вышел %1% have left then returned to the room %1% вышли, затем вернулись %1% has left then returned to the room %1% вышел, затем вернулся %1% wants to add you to his/her contact list %1% хочет добавить Вас в свой ​​список контактов Error Ошибка %1% has invited you to enter the %2% room %1 приглашает Вас войти в конференцию %2 User address invalid. User address should be of the form 'alice@wonderland.lit' Адрес пользователя недействителен. Он должен иметь вид"vasya@pup.kin" Unknown Error Неизвестная ошибка Unable to find server Не удаётся найти сервер Error connecting to server Ошибка подключения к серверу Error while receiving server data Ошибка получения данных Error while sending data to the server Ошибка отправки данных на сервер Error parsing server data Ошибка обработки данных от сервера Login/password invalid Неверный логин/пароль Error while compressing stream Ошибка сжатия потока Server verification failed Проверка сервера не удалась Authentication mechanisms not supported Механизм авторизации не поддерживается Unexpected response Неожиданный ответ Error binding resource Ошибка назначения ресурса Error starting session Ошибка при запуске сессии Stream error Ошибка потока Encryption error Ошибка шифрования Error loading certificate (Invalid password?) Ошибка загрузки сертификата (неверный пароль?) Certificate not authorized Сертификат не авторизован Certificate card removed Сертификат удалён Unknown certificate Неизвестный сертификат Certificate has expired Срок действия сертификата истек Certificate is not yet valid Сертификат ещё не действителен Certificate is self-signed Сертификат самоподписанный Certificate has been rejected Сертификат отклонён Certificate is not trusted Сертификат не является доверенным Certificate cannot be used for encrypting your connection Сертификат не может быть использован для шифрования Вашего соединения Certificate path length constraint exceeded Превышена длина пути сертификата Invalid certificate signature Подпись сертификата недействительна Invalid Certificate Authority Центр сертификации недействителен Certificate does not match the host identity Сертификат не соответствует серверу Certificate has been revoked Сертификат отозван Unable to determine certificate revocation state Невозможно проверить состояние отзыва сертификата Certificate error Ошибка сертификата Re-enter credentials and retry Повторно введите учётные данные и повторите Disconnected from %1%: %2%. To reconnect, Sign Out and provide your password again. Разорвано соединение с %1%: %2%. Чтобы восстановить связь, отключитесь и введите пароль ещё раз. Reconnect to %1% failed: %2%. Will retry in %3% seconds. Переподключение к %1% не удалось: %2%. Повтор через %3% секунд. Disconnected from %1%: %2%. Отключение от %1%: %2%. Contacts Контакты Server %1% rejected contact list change to item '%2%' Сервер %1% отклонил изменение списка контактов с элементом '%2%' Available Доступен Away Отсутствую Busy Не беспокоить Offline Отключён There was an error publishing your profile data Ошибка публикации данных Вашего профиля %1% (%2%) %1% (%2%) %1% and %2% others (%3%) xzxz %1% и %2% другие (%3%) %1%, %2% (%3%) %1%, %2% (%3%) TLS Client Certificate Selection Выбор клиентского сертификата TLS Select a certificate to use for authentication Выбрать сертификат для аутентификации CloseButton Close Tab Закрыть вкладку MAC_APPLICATION_MENU Services Службы Hide %1 Скрыть %1 Hide Others Скрыть остальные Show All Показать все Preferences... Настройки… Quit %1 Завершить %1 About %1 О %1 QApplication QT_LAYOUT_DIRECTION Translate this to LTR for left-to-right or RTL for right-to-left languages слева направо QDialogButtonBox &Yes &Да &No &Нет &OK &ОК OK ОК &Cancel О&тмена Cancel Отмена QLineEdit Select All Выделить всё &Undo &Отменить действие &Redo &Повторить действие Cu&t &Вырезать &Copy &Копировать &Paste В&ставить Delete Удалить QMessageBox Show Details... Показать подробности... Hide Details... Скрыть подробности... QObject No rooms found Комнаты не найдены %1 would like to add you to their contact list. %1 хочет добавить Вас в свой ​​список контактов. %1 would like to add you to their contact list, saying '%2' %1 хочет добавить Вас в свой ​​список контактов, говоря '%2' %1 has invited you to enter the %2 room. %1 приглашает Вас войти в конференцию %2. You've been invited to enter the %1 room. Вас пригласили войти в конференцию %1. Reason: %1 Причина: %1 This person may not have really sent this invitation! Этот человек, возможно, не посылал это приглашение! Direction xzxz Направление Other Party Другая сторона State Регион Progress Прогресс Size Размер Incoming Входящий Outgoing Исходящий Waiting for start Ожидание старта Waiting for other side to accept Ожидание принятия на другой стороне Negotiating Переговоры Transferring Передача Finished xzxz Завершено Failed Не удалось Canceled Отменено Connection Options Параметры подключения QScrollBar Scroll here Прокрутить сюда Left edge К левой границе Top Вверх Right edge К правой границе Bottom Вниз Page left На страницу влево Page up На страницу вверх Page right На страницу вправо Page down На страницу вниз Scroll left Прокрутить влево Scroll up Прокрутить вверх Scroll right Прокрутить вправо Scroll down Прокрутить вниз Line up На строку вверх Position Положение Line down На строку вниз QTextControl &Undo &Отменить действие &Redo &Повторить действие Cu&t &Вырезать &Copy &Копировать Copy &Link Location Скопировать &адрес ссылки &Paste В&ставить Delete Удалить Select All Выделить всё QWebPage Copy Link Скопировать адрес Copy Image Копировать изображение Copy Копировать Scroll here Прокрутить сюда Left edge К левой границе Top Вверх Right edge К правой границе Bottom Вниз Page left На страницу влево Page up На страницу вверх Page right На страницу вправо Page down На страницу вниз Scroll left Прокрутить влево Scroll up Прокрутить вверх Scroll right Прокрутить вправо Scroll down Прокрутить вниз QWizard < &Back < &Назад &Finish &Готово &Help &Помощь Go Back Назад Continue Продолжить Commit wtf Фиксировать Done Готово Quit Выход Help Помощь Cancel Отмена &Next &Далее &Next > &Далее > QtAffiliationEditor Edit Affiliations Редактирование рангов Affiliation: Ранг: Owner Владелец Administrator Администратор Member Зарегистрированный пользователь Outcast (Banned) Заблокированный Add User Добавить пользователя Remove User Удалить пользователя QtBookmarkDetailWindow Edit Bookmark Details Редактирование закладки Bookmark Name: Название закладки: Room Address: Адрес комнаты: Your Nickname: Ваш ник: Room password: Пароль комнаты: Enter automatically Входить автоматически Join automatically Входить автоматически QtCertificateViewerDialog Certificate Viewer Просмотр сертификата QtConnectionSettings Connection Options Параметры подключения Connection Method: Метод подключения: Automatic Автоматически Manual Вручную BOSH BOSH Secure connection: Безопасное подключение: Never Никогда Encrypt when possible Шифровать соединение если возможно Always encrypt Всегда шифровать Allow Compression Разрешить сжатие Allow sending password over insecure connection Разрешить отправку пароля через незащищённое соединение Manually select server Вручную выбрать сервер Hostname: Сервер: Port: Порт: Connection Proxy Прокси для подключения Proxy type: Тип прокси: None Отсутствует Use system-configured proxy Использовать системные настройки прокси SOCKS5 SOCKS5 HTTP Connect HTTP Connect Override system-configured proxy Перезаписать системные настройки прокси BOSH URI: BOSH URI: Manually select HTTP proxy Вручную выбрать HTTP прокси QtHistoryWindow History История Search: Поиск: Next xzxz Следующий Previous Предыдущий QtJoinMUCWindow Enter Room Войти в комнату Room Address: Адрес комнаты: Your Nickname: Ваш ник: Room Password: Пароль комнаты: Automatically configure newly created rooms Автоматически откывать окно конфигурации комнаты при её создании Room: Комната: Search ... Поиск ... Nickname: Ник: Enter automatically in future Входить автоматически в будущем QtMUCSearchWindow Search Room Поик комнаты Service: Сервис: Cancel Отмена OK ОК List rooms Список комнат QtUserSearchFieldsPage Nickname: Ник: First name: Имя: Last name: Фамилия: E-Mail: E-Mail: Fetching search fields Получение полей поиска QtUserSearchFirstPage Add a user Добавить пользователя Add another user to your contact list. If you know their address you can add them directly, or you can search for them. Добавить пользователя в список контактов. Вы можете ввести его адрес или воспользоваться поиском. I know their address: Я знаю его адрес: I'd like to search my server Я хочу искать на моём сервере I'd like to search another server: Я хочу искать на другом сервере: QtUserSearchResultsPage No results. Нет результатов. QtUserSearchWizard Find User Поиск пользователя Swift::ChatListModel Bookmarked Rooms Закладки комнат Recent Chats Последние чаты Opened Whiteboards Открытые доски Swift::QtAboutWidget About %1 О %1 Version %1 Версия %1 Built with Qt %1 Собрано с Qt %1 Running with Qt %1 Запущено с Qt %1 Using the English translation by %1 Перевод на русский язык: %1 View License Лицензия Swift::QtAdHocCommandWindow Cancel Отмена Back Назад Next xzxz следующий Далее Complete xzxz ejabberd buggy ad-hoc Выполнено Error: %1 Ошибка: %1 Warning: %1 Предупреждение: %1 Error executing command Ошибка выполнения команды Swift::QtAffiliationEditor Add User Добавить пользователя Added User's Address: Введите адрес пользователя: Swift::QtAvatarWidget No picture Нет изображения Select picture ... Выбрать изображение ... Clear picture Очистить изображение Select picture Выбор изображения Image Files (*.png *.jpg *.jpeg *.gif) Изображения (*.png *.jpg *.jpeg *.gif) Image Files (*.png *.jpg *.gif) Изображения (*.png *.jpg *.gif) Error Ошибка The selected picture is in an unrecognized format Неизвестный формат выбранного изображения Swift::QtBookmarkDetailWindow Bookmark not valid Закладка не работает You must specify a valid room address (e.g. someroom@rooms.example.com). Вы должны указать адрес комнаты (например, someroom@rooms.example.com). Swift::QtCertificateViewerDialog General Общее Valid From Действует с Valid To Действителен до Serial Number Серийный номер Version Версия Subject Тема Organization Организация Common Name Полное имя Locality Местонахождение Organizational Unit Подразделение Country Страна State Регион Alternate Subject Names Альтернативные имена субъекта E-mail Address E-Mail адрес DNS Name DNS имя Issuer Издатель Swift::QtChatListWindow Add New Bookmark Добавить закладку Edit Bookmark Редактировать закладку Remove Bookmark Удалить закладку Clear recents Удалить последние Swift::QtChatView Clear log Очистить лог You are about to clear the contents of your chat log. Вы собираетесь очистить содержимое Вашего чата. Are you sure? Вы уверены? %1 edited %1 отредактировано Waiting for other side to accept the transfer. Ожидание принятия на другой стороне. Cancel Отмена Negotiating... Переговоры... Transfer has been canceled! Передача была отменена! Transfer completed successfully. Передача успешно завершена. Transfer failed. Передача не удалась. Started whiteboard chat Открыта доска для рисования Show whiteboard Показать доску для рисования Whiteboard chat has been canceled Доска для рисования была отменена Whiteboard chat request has been rejected Запрос порисовать был отклонён Return to room Вернуться в комнату Swift::QtChatWindow Correcting Исправление This chat may not support message correction. If you send a correction anyway, it may appear as a duplicate message Этот чат, возможно, не поддерживает исправление сообщений. Если Вы отправляете исправление, это может выглядеть как дубликат сообщения This chat does not support message correction. If you send a correction anyway, it will appear as a duplicate message Этот чат не поддерживает исправление сообщений. Если Вы отправляете исправление, это будет выглядеть как дубликат сообщения This message has not been received by your server yet. Это сообщение не может быть получено. This message may not have been transmitted. Это сообщение не может быть передано. The receipt for this message has been received. Отчёт о доставке этого сообщения был получен. The receipt for this message has not yet been received. The recipient(s) might not have received this message. Отчёт о доставке этого сообщения пока не получен. Получатель, возможно, не получил это сообщение. Send file Отправить файл Cancel Отмена Set Description Установка описания Send Отправка Receiving file Получить файл Accept Принять Starting whiteboard chat Открывается доска для рисования %1 would like to start a whiteboard chat %1 хочет порисовать File transfer description Описание передачи файлов Description: Описание: Save File Сохранить файл Change subject… Изменить тему… Configure room… Настроить комнату… Edit affiliations… Редактировать ранги… Destroy room Уничтожить комнату Invite person to this room… Пригласить в эту комнату… Change room subject Изменение темы конференции New subject: Новая тема: Confirm room destruction Подтверждение удаления комнаты Are you sure you want to destroy the room? Вы уверены, что хотите удалить комнату? This will destroy the room. Комната будет уничтожена. Accept Invite Принять приглашение Couldn't send message: %1 Ошибка отправки: %1 Swift::QtContactEditWidget Name: Имя: Groups: Группы: New Group: Новая группа: Swift::QtContactEditWindow Edit contact Редактировать контакт Remove contact Удалить контакт OK ОК Confirm contact deletion Подтверждение удаления Are you sure you want to delete this contact? Вы уверены, что хотите удалить этот контакт? This will remove the contact '%1' from all groups they may be in. Это позволит удалить контакт '%1' из всех групп, где он может быть Swift::QtEventWindow Display Notice Показать уведомление Swift::QtFileTransferListWidget Clear Finished Transfers Очистить завершённые передачи File Transfer List Список передачи файлов Swift::QtHistoryWindow History История Swift::QtInviteToChatWindow Users to invite to this chat (one per line): Пользователи для приглашения в этот чат (один на строку): If you want to provide a reason for the invitation, enter it here Если Вы хотите указать причину для приглашения, введите её здесь Swift::QtJoinMUCWindow someroom@rooms.example.com someroom@rooms.example.com Swift::QtLoginWindow User address: Адрес пользователя: User address - looks like someuser@someserver.com Например, вася@jabber.ru/стриж Example: alice@wonderland.lit Например, ivan@jabber.ru Password: Пароль: Click if you have a personal certificate used for login to the service. Нажмите, если у Вас есть личный сертификат, используемый для входа. Connect Подключиться Remember Password? Запомнить пароль Login Automatically? Подключаться автоматически &Swift &Swift &General &Общие &About %1 &О %1 &Show Debug Console &Показать консоль отладки Show &File Transfer Overview Показать окно передачи &файлов &Play Sounds &Воспроизводить звуки Display Pop-up &Notifications Показывать всплывающие &уведомления &Quit В&ыход Remove profile Удалить профиль Remove the profile '%1'? Удалить профиль '%1'? Cancel Отмена Confirm terms of use Подтверждение условий использования Select an authentication certificate Выберите сертификат проверки подлинности P12 files (*.cert *.p12 *.pfx);;All files (*.*) P12 файлы (*.cert *.p12 *.pfx);;Все файлы (*.*) The certificate presented by the server is not valid. Сертификат, предоставленный сервером, является недопустимым. Would you like to permanently trust this certificate? This must only be done if you know it is correct. Хотели бы Вы доверять этому сертификату? Show Certificate Просмотр сертификата Subject: %1 Тема: %1 SHA-1 Fingerprint: %1 Отпечаток SHA-1: %1 Swift::QtMUCConfigurationWindow Cancel Отмена OK ОК Swift::QtMUCSearchWindow Searching Поиск Swift::QtMainWindow &Contacts &Контакты &Notices &Уведомления C&hats Ч&аты &View &Вид &Show offline contacts &Показывать отключённых &Show Emoticons Показывать &смайлы &Actions &Действия Edit &Profile… Редактировать &профиль… Enter &Room… &Войти в комнату… &View History… Просмотр &истории… &Add Contact… До&бавить контакт… &Edit Selected Contact… &Редактировать выделенный контакт… Start &Chat… Нач&ать чат… Run Server Command Выполнить серверную команду &Sign Out &Отключиться &Request Delivery Receipts &Запрашивать уведомления о доставке Collecting commands... Сбор команд... &Chats &Чаты No Available Commands Нет доступных комманд Swift::QtNameWidget Show Nickname Показывать ник (No Nickname Set) (ник не установлен) Show Address Показывать адрес Edit Profile Редактировать профиль Swift::QtOccupantListWidget No actions for this user Нет действий для данного пользователя Kick user Выгнать Kick and ban user Выгнать и забанить Make moderator Сделать модератором Make participant Сделать участником Remove voice Лишить голоса Add to contacts Добавить в контакты Swift::QtProfileWindow Edit Profile Редактировать профиль Nickname: Ник: Save Сохранить Swift::QtRosterHeader Connection is secured Подключение защищено Swift::QtRosterWidget Edit… Редактировать… Remove Удалить Send File Отправить файл Start Whiteboard Chat Открыть доску для рисования All Files (*);; Все файлы (*);; Rename Переименовать Rename group Переименовать группу Enter a new name for group '%1': Введите новое название группы '%1': Swift::QtStatusWidget Connecting Соединение (No message) (нет сообщения) Swift::QtSubscriptionRequestWindow %1 would like to add you to their contact list. Would you like to add them to your contact list and share your status when you're online? If you choose to defer this choice, you will be asked again when you next login. %1 хочет добавить Вас в свой ​​список контактов. Вы хотите добавить его в список контактов и обмениваться статусами, когда Вы в сети? Если Вы решили отложить выбор, то запрос придёт снова при следующем подключении. You have already replied to this request OK ОК Yes Да No Нет Defer Отложить Swift::QtTreeWidget Edit Редактировать Remove Удалить Rename Переименовать Rename group Переименовать группу Enter a new name for group '%1': Введите новое название группы '%1': Swift::QtUserSearchDetailsPage Please choose a name for the contact, and select the groups you want to add the contact to. Пожалуйста, выберите имя контакта и выберите группы, в которые Вы хотите добавить контакт. Swift::QtUserSearchFirstPage %1. If you know their address you can enter it directly, or you can search for them. %1. Вы можете ввести его адрес или воспользоваться поиском. Add another user to your contact list Добавить пользователя в список контактов Chat to another user Чат с пользователем Swift::QtUserSearchWindow Add Contact Добавить контакт Chat to User Чат с пользователем alice@wonderland.lit alice@wonderland.lit How would you like to find the user to add? Вы хотите найти пользователя чтобы его добавить? How would you like to find the user to chat to? Вы хотите найти пользователя чтобы начать с ним чат? Error while searching Ошибка поиска This server doesn't support searching for users. Этот сервер не поддерживает поиск пользователей. Swift::QtWebView Clear Очистить Increase font size Увеличить размер шрифта Decrease font size Уменьшить размер шрифта Swift::QtWhiteboardWindow Closing window is equivalent closing the session. Are you sure you want to do this? Закрытие окна эквивалентно закрытию сессии. Вы уверены, что хотите это сделать? Swift::QtXMLConsoleWidget Console Консоль Trace input/output Трассировка ввода/вывода Clear Очистить Debug Console Консоль отладки <!-- IN --> <!-- IN --> <!-- OUT --> <!-- OUT --> TRANSLATION_INFO TRANSLATION_AUTHOR Иван Тюменцев TRANSLATION_LICENSE This string contains the license under which this translation is licensed. We ask you to license the translation under the BSD license. Please read http://www.opensource.org/licenses/bsd-license.php, and if you agree to release your translation under this license, use the following (untranslated) text: 'This translation is licensed under the BSD License. See http://www.opensource.org/licenses/bsd-license.php' This translation is licensed under the BSD License. See http://www.opensource.org/licenses/bsd-license.php swift-im-2.0+dev6/Swift/Translations/swift_gl.ts0000644000175000017500000021023112227051774021625 0ustar kismithkismith UTF-8 Starting chat with %1% in chatroom %2% Iniciando conversa con %1% na sala %2% Starting chat with %1% - %2% Iniciando conversa con %1% - %2% me eu %1% has gone offline %1% desconectouse %1% has become available %1% atópase dispoñible %1% has gone away %1% ausentouse %1% is now busy %1% está ocupado/a The day is now %1% O día é agora %1% Error sending message Erro ao enviar a mensaxe Bad request Solicitude incorrecta Conflict Conflito This feature is not implemented Esta funcionalidade non está implementada Forbidden Prohibido Recipient can no longer be contacted Xa non se pode contactar co destinatario Internal server error Erro interno do servidor Item not found Elemento non atopado JID Malformed JID formado incorrectamente Message was rejected A mensaxe foi rexeitada Not allowed Non permitido Not authorized Non autorizado Payment is required Pagamento requerido Recipient is unavailable Destinatario non dispoñible Redirect Redirección Registration required Rexistro requerido Recipient's server not found Non se atopou o servidor do destinatario Remote server timeout Tempo de espera do servidor remoto esgotado The server is low on resources O servidor ten poucos recursos dispoñibles The service is unavailable O servidor non está dispoñible A subscription is required Requírese unha subscrición Undefined condition Condición indefinida Unexpected request Solicitude inesperada Room %1% is not responding. This operation may never complete. A sala «%1%» non responde. É posible que esta operación non se complete nunca. Unable to enter this room Non é posible entrar nesta sala Unable to enter this room as %1%, retrying as %2% Non é posible entrar nesta sala como «%1%», tentandoo de novo como «%2%» No nickname specified Non se especificou o alcume A password needed Precísase contrasinal Only members may enter Só os membros poden entrar You are banned from the room Estás excluído desta sala The room is full A sala está chea The room does not exist A sala non existe You have entered room %1% as %2%. Entraches na sala «%1%» como «%2%». %1% has entered the room as a %2%. «%1%» entrou na sala como «%2%». %1% has entered the room. «%1%» entrou na sala. moderator moderador participant participante visitor visitante The room subject is now: %1% O asunto da sala é agora: %1% %1% is now a %2% «%1%» é agora un %2% Moderators Moderadores Participants Participantes Visitors Visitantes Occupants Onde cona está isto? Ocupantes Trying to enter room %1% Tentando entrar na sala «%1%» %1% has left the room %1% saíu da sala You have left the room Saíches da sala and e %1% have entered the room %1% entraron na sala %1% has entered the room %1% entrou na sala %1% have left the room %1% saíron da sala %1% have entered then left the room %1% entraron e saíron da sala %1% has entered then left the room %1% entrou e saíu da sala %1% have left then returned to the room %1% entraron e volveron á sala %1% has left then returned to the room %1% entrou e volveu á sala %1% wants to add you to his/her contact list %1% desexa engadirte á súa lista de contactos Error Erro Unknown Error Erro descoñecido Unable to find server Non é posible atopar o servidor Error connecting to server Erro ao conectar co servidor Error while receiving server data Erro ao recibir datos do servidor Error while sending data to the server Erro ao enviar datos ao servidor Error parsing server data Erro na análise dos datos do servidor Login/password invalid Usuario/Contrasinal non válidos Error while compressing stream Erro ao comprimir o fluxo de datos Server verification failed Fallou a verificación do servidor Authentication mechanisms not supported Mecanismo de autenticación non soportado Unexpected response Resposta inesperada Error binding resource Erro asociado do recurso Error starting session Erro ao iniciar sesión Stream error Erro de fluxo de datos Encryption error Erro de cifrado Error loading certificate (Invalid password?) Erro ao cargar o certificado (Contrasinal non válida?) Certificate not authorized Certificado non autorizado Unknown certificate Certificado descoñecido Certificate has expired O certificado caducou Certificate is not yet valid O certificado aínda non é válido Certificate is self-signed Sinatura persoal? O certificado é autoasinado Certificate has been rejected O certificado foi rexeitado Certificate is not trusted O certificado non é de confianza Certificate cannot be used for encrypting your connection O certificado non pode empregarse para cifrar a túa conexión Certificate path length constraint exceeded Excedeuse a restrición da lonxitude da ruta do certificado Invalid certificate signature Sinatura do certificado non válida Invalid Certificate Authority Autoridade certificadora non válida Certificate does not match the host identity O certificado non concorda coa identidade do servidor Certificate error Erro no certificado Reconnect to %1% failed: %2%. Will retry in %3% seconds. A reconexión con «%1%» fallou: «%2%». Tentarase de novo en %3% segundos. Disconnected from %1%: %2%. Desconectado de %1%: %2%. Contacts Contactos Server %1% rejected contact list change to item '%2%' O servidor «%1%» rexeitou o troco ao elemento «%2%» da lista de contactos Available Dispoñible Away Ausente Busy Ocupado/a Offline Desconectado There was an error publishing your profile data Ocorreu un erro ao publicar os datos do teu perfil CloseButton Close Tab Pechar lapela MAC_APPLICATION_MENU Services Servizos Hide %1 Agochar %1 Hide Others Agochar outros Show All Amosar todo Preferences... Preferencias... Quit %1 Saír de %1 About %1 Sobre %1 QApplication QT_LAYOUT_DIRECTION Translate this to LTR for left-to-right or RTL for right-to-left languages LTR QDialogButtonBox &Yes &Si &No &Non &OK &Aceptar OK Aceptar &Cancel &Cancelar Cancel Cancelar QLineEdit Select All Seleccionar todo &Undo &Desfacer &Redo &Refacer Cu&t C&ortar &Copy &Copiar &Paste &Pegar Delete Eliminar QMessageBox Show Details... Amosar detalles... Hide Details... Agochar detalles... QObject No rooms found Non se atoparon salas %1 would like to add you to their contact list. %1 desexa engadirte á súa lista de contactos. %1 would like to add you to their contact list, saying '%2' %1 desexa engadirte á súa lista de contactos, dicindo «%2» QScrollBar Scroll here Desprazarse aquí Top Arriba Bottom Abaixo Page up Avanzar páxina Page down Retroceder páxina Scroll up Desprazarse arriba Scroll down Desprazarse abaixo QTextControl Select All Seleccionar todo &Copy &Copiar &Undo &Desfacer &Redo &Refacer Cu&t C&ortar &Paste &Pegar Delete Eliminar QWebPage Copy Link Copiar ligazón Copy Copiar Copy Image Copiar imaxe Scroll here Desprazarse aquí Top Arriba Bottom Abaixo Page up Avanzar páxina Page down Retroceder páxina Scroll up Desprazarse arriba Scroll down Desprazarse abaixo QWizard < &Back < &Volver &Finish &Finalizar &Help &Axuda Go Back Voltar Continue Continuar Commit Actualizar Done Feito Quit Saír Help Axuda Cancel Cancelar &Next &Seguinte &Next > &Seguinte > QtBookmarkDetailWindow Edit Bookmark Details Editar detalles do marcador Bookmark Name: Nome do marcador: Room Address: Enderezo da sala: Your Nickname: O teu alcume: Room password: Contrasinal da sala: Join automatically Unirse automaticamente QtJoinMUCWindow Enter Room Entrar á sala Room: Sala: Search ... Buscar ... Nickname: Alcume: Enter automatically in future Entrar automaticamente no futuro QtMUCSearchWindow Search Room Buscar sala Service: Servizo: Cancel Cancelar OK Aceptar List rooms Amosar salas QtUserSearchFieldsPage Nickname: Alcume: First name: Nome: Last name: Apelidos: E-Mail: Correo electrónico: Fetching search fields Recibindo campos de busca QtUserSearchFirstPage Add a user Engadir un usuario Add another user to your contact list. If you know their address you can add them directly, or you can search for them. Engade outro usuario á túa lista de contactos. Se coñeces o seu enderezo, podes engadilo ou buscalo directamente. I know their address: Coñezo o seu enderezo: I'd like to search my server Desexo buscar no meu servidor I'd like to search another server: Desexo buscar noutro servidor: QtUserSearchWizard Find User Atopar usuario Swift::ChatListModel Bookmarked Rooms Marcadores de salas Swift::QtAboutWidget About %1 Sobre %1 Version %1 Versión %1 Built with Qt %1 Compilado con Qt %1 Running with Qt %1 Executándose con Qt %1 Using the English translation by %1 Traducido ao galego por %1 View License Ver licenza Swift::QtAvatarWidget No picture Sen imaxe Select picture ... Seleccionar imaxe ... Clear picture Borrar imaxe Select picture Seleccionar imaxe Image Files (*.png *.jpg *.gif) Ficheiro da imaxe (*.png *.jpg *.gif) Error Erro The selected picture is in an unrecognized format A imaxe seleccionada ten un formato non coñecido Swift::QtBookmarkDetailWindow Bookmark not valid Marcador incorrecto You must specify a valid room address (e.g. someroom@rooms.example.com). Debes especificar un enderezo da sala correcto (ex: algunhasala@salas.exemplo.com). Swift::QtChatListWindow Add New Bookmark Engadir novo marcador Edit Bookmark Editar marcador Remove Bookmark Eliminar marcador Swift::QtChatView Clear log Non sei se máis adiante poñer «Limpar texto» Limpar rexistro You are about to clear the contents of your chat log. Estás a piques de eliminar o contido desta conversa. Are you sure? Máis adiante trocalo? Realmente o desexas? Swift::QtChatWindow This message has not been received by your server yet. Esta mensaxe aínda non foi recibida polo teu servidor. This message may not have been transmitted. É posible que esta mensaxe aínda non fora transmitida. Couldn't send message: %1 Non se puido enviar a mensaxe: %1 Swift::QtContactEditWidget Name: Nome: Groups: Grupos: New Group: Novo grupo: Swift::QtContactEditWindow Edit contact Editar contacto Remove contact Eliminar contacto OK Aceptar Confirm contact deletion Confirmar eliminación do contacto Are you sure you want to delete this contact? Realmente desexas eliminar este contacto? This will remove the contact '%1' from all groups they may be in. Isto eliminará o contacto «%1» de todos os grupos nos que esté. Swift::QtEventWindow Display Notice Amosar aviso Swift::QtJoinMUCWindow someroom@rooms.example.com algunhasala@salas.exemplo.com Swift::QtLoginWindow User address: Enderezo do usuario: User address - looks like someuser@someserver.com Enderezo do usuario - semellante a usuario@algunservidor.com Example: alice@wonderland.lit Exemplo: alicia@paisdasmarabillas.lit Password: Contrasinal: Click if you have a personal certificate used for login to the service. Prema aquí se tes un certificado persoal para conectar co servizo. Connect Conectar Remember Password? Lembrar contrasinal? Login Automatically? Conectar automaticamente? &Swift &Swift &General &Xeral &About %1 &Sobre %1 &Show Debug Console &Amosar consola de depuración &Play Sounds &Reproducir sons Display Pop-up &Notifications Amosar &notificacións emerxentes &Quit &Saír Remove profile Eliminar perfil Remove the profile '%1'? Eliminar o perfil: «%1»? Cancel Cancelar Select an authentication certificate Escolle un certificado de autenticación The certificate presented by the server is not valid. O certificado presentado polo servidor non é válido. Would you like to permanently trust this certificate? This must only be done if you know it is correct. Queres confiar neste certificado permanentemente? Isto soamente debe facerse se sabes que é o correcto. Subject: %1 Asunto: %1 SHA-1 Fingerprint: %1 Pegada dixital SHA: %1 Swift::QtMUCSearchWindow Searching Buscando Swift::QtMainWindow &Contacts &Contactos &Notices &Avisos C&hats C&onversas &View &Vista &Show offline contacts &Amosar contactos desconectados &Actions &Accións Edit &Profile… Editar &perfil… Enter &Room… Entrar á &sala… &Add Contact… &Engadir contacto… &Edit Selected Contact… &Editar contacto seleccionado… Start &Chat… Comezar &conversa &Sign Out &Desconectar Swift::QtNameWidget Show Nickname Amosar alcume (No Nickname Set) (Sen alcume establecido) Show Address Amosar enderezo Edit Profile Editar perfil Swift::QtProfileWindow Edit Profile Editar perfil Nickname: Alcume: Save Gardar Swift::QtStatusWidget Connecting Conectando (No message) (Sen mensaxe) Swift::QtSubscriptionRequestWindow %1 would like to add you to their contact list. Would you like to add them to your contact list and share your status when you're online? If you choose to defer this choice, you will be asked again when you next login. %1 desexa engadirche á súa lista de contactos. Queres engadilo á túa lista de contactos e compartir o teu estado ao conectarte? Se escolles pospoñer esta escolla, preguntaráseche de novo na vindeira conexión. You have already replied to this request Xa respondeches a esta solicitude OK Aceptar Yes Si No Non Defer Pospoñer Swift::QtTreeWidget Edit Editar Remove Eliminar Rename Mudar o nome Rename group Mudar o nome do grupo Enter a new name for group '%1': Introduce un nome novo para o grupo «%1»: Swift::QtUserSearchDetailsPage Please choose a name for the contact, and select the groups you want to add the contact to. Escolle un nome para o contacto, e selecciona os grupos aos que desexas engadilo. Swift::QtUserSearchFirstPage %1. If you know their address you can enter it directly, or you can search for them. %1. Se coñeces o seu enderezo, podes introducilo directamente ou podes buscar ao usuario. Add another user to your contact list Engadir outro usuario á túa lista de contactos Chat to another user Conversar con outro usuario Swift::QtUserSearchWindow Add Contact Engadir contacto Chat to User Conversar co usuario alice@wonderland.lit alicia@paisdasmarabillas.lit How would you like to find the user to add? Como desexas buscar ao usuario a engadir? How would you like to find the user to chat to? Como desexas buscar ao usuario co que conversar? Error while searching Produciuse un erro durante na busca This server doesn't support searching for users. Este servidor non permite busca de usuarios. Swift::QtWebView Clear Limpar Swift::QtXMLConsoleWidget Console Consola Trace input/output Rastrexar entrada/saída Clear Limpar Debug Console Consola de depuración <!-- IN --> <!-- ENTRANTE --> <!-- OUT --> <!-- SAÍNTE --> TRANSLATION_INFO TRANSLATION_AUTHOR Anxo Outeiral TRANSLATION_LICENSE This string contains the license under which this translation is licensed. We ask you to license the translation under the BSD license. Please read http://www.opensource.org/licenses/bsd-license.php, and if you agree to release your translation under this license, use the following (untranslated) text: 'This translation is licensed under the BSD License. See http://www.opensource.org/licenses/bsd-license.php' This translation is licensed under the BSD License. See http://www.opensource.org/licenses/bsd-license.php