From e8360f0dd62ea651e94f681499faef58747f2ece Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Remko=20Tron=C3=A7on?= Date: Mon, 22 Jun 2009 19:38:29 +0200 Subject: Support vCard-based avatars in MUCs. diff --git a/Swift/Controllers/ChatController.cpp b/Swift/Controllers/ChatController.cpp index 39e9144..5fee3d2 100644 --- a/Swift/Controllers/ChatController.cpp +++ b/Swift/Controllers/ChatController.cpp @@ -31,7 +31,7 @@ void ChatController::postSendMessage(const String& body) { chatWindow_->addMessage(body, "me", true, labelsEnabled_ ? chatWindow_->getSelectedSecurityLabel() : boost::optional(), ""); } -String ChatController::senderDisplayNameFromMessage(JID from) { +String ChatController::senderDisplayNameFromMessage(const JID& from) { return nickResolver_->jidToNick(from); } diff --git a/Swift/Controllers/ChatController.h b/Swift/Controllers/ChatController.h index 265bc91..8168510 100644 --- a/Swift/Controllers/ChatController.h +++ b/Swift/Controllers/ChatController.h @@ -16,7 +16,7 @@ namespace Swift { bool isIncomingMessageFromMe(boost::shared_ptr message); void postSendMessage(const String &body); void preHandleIncomingMessage(boost::shared_ptr message); - String senderDisplayNameFromMessage(JID from); + String senderDisplayNameFromMessage(const JID& from); private: NickResolver* nickResolver_; diff --git a/Swift/Controllers/ChatControllerBase.cpp b/Swift/Controllers/ChatControllerBase.cpp index e84ee04..91d088e 100644 --- a/Swift/Controllers/ChatControllerBase.cpp +++ b/Swift/Controllers/ChatControllerBase.cpp @@ -3,12 +3,12 @@ #include #include -#include "Swiften/Avatars/AvatarManager.h" #include "Swiften/Client/StanzaChannel.h" #include "Swiften/Base/foreach.h" #include "Swift/Controllers/ChatWindow.h" #include "Swift/Controllers/ChatWindowFactory.h" #include "Swiften/Queries/Requests/GetSecurityLabelsCatalogRequest.h" +#include "Swiften/Avatars/AvatarManager.h" namespace Swift { @@ -109,11 +109,6 @@ void ChatControllerBase::showChatWindow() { chatWindow_->show(); } -String ChatControllerBase::senderDisplayNameFromMessage(JID from) { - return from; -} - - void ChatControllerBase::handleIncomingMessage(boost::shared_ptr messageEvent) { unreadMessages_.push_back(messageEvent); diff --git a/Swift/Controllers/ChatControllerBase.h b/Swift/Controllers/ChatControllerBase.h index 58d3a1d..5a322d2 100644 --- a/Swift/Controllers/ChatControllerBase.h +++ b/Swift/Controllers/ChatControllerBase.h @@ -5,6 +5,7 @@ #include #include #include +#include #include "Swiften/Base/String.h" #include "Swiften/Elements/DiscoInfo.h" @@ -32,7 +33,7 @@ namespace Swift { ChatControllerBase(StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &toJID, PresenceOracle* presenceOracle, AvatarManager* avatarManager); virtual void postSendMessage(const String&) {}; - virtual String senderDisplayNameFromMessage(JID from); + virtual String senderDisplayNameFromMessage(const JID& from) = 0; void handlePresenceChange(boost::shared_ptr newPresence, boost::shared_ptr previousPresence); virtual bool isIncomingMessageFromMe(boost::shared_ptr) = 0; virtual void preHandleIncomingMessage(boost::shared_ptr) {}; diff --git a/Swift/Controllers/MUCController.cpp b/Swift/Controllers/MUCController.cpp index 27ddcb8..e3615d9 100644 --- a/Swift/Controllers/MUCController.cpp +++ b/Swift/Controllers/MUCController.cpp @@ -66,7 +66,7 @@ bool MUCController::isIncomingMessageFromMe(boost::shared_ptr message) return nick_ == from.getResource(); } -String MUCController::senderDisplayNameFromMessage(JID from) { +String MUCController::senderDisplayNameFromMessage(const JID& from) { return from.getResource(); } diff --git a/Swift/Controllers/MUCController.h b/Swift/Controllers/MUCController.h index b2f396c..77335da 100644 --- a/Swift/Controllers/MUCController.h +++ b/Swift/Controllers/MUCController.h @@ -28,12 +28,15 @@ namespace Swift { protected: void preSendMessageRequest(boost::shared_ptr message); bool isIncomingMessageFromMe(boost::shared_ptr message); - String senderDisplayNameFromMessage(JID from); + String senderDisplayNameFromMessage(const JID& from); + private: void handleWindowClosed(); void handleOccupantJoined(const MUCOccupant& occupant); void handleOccupantLeft(const MUCOccupant& occupant, MUC::LeavingType type, const String& reason); void handleOccupantPresenceChange(boost::shared_ptr presence); + + private: MUC *muc_; String nick_; TreeWidgetFactory *treeWidgetFactory_; diff --git a/Swift/Controllers/MainController.cpp b/Swift/Controllers/MainController.cpp index 20dfaa1..93b7c2a 100644 --- a/Swift/Controllers/MainController.cpp +++ b/Swift/Controllers/MainController.cpp @@ -105,7 +105,7 @@ void MainController::handleConnected() { loginWindow_->morphInto(rosterController_->getWindow()); delete avatarManager_; - avatarManager_ = new AvatarManager(client_, client_, avatarStorage_); + avatarManager_ = new AvatarManager(client_, client_, avatarStorage_, this); DiscoInfo discoInfo; discoInfo.addIdentity(DiscoInfo::Identity(CLIENT_NAME, "client", "pc")); @@ -271,4 +271,9 @@ void MainController::handleServerDiscoInfoResponse(boost::shared_ptr } } +bool MainController::isMUC(const JID& jid) const { + return mucControllers_.find(jid.toBare()) != mucControllers_.end(); +} + + } diff --git a/Swift/Controllers/MainController.h b/Swift/Controllers/MainController.h index aa6a85b..650afd1 100644 --- a/Swift/Controllers/MainController.h +++ b/Swift/Controllers/MainController.h @@ -1,6 +1,9 @@ #ifndef SWIFTEN_MainController_H #define SWIFTEN_MainController_H +#include +#include +#include #include "Swiften/Base/String.h" #include "Swiften/Client/ClientError.h" @@ -11,12 +14,7 @@ #include "Swiften/Elements/Message.h" #include "Swiften/Settings/SettingsProvider.h" #include "Swiften/Elements/CapsInfo.h" - - -#include -#include - -#include +#include "Swiften/MUC/MUCRegistry.h" namespace Swift { class AvatarStorage; @@ -42,7 +40,7 @@ namespace Swift { class SystemTray; class SystemTrayController; - class MainController { + class MainController : public MUCRegistry { public: MainController(ChatWindowFactory* chatWindowFactory, MainWindowFactory *mainWindowFactory, LoginWindowFactory *loginWindowFactory, TreeWidgetFactory* treeWidgetFactory, SettingsProvider *settings, Application* application, SystemTray* systemTray); ~MainController(); @@ -62,7 +60,10 @@ namespace Swift { void handleEventQueueLengthChange(int count); ChatController* getChatController(const JID &contact); void logout(); - + + virtual bool isMUC(const JID& muc) const; + + private: Client* client_; ChatWindowFactory* chatWindowFactory_; MainWindowFactory* mainWindowFactory_; diff --git a/Swiften/Avatars/AvatarManager.cpp b/Swiften/Avatars/AvatarManager.cpp index f0b04b9..c15d002 100644 --- a/Swiften/Avatars/AvatarManager.cpp +++ b/Swiften/Avatars/AvatarManager.cpp @@ -7,10 +7,11 @@ #include "Swiften/Queries/Requests/GetVCardRequest.h" #include "Swiften/StringCodecs/SHA1.h" #include "Swiften/Avatars/AvatarStorage.h" +#include "Swiften/MUC/MUCRegistry.h" namespace Swift { -AvatarManager::AvatarManager(StanzaChannel* stanzaChannel, IQRouter* iqRouter, AvatarStorage* avatarStorage) : stanzaChannel_(stanzaChannel), iqRouter_(iqRouter), avatarStorage_(avatarStorage) { +AvatarManager::AvatarManager(StanzaChannel* stanzaChannel, IQRouter* iqRouter, AvatarStorage* avatarStorage, MUCRegistry* mucRegistry) : stanzaChannel_(stanzaChannel), iqRouter_(iqRouter), avatarStorage_(avatarStorage), mucRegistry_(mucRegistry) { stanzaChannel->onPresenceReceived.connect(boost::bind(&AvatarManager::handlePresenceReceived, this, _1)); } @@ -19,33 +20,42 @@ void AvatarManager::handlePresenceReceived(boost::shared_ptr presence) if (!update) { return; } - JID from = presence->getFrom().toBare(); + JID from = getAvatarJID(presence->getFrom()); String& hash = avatarHashes_[from]; if (hash != update->getPhotoHash()) { - hash = update->getPhotoHash(); - if (!avatarStorage_->hasAvatar(hash)) { - boost::shared_ptr request(new GetVCardRequest(from, iqRouter_)); - request->onResponse.connect(boost::bind(&AvatarManager::handleVCardReceived, this, from, _1, _2)); - request->send(); + String newHash = update->getPhotoHash(); + if (avatarStorage_->hasAvatar(newHash)) { + setAvatarHash(from, newHash); } else { - onAvatarChanged(from, hash); + boost::shared_ptr request(new GetVCardRequest(from, iqRouter_)); + request->onResponse.connect(boost::bind(&AvatarManager::handleVCardReceived, this, from, newHash, _1, _2)); + request->send(); } } } -void AvatarManager::handleVCardReceived(JID from, boost::shared_ptr vCard, const boost::optional& error) { +void AvatarManager::handleVCardReceived(const JID& from, const String& promisedHash, boost::shared_ptr vCard, const boost::optional& error) { if (error) { // FIXME: What to do here? + std::cerr << "Warning: " << from << ": Could not get vCard" << std::endl; return; } - String hash = SHA1::getHexHash(vCard->getPhoto()); - avatarStorage_->addAvatar(hash, vCard->getPhoto()); + String realHash = SHA1::getHexHash(vCard->getPhoto()); + if (promisedHash != realHash) { + std::cerr << "Warning: " << from << ": Got different vCard photo hash (" << promisedHash << " != " << realHash << ")" << std::endl; + } + avatarStorage_->addAvatar(realHash, vCard->getPhoto()); + setAvatarHash(from, realHash); +} + +void AvatarManager::setAvatarHash(const JID& from, const String& hash) { + avatarHashes_[from] = hash; onAvatarChanged(from, hash); } String AvatarManager::getAvatarHash(const JID& jid) const { - std::map::const_iterator i = avatarHashes_.find(jid.toBare()); + std::map::const_iterator i = avatarHashes_.find(getAvatarJID(jid)); if (i != avatarHashes_.end()) { return i->second; } @@ -62,4 +72,10 @@ boost::filesystem::path AvatarManager::getAvatarPath(const JID& jid) const { return boost::filesystem::path(); } +JID AvatarManager::getAvatarJID(const JID& jid) const { + JID bareFrom = jid.toBare(); + return (mucRegistry_->isMUC(bareFrom) ? jid : bareFrom); +} + + } diff --git a/Swiften/Avatars/AvatarManager.h b/Swiften/Avatars/AvatarManager.h index 0085405..13c6cb7 100644 --- a/Swiften/Avatars/AvatarManager.h +++ b/Swiften/Avatars/AvatarManager.h @@ -12,13 +12,14 @@ #include "Swiften/Elements/Error.h" namespace Swift { + class MUCRegistry; class AvatarStorage; class StanzaChannel; class IQRouter; class AvatarManager { public: - AvatarManager(StanzaChannel*, IQRouter*, AvatarStorage*); + AvatarManager(StanzaChannel*, IQRouter*, AvatarStorage*, MUCRegistry*); String getAvatarHash(const JID&) const; boost::filesystem::path getAvatarPath(const JID&) const; @@ -28,12 +29,15 @@ namespace Swift { private: void handlePresenceReceived(boost::shared_ptr); - void handleVCardReceived(JID from, boost::shared_ptr, const boost::optional&); + void handleVCardReceived(const JID& from, const String& hash, boost::shared_ptr, const boost::optional&); + void setAvatarHash(const JID& from, const String& hash); + JID getAvatarJID(const JID& o) const; private: StanzaChannel* stanzaChannel_; IQRouter* iqRouter_; AvatarStorage* avatarStorage_; + MUCRegistry* mucRegistry_; std::map avatarHashes_; }; } diff --git a/Swiften/Avatars/UnitTest/AvatarManagerTest.cpp b/Swiften/Avatars/UnitTest/AvatarManagerTest.cpp new file mode 100644 index 0000000..b8a6246 --- /dev/null +++ b/Swiften/Avatars/UnitTest/AvatarManagerTest.cpp @@ -0,0 +1,101 @@ +#include +#include + +#include "Swiften/Avatars/AvatarManager.h" +#include "Swiften/Avatars/AvatarStorage.h" +#include "Swiften/MUC/MUCRegistry.h" +#include "Swiften/Queries/IQRouter.h" +#include "Swiften/Client/DummyStanzaChannel.h" + +using namespace Swift; + +class AvatarManagerTest : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(AvatarManagerTest); + CPPUNIT_TEST(testUpdate_UpdateNewHash); + CPPUNIT_TEST(testUpdate_UpdateNewHashAlreadyHaveAvatar); + CPPUNIT_TEST(testUpdate_UpdateNewHashFromMUC); + CPPUNIT_TEST(testUpdate_UpdateSameHash); + CPPUNIT_TEST(testUpdate_UpdateNewHashSameThanOtherUser); + CPPUNIT_TEST(testReceiveVCard); + CPPUNIT_TEST(testGetAvatarPath); + CPPUNIT_TEST(testGetAvatarPathFromMUC); + CPPUNIT_TEST_SUITE_END(); + + public: + AvatarManagerTest() {} + + void setUp() { + stanzaChannel_ = new DummyStanzaChannel(); + iqRouter_ = new IQRouter(stanzaChannel_); + mucRegistry_ = new DummyMUCRegistry(); + avatarStorage_ = new DummyAvatarStorage(); + } + + void tearDown() { + delete avatarStorage_; + delete mucRegistry_; + delete iqRouter_; + delete stanzaChannel_; + } + + void testUpdateNewHash() { + std::auto_ptr testling = createManager(); + } + + void testUpdate_UpdateNewHash() { + std::auto_ptr testling = createManager(); + } + + void testUpdate_UpdateNewHashAlreadyHaveAvatar() { + std::auto_ptr testling = createManager(); + } + + void testUpdate_UpdateNewHashFromMUC() { + std::auto_ptr testling = createManager(); + } + + void testUpdate_UpdateSameHash() { + std::auto_ptr testling = createManager(); + } + + void testUpdate_UpdateNewHashSameThanOtherUser() { + std::auto_ptr testling = createManager(); + } + + void testReceiveVCard() { + std::auto_ptr testling = createManager(); + } + + void testGetAvatarPath() { + std::auto_ptr testling = createManager(); + } + + void testGetAvatarPathFromMUC() { + std::auto_ptr testling = createManager(); + } + + private: + std::auto_ptr createManager() { + return std::auto_ptr(new AvatarManager(stanzaChannel_, iqRouter_, avatarStorage_, mucRegistry_)); + } + + private: + struct DummyMUCRegistry : public MUCRegistry { + bool isMUC(const JID& jid) const { return std::find(mucs_.begin(), mucs_.end(), jid) != mucs_.end(); } + std::vector mucs_; + }; + struct DummyAvatarStorage : public AvatarStorage { + virtual bool hasAvatar(const String& hash) const { return avatars.find(hash) != avatars.end(); } + virtual void addAvatar(const String& hash, const ByteArray& avatar) { avatars[hash] = avatar; } + virtual boost::filesystem::path getAvatarPath(const String& hash) const { + return boost::filesystem::path("/avatars") / hash.getUTF8String(); + } + std::map avatars; + }; + DummyStanzaChannel* stanzaChannel_; + IQRouter* iqRouter_; + DummyMUCRegistry* mucRegistry_; + DummyAvatarStorage* avatarStorage_; +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(AvatarManagerTest); diff --git a/Swiften/Avatars/UnitTest/Makefile.inc b/Swiften/Avatars/UnitTest/Makefile.inc index e69de29..c089c02 100644 --- a/Swiften/Avatars/UnitTest/Makefile.inc +++ b/Swiften/Avatars/UnitTest/Makefile.inc @@ -0,0 +1,2 @@ +UNITTEST_SOURCES += \ + Swiften/Avatars/UnitTest/AvatarManagerTest.cpp diff --git a/Swiften/Client/DummyStanzaChannel.h b/Swiften/Client/DummyStanzaChannel.h new file mode 100644 index 0000000..4b66a9d --- /dev/null +++ b/Swiften/Client/DummyStanzaChannel.h @@ -0,0 +1,34 @@ +#pragma once + +#include + +#include "Swiften/Client/StanzaChannel.h" + +namespace Swift { + class DummyStanzaChannel : public StanzaChannel { + public: + DummyStanzaChannel() {} + + virtual void sendStanza(boost::shared_ptr stanza) { + sentStanzas_.push_back(stanza); + } + + 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 String getNewIQID() { + return "test-id"; + } + + std::vector > sentStanzas_; + }; +} diff --git a/Swiften/MUC/MUCRegistry.cpp b/Swiften/MUC/MUCRegistry.cpp new file mode 100644 index 0000000..95bab08 --- /dev/null +++ b/Swiften/MUC/MUCRegistry.cpp @@ -0,0 +1,8 @@ +#include "Swiften/MUC/MUCRegistry.h" + +namespace Swift { + +MUCRegistry::~MUCRegistry() { +} + +} diff --git a/Swiften/MUC/MUCRegistry.h b/Swiften/MUC/MUCRegistry.h new file mode 100644 index 0000000..a843abb --- /dev/null +++ b/Swiften/MUC/MUCRegistry.h @@ -0,0 +1,12 @@ +#pragma once + +namespace Swift { + class JID; + + class MUCRegistry { + public: + virtual ~MUCRegistry(); + + virtual bool isMUC(const JID&) const = 0; + }; +} diff --git a/Swiften/MUC/Makefile.inc b/Swiften/MUC/Makefile.inc index d97b9fa..dc47d97 100644 --- a/Swiften/MUC/Makefile.inc +++ b/Swiften/MUC/Makefile.inc @@ -1,3 +1,4 @@ SWIFTEN_SOURCES += \ + Swiften/MUC/MUCRegistry.cpp \ Swiften/MUC/MUC.cpp \ Swiften/MUC/MUCOccupant.cpp diff --git a/Swiften/Parser/PayloadParsers/VCardUpdateParser.cpp b/Swiften/Parser/PayloadParsers/VCardUpdateParser.cpp index 855f9b0..e195eb7 100644 --- a/Swiften/Parser/PayloadParsers/VCardUpdateParser.cpp +++ b/Swiften/Parser/PayloadParsers/VCardUpdateParser.cpp @@ -5,7 +5,7 @@ namespace Swift { VCardUpdateParser::VCardUpdateParser() : level_(TopLevel) { } -void VCardUpdateParser::handleStartElement(const String& element, const String&, const AttributeMap& attributes) { +void VCardUpdateParser::handleStartElement(const String&, const String&, const AttributeMap&) { if (level_ == PayloadLevel) { currentText_ = ""; } diff --git a/Swiften/Queries/Requests/GetVCardRequest.h b/Swiften/Queries/Requests/GetVCardRequest.h index e403096..8fc6e17 100644 --- a/Swiften/Queries/Requests/GetVCardRequest.h +++ b/Swiften/Queries/Requests/GetVCardRequest.h @@ -1,16 +1,12 @@ #pragma once -#include - #include "Swiften/Queries/GenericRequest.h" #include "Swiften/Elements/VCard.h" namespace Swift { class GetVCardRequest : public GenericRequest { public: - GetVCardRequest(const JID& jid, IQRouter* router) : - GenericRequest(IQ::Get, jid, boost::shared_ptr(new VCard()), router) { - assert(jid.isBare()); + GetVCardRequest(const JID& jid, IQRouter* router) : GenericRequest(IQ::Get, jid, boost::shared_ptr(new VCard()), router) { } }; } diff --git a/tools/Copyrighter.py b/tools/Copyrighter.py index 9726f03..189dcf5 100755 --- a/tools/Copyrighter.py +++ b/tools/Copyrighter.py @@ -1,19 +1,96 @@ #!/usr/bin/env python +#coding=utf-8 -import os +import os, re, datetime TEMPLATE = """/* - * Copyright (c) %(year)s %(author)s - * %(license)s - */""" - -for (path, dirs, files) in os.walk("src") : - if "3rdParty" in path : - continue - for filename in files : - if not filename.endswith(".cpp") and not filename.endswith(".h") : - continue - if filename.startswith("moc_") : - continue - fullFilename = path + "/" + filename - print fullFilename + * Copyright (c) %(year)s %(author)s. + * See the included COPYING file for license details. + */ + +""" + +def updateCopyright(fileName) : + file = open(fileName) + fileData = "" + + author = "" + startYear = "" + endYear = "" + previousCopyright = "" + + # Retrieve previous copyright information + header = "" + inHeader = False + inSpaceBelowHeader = False + lines = file.readlines() + lines2 = lines + for line in lines2 : + lines.pop(0) + if inSpaceBelowHeader : + if line.strip() != "" : + break + elif inHeader : + if line.startswith(" */") : + inSpaceBelowHeader = True + else : + header += line + else : + if line.strip() == "" : + continue + elif line.startswith("/*") : + inHeader = True + header += line + else : + fileData += line + break + if "Copyright" in header : + previousCopyright = header + m = re.match("\* Copyright \(c\) (?P\d\d\d\d)(-(?P\d\d\d\d))? (?P.*)", header) + if m : + author = m.group("author") + startYear = m.group("startYear") + endYear = m.group("endYear") + elif header != "" : + fileData = header + file.close() + + # Read in the rest of the data + fileData += "".join(lines) + + # Guess empty values + if author == "" : + if "Swift/" in fileName : + author = "Kevin Smith" + else : + author = u"Remko Tronçon" + if startYear == "" : + startYear = datetime.date.today().strftime("%Y") + elif endYear == "" : + ## TODO: Guess end year by looking at git log --pretty=format:%ai -- + pass + + # Generate a copyright + year = startYear + "-" + endYear if len(endYear) > 0 else startYear + copyright = TEMPLATE % { + "author" : author, + "year" : year + } + + # Write the copyright to the file + if copyright.encode("utf-8") != previousCopyright : + file = open(fileName, "w") + file.write(copyright.encode("utf-8")) + file.write(fileData) + file.close() + +for (path, dirs, files) in os.walk("Swiften/JID") : + if "3rdParty" in path : + continue + for filename in files : + if not filename.endswith(".cpp") and not filename.endswith(".h") : + continue + if filename.startswith("moc_") : + continue + fullFilename = path + "/" + filename + updateCopyright(fullFilename) -- cgit v0.10.2-6-g49f6