From e8360f0dd62ea651e94f681499faef58747f2ece Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Remko=20Tron=C3=A7on?= <git@el-tramo.be>
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<SecurityLabel>(), "");
 }
 
-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> message);
 			void postSendMessage(const String &body);
 			void preHandleIncomingMessage(boost::shared_ptr<Message> 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 <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"
 #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> 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 <vector>
 #include <boost/shared_ptr.hpp>
 #include <boost/signals.hpp>
+#include <boost/filesystem.hpp>
 
 #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<Presence> newPresence, boost::shared_ptr<Presence> previousPresence);
 			virtual bool isIncomingMessageFromMe(boost::shared_ptr<Message>) = 0;
 			virtual void preHandleIncomingMessage(boost::shared_ptr<Message>) {};
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> 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> message);
 			bool isIncomingMessageFromMe(boost::shared_ptr<Message> 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> 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<DiscoInfo>
 	}
 }
 
+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 <boost/signals.hpp>
+#include <boost/shared_ptr.hpp>
+#include <vector>
 
 #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 <boost/signals.hpp>
-#include <boost/shared_ptr.hpp>
-
-#include <vector>
+#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> 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<GetVCardRequest> 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<GetVCardRequest> 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> vCard, const boost::optional<Error>& error) {
+void AvatarManager::handleVCardReceived(const JID& from, const String& promisedHash, boost::shared_ptr<VCard> vCard, const boost::optional<Error>& 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<JID, String>::const_iterator i = avatarHashes_.find(jid.toBare());
+	std::map<JID, String>::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<Presence>);
-			void handleVCardReceived(JID from, boost::shared_ptr<VCard>, const boost::optional<Error>&);
+			void handleVCardReceived(const JID& from, const String& hash, boost::shared_ptr<VCard>, const boost::optional<Error>&);
+			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<JID, String> 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 <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#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<AvatarManager> testling = createManager();
+		}
+
+		void testUpdate_UpdateNewHash() {
+			std::auto_ptr<AvatarManager> testling = createManager();
+		}
+
+		void testUpdate_UpdateNewHashAlreadyHaveAvatar() {
+			std::auto_ptr<AvatarManager> testling = createManager();
+		}
+
+		void testUpdate_UpdateNewHashFromMUC() {
+			std::auto_ptr<AvatarManager> testling = createManager();
+		}
+
+		void testUpdate_UpdateSameHash() {
+			std::auto_ptr<AvatarManager> testling = createManager();
+		}
+
+		void testUpdate_UpdateNewHashSameThanOtherUser() {
+			std::auto_ptr<AvatarManager> testling = createManager();
+		}
+
+		void testReceiveVCard() {
+			std::auto_ptr<AvatarManager> testling = createManager();
+		}
+
+		void testGetAvatarPath() {
+			std::auto_ptr<AvatarManager> testling = createManager();
+		}
+
+		void testGetAvatarPathFromMUC() {
+			std::auto_ptr<AvatarManager> testling = createManager();
+		}
+
+	private:
+		std::auto_ptr<AvatarManager> createManager() {
+			return std::auto_ptr<AvatarManager>(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<JID> 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<String, ByteArray> 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 <vector>
+
+#include "Swiften/Client/StanzaChannel.h"
+
+namespace Swift {
+	class DummyStanzaChannel : public StanzaChannel {
+		public:
+			DummyStanzaChannel() {}
+
+			virtual void sendStanza(boost::shared_ptr<Stanza> stanza) {
+				sentStanzas_.push_back(stanza);
+			}
+
+      virtual void sendIQ(boost::shared_ptr<IQ> iq) {
+        sentStanzas_.push_back(iq);
+      }
+
+      virtual void sendMessage(boost::shared_ptr<Message> message) {
+        sentStanzas_.push_back(message);
+      }
+
+      virtual void sendPresence(boost::shared_ptr<Presence> presence) {
+        sentStanzas_.push_back(presence);
+      }
+
+			virtual String getNewIQID() {
+				return "test-id";
+			}
+
+			std::vector<boost::shared_ptr<Stanza> > 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 <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());
+			GetVCardRequest(const JID& jid, IQRouter* router) : GenericRequest<VCard>(IQ::Get, jid, boost::shared_ptr<Payload>(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<startYear>\d\d\d\d)(-(?P<endYear>\d\d\d\d))? (?P<author>.*)", 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 -- <filename>
+    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