summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRemko Tronçon <git@el-tramo.be>2009-06-20 11:47:21 (GMT)
committerRemko Tronçon <git@el-tramo.be>2009-06-20 14:38:58 (GMT)
commit6eb30e0e1f0a8e7ee936f3c006c7f710785653df (patch)
tree1e72dd6445d7d8d4e06f71c52299260a941a5be6
parent6d50c38e2bc6a17afb19effe06d2103f06c8ea1c (diff)
downloadswift-6eb30e0e1f0a8e7ee936f3c006c7f710785653df.zip
swift-6eb30e0e1f0a8e7ee936f3c006c7f710785653df.tar.bz2
Added vCard-based avatars support.
-rw-r--r--Swift/Controllers/ChatController.cpp6
-rw-r--r--Swift/Controllers/ChatController.h7
-rw-r--r--Swift/Controllers/ChatControllerBase.cpp5
-rw-r--r--Swift/Controllers/ChatControllerBase.h20
-rw-r--r--Swift/Controllers/ChatWindow.h5
-rw-r--r--Swift/Controllers/MUCController.cpp5
-rw-r--r--Swift/Controllers/MUCController.h3
-rw-r--r--Swift/Controllers/MainController.cpp16
-rw-r--r--Swift/Controllers/MainController.h4
-rw-r--r--Swift/QtUI/QtChatWindow.cpp4
-rw-r--r--Swift/QtUI/QtChatWindow.h2
-rw-r--r--Swiften/Application/Application.cpp4
-rw-r--r--Swiften/Application/Application.h1
-rw-r--r--Swiften/Avatars/AvatarFileStorage.cpp26
-rw-r--r--Swiften/Avatars/AvatarFileStorage.h24
-rw-r--r--Swiften/Avatars/AvatarManager.cpp65
-rw-r--r--Swiften/Avatars/AvatarManager.h39
-rw-r--r--Swiften/Avatars/AvatarStorage.cpp8
-rw-r--r--Swiften/Avatars/AvatarStorage.h18
-rw-r--r--Swiften/Avatars/Makefile.inc6
-rw-r--r--Swiften/Avatars/UnitTest/Makefile.inc0
-rw-r--r--Swiften/Makefile.inc1
-rw-r--r--Swiften/Queries/Requests/GetVCardRequest.h16
-rw-r--r--Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp2
-rw-r--r--Swiften/Serializer/PayloadSerializers/Makefile.inc3
-rw-r--r--Swiften/Serializer/PayloadSerializers/VCardSerializer.cpp18
-rw-r--r--Swiften/Serializer/PayloadSerializers/VCardSerializer.h13
27 files changed, 295 insertions, 26 deletions
diff --git a/Swift/Controllers/ChatController.cpp b/Swift/Controllers/ChatController.cpp
index 8c0b8bb..39e9144 100644
--- a/Swift/Controllers/ChatController.cpp
+++ b/Swift/Controllers/ChatController.cpp
@@ -9,8 +9,8 @@ namespace Swift {
/**
* The controller does not gain ownership of the stanzaChannel, nor the factory.
*/
-ChatController::ChatController(StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &contact, NickResolver* nickResolver, PresenceOracle* presenceOracle)
- : ChatControllerBase(stanzaChannel, iqRouter, chatWindowFactory, contact, presenceOracle) {
+ChatController::ChatController(StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &contact, NickResolver* nickResolver, PresenceOracle* presenceOracle, AvatarManager* avatarManager)
+ : ChatControllerBase(stanzaChannel, iqRouter, chatWindowFactory, contact, presenceOracle, avatarManager) {
nickResolver_ = nickResolver;
}
@@ -28,7 +28,7 @@ void ChatController::preHandleIncomingMessage(boost::shared_ptr<Message> message
}
void ChatController::postSendMessage(const String& body) {
- chatWindow_->addMessage(body, "me", true, labelsEnabled_ ? chatWindow_->getSelectedSecurityLabel() : boost::optional<SecurityLabel>());
+ chatWindow_->addMessage(body, "me", true, labelsEnabled_ ? chatWindow_->getSelectedSecurityLabel() : boost::optional<SecurityLabel>(), "");
}
String ChatController::senderDisplayNameFromMessage(JID from) {
diff --git a/Swift/Controllers/ChatController.h b/Swift/Controllers/ChatController.h
index 98e66bc..265bc91 100644
--- a/Swift/Controllers/ChatController.h
+++ b/Swift/Controllers/ChatController.h
@@ -4,17 +4,20 @@
#include "Swift/Controllers/ChatControllerBase.h"
namespace Swift {
+ class AvatarManager;
class NickResolver;
class ChatController : public ChatControllerBase {
public:
- ChatController(StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &contact, NickResolver* nickResolver, PresenceOracle* presenceOracle);
- ~ChatController() {};
+ ChatController(StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &contact, NickResolver* nickResolver, PresenceOracle* presenceOracle, AvatarManager*);
+
//boost::signal<void (const JID&, const JID&)> onJIDChanged;
+
protected:
bool isIncomingMessageFromMe(boost::shared_ptr<Message> message);
void postSendMessage(const String &body);
void preHandleIncomingMessage(boost::shared_ptr<Message> message);
String senderDisplayNameFromMessage(JID from);
+
private:
NickResolver* nickResolver_;
JID contact_;
diff --git a/Swift/Controllers/ChatControllerBase.cpp b/Swift/Controllers/ChatControllerBase.cpp
index 0d22571..e84ee04 100644
--- a/Swift/Controllers/ChatControllerBase.cpp
+++ b/Swift/Controllers/ChatControllerBase.cpp
@@ -3,6 +3,7 @@
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
+#include "Swiften/Avatars/AvatarManager.h"
#include "Swiften/Client/StanzaChannel.h"
#include "Swiften/Base/foreach.h"
#include "Swift/Controllers/ChatWindow.h"
@@ -11,7 +12,7 @@
namespace Swift {
-ChatControllerBase::ChatControllerBase(StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &toJID, PresenceOracle* presenceOracle) : stanzaChannel_(stanzaChannel), iqRouter_(iqRouter), chatWindowFactory_(chatWindowFactory), toJID_(toJID), labelsEnabled_(false), presenceOracle_(presenceOracle) {
+ChatControllerBase::ChatControllerBase(StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &toJID, PresenceOracle* presenceOracle, AvatarManager* avatarManager) : stanzaChannel_(stanzaChannel), iqRouter_(iqRouter), chatWindowFactory_(chatWindowFactory), toJID_(toJID), labelsEnabled_(false), presenceOracle_(presenceOracle), avatarManager_(avatarManager) {
chatWindow_ = chatWindowFactory_->createChatWindow(toJID);
chatWindow_->onAllMessagesRead.connect(boost::bind(&ChatControllerBase::handleAllMessagesRead, this));
chatWindow_->onSendMessageRequest.connect(boost::bind(&ChatControllerBase::handleSendMessageRequest, this, _1));
@@ -130,7 +131,7 @@ void ChatControllerBase::handleIncomingMessage(boost::shared_ptr<MessageEvent> m
boost::shared_ptr<SecurityLabel> label = message->getPayload<SecurityLabel>();
boost::optional<SecurityLabel> maybeLabel = label ? boost::optional<SecurityLabel>(*label) : boost::optional<SecurityLabel>();
JID from = message->getFrom();
- chatWindow_->addMessage(body, senderDisplayNameFromMessage(from), isIncomingMessageFromMe(message), maybeLabel);
+ chatWindow_->addMessage(body, senderDisplayNameFromMessage(from), isIncomingMessageFromMe(message), maybeLabel, String(avatarManager_->getAvatarPath(from).string()));
}
}
diff --git a/Swift/Controllers/ChatControllerBase.h b/Swift/Controllers/ChatControllerBase.h
index 1967977..58d3a1d 100644
--- a/Swift/Controllers/ChatControllerBase.h
+++ b/Swift/Controllers/ChatControllerBase.h
@@ -19,6 +19,7 @@ namespace Swift {
class StanzaChannel;
class ChatWindow;
class ChatWindowFactory;
+ class AvatarManager;
class ChatControllerBase {
public:
@@ -28,7 +29,8 @@ namespace Swift {
void handleIncomingMessage(boost::shared_ptr<MessageEvent> message);
protected:
- ChatControllerBase(StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &toJID, PresenceOracle* presenceOracle);
+ ChatControllerBase(StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &toJID, PresenceOracle* presenceOracle, AvatarManager* avatarManager);
+
virtual void postSendMessage(const String&) {};
virtual String senderDisplayNameFromMessage(JID from);
void handlePresenceChange(boost::shared_ptr<Presence> newPresence, boost::shared_ptr<Presence> previousPresence);
@@ -36,6 +38,14 @@ namespace Swift {
virtual void preHandleIncomingMessage(boost::shared_ptr<Message>) {};
virtual void preSendMessageRequest(boost::shared_ptr<Message>) {};
+ private:
+ void handleSendMessageRequest(const String &body);
+ String getStatusChangeString(boost::shared_ptr<Presence> presence);
+ void handleAllMessagesRead();
+ void handleSecurityLabelsCatalogResponse(boost::shared_ptr<SecurityLabelsCatalog>, const boost::optional<Error>& error);
+ String getErrorMessage(boost::shared_ptr<Error>);
+
+ protected:
std::vector<boost::shared_ptr<MessageEvent> > unreadMessages_;
StanzaChannel* stanzaChannel_;
IQRouter* iqRouter_;
@@ -44,13 +54,7 @@ namespace Swift {
JID toJID_;
bool labelsEnabled_;
PresenceOracle* presenceOracle_;
-
- private:
- void handleSendMessageRequest(const String &body);
- String getStatusChangeString(boost::shared_ptr<Presence> presence);
- void handleAllMessagesRead();
- void handleSecurityLabelsCatalogResponse(boost::shared_ptr<SecurityLabelsCatalog>, const boost::optional<Error>& error);
- String getErrorMessage(boost::shared_ptr<Error>);
+ AvatarManager* avatarManager_;
};
}
diff --git a/Swift/Controllers/ChatWindow.h b/Swift/Controllers/ChatWindow.h
index 04d0007..dd71bf9 100644
--- a/Swift/Controllers/ChatWindow.h
+++ b/Swift/Controllers/ChatWindow.h
@@ -10,12 +10,15 @@
#include "Swiften/Elements/SecurityLabel.h"
namespace Swift {
+ class AvatarManager;
class TreeWidget;
+
class ChatWindow {
public:
+ ChatWindow() {}
virtual ~ChatWindow() {};
- virtual void addMessage(const String& message, const String& senderName, bool senderIsSelf, const boost::optional<SecurityLabel>& label) = 0;
+ virtual void addMessage(const String& message, const String& senderName, bool senderIsSelf, const boost::optional<SecurityLabel>& label, const String& avatarPath) = 0;
virtual void addSystemMessage(const String& message) = 0;
virtual void addErrorMessage(const String& message) = 0;
diff --git a/Swift/Controllers/MUCController.cpp b/Swift/Controllers/MUCController.cpp
index 93880c4..27ddcb8 100644
--- a/Swift/Controllers/MUCController.cpp
+++ b/Swift/Controllers/MUCController.cpp
@@ -23,8 +23,9 @@ MUCController::MUCController (
IQRouter* iqRouter,
ChatWindowFactory* chatWindowFactory,
TreeWidgetFactory *treeWidgetFactory,
- PresenceOracle* presenceOracle) :
- ChatControllerBase(stanzaChannel, iqRouter, chatWindowFactory, muc, presenceOracle),
+ PresenceOracle* presenceOracle,
+ AvatarManager* avatarManager) :
+ ChatControllerBase(stanzaChannel, iqRouter, chatWindowFactory, muc, presenceOracle, avatarManager),
muc_(new MUC(stanzaChannel, muc)),
nick_(nick),
treeWidgetFactory_(treeWidgetFactory) {
diff --git a/Swift/Controllers/MUCController.h b/Swift/Controllers/MUCController.h
index c87695e..b2f396c 100644
--- a/Swift/Controllers/MUCController.h
+++ b/Swift/Controllers/MUCController.h
@@ -18,10 +18,11 @@ namespace Swift {
class ChatWindowFactory;
class Roster;
class TreeWidgetFactory;
+ class AvatarManager;
class MUCController : public ChatControllerBase {
public:
- MUCController(const JID &muc, const String &nick, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, TreeWidgetFactory *treeWidgetFactory, PresenceOracle* presenceOracle);
+ MUCController(const JID &muc, const String &nick, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, TreeWidgetFactory *treeWidgetFactory, PresenceOracle* presenceOracle, AvatarManager* avatarManager);
~MUCController();
protected:
diff --git a/Swift/Controllers/MainController.cpp b/Swift/Controllers/MainController.cpp
index e9177f8..20dfaa1 100644
--- a/Swift/Controllers/MainController.cpp
+++ b/Swift/Controllers/MainController.cpp
@@ -32,6 +32,8 @@
#include "Swiften/Queries/Responders/DiscoInfoResponder.h"
#include "Swiften/Disco/CapsInfoGenerator.h"
#include "Swiften/Queries/Requests/GetDiscoInfoRequest.h"
+#include "Swiften/Avatars/AvatarFileStorage.h"
+#include "Swiften/Avatars/AvatarManager.h"
namespace Swift {
@@ -45,8 +47,11 @@ typedef std::pair<JID, MUCController*> JIDMUCControllerPair;
MainController::MainController(ChatWindowFactory* chatWindowFactory, MainWindowFactory *mainWindowFactory, LoginWindowFactory *loginWindowFactory, TreeWidgetFactory *treeWidgetFactory, SettingsProvider *settings, Application* application, SystemTray* systemTray)
: client_(NULL), chatWindowFactory_(chatWindowFactory), mainWindowFactory_(mainWindowFactory), loginWindowFactory_(loginWindowFactory), treeWidgetFactory_(treeWidgetFactory), settings_(settings),
xmppRosterController_(NULL), rosterController_(NULL), loginWindow_(NULL), clientVersionResponder_(NULL), nickResolver_(NULL), discoResponder_(NULL),
- serverDiscoInfo_(new DiscoInfo()), presenceOracle_(NULL) {
+ serverDiscoInfo_(new DiscoInfo()), presenceOracle_(NULL), avatarManager_(NULL) {
application_ = application;
+
+ avatarStorage_ = new AvatarFileStorage(application_->getAvatarDir());
+
eventController_ = new EventController();
eventController_->onEventQueueLengthChange.connect(boost::bind(&MainController::handleEventQueueLengthChange, this, _1));
systemTrayController_ = new SystemTrayController(eventController_, systemTray);
@@ -55,6 +60,7 @@ MainController::MainController(ChatWindowFactory* chatWindowFactory, MainWindowF
}
MainController::~MainController() {
+ delete avatarManager_;
delete discoResponder_;
delete clientVersionResponder_;
delete xmppRosterController_;
@@ -69,6 +75,7 @@ MainController::~MainController() {
delete nickResolver_;
delete client_;
delete systemTrayController_;
+ delete avatarStorage_;
}
void MainController::handleConnected() {
@@ -97,6 +104,9 @@ void MainController::handleConnected() {
clientVersionResponder_ = new SoftwareVersionResponder(CLIENT_NAME, CLIENT_VERSION, client_);
loginWindow_->morphInto(rosterController_->getWindow());
+ delete avatarManager_;
+ avatarManager_ = new AvatarManager(client_, client_, avatarStorage_);
+
DiscoInfo discoInfo;
discoInfo.addIdentity(DiscoInfo::Identity(CLIENT_NAME, "client", "pc"));
capsInfo_ = boost::shared_ptr<CapsInfo>(new CapsInfo(CapsInfoGenerator(CLIENT_NODE).generateCapsInfo(discoInfo)));
@@ -206,7 +216,7 @@ ChatController* MainController::getChatController(const JID &contact) {
lookupContact = JID(contact.toBare());
}
if (chatControllers_.find(lookupContact) == chatControllers_.end()) {
- chatControllers_[contact] = new ChatController(client_, client_, chatWindowFactory_, contact, nickResolver_, presenceOracle_);
+ chatControllers_[contact] = new ChatController(client_, client_, chatWindowFactory_, contact, nickResolver_, presenceOracle_, avatarManager_);
chatControllers_[contact]->setAvailableServerFeatures(serverDiscoInfo_);
lookupContact = contact;
}
@@ -219,7 +229,7 @@ void MainController::handleChatControllerJIDChanged(const JID& from, const JID&
}
void MainController::handleJoinMUCRequest(const JID &muc, const String &nick) {
- mucControllers_[muc] = new MUCController(muc, nick, client_, client_, chatWindowFactory_, treeWidgetFactory_, presenceOracle_);
+ mucControllers_[muc] = new MUCController(muc, nick, client_, client_, chatWindowFactory_, treeWidgetFactory_, presenceOracle_, avatarManager_);
mucControllers_[muc]->setAvailableServerFeatures(serverDiscoInfo_);
}
diff --git a/Swift/Controllers/MainController.h b/Swift/Controllers/MainController.h
index 8b3a702..aa6a85b 100644
--- a/Swift/Controllers/MainController.h
+++ b/Swift/Controllers/MainController.h
@@ -19,6 +19,7 @@
#include <vector>
namespace Swift {
+ class AvatarStorage;
class Application;
class Client;
class ChatWindowFactory;
@@ -30,6 +31,7 @@ namespace Swift {
class RosterController;
class XMPPRosterController;
class DiscoInfoResponder;
+ class AvatarManager;
class LoginWindow;
class EventLoop;
class SoftwareVersionResponder;
@@ -68,6 +70,7 @@ namespace Swift {
TreeWidgetFactory* treeWidgetFactory_;
SettingsProvider *settings_;
Application* application_;
+ AvatarStorage* avatarStorage_;
ChatController* chatController_;
XMPPRosterController* xmppRosterController_;
RosterController* rosterController_;
@@ -82,6 +85,7 @@ namespace Swift {
boost::shared_ptr<DiscoInfo> serverDiscoInfo_;
PresenceOracle* presenceOracle_;
SystemTrayController* systemTrayController_;
+ AvatarManager* avatarManager_;
};
}
#endif
diff --git a/Swift/QtUI/QtChatWindow.cpp b/Swift/QtUI/QtChatWindow.cpp
index 5b63e55..8e916c3 100644
--- a/Swift/QtUI/QtChatWindow.cpp
+++ b/Swift/QtUI/QtChatWindow.cpp
@@ -126,7 +126,7 @@ void QtChatWindow::updateTitleWithUnreadCount() {
emit titleUpdated();
}
-void QtChatWindow::addMessage(const String &message, const String &senderName, bool senderIsSelf, const boost::optional<SecurityLabel>& label) {
+void QtChatWindow::addMessage(const String &message, const String &senderName, bool senderIsSelf, const boost::optional<SecurityLabel>& label, const String& avatarPath) {
if (isActiveWindow()) {
onAllMessagesRead();
}
@@ -141,7 +141,7 @@ void QtChatWindow::addMessage(const String &message, const String &senderName, b
htmlString += messageHTML;
bool appendToPrevious = !previousMessageWasSystem_ && ((senderIsSelf && previousMessageWasSelf_) || (!senderIsSelf && !previousMessageWasSelf_ && previousSenderName_ == P2QSTRING(senderName)));
- messageLog_->addMessage(MessageSnippet(htmlString, Qt::escape(P2QSTRING(senderName)), QDateTime::currentDateTime(), "qrc:/icons/avatar.png", senderIsSelf, appendToPrevious));
+ messageLog_->addMessage(MessageSnippet(htmlString, Qt::escape(P2QSTRING(senderName)), QDateTime::currentDateTime(), (avatarPath.isEmpty() ? "qrc:/icons/avatar.png" : "file://" + P2QSTRING(avatarPath)), senderIsSelf, appendToPrevious));
previousMessageWasSelf_ = senderIsSelf;
previousSenderName_ = P2QSTRING(senderName);
diff --git a/Swift/QtUI/QtChatWindow.h b/Swift/QtUI/QtChatWindow.h
index bbb1a7e..20a53b9 100644
--- a/Swift/QtUI/QtChatWindow.h
+++ b/Swift/QtUI/QtChatWindow.h
@@ -18,7 +18,7 @@ namespace Swift {
Q_OBJECT
public:
QtChatWindow(const QString &contact, QtTreeWidgetFactory* treeWidgetFactory);
- void addMessage(const String &message, const String &senderName, bool senderIsSelf, const boost::optional<SecurityLabel>& label);
+ void addMessage(const String &message, const String &senderName, bool senderIsSelf, const boost::optional<SecurityLabel>& label, const String& avatarPath);
void addSystemMessage(const String& message);
void addErrorMessage(const String& errorMessage);
void show();
diff --git a/Swiften/Application/Application.cpp b/Swiften/Application/Application.cpp
index e21d9e7..6ffb46f 100644
--- a/Swiften/Application/Application.cpp
+++ b/Swiften/Application/Application.cpp
@@ -17,6 +17,10 @@ boost::filesystem::path Application::getSettingsFileName() const {
return getSettingsDir() / "settings";
}
+boost::filesystem::path Application::getAvatarDir() const {
+ return getSettingsDir() / "avatars";
+}
+
boost::filesystem::path Application::getHomeDir() const {
// FIXME: Does this work on windows?
char* homeDirRaw = getenv("HOME");
diff --git a/Swiften/Application/Application.h b/Swiften/Application/Application.h
index 4fda7af..a65b1de 100644
--- a/Swiften/Application/Application.h
+++ b/Swiften/Application/Application.h
@@ -14,6 +14,7 @@ namespace Swift {
virtual ~Application();
boost::filesystem::path getSettingsFileName() const;
+ boost::filesystem::path getAvatarDir() const;
boost::filesystem::path getHomeDir() const;
virtual boost::filesystem::path getSettingsDir() const = 0;
boost::filesystem::path getProfileDir(const String& profile) const;
diff --git a/Swiften/Avatars/AvatarFileStorage.cpp b/Swiften/Avatars/AvatarFileStorage.cpp
new file mode 100644
index 0000000..1348018
--- /dev/null
+++ b/Swiften/Avatars/AvatarFileStorage.cpp
@@ -0,0 +1,26 @@
+#include "Swiften/Avatars/AvatarFileStorage.h"
+
+#include <iostream>
+#include <boost/filesystem/fstream.hpp>
+
+namespace Swift {
+
+AvatarFileStorage::AvatarFileStorage(const boost::filesystem::path& path) : path_(path) {
+ boost::filesystem::create_directory(path_);
+}
+
+bool AvatarFileStorage::hasAvatar(const String& hash) const {
+ return boost::filesystem::exists(getAvatarPath(hash));
+}
+
+void AvatarFileStorage::addAvatar(const String& hash, const ByteArray& avatar) {
+ boost::filesystem::ofstream file(getAvatarPath(hash), boost::filesystem::ofstream::binary|boost::filesystem::ofstream::out);
+ file.write(avatar.getData(), avatar.getSize());
+ file.close();
+}
+
+boost::filesystem::path AvatarFileStorage::getAvatarPath(const String& hash) const {
+ return path_ / hash.getUTF8String();
+}
+
+}
diff --git a/Swiften/Avatars/AvatarFileStorage.h b/Swiften/Avatars/AvatarFileStorage.h
new file mode 100644
index 0000000..1afa703
--- /dev/null
+++ b/Swiften/Avatars/AvatarFileStorage.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include <map>
+#include <boost/filesystem.hpp>
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Base/ByteArray.h"
+#include "Swiften/Avatars/AvatarStorage.h"
+
+namespace Swift {
+ class AvatarFileStorage : public AvatarStorage {
+ public:
+ AvatarFileStorage(const boost::filesystem::path& path);
+
+ virtual bool hasAvatar(const String& hash) const;
+ virtual void addAvatar(const String& hash, const ByteArray& avatar);
+
+ virtual boost::filesystem::path getAvatarPath(const String& hash) const;
+
+ private:
+ boost::filesystem::path path_;
+ };
+
+}
diff --git a/Swiften/Avatars/AvatarManager.cpp b/Swiften/Avatars/AvatarManager.cpp
new file mode 100644
index 0000000..f0b04b9
--- /dev/null
+++ b/Swiften/Avatars/AvatarManager.cpp
@@ -0,0 +1,65 @@
+#include "Swiften/Avatars/AvatarManager.h"
+
+#include <boost/bind.hpp>
+
+#include "Swiften/Client/StanzaChannel.h"
+#include "Swiften/Elements/VCardUpdate.h"
+#include "Swiften/Queries/Requests/GetVCardRequest.h"
+#include "Swiften/StringCodecs/SHA1.h"
+#include "Swiften/Avatars/AvatarStorage.h"
+
+namespace Swift {
+
+AvatarManager::AvatarManager(StanzaChannel* stanzaChannel, IQRouter* iqRouter, AvatarStorage* avatarStorage) : stanzaChannel_(stanzaChannel), iqRouter_(iqRouter), avatarStorage_(avatarStorage) {
+ stanzaChannel->onPresenceReceived.connect(boost::bind(&AvatarManager::handlePresenceReceived, this, _1));
+}
+
+void AvatarManager::handlePresenceReceived(boost::shared_ptr<Presence> presence) {
+ boost::shared_ptr<VCardUpdate> update = presence->getPayload<VCardUpdate>();
+ if (!update) {
+ return;
+ }
+ JID from = presence->getFrom().toBare();
+ String& hash = avatarHashes_[from];
+ if (hash != update->getPhotoHash()) {
+ hash = update->getPhotoHash();
+ if (!avatarStorage_->hasAvatar(hash)) {
+ boost::shared_ptr<GetVCardRequest> request(new GetVCardRequest(from, iqRouter_));
+ request->onResponse.connect(boost::bind(&AvatarManager::handleVCardReceived, this, from, _1, _2));
+ request->send();
+ }
+ else {
+ onAvatarChanged(from, hash);
+ }
+ }
+}
+
+void AvatarManager::handleVCardReceived(JID from, boost::shared_ptr<VCard> vCard, const boost::optional<Error>& error) {
+ if (error) {
+ // FIXME: What to do here?
+ return;
+ }
+ String hash = SHA1::getHexHash(vCard->getPhoto());
+ avatarStorage_->addAvatar(hash, vCard->getPhoto());
+ onAvatarChanged(from, hash);
+}
+
+String AvatarManager::getAvatarHash(const JID& jid) const {
+ std::map<JID, String>::const_iterator i = avatarHashes_.find(jid.toBare());
+ if (i != avatarHashes_.end()) {
+ return i->second;
+ }
+ else {
+ return "";
+ }
+}
+
+boost::filesystem::path AvatarManager::getAvatarPath(const JID& jid) const {
+ String hash = getAvatarHash(jid);
+ if (!hash.isEmpty()) {
+ return avatarStorage_->getAvatarPath(hash);
+ }
+ return boost::filesystem::path();
+}
+
+}
diff --git a/Swiften/Avatars/AvatarManager.h b/Swiften/Avatars/AvatarManager.h
new file mode 100644
index 0000000..0085405
--- /dev/null
+++ b/Swiften/Avatars/AvatarManager.h
@@ -0,0 +1,39 @@
+#pragma once
+
+#include <boost/filesystem.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/optional.hpp>
+#include <boost/signal.hpp>
+#include <map>
+
+#include "Swiften/JID/JID.h"
+#include "Swiften/Elements/Presence.h"
+#include "Swiften/Elements/VCard.h"
+#include "Swiften/Elements/Error.h"
+
+namespace Swift {
+ class AvatarStorage;
+ class StanzaChannel;
+ class IQRouter;
+
+ class AvatarManager {
+ public:
+ AvatarManager(StanzaChannel*, IQRouter*, AvatarStorage*);
+
+ String getAvatarHash(const JID&) const;
+ boost::filesystem::path getAvatarPath(const JID&) const;
+
+ public:
+ boost::signal<void (const JID&, const String&)> onAvatarChanged;
+
+ private:
+ void handlePresenceReceived(boost::shared_ptr<Presence>);
+ void handleVCardReceived(JID from, boost::shared_ptr<VCard>, const boost::optional<Error>&);
+
+ private:
+ StanzaChannel* stanzaChannel_;
+ IQRouter* iqRouter_;
+ AvatarStorage* avatarStorage_;
+ std::map<JID, String> avatarHashes_;
+ };
+}
diff --git a/Swiften/Avatars/AvatarStorage.cpp b/Swiften/Avatars/AvatarStorage.cpp
new file mode 100644
index 0000000..4c98314
--- /dev/null
+++ b/Swiften/Avatars/AvatarStorage.cpp
@@ -0,0 +1,8 @@
+#include "Swiften/Avatars/AvatarStorage.h"
+
+namespace Swift {
+
+AvatarStorage::~AvatarStorage() {
+}
+
+}
diff --git a/Swiften/Avatars/AvatarStorage.h b/Swiften/Avatars/AvatarStorage.h
new file mode 100644
index 0000000..b5c0f32
--- /dev/null
+++ b/Swiften/Avatars/AvatarStorage.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#include <boost/filesystem.hpp>
+
+namespace Swift {
+ class String;
+ class ByteArray;
+
+ class AvatarStorage {
+ public:
+ virtual ~AvatarStorage();
+
+ virtual bool hasAvatar(const String& hash) const = 0;
+ virtual void addAvatar(const String& hash, const ByteArray& avatar) = 0;
+ virtual boost::filesystem::path getAvatarPath(const String& hash) const = 0;
+ };
+
+}
diff --git a/Swiften/Avatars/Makefile.inc b/Swiften/Avatars/Makefile.inc
new file mode 100644
index 0000000..4887a49
--- /dev/null
+++ b/Swiften/Avatars/Makefile.inc
@@ -0,0 +1,6 @@
+SWIFTEN_SOURCES += \
+ Swiften/Avatars/AvatarManager.cpp \
+ Swiften/Avatars/AvatarStorage.cpp \
+ Swiften/Avatars/AvatarFileStorage.cpp
+
+include Swiften/Avatars/UnitTest/Makefile.inc
diff --git a/Swiften/Avatars/UnitTest/Makefile.inc b/Swiften/Avatars/UnitTest/Makefile.inc
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Swiften/Avatars/UnitTest/Makefile.inc
diff --git a/Swiften/Makefile.inc b/Swiften/Makefile.inc
index ce14110..4b09bf3 100644
--- a/Swiften/Makefile.inc
+++ b/Swiften/Makefile.inc
@@ -20,6 +20,7 @@ include Swiften/Disco/Makefile.inc
include Swiften/Presence/Makefile.inc
include Swiften/Notifier/Makefile.inc
include Swiften/History/Makefile.inc
+include Swiften/Avatars/Makefile.inc
CPPFLAGS += $(SQLITE_CPPFLAGS)
diff --git a/Swiften/Queries/Requests/GetVCardRequest.h b/Swiften/Queries/Requests/GetVCardRequest.h
new file mode 100644
index 0000000..e403096
--- /dev/null
+++ b/Swiften/Queries/Requests/GetVCardRequest.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include <cassert>
+
+#include "Swiften/Queries/GenericRequest.h"
+#include "Swiften/Elements/VCard.h"
+
+namespace Swift {
+ class GetVCardRequest : public GenericRequest<VCard> {
+ public:
+ GetVCardRequest(const JID& jid, IQRouter* router) :
+ GenericRequest<VCard>(IQ::Get, jid, boost::shared_ptr<Payload>(new VCard()), router) {
+ assert(jid.isBare());
+ }
+ };
+}
diff --git a/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp b/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp
index 9799802..f9a0789 100644
--- a/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp
+++ b/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp
@@ -15,6 +15,7 @@
#include "Swiften/Serializer/PayloadSerializers/StartSessionSerializer.h"
#include "Swiften/Serializer/PayloadSerializers/SecurityLabelSerializer.h"
#include "Swiften/Serializer/PayloadSerializers/SecurityLabelsCatalogSerializer.h"
+#include "Swiften/Serializer/PayloadSerializers/VCardSerializer.h"
namespace Swift {
@@ -33,6 +34,7 @@ FullPayloadSerializerCollection::FullPayloadSerializerCollection() {
serializers_.push_back(new StartSessionSerializer());
serializers_.push_back(new SecurityLabelSerializer());
serializers_.push_back(new SecurityLabelsCatalogSerializer());
+ serializers_.push_back(new VCardSerializer());
foreach(PayloadSerializer* serializer, serializers_) {
addSerializer(serializer);
}
diff --git a/Swiften/Serializer/PayloadSerializers/Makefile.inc b/Swiften/Serializer/PayloadSerializers/Makefile.inc
index 61f603a..893da6c 100644
--- a/Swiften/Serializer/PayloadSerializers/Makefile.inc
+++ b/Swiften/Serializer/PayloadSerializers/Makefile.inc
@@ -8,6 +8,7 @@ SWIFTEN_SOURCES += \
Swiften/Serializer/PayloadSerializers/CapsInfoSerializer.cpp \
Swiften/Serializer/PayloadSerializers/ResourceBindSerializer.cpp \
Swiften/Serializer/PayloadSerializers/SecurityLabelSerializer.cpp \
- Swiften/Serializer/PayloadSerializers/SecurityLabelsCatalogSerializer.cpp
+ Swiften/Serializer/PayloadSerializers/SecurityLabelsCatalogSerializer.cpp \
+ Swiften/Serializer/PayloadSerializers/VCardSerializer.cpp
include Swiften/Serializer/PayloadSerializers/UnitTest/Makefile.inc
diff --git a/Swiften/Serializer/PayloadSerializers/VCardSerializer.cpp b/Swiften/Serializer/PayloadSerializers/VCardSerializer.cpp
new file mode 100644
index 0000000..ce4e399
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/VCardSerializer.cpp
@@ -0,0 +1,18 @@
+#include "Swiften/Serializer/PayloadSerializers/VCardSerializer.h"
+
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Serializer/XML/XMLElement.h"
+
+namespace Swift {
+
+VCardSerializer::VCardSerializer() : GenericPayloadSerializer<VCard>() {
+}
+
+String VCardSerializer::serializePayload(boost::shared_ptr<VCard> discoInfo) const {
+ XMLElement queryElement("vCard", "vcard-temp");
+ // TODO
+ return queryElement.serialize();
+}
+
+}
diff --git a/Swiften/Serializer/PayloadSerializers/VCardSerializer.h b/Swiften/Serializer/PayloadSerializers/VCardSerializer.h
new file mode 100644
index 0000000..baf5947
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/VCardSerializer.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include "Swiften/Serializer/GenericPayloadSerializer.h"
+#include "Swiften/Elements/VCard.h"
+
+namespace Swift {
+ class VCardSerializer : public GenericPayloadSerializer<VCard> {
+ public:
+ VCardSerializer();
+
+ virtual String serializePayload(boost::shared_ptr<VCard>) const;
+ };
+}