From f5c2750f56c78d115bb9e8a7c5d50316da98b6d5 Mon Sep 17 00:00:00 2001
From: Kevin Smith <git@kismith.co.uk>
Date: Sun, 21 Mar 2010 22:33:09 +0000
Subject: Lots of plumbing for event view.

This isn't ready yet, but clicking on a message in the event view will now cause the chat to pop up, and the plumbing is there for doing something with subscription requests - I just don't, yet.

diff --git a/Swift/Controllers/Chat/ChatsManager.cpp b/Swift/Controllers/Chat/ChatsManager.cpp
index 5009588..250f02d 100644
--- a/Swift/Controllers/Chat/ChatsManager.cpp
+++ b/Swift/Controllers/Chat/ChatsManager.cpp
@@ -7,6 +7,7 @@
 #include "Swift/Controllers/Chat/ChatController.h"
 #include "Swift/Controllers/EventController.h"
 #include "Swift/Controllers/Chat/MUCController.h"
+#include "Swift/Controllers/UIEvents/RequestChatUIEvent.h"
 #include "Swiften/Presence/PresenceSender.h"
 #include "Swiften/Elements/ChatState.h"
 
@@ -15,7 +16,7 @@ namespace Swift {
 typedef std::pair<JID, ChatController*> JIDChatControllerPair;
 typedef std::pair<JID, MUCController*> JIDMUCControllerPair;
 
-ChatsManager::ChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRouter, EventController* eventController, ChatWindowFactory* chatWindowFactory, TreeWidgetFactory* treeWidgetFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, boost::shared_ptr<DiscoInfo> serverDiscoInfo, PresenceSender* presenceSender) : jid_(jid) {
+ChatsManager::ChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRouter, EventController* eventController, ChatWindowFactory* chatWindowFactory, TreeWidgetFactory* treeWidgetFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, boost::shared_ptr<DiscoInfo> serverDiscoInfo, PresenceSender* presenceSender, UIEventStream* uiEventStream) : jid_(jid) {
 	eventController_ = eventController;
 	stanzaChannel_ = stanzaChannel;
 	iqRouter_ = iqRouter;
@@ -26,7 +27,9 @@ ChatsManager::ChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRo
 	avatarManager_ = NULL;
 	serverDiscoInfo_ = serverDiscoInfo;
 	presenceSender_ = presenceSender;
+	uiEventStream_ = uiEventStream;
 	presenceOracle_->onPresenceChange.connect(boost::bind(&ChatsManager::handlePresenceChange, this, _1, _2));
+	uiEventStream_->onUIEvent.connect(boost::bind(&ChatsManager::handleUIEvent, this, _1));
 }
 
 ChatsManager::~ChatsManager() {
@@ -39,6 +42,13 @@ ChatsManager::~ChatsManager() {
 
 }
 
+void ChatsManager::handleUIEvent(boost::shared_ptr<UIEvent> event) {
+	boost::shared_ptr<RequestChatUIEvent> chatEvent = boost::dynamic_pointer_cast<RequestChatUIEvent>(event);
+	if (chatEvent) {
+		handleChatRequest(chatEvent->getContact());
+	}
+}
+
 /**
  * If a resource goes offline, release bound chatdialog to that resource.
  */
diff --git a/Swift/Controllers/Chat/ChatsManager.h b/Swift/Controllers/Chat/ChatsManager.h
index dd80d95..04d9330 100644
--- a/Swift/Controllers/Chat/ChatsManager.h
+++ b/Swift/Controllers/Chat/ChatsManager.h
@@ -10,6 +10,7 @@
 #include "Swiften/Elements/Presence.h"
 #include "Swiften/JID/JID.h"
 #include "Swiften/MUC/MUCRegistry.h"
+#include "Swift/Controllers/UIEvents/UIEventStream.h"
 
 namespace Swift {
 	class EventController;
@@ -23,9 +24,10 @@ namespace Swift {
 	class StanzaChannel;
 	class IQRouter;
 	class PresenceSender;
+
 	class ChatsManager : public MUCRegistry {
 		public:
-			ChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRouter, EventController* eventController, ChatWindowFactory* chatWindowFactory, TreeWidgetFactory* treeWidgetFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, boost::shared_ptr<DiscoInfo> serverDiscoInfo, PresenceSender* presenceSender);
+			ChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRouter, EventController* eventController, ChatWindowFactory* chatWindowFactory, TreeWidgetFactory* treeWidgetFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, boost::shared_ptr<DiscoInfo> serverDiscoInfo, PresenceSender* presenceSender, UIEventStream* uiEventStream);
 			~ChatsManager();
 			void setAvatarManager(AvatarManager* avatarManager);
 			void setEnabled(bool enabled);
@@ -36,6 +38,7 @@ namespace Swift {
 		private:
 			void rebindControllerJID(const JID& from, const JID& to);
 			void handlePresenceChange(boost::shared_ptr<Presence> newPresence, boost::shared_ptr<Presence> lastPresence);
+			void handleUIEvent(boost::shared_ptr<UIEvent> event);
 			ChatController* getChatController(const JID &contact);
 			virtual bool isMUC(const JID& muc) const;
 
@@ -51,6 +54,7 @@ namespace Swift {
 			PresenceOracle* presenceOracle_;
 			AvatarManager* avatarManager_;
 			PresenceSender* presenceSender_;
+			UIEventStream* uiEventStream_;
 			boost::shared_ptr<DiscoInfo> serverDiscoInfo_;
 	};
 }
diff --git a/Swift/Controllers/EventController.cpp b/Swift/Controllers/EventController.cpp
index 3849aa7..71ccaeb 100644
--- a/Swift/Controllers/EventController.cpp
+++ b/Swift/Controllers/EventController.cpp
@@ -3,23 +3,26 @@
 #include <boost/bind.hpp>
 #include <algorithm>
 
+#include "Swiften/Events/MessageEvent.h"
+#include "Swiften/Events/SubscriptionRequestEvent.h"
 
 namespace Swift {
 
 EventController::EventController() {
 }
 
-void EventController::handleIncomingEvent(boost::shared_ptr<Event> sourceEvent) {
+void EventController::handleIncomingEvent(boost::shared_ptr<StanzaEvent> sourceEvent) {
 	boost::shared_ptr<MessageEvent> messageEvent = boost::dynamic_pointer_cast<MessageEvent>(sourceEvent);
-	if (messageEvent && messageEvent->isReadable()) {
+	boost::shared_ptr<SubscriptionRequestEvent> subscriptionEvent = boost::dynamic_pointer_cast<SubscriptionRequestEvent>(sourceEvent);
+	if ((messageEvent && messageEvent->isReadable()) || subscriptionEvent) {
 		events_.push_back(sourceEvent);
-		messageEvent->onConclusion.connect(boost::bind(&EventController::handleEventConcluded, this, messageEvent));
+		sourceEvent->onConclusion.connect(boost::bind(&EventController::handleEventConcluded, this, sourceEvent));
 		onEventQueueLengthChange(events_.size());
 		onEventQueueEventAdded(sourceEvent);
 	}
 }
 
-void EventController::handleEventConcluded(boost::shared_ptr<Event> event) {
+void EventController::handleEventConcluded(boost::shared_ptr<StanzaEvent> event) {
 	events_.erase(std::remove(events_.begin(), events_.end(), event), events_.end());
 	onEventQueueLengthChange(events_.size());
 }
diff --git a/Swift/Controllers/EventController.h b/Swift/Controllers/EventController.h
index 482be58..1f4051c 100644
--- a/Swift/Controllers/EventController.h
+++ b/Swift/Controllers/EventController.h
@@ -6,20 +6,20 @@
 #include <boost/shared_ptr.hpp>
 #include <vector>
 
-#include "Swiften/Events/Event.h"
+#include "Swiften/Events/StanzaEvent.h"
 #include "Swiften/Events/MessageEvent.h"
 
 namespace Swift {
 	class EventController {
 		public:
 			EventController();
-			void handleIncomingEvent(boost::shared_ptr<Event> sourceEvent);
+			void handleIncomingEvent(boost::shared_ptr<StanzaEvent> sourceEvent);
 			boost::signal<void (int)> onEventQueueLengthChange;
-			boost::signal<void (boost::shared_ptr<Event>)> onEventQueueEventAdded;
+			boost::signal<void (boost::shared_ptr<StanzaEvent>)> onEventQueueEventAdded;
 
 		private:
-			void handleEventConcluded(boost::shared_ptr<Event> event);
-			std::vector<boost::shared_ptr<Event> > events_;
+			void handleEventConcluded(boost::shared_ptr<StanzaEvent> event);
+			std::vector<boost::shared_ptr<StanzaEvent> > events_;
 	};
 }
 #endif
diff --git a/Swift/Controllers/EventWindowController.cpp b/Swift/Controllers/EventWindowController.cpp
index a6611fc..0f91db7 100644
--- a/Swift/Controllers/EventWindowController.cpp
+++ b/Swift/Controllers/EventWindowController.cpp
@@ -15,12 +15,13 @@ EventWindowController::~EventWindowController() {
 	delete window_;
 }
 
-void EventWindowController::handleEventQueueEventAdded(boost::shared_ptr<Event> event) {
+void EventWindowController::handleEventQueueEventAdded(boost::shared_ptr<StanzaEvent> event) {
 	event->onConclusion.connect(boost::bind(&EventWindowController::handleEventConcluded, this, event));
+	fprintf(stderr, "Adding to ewc\n");
 	window_->addEvent(event, true);
 }
 
-void EventWindowController::handleEventConcluded(boost::shared_ptr<Event> event) {
+void EventWindowController::handleEventConcluded(boost::shared_ptr<StanzaEvent> event) {
 	window_->removeEvent(event);
 	window_->addEvent(event, false);
 }
diff --git a/Swift/Controllers/EventWindowController.h b/Swift/Controllers/EventWindowController.h
index c612265..7c58be1 100644
--- a/Swift/Controllers/EventWindowController.h
+++ b/Swift/Controllers/EventWindowController.h
@@ -12,8 +12,8 @@ namespace Swift {
 			EventWindowController(EventController* eventController, EventWindowFactory* windowFactory);
 			~EventWindowController();
 		private:
-			void handleEventQueueEventAdded(boost::shared_ptr<Event> event);
-			void handleEventConcluded(boost::shared_ptr<Event> event);
+			void handleEventQueueEventAdded(boost::shared_ptr<StanzaEvent> event);
+			void handleEventConcluded(boost::shared_ptr<StanzaEvent> event);
 
 			EventController* eventController_;
 			EventWindowFactory* windowFactory_;
diff --git a/Swift/Controllers/MainController.cpp b/Swift/Controllers/MainController.cpp
index d013f5f..310d28f 100644
--- a/Swift/Controllers/MainController.cpp
+++ b/Swift/Controllers/MainController.cpp
@@ -134,10 +134,8 @@ void MainController::handleConnected() {
 		presenceOracle_ = new PresenceOracle(client_);
 		nickResolver_ = new NickResolver(xmppRoster_);		
 		lastSentPresence_ = boost::shared_ptr<Presence>();
-
-		client_->onPresenceReceived.connect(boost::bind(&MainController::handleIncomingPresence, this, _1));
-
-		chatsManager_ = new ChatsManager(jid_, client_, client_, eventController_, chatWindowFactory_, treeWidgetFactory_, nickResolver_, presenceOracle_, serverDiscoInfo_, presenceSender_);
+		
+		chatsManager_ = new ChatsManager(jid_, client_, client_, eventController_, chatWindowFactory_, treeWidgetFactory_, nickResolver_, presenceOracle_, serverDiscoInfo_, presenceSender_, uiEventStream_);
 		client_->onMessageReceived.connect(boost::bind(&ChatsManager::handleIncomingMessage, chatsManager_, _1));
 
 		avatarManager_ = new AvatarManager(client_, client_, avatarStorage_, chatsManager_);
@@ -145,7 +143,7 @@ void MainController::handleConnected() {
 		chatsManager_->setAvatarManager(avatarManager_);
 
 
-		rosterController_ = new RosterController(jid_, xmppRoster_, avatarManager_, mainWindowFactory_, treeWidgetFactory_, nickResolver_);
+		rosterController_ = new RosterController(jid_, xmppRoster_, avatarManager_, mainWindowFactory_, treeWidgetFactory_, nickResolver_, presenceOracle_, eventController_, uiEventStream_);
 		rosterController_->onChangeStatusRequest.connect(boost::bind(&MainController::handleChangeStatusRequest, this, _1, _2));
 		rosterController_->onSignOutRequest.connect(boost::bind(&MainController::signOut, this));
 				rosterController_->onStartChatRequest.connect(boost::bind(&ChatsManager::handleChatRequest, chatsManager_, _1));
@@ -254,11 +252,6 @@ void MainController::handleInputIdleChanged(bool idle) {
 	}
 }
 
-void MainController::handleIncomingPresence(boost::shared_ptr<Presence> presence) {
-	//FIXME: subscribe, subscribed
-	rosterController_->handleIncomingPresence(presence);
-}
-
 void MainController::handleLoginRequest(const String &username, const String &password, const String& certificateFile, bool remember) {
 	loginWindow_->setMessage("");
 	ProfileSettingsProvider* profileSettings = new ProfileSettingsProvider(username, settings_);
diff --git a/Swift/Controllers/MainController.h b/Swift/Controllers/MainController.h
index 63be284..387fcb6 100644
--- a/Swift/Controllers/MainController.h
+++ b/Swift/Controllers/MainController.h
@@ -63,7 +63,6 @@ namespace Swift {
 			void handleConnected();
 			void handleLoginRequest(const String& username, const String& password, const String& certificateFile, bool remember);
 			void handleCancelLoginRequest();
-			void handleIncomingPresence(boost::shared_ptr<Presence> presence);
 			void handleChangeStatusRequest(StatusShow::Type show, const String &statusText);
 			void handleError(const ClientError& error);
 			void handleServerDiscoInfoResponse(boost::shared_ptr<DiscoInfo>, const boost::optional<ErrorPayload>&);
diff --git a/Swift/Controllers/MainWindowFactory.h b/Swift/Controllers/MainWindowFactory.h
index cf5a061..ce612b3 100644
--- a/Swift/Controllers/MainWindowFactory.h
+++ b/Swift/Controllers/MainWindowFactory.h
@@ -2,6 +2,7 @@
 #define SWIFTEN_MainWindowFactory_H
 
 #include "Swiften/JID/JID.h"
+#include "Swift/Controllers/UIEvents/UIEventStream.h"
 
 namespace Swift {
 	class MainWindow;
@@ -12,7 +13,7 @@ namespace Swift {
 			/**
 			 * Transfers ownership of result.
 			 */
-			virtual MainWindow* createMainWindow() = 0;
+			virtual MainWindow* createMainWindow(UIEventStream* eventStream) = 0;
 
 	};
 }
diff --git a/Swift/Controllers/RosterController.cpp b/Swift/Controllers/RosterController.cpp
index df3453d..4c55ea8 100644
--- a/Swift/Controllers/RosterController.cpp
+++ b/Swift/Controllers/RosterController.cpp
@@ -7,7 +7,9 @@
 #include "Swift/Controllers/MainWindowFactory.h"
 #include "Swift/Controllers/NickResolver.h"
 #include "Swiften/Queries/Requests/GetRosterRequest.h"
-#include "Swiften/EventLoop/MainEventLoop.h"
+#include "Swiften/Events/SubscriptionRequestEvent.h"
+#include "Swiften/Presence/PresenceOracle.h"
+#include "Swift/Controllers/EventController.h"
 #include "Swiften/Roster/Roster.h"
 #include "Swiften/Roster/SetPresence.h"
 #include "Swiften/Roster/AppearOffline.h"
@@ -23,9 +25,10 @@ namespace Swift {
 /**
  * The controller does not gain ownership of these parameters.
  */
-RosterController::RosterController(const JID& jid, boost::shared_ptr<XMPPRoster> xmppRoster, AvatarManager* avatarManager, MainWindowFactory* mainWindowFactory, TreeWidgetFactory* treeWidgetFactory, NickResolver* nickResolver)
- : myJID_(jid), xmppRoster_(xmppRoster), mainWindowFactory_(mainWindowFactory), treeWidgetFactory_(treeWidgetFactory), mainWindow_(mainWindowFactory_->createMainWindow()), roster_(new Roster(mainWindow_->getTreeWidget(), treeWidgetFactory_)), offlineFilter_(new OfflineRosterFilter()) {
-	
+RosterController::RosterController(const JID& jid, boost::shared_ptr<XMPPRoster> xmppRoster, AvatarManager* avatarManager, MainWindowFactory* mainWindowFactory, TreeWidgetFactory* treeWidgetFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, EventController* eventController, UIEventStream* uiEventStream)
+ : myJID_(jid), xmppRoster_(xmppRoster), mainWindowFactory_(mainWindowFactory), treeWidgetFactory_(treeWidgetFactory), mainWindow_(mainWindowFactory_->createMainWindow(uiEventStream)), roster_(new Roster(mainWindow_->getTreeWidget(), treeWidgetFactory_)), offlineFilter_(new OfflineRosterFilter()) {
+	presenceOracle_ = presenceOracle;
+	eventController_ = eventController;
 	roster_->addFilter(offlineFilter_);
 	
 	mainWindow_->onJoinMUCRequest.connect(boost::bind(&RosterController::handleJoinMUCRequest, this, _1, _2));
@@ -36,6 +39,8 @@ RosterController::RosterController(const JID& jid, boost::shared_ptr<XMPPRoster>
 	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));
+	presenceOracle_->onPresenceSubscriptionRequest.connect(boost::bind(&RosterController::handleSubscriptionRequest, this, _1, _2));
+	presenceOracle_->onPresenceChange.connect(boost::bind(&RosterController::handleIncomingPresence, this, _1, _2));
 	avatarManager_ = NULL;
 	setAvatarManager(avatarManager);
 	setNickResolver(nickResolver);
@@ -132,8 +137,24 @@ void RosterController::handleOnJIDUpdated(const JID& jid, const String& oldName,
 	
 }
 
-void RosterController::handleIncomingPresence(boost::shared_ptr<Presence> presence) {
-	roster_->applyOnItems(SetPresence(presence));
+void RosterController::handleIncomingPresence(boost::shared_ptr<Presence> newPresence, boost::shared_ptr<Presence> /*oldPresence*/) {
+	roster_->applyOnItems(SetPresence(newPresence));
+}
+
+void RosterController::handleSubscriptionRequest(const JID& jid, const String& message) {
+	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<StanzaEvent> event(eventPointer);
+	eventController_->handleIncomingEvent(event);
+}
+
+void RosterController::handleSubscriptionRequestAccepted(SubscriptionRequestEvent* event) {
+		//FIXME: do something
+}
+
+void RosterController::handleSubscriptionRequestDeclined(SubscriptionRequestEvent* event) {
+	//FIXME: do something
 }
 
 void RosterController::handleAvatarChanged(const JID& jid, const String&) {
diff --git a/Swift/Controllers/RosterController.h b/Swift/Controllers/RosterController.h
index f3d96e1..cef0e48 100644
--- a/Swift/Controllers/RosterController.h
+++ b/Swift/Controllers/RosterController.h
@@ -19,10 +19,14 @@ namespace Swift {
 	class TreeWidgetFactory;
 	class OfflineRosterFilter;
 	class NickResolver;
+	class PresenceOracle;
+	class EventController;
+	class SubscriptionRequestEvent;
+	class UIEventStream;
 
 	class RosterController {
 		public:
-			RosterController(const JID& jid, boost::shared_ptr<XMPPRoster> xmppRoster, AvatarManager* avatarManager, MainWindowFactory* mainWindowFactory, TreeWidgetFactory* treeWidgetFactory, NickResolver* nickResolver);
+			RosterController(const JID& jid, boost::shared_ptr<XMPPRoster> xmppRoster, AvatarManager* avatarManager, MainWindowFactory* mainWindowFactory, TreeWidgetFactory* treeWidgetFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, EventController* eventController, UIEventStream* uiEventStream);
 			~RosterController();
 			void showRosterWindow();
 			MainWindow* getWindow() {return mainWindow_;};
@@ -32,7 +36,6 @@ namespace Swift {
 			boost::signal<void (const JID&, const String&)> onJoinMUCRequest;
 			boost::signal<void (StatusShow::Type, const String&)> onChangeStatusRequest;
 			boost::signal<void ()> onSignOutRequest;
-			void handleIncomingPresence(boost::shared_ptr<Presence> presence);
 			void handleAvatarChanged(const JID& jid, const String& hash);
 			void setEnabled(bool enabled);
 		private:
@@ -44,6 +47,10 @@ namespace Swift {
 			void handleUserAction(boost::shared_ptr<UserRosterAction> action);
 			void handleChangeStatusRequest(StatusShow::Type show, const String &statusText);
 			void handleShowOfflineToggled(bool state);
+			void handleIncomingPresence(boost::shared_ptr<Presence> newPresence, boost::shared_ptr<Presence> oldPresence);
+			void handleSubscriptionRequest(const JID& jid, const String& message);
+			void handleSubscriptionRequestAccepted(SubscriptionRequestEvent* event);
+			void handleSubscriptionRequestDeclined(SubscriptionRequestEvent* event);
 			JID myJID_;
 			boost::shared_ptr<XMPPRoster> xmppRoster_;
 			MainWindowFactory* mainWindowFactory_;
@@ -53,6 +60,8 @@ namespace Swift {
 			OfflineRosterFilter* offlineFilter_;
 			AvatarManager* avatarManager_;
 			NickResolver* nickResolver_;
+			PresenceOracle* presenceOracle_;
+			EventController* eventController_;
 	};
 }
 #endif
diff --git a/Swift/Controllers/SoundEventController.cpp b/Swift/Controllers/SoundEventController.cpp
index 392b4e9..456de44 100644
--- a/Swift/Controllers/SoundEventController.cpp
+++ b/Swift/Controllers/SoundEventController.cpp
@@ -14,7 +14,7 @@ SoundEventController::SoundEventController(EventController* eventController, Sou
 	playSounds_ = playSounds;
 }
 
-void SoundEventController::handleEventQueueEventAdded(boost::shared_ptr<Event>) {
+void SoundEventController::handleEventQueueEventAdded(boost::shared_ptr<StanzaEvent>) {
 	if (playSounds_) soundPlayer_->playSound(SoundPlayer::MessageReceived);
 }
 
diff --git a/Swift/Controllers/SoundEventController.h b/Swift/Controllers/SoundEventController.h
index 777e55f..07ac667 100644
--- a/Swift/Controllers/SoundEventController.h
+++ b/Swift/Controllers/SoundEventController.h
@@ -2,7 +2,7 @@
 
 #include <boost/shared_ptr.hpp>
 
-#include "Swiften/Events/Event.h"
+#include "Swiften/Events/StanzaEvent.h"
 
 namespace Swift {
 	class EventController;
@@ -12,7 +12,7 @@ namespace Swift {
 			SoundEventController(EventController* eventController, SoundPlayer* soundPlayer, bool playSounds);
 			void setPlaySounds(bool playSounds);
 		private:
-			void handleEventQueueEventAdded(boost::shared_ptr<Event> event);
+			void handleEventQueueEventAdded(boost::shared_ptr<StanzaEvent> event);
 			EventController* eventController_;
 			SoundPlayer* soundPlayer_;
 			bool playSounds_;
diff --git a/Swift/Controllers/UIInterfaces/EventWindow.h b/Swift/Controllers/UIInterfaces/EventWindow.h
index 95bd7dd..b85840b 100644
--- a/Swift/Controllers/UIInterfaces/EventWindow.h
+++ b/Swift/Controllers/UIInterfaces/EventWindow.h
@@ -1,13 +1,13 @@
 #pragma once
 
 #include "boost/shared_ptr.hpp"
-#include "Swiften/Events/Event.h"
+#include "Swiften/Events/StanzaEvent.h"
 
 namespace Swift {
 	class EventWindow {
 		public:
 			virtual ~EventWindow() {};	
-			virtual void addEvent(boost::shared_ptr<Event> event, bool active) = 0;
-			virtual void removeEvent(boost::shared_ptr<Event> event) = 0;
+			virtual void addEvent(boost::shared_ptr<StanzaEvent> event, bool active) = 0;
+			virtual void removeEvent(boost::shared_ptr<StanzaEvent> event) = 0;
 	};
 }
diff --git a/Swift/Controllers/UnitTest/RosterControllerTest.cpp b/Swift/Controllers/UnitTest/RosterControllerTest.cpp
index 0296cdc..67884d0 100644
--- a/Swift/Controllers/UnitTest/RosterControllerTest.cpp
+++ b/Swift/Controllers/UnitTest/RosterControllerTest.cpp
@@ -8,8 +8,11 @@
 // #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/XMPPRoster.h"
+#include "Swift/Controllers/EventController.h"
+#include "Swiften/Presence/PresenceOracle.h"
 #include "Swift/Controllers/NickResolver.h"
 
 using namespace Swift;
@@ -30,10 +33,13 @@ class RosterControllerTest : public CppUnit::TestFixture
 			treeWidgetFactory_ = new MockTreeWidgetFactory();
 			mainWindowFactory_ = new MockMainWindowFactory(treeWidgetFactory_);
 			nickResolver_ = new NickResolver(xmppRoster_);
-			rosterController_ = new RosterController(jid_, xmppRoster_, avatarManager_, mainWindowFactory_, treeWidgetFactory_, nickResolver_);
-
 			channel_ = new DummyIQChannel();
 			router_ = new IQRouter(channel_);
+			stanzaChannel_ = new DummyStanzaChannel();
+			presenceOracle_ = new PresenceOracle(stanzaChannel_);
+			eventController_ = new EventController();
+			rosterController_ = new RosterController(jid_, xmppRoster_, avatarManager_, mainWindowFactory_, treeWidgetFactory_, nickResolver_, presenceOracle_, eventController_);
+
 
 		};
 
@@ -45,6 +51,9 @@ class RosterControllerTest : public CppUnit::TestFixture
 			delete avatarManager_;
 			delete channel_;
 			delete router_;
+			delete eventController_;
+			delete presenceOracle_;
+			delete stanzaChannel_;
 		};
 
 		void testAdd() {
@@ -66,6 +75,9 @@ class RosterControllerTest : public CppUnit::TestFixture
 		NickResolver* nickResolver_;
 		RosterController* rosterController_;
 		DummyIQChannel* channel_;
+		DummyStanzaChannel* stanzaChannel_;	
 		IQRouter* router_;
+		PresenceOracle* presenceOracle_;
+		EventController* eventController_;
 
 };
diff --git a/Swift/QtUI/EventViewer/EventModel.cpp b/Swift/QtUI/EventViewer/EventModel.cpp
index f75fe74..902004b 100644
--- a/Swift/QtUI/EventViewer/EventModel.cpp
+++ b/Swift/QtUI/EventViewer/EventModel.cpp
@@ -16,14 +16,16 @@ EventModel::~EventModel() {
 	}
 }
 
+QtEvent* EventModel::getItem(int row) const {
+	return row < activeEvents_.size() ? activeEvents_[row] : inactiveEvents_[row - activeEvents_.size()];
+}
+
 QVariant EventModel::data(const QModelIndex& index, int role) const {
 	if (!index.isValid()) {
 		return QVariant();
 	}
-	int row = index.row();
-	QtEvent* item = index.row() < activeEvents_.size() ? activeEvents_[row] : inactiveEvents_[row - activeEvents_.size()];
+	QtEvent* item = getItem(index.row());
 	QVariant result = item ? item->data(role) : QVariant();
-	qDebug() << "Asked for data of " << index << ", " << role << " returning " << result;
 	return result;
 }
 
@@ -33,8 +35,7 @@ int EventModel::rowCount(const QModelIndex& parent) const {
 	return count;
 }
 
-void EventModel::addEvent(boost::shared_ptr<Event> event, bool active) {
-	qDebug() << " Adding Event";
+void EventModel::addEvent(boost::shared_ptr<StanzaEvent> event, bool active) {
 	if (active) {
 		activeEvents_.push_front(new QtEvent(event, active));
 		emit dataChanged(createIndex(0, 0), createIndex(1, 0));
@@ -48,7 +49,7 @@ void EventModel::addEvent(boost::shared_ptr<Event> event, bool active) {
 	emit layoutChanged();
 }
 
-void EventModel::removeEvent(boost::shared_ptr<Event> event) {
+void EventModel::removeEvent(boost::shared_ptr<StanzaEvent> event) {
 	for (int i = inactiveEvents_.size() - 1; i >= 0; i--) {
 		if (event == inactiveEvents_[i]->getEvent()) {
 			inactiveEvents_.removeAt(i);
diff --git a/Swift/QtUI/EventViewer/EventModel.h b/Swift/QtUI/EventViewer/EventModel.h
index bc54cf4..7882c6b 100644
--- a/Swift/QtUI/EventViewer/EventModel.h
+++ b/Swift/QtUI/EventViewer/EventModel.h
@@ -5,7 +5,7 @@
 #include <QAbstractListModel>
 #include <QList>
 
-#include "Swiften/Events/Event.h"
+#include "Swiften/Events/StanzaEvent.h"
 
 #include "Swift/QtUI/EventViewer/QtEvent.h"
 
@@ -15,10 +15,11 @@ Q_OBJECT
 public:
 	EventModel();
 	~EventModel();
-	void addEvent(boost::shared_ptr<Event> event, bool active);
-	void removeEvent(boost::shared_ptr<Event> event);
+	void addEvent(boost::shared_ptr<StanzaEvent> event, bool active);
+	void removeEvent(boost::shared_ptr<StanzaEvent> event);
 	QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
 	int rowCount(const QModelIndex& parent = QModelIndex()) const;
+	QtEvent* getItem(int row) const;
 private:
 	QList<QtEvent*> activeEvents_;
 	QList<QtEvent*> inactiveEvents_;
diff --git a/Swift/QtUI/EventViewer/QtEvent.cpp b/Swift/QtUI/EventViewer/QtEvent.cpp
index 2dc1fb0..3aae213 100644
--- a/Swift/QtUI/EventViewer/QtEvent.cpp
+++ b/Swift/QtUI/EventViewer/QtEvent.cpp
@@ -1,22 +1,23 @@
 #include "Swift/QtUI/EventViewer/QtEvent.h"
 
 #include "Swiften/Events/MessageEvent.h"
+#include "Swiften/Events/SubscriptionRequestEvent.h"
 
 #include "Swift/QtUI/QtSwiftUtil.h"
 
 namespace Swift {
 
-QtEvent::QtEvent(boost::shared_ptr<Event> event, bool active) : event_(event) {
+QtEvent::QtEvent(boost::shared_ptr<StanzaEvent> event, bool active) : event_(event) {
 	active_ = active;
 }
 
 QVariant QtEvent::data(int role) {
  	switch (role) {
+		case Qt::ToolTipRole:
 		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 Qt::ToolTipRole: return isContact() ? toolTipString() : QVariant();
-	 	case StatusTextRole: return statusText_;
+			/*case StatusTextRole: return statusText_;
 		case AvatarRole: return avatar_;
 		case PresenceIconRole: return getPresenceIcon();*/
 	 	default: return QVariant();
@@ -25,7 +26,16 @@ QVariant QtEvent::data(int role) {
 
 QString QtEvent::text() {
 	boost::shared_ptr<MessageEvent> messageEvent = boost::dynamic_pointer_cast<MessageEvent>(event_);
-	return messageEvent ? P2QSTRING(messageEvent->getStanza()->getBody()) :  "Bob: ";
+	if (messageEvent) {
+		return P2QSTRING(messageEvent->getStanza()->getBody());
+	}
+	boost::shared_ptr<SubscriptionRequestEvent> subscriptionRequestEvent = boost::dynamic_pointer_cast<SubscriptionRequestEvent>(event_);
+	if (subscriptionRequestEvent) {
+		String reason = subscriptionRequestEvent->getReason();
+		String message = subscriptionRequestEvent->getJID().toBare().toString() + " would like to add you to their roster" + (reason.isEmpty() ? "." : ", saying '" + reason + "'.");
+		return P2QSTRING(message);
+	}
+	return "";
 }
 
 }
diff --git a/Swift/QtUI/EventViewer/QtEvent.h b/Swift/QtUI/EventViewer/QtEvent.h
index 7083c6e..6dec858 100644
--- a/Swift/QtUI/EventViewer/QtEvent.h
+++ b/Swift/QtUI/EventViewer/QtEvent.h
@@ -4,17 +4,17 @@
 
 #include <QVariant>
 
-#include "Swiften/Events/Event.h"
+#include "Swiften/Events/StanzaEvent.h"
 
 namespace Swift {
 	class QtEvent {
 		public:
-			QtEvent(boost::shared_ptr<Event> event, bool active);
+			QtEvent(boost::shared_ptr<StanzaEvent> event, bool active);
 			QVariant data(int role);
-			boost::shared_ptr<Event> getEvent() { return event_; };
+			boost::shared_ptr<StanzaEvent> getEvent() { return event_; };
 		private:
 			QString text();
-			boost::shared_ptr<Event> event_;
+			boost::shared_ptr<StanzaEvent> event_;
 			bool active_;
 	};
 }
diff --git a/Swift/QtUI/EventViewer/QtEventWindow.cpp b/Swift/QtUI/EventViewer/QtEventWindow.cpp
index fda957d..91eb2c1 100644
--- a/Swift/QtUI/EventViewer/QtEventWindow.cpp
+++ b/Swift/QtUI/EventViewer/QtEventWindow.cpp
@@ -1,10 +1,17 @@
 #include "Swift/QtUI/EventViewer/QtEventWindow.h"
 
+#include <qdebug>
+
+#include "Swiften/Events/MessageEvent.h"
+#include "Swiften/Events/SubscriptionRequestEvent.h"
+#include "Swift/Controllers/UIEvents/RequestChatUIEvent.h"
+
 #include "Swiften/Base/Platform.h"
 
 namespace Swift {
 
-QtEventWindow::QtEventWindow(QWidget* parent) : QTreeView(parent) {
+QtEventWindow::QtEventWindow(UIEventStream* eventStream, QWidget* parent) : QTreeView(parent) {
+	eventStream_ = eventStream;
 	model_ = new EventModel();
 	setModel(model_);
 	delegate_ = new EventDelegate();
@@ -16,6 +23,7 @@ QtEventWindow::QtEventWindow(QWidget* parent) : QTreeView(parent) {
 	setAnimated(true);
 	setIndentation(0);
 	setRootIsDecorated(true);
+	connect(this, SIGNAL(activated(const QModelIndex&)), this, SLOT(handleItemActivated(const QModelIndex&)));
 }
 
 QtEventWindow::~QtEventWindow() {
@@ -23,11 +31,27 @@ QtEventWindow::~QtEventWindow() {
 	delete delegate_;
 }
 
-void QtEventWindow::addEvent(boost::shared_ptr<Event> event, bool active) {
+void QtEventWindow::handleItemActivated(const QModelIndex& item) {
+	QtEvent* event = model_->getItem(item.row());
+	boost::shared_ptr<MessageEvent> messageEvent = boost::dynamic_pointer_cast<MessageEvent>(event->getEvent());
+	boost::shared_ptr<SubscriptionRequestEvent> subscriptionEvent = boost::dynamic_pointer_cast<SubscriptionRequestEvent>(event->getEvent());
+	
+	if (messageEvent) {
+		eventStream_->send(boost::shared_ptr<UIEvent>(new RequestChatUIEvent(messageEvent->getStanza()->getFrom())));
+	} else if (subscriptionEvent) {
+		printf("Subscription activated\n");
+		//FIXME: do something
+	} else {
+		qWarning() << "Trying to activate an unexpected event";
+	}
+
+}
+
+void QtEventWindow::addEvent(boost::shared_ptr<StanzaEvent> event, bool active) {
 	model_->addEvent(event, active);
 }
 
-void QtEventWindow::removeEvent(boost::shared_ptr<Event> event) {
+void QtEventWindow::removeEvent(boost::shared_ptr<StanzaEvent> event) {
 	model_->removeEvent(event);
 }
 
diff --git a/Swift/QtUI/EventViewer/QtEventWindow.h b/Swift/QtUI/EventViewer/QtEventWindow.h
index cab50ef..283ba52 100644
--- a/Swift/QtUI/EventViewer/QtEventWindow.h
+++ b/Swift/QtUI/EventViewer/QtEventWindow.h
@@ -5,6 +5,7 @@
 #include <QTreeView>
 
 #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"
@@ -13,13 +14,16 @@ namespace Swift {
 	class QtEventWindow : public QTreeView, public EventWindow {
 		Q_OBJECT
 		public:
-			QtEventWindow(QWidget* parent = 0);
+			QtEventWindow(UIEventStream* eventStream, QWidget* parent = 0);
 			~QtEventWindow();
-			void addEvent(boost::shared_ptr<Event> event, bool active);
-			void removeEvent(boost::shared_ptr<Event> event);
+			void addEvent(boost::shared_ptr<StanzaEvent> event, bool active);
+			void removeEvent(boost::shared_ptr<StanzaEvent> event);
+		private slots:
+			void handleItemActivated(const QModelIndex& item);
 		private:
 			EventModel* model_;
 			EventDelegate* delegate_;
+			UIEventStream* eventStream_;
 	};
 		
 }
diff --git a/Swift/QtUI/QtMainWindow.cpp b/Swift/QtUI/QtMainWindow.cpp
index 10dcfa6..ebafaff 100644
--- a/Swift/QtUI/QtMainWindow.cpp
+++ b/Swift/QtUI/QtMainWindow.cpp
@@ -19,7 +19,8 @@
 
 namespace Swift {
 
-QtMainWindow::QtMainWindow(QtTreeWidgetFactory *treeWidgetFactory) : QWidget() {
+QtMainWindow::QtMainWindow(UIEventStream* uiEventStream, QtTreeWidgetFactory *treeWidgetFactory) : QWidget() {
+	uiEventStream_ = uiEventStream;
 	setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));
 	QBoxLayout *mainLayout = new QBoxLayout(QBoxLayout::TopToBottom, this);
 	mainLayout->setContentsMargins(0,0,0,0);
@@ -45,7 +46,7 @@ QtMainWindow::QtMainWindow(QtTreeWidgetFactory *treeWidgetFactory) : QWidget() {
 
 	tabs_->addTab(contactsTabWidget_, "Contacts");
 	
-	eventWindow_ = new QtEventWindow();
+	eventWindow_ = new QtEventWindow(uiEventStream_);
 	
 	tabs_->addTab(eventWindow_, "Events");
 	
diff --git a/Swift/QtUI/QtMainWindow.h b/Swift/QtUI/QtMainWindow.h
index ce3272a..1c862e6 100644
--- a/Swift/QtUI/QtMainWindow.h
+++ b/Swift/QtUI/QtMainWindow.h
@@ -21,11 +21,12 @@ namespace Swift {
 	class QtTreeWidget;
 	class QtTreeWidgetFactory;
 	class TreeWidget;
+	class UIEventStream;
 
 	class QtMainWindow : public QWidget, public MainWindow {
 		Q_OBJECT
 		public:
-			QtMainWindow(QtTreeWidgetFactory *treeWidgetFactory);
+			QtMainWindow(UIEventStream* eventStream, QtTreeWidgetFactory *treeWidgetFactory);
 			TreeWidget* getTreeWidget();
 			std::vector<QMenu*> getMenus() {return menus_;}
 			void setMyName(const String& name);
@@ -53,6 +54,7 @@ namespace Swift {
 			QWidget* contactsTabWidget_;
 			QWidget* eventsTabWidget_;
 			QtEventWindow* eventWindow_;
+			UIEventStream* uiEventStream_;
 	};
 }
 
diff --git a/Swift/QtUI/QtMainWindowFactory.cpp b/Swift/QtUI/QtMainWindowFactory.cpp
index 0794780..dc1949e 100644
--- a/Swift/QtUI/QtMainWindowFactory.cpp
+++ b/Swift/QtUI/QtMainWindowFactory.cpp
@@ -8,8 +8,8 @@ QtMainWindowFactory::QtMainWindowFactory(QtTreeWidgetFactory *treeWidgetFactory)
 	lastWindow_ = NULL;
 }
 
-MainWindow* QtMainWindowFactory::createMainWindow() {
-	QtMainWindow* window = new QtMainWindow(treeWidgetFactory_);
+MainWindow* QtMainWindowFactory::createMainWindow(UIEventStream* eventStream) {
+	QtMainWindow* window = new QtMainWindow(eventStream, treeWidgetFactory_);
 	lastWindow_ = window;
 	return window;
 }
diff --git a/Swift/QtUI/QtMainWindowFactory.h b/Swift/QtUI/QtMainWindowFactory.h
index bd8342c..8567efd 100644
--- a/Swift/QtUI/QtMainWindowFactory.h
+++ b/Swift/QtUI/QtMainWindowFactory.h
@@ -8,7 +8,7 @@ namespace Swift {
 	class QtMainWindowFactory : public MainWindowFactory{
 		public:
 			QtMainWindowFactory(QtTreeWidgetFactory *treeWidgetFactory);
-			MainWindow* createMainWindow();
+			MainWindow* createMainWindow(UIEventStream* eventStream);
 			MainWindow* getLastCreatedWindow();
 		private:
 			QtTreeWidgetFactory *treeWidgetFactory_;
diff --git a/Swiften/Events/Event.h b/Swiften/Events/Event.h
deleted file mode 100644
index b4a71f6..0000000
--- a/Swiften/Events/Event.h
+++ /dev/null
@@ -1,13 +0,0 @@
-#pragma once
-
-#include <boost/signals.hpp>
-#include <boost/shared_ptr.hpp>
-
-namespace Swift {
-	class Event {
-		public:
-			Event(){};
-			virtual ~Event() {};
-			boost::signal<void()> onConclusion;
-	};
-}
diff --git a/Swiften/Events/MessageEvent.h b/Swiften/Events/MessageEvent.h
index 0adfa82..1f71493 100644
--- a/Swiften/Events/MessageEvent.h
+++ b/Swiften/Events/MessageEvent.h
@@ -6,11 +6,11 @@
 #include <boost/signals.hpp>
 #include <boost/shared_ptr.hpp>
 
-#include "Swiften/Events/Event.h"
+#include "Swiften/Events/StanzaEvent.h"
 #include "Swiften/Elements/Message.h"
 
 namespace Swift {
-	class MessageEvent : public Event {
+	class MessageEvent : public StanzaEvent {
 		public:
 			MessageEvent(boost::shared_ptr<Message> stanza) : stanza_(stanza){};
 			virtual ~MessageEvent(){};
diff --git a/Swiften/Events/StanzaEvent.h b/Swiften/Events/StanzaEvent.h
new file mode 100644
index 0000000..adef112
--- /dev/null
+++ b/Swiften/Events/StanzaEvent.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include <boost/signals.hpp>
+#include <boost/shared_ptr.hpp>
+
+namespace Swift {
+	class StanzaEvent {
+		public:
+			StanzaEvent(){};
+			virtual ~StanzaEvent() {};
+			boost::signal<void()> onConclusion;
+	};
+}
diff --git a/Swiften/Events/SubscriptionRequestEvent.h b/Swiften/Events/SubscriptionRequestEvent.h
new file mode 100644
index 0000000..fe41df7
--- /dev/null
+++ b/Swiften/Events/SubscriptionRequestEvent.h
@@ -0,0 +1,36 @@
+#pragma once
+
+#include <cassert>
+
+#include <boost/signals.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Events/StanzaEvent.h"
+#include "Swiften/Base/String.h"
+#include "Swiften/JID/JID.h"
+
+namespace Swift {
+	class SubscriptionRequestEvent : public StanzaEvent {
+		public:
+			SubscriptionRequestEvent(const JID& jid, const String& reason) : jid_(jid), reason_(reason){};
+			virtual ~SubscriptionRequestEvent(){};
+			const JID& getJID() const {return jid_;};
+			const String& getReason() const {return reason_;};
+			boost::signal<void()> onAccept;
+			boost::signal<void()> onDecline;
+			void accept() {
+				onAccept();
+				onConclusion();
+			}
+
+			void decline() {
+				onDecline();
+				onConclusion();
+			}
+
+		private:
+			JID jid_;
+			String reason_;
+	};
+}
+
diff --git a/Swiften/Presence/PresenceOracle.cpp b/Swiften/Presence/PresenceOracle.cpp
index af98510..f1e6adb 100644
--- a/Swiften/Presence/PresenceOracle.cpp
+++ b/Swiften/Presence/PresenceOracle.cpp
@@ -13,17 +13,23 @@ PresenceOracle::PresenceOracle(StanzaChannel* stanzaChannel) {
 
 void PresenceOracle::handleIncomingPresence(boost::shared_ptr<Presence> presence) {
 	JID bareJID = JID(presence->getFrom().toBare());
-	std::map<JID, boost::shared_ptr<Presence> > jidMap = entries_[bareJID];
-	boost::shared_ptr<Presence> last;
-	foreach(JIDPresencePair pair, jidMap) {
-		if (pair.first == presence->getFrom()) {
-			last = pair.second;
-			break;
+
+	if (presence->getType() == Presence::Subscribe) {
+		fprintf(stderr, "rarr, sub\n");
+		onPresenceSubscriptionRequest(bareJID, presence->getStatus());
+	} else {
+		std::map<JID, boost::shared_ptr<Presence> > jidMap = entries_[bareJID];
+		boost::shared_ptr<Presence> last;
+		foreach(JIDPresencePair pair, jidMap) {
+			if (pair.first == presence->getFrom()) {
+				last = pair.second;
+				break;
+			}
 		}
+		jidMap[presence->getFrom()] = presence;
+		entries_[bareJID] = jidMap;
+		onPresenceChange(presence, last);
 	}
-	jidMap[presence->getFrom()] = presence;
-	entries_[bareJID] = jidMap;
-	onPresenceChange(presence, last);
 }
 
 }
diff --git a/Swiften/Presence/UnitTest/PresenceOracleTest.cpp b/Swiften/Presence/UnitTest/PresenceOracleTest.cpp
index e6f56b2..2c9c526 100644
--- a/Swiften/Presence/UnitTest/PresenceOracleTest.cpp
+++ b/Swiften/Presence/UnitTest/PresenceOracleTest.cpp
@@ -15,11 +15,18 @@ class PresencePointerPair {
 		boost::shared_ptr<Presence> two;
 };
 
+class SubscriptionRequestInfo {
+	public:
+		boost::optional<JID> jid;
+		boost::optional<String> reason;
+};
+
 class PresenceOracleTest : public CppUnit::TestFixture
 {
 		CPPUNIT_TEST_SUITE(PresenceOracleTest);
 		CPPUNIT_TEST(testFirstPresence);
 		CPPUNIT_TEST(testSecondPresence);
+		CPPUNIT_TEST(testSubscriptionRequest);
 		CPPUNIT_TEST_SUITE_END();
 
 	private:
@@ -37,11 +44,11 @@ class PresenceOracleTest : public CppUnit::TestFixture
 			CPPUNIT_ASSERT_EQUAL(newPresence, out->one);
 		}
 		
-		void handlePresenceSubscriptionRequest(const JID& jid, const String& reason, boost::optional<JID>& outJID, boost::optional<String>& outReason) {
-			CPPUNIT_ASSERT(!outJID);
-			CPPUNIT_ASSERT(!outReason);
-			outJID = jid;
-			outReason = reason;
+		void handlePresenceSubscriptionRequest(const JID& jid, const String& reason, SubscriptionRequestInfo* info) {
+			CPPUNIT_ASSERT(!info->jid);
+			CPPUNIT_ASSERT(!info->reason);
+			info->jid = jid;
+			info->reason = reason;
 		}
 
 		void setUp() {
@@ -58,15 +65,14 @@ class PresenceOracleTest : public CppUnit::TestFixture
 			PresencePointerPair out;
 			oracle_->onPresenceChange.connect(boost::bind(&PresenceOracleTest::handlePresenceChange, this, _1, _2, &out));
 
-			boost::optional<JID> jid;
-			boost::optional<String> reason;
-			oracle_->onPresenceSubscriptionRequest.connect(boost::bind(&PresenceOracleTest::handlePresenceSubscriptionRequest, this, _1, _2, jid, reason));
+			SubscriptionRequestInfo info;
+			oracle_->onPresenceSubscriptionRequest.connect(boost::bind(&PresenceOracleTest::handlePresenceSubscriptionRequest, this, _1, _2, &info));
 
 			boost::shared_ptr<Presence> sentPresence(new Presence("blarb"));
 			stanzaChannel_->onPresenceReceived(sentPresence);
 
-			CPPUNIT_ASSERT(!jid);
-			CPPUNIT_ASSERT(!reason);
+			CPPUNIT_ASSERT(!info.jid);
+			CPPUNIT_ASSERT(!info.reason);
 			CPPUNIT_ASSERT(out.two.get() == NULL);
 			CPPUNIT_ASSERT_EQUAL(sentPresence, out.one);
 		}
@@ -80,19 +86,41 @@ class PresenceOracleTest : public CppUnit::TestFixture
 			CPPUNIT_ASSERT_EQUAL(sentPresence1, out.one);
 			out.one = boost::shared_ptr<Presence>();
 			
-			boost::optional<JID> jid;
-			boost::optional<String> reason;
-			oracle_->onPresenceSubscriptionRequest.connect(boost::bind(&PresenceOracleTest::handlePresenceSubscriptionRequest, this, _1, _2, jid, reason));
+			SubscriptionRequestInfo info;
+			oracle_->onPresenceSubscriptionRequest.connect(boost::bind(&PresenceOracleTest::handlePresenceSubscriptionRequest, this, _1, _2, &info));
 
 			boost::shared_ptr<Presence> sentPresence2(new Presence("test2"));
 			stanzaChannel_->onPresenceReceived(sentPresence2);
 
-			CPPUNIT_ASSERT(!jid);
-			CPPUNIT_ASSERT(!reason);
+			CPPUNIT_ASSERT(!info.jid);
+			CPPUNIT_ASSERT(!info.reason);
 			CPPUNIT_ASSERT_EQUAL(sentPresence1, out.two);
 			CPPUNIT_ASSERT_EQUAL(sentPresence2, out.one);
 		}
 
+		void testSubscriptionRequest() {
+			PresencePointerPair out;
+			oracle_->onPresenceChange.connect(boost::bind(&PresenceOracleTest::handlePresenceChange, this, _1, _2, &out));
+			SubscriptionRequestInfo info;
+			oracle_->onPresenceSubscriptionRequest.connect(boost::bind(&PresenceOracleTest::handlePresenceSubscriptionRequest, this, _1, _2, &info));
+
+			String reasonText = "Because I want to";
+			JID sentJID = JID("me@example.com");
+
+			boost::shared_ptr<Presence> sentPresence(new Presence());
+			sentPresence->setType(Presence::Subscribe);
+			sentPresence->setFrom(sentJID);
+			sentPresence->setStatus(reasonText);
+			stanzaChannel_->onPresenceReceived(sentPresence);
+
+			CPPUNIT_ASSERT(info.jid);
+			CPPUNIT_ASSERT(info.reason);
+			CPPUNIT_ASSERT_EQUAL(sentJID, info.jid.get());
+			CPPUNIT_ASSERT_EQUAL(reasonText, info.reason.get());
+
+			CPPUNIT_ASSERT(!out.two);
+			CPPUNIT_ASSERT(!out.one);
+		}
 };
 CPPUNIT_TEST_SUITE_REGISTRATION(PresenceOracleTest);
 
-- 
cgit v0.10.2-6-g49f6