summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKevin Smith <git@kismith.co.uk>2010-05-22 20:45:53 (GMT)
committerKevin Smith <git@kismith.co.uk>2010-05-23 08:15:20 (GMT)
commita68530cbbfeb17e01fd684b1ef41b960bc173f66 (patch)
tree98b5cd195f04d781164cf1394cee9092bb7b2497 /Swift/Controllers/Chat
parentc9659b556b932e2f887cf1d8ab6c5a0bead835eb (diff)
downloadswift-a68530cbbfeb17e01fd684b1ef41b960bc173f66.zip
swift-a68530cbbfeb17e01fd684b1ef41b960bc173f66.tar.bz2
Implement XEP-0045 joining, and appropriate error handling.
Resolves: #211
Diffstat (limited to 'Swift/Controllers/Chat')
-rw-r--r--Swift/Controllers/Chat/MUCController.cpp120
-rw-r--r--Swift/Controllers/Chat/MUCController.h14
-rw-r--r--Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp2
3 files changed, 116 insertions, 20 deletions
diff --git a/Swift/Controllers/Chat/MUCController.cpp b/Swift/Controllers/Chat/MUCController.cpp
index a8a6747..dbf03c9 100644
--- a/Swift/Controllers/Chat/MUCController.cpp
+++ b/Swift/Controllers/Chat/MUCController.cpp
@@ -45,7 +45,6 @@ MUCController::MUCController (
ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, muc, presenceOracle, avatarManager, useDelayForLatency, uiEventStream),
muc_(new MUC(stanzaChannel, presenceSender, muc)),
nick_(nick) {
- loginCheckTimer_ = boost::shared_ptr<Timer>(timerFactory->createTimer(MUC_JOIN_WARNING_TIMEOUT_MILLISECONDS));
parting_ = false;
events_ = uiEventStream;
@@ -53,11 +52,16 @@ MUCController::MUCController (
chatWindow_->setRosterModel(roster_);
chatWindow_->onClosed.connect(boost::bind(&MUCController::handleWindowClosed, this));
muc_->onJoinComplete.connect(boost::bind(&MUCController::handleJoinComplete, this, _1));
+ muc_->onJoinFailed.connect(boost::bind(&MUCController::handleJoinFailed, this, _1));
muc_->onOccupantJoined.connect(boost::bind(&MUCController::handleOccupantJoined, this, _1));
muc_->onOccupantPresenceChange.connect(boost::bind(&MUCController::handleOccupantPresenceChange, this, _1));
muc_->onOccupantLeft.connect(boost::bind(&MUCController::handleOccupantLeft, this, _1, _2, _3));
- loginCheckTimer_->onTick.connect(boost::bind(&MUCController::handleJoinTimeoutTick, this));
- loginCheckTimer_->start();
+ muc_->onOccupantRoleChanged.connect(boost::bind(&MUCController::handleOccupantRoleChanged, this, _1, _2, _3));
+ if (timerFactory) {
+ loginCheckTimer_ = boost::shared_ptr<Timer>(timerFactory->createTimer(MUC_JOIN_WARNING_TIMEOUT_MILLISECONDS));
+ loginCheckTimer_->onTick.connect(boost::bind(&MUCController::handleJoinTimeoutTick, this));
+ loginCheckTimer_->start();
+ }
muc_->joinAs(nick);
chatWindow_->convertToMUC();
@@ -72,21 +76,51 @@ MUCController::~MUCController() {
delete muc_;
chatWindow_->setRosterModel(NULL);
delete roster_;
- loginCheckTimer_->stop();
+ if (loginCheckTimer_) {
+ loginCheckTimer_->stop();
+ }
}
void MUCController::handleJoinTimeoutTick() {
- loginCheckTimer_->stop();
+ receivedActivity();
chatWindow_->addSystemMessage("Room " + toJID_.toString() + " is not responding. This operation may never complete");
}
-void MUCController::handleJoinComplete(MUC::JoinResult result) {
- loginCheckTimer_->stop();
- if (result == MUC::JoinFailed) {
- chatWindow_->addErrorMessage("Unable to join this room");
- }
+void MUCController::receivedActivity() {
+ if (loginCheckTimer_) {
+ loginCheckTimer_->stop();
+ }
+}
+
+void MUCController::handleJoinFailed(boost::shared_ptr<ErrorPayload> error) {
+ receivedActivity();
+ String errorMessage = "Unable to join this room";
+ String rejoinNick;
+ if (error) {
+ switch (error->getCondition()) {
+ case ErrorPayload::Conflict: rejoinNick = nick_ + "_"; errorMessage += " as " + nick_ + ", retrying as " + rejoinNick; break;
+ case ErrorPayload::JIDMalformed: errorMessage += ", no nickname specified";break;
+ case ErrorPayload::NotAuthorized: errorMessage += ", a password needed";break;
+ case ErrorPayload::RegistrationRequired: errorMessage += ", only members may join"; break;
+ case ErrorPayload::Forbidden: errorMessage += ", you are banned from the room"; break;
+ case ErrorPayload::ServiceUnavailable: errorMessage += ", the room is full";break;
+ case ErrorPayload::ItemNotFound: errorMessage += ", the room does not exist";break;
+
+ default: break;
+ }
+ }
+ errorMessage += ".";
+ chatWindow_->addErrorMessage(errorMessage);
+ if (!rejoinNick.isEmpty()) {
+ muc_->joinAs(rejoinNick);
+ }
+}
+
+void MUCController::handleJoinComplete(const String& nick) {
+ receivedActivity();
joined_ = true;
- String joinMessage = "You have joined room " + toJID_.toString() + " as " + nick_;
+ String joinMessage = "You have joined room " + toJID_.toString() + " as " + nick;
+ nick_ = nick;
chatWindow_->addSystemMessage(joinMessage);
}
@@ -105,18 +139,76 @@ void MUCController::handleWindowClosed() {
}
void MUCController::handleOccupantJoined(const MUCOccupant& occupant) {
- JID jid(JID(toJID_.getNode(), toJID_.getDomain(), occupant.getNick()));
- roster_->addContact(jid, occupant.getNick(), "Occupants");
+ receivedActivity();
+ JID jid(nickToJID(occupant.getNick()));
+ roster_->addContact(jid, occupant.getNick(), roleToGroupName(occupant.getRole()));
+ if (joined_) {
+ String joinString = occupant.getNick() + " has joined the room";
+ MUCOccupant::Role role = occupant.getRole();
+ if (role != MUCOccupant::NoRole && role != MUCOccupant::Participant) {
+ joinString += " as a " + roleToFriendlyName(role);
+
+ }
+ joinString += ".";
+ chatWindow_->addSystemMessage(joinString);
+ }
if (avatarManager_ != NULL) {
handleAvatarChanged(jid, "dummy");
}
}
-void MUCController::handleOccupantLeft(const MUCOccupant& occupant, MUC::LeavingType, const String& /*reason*/) {
+String MUCController::roleToFriendlyName(MUCOccupant::Role role) {
+ switch (role) {
+ case MUCOccupant::Moderator: return "moderator";
+ case MUCOccupant::Participant: return "participant";
+ case MUCOccupant::Visitor: return "visitor";
+ case MUCOccupant::NoRole: return "";
+ }
+ return "";
+}
+
+JID MUCController::nickToJID(const String& nick) {
+ return JID(toJID_.getNode(), toJID_.getDomain(), nick);
+}
+
+void MUCController::preHandleIncomingMessage(boost::shared_ptr<Message>) {
+ /*Buggy implementations never send the status code, so use an incoming message as a hint that joining's done (e.g. the old ejabberd on psi-im.org).*/
+ receivedActivity();
+ joined_ = true;
+}
+
+void MUCController::handleOccupantRoleChanged(const String& nick, const MUCOccupant::Role& newRole, const MUCOccupant::Role& oldRole) {
+ receivedActivity();
+ JID jid(nickToJID(nick));
+ roster_->removeContactFromGroup(jid, roleToGroupName(oldRole));
+ roster_->addContact(jid, nick, roleToGroupName(newRole));
+ chatWindow_->addSystemMessage(nick + " is now a " + roleToFriendlyName(newRole));
+}
+
+String MUCController::roleToGroupName(MUCOccupant::Role role) {
+ String result;
+ switch (role) {
+ case MUCOccupant::Moderator: result = "Moderators"; break;
+ case MUCOccupant::Participant: result = "Participants"; break;
+ case MUCOccupant::Visitor: result = "Visitors"; break;
+ case MUCOccupant::NoRole: result = "Occupants"; break;
+ default: assert(false);
+ }
+ return result;
+}
+
+void MUCController::handleOccupantLeft(const MUCOccupant& occupant, MUC::LeavingType, const String& reason) {
+ String partMessage = occupant.getNick() + " has left the room";
+ if (!reason.isEmpty()) {
+ partMessage += " (" + reason + ")";
+ }
+ partMessage += ".";
+ chatWindow_->addSystemMessage(partMessage);
roster_->removeContact(JID(toJID_.getNode(), toJID_.getDomain(), occupant.getNick()));
}
void MUCController::handleOccupantPresenceChange(boost::shared_ptr<Presence> presence) {
+ receivedActivity();
roster_->applyOnItems(SetPresence(presence, JID::WithResource));
}
diff --git a/Swift/Controllers/Chat/MUCController.h b/Swift/Controllers/Chat/MUCController.h
index 9e79835..247a942 100644
--- a/Swift/Controllers/Chat/MUCController.h
+++ b/Swift/Controllers/Chat/MUCController.h
@@ -4,8 +4,7 @@
* See Documentation/Licenses/GPLv3.txt for more information.
*/
-#ifndef SWIFTEN_MUCController_H
-#define SWIFTEN_MUCController_H
+#pragma once
#include <boost/shared_ptr.hpp>
#include <boost/signals.hpp>
@@ -47,9 +46,15 @@ namespace Swift {
void handleOccupantJoined(const MUCOccupant& occupant);
void handleOccupantLeft(const MUCOccupant& occupant, MUC::LeavingType type, const String& reason);
void handleOccupantPresenceChange(boost::shared_ptr<Presence> presence);
- void handleJoinComplete(MUC::JoinResult result);
+ void handleOccupantRoleChanged(const String& nick, const MUCOccupant::Role& newRole, const MUCOccupant::Role& oldRole);
+ void handleJoinComplete(const String& nick);
+ void handleJoinFailed(boost::shared_ptr<ErrorPayload> error);
void handleJoinTimeoutTick();
-
+ String roleToGroupName(MUCOccupant::Role role);
+ JID nickToJID(const String& nick);
+ String roleToFriendlyName(MUCOccupant::Role role);
+ void receivedActivity();
+ void preHandleIncomingMessage(boost::shared_ptr<Message>);
private:
MUC* muc_;
UIEventStream* events_;
@@ -61,5 +66,4 @@ namespace Swift {
boost::shared_ptr<Timer> loginCheckTimer_;
};
}
-#endif
diff --git a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp
index 99b2f90..2204366 100644
--- a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp
+++ b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp
@@ -64,7 +64,7 @@ public:
uiEventStream_ = new UIEventStream();
chatListWindowFactory_ = mocks_->InterfaceMock<ChatListWindowFactory>();
mocks_->ExpectCall(chatListWindowFactory_, ChatListWindowFactory::createWindow).With(uiEventStream_).Return(NULL);
- manager_ = new ChatsManager(jid_, stanzaChannel_, iqRouter_, eventController_, chatWindowFactory_, nickResolver_, presenceOracle_, serverDiscoInfo_, presenceSender_, uiEventStream_, chatListWindowFactory_, true);
+ manager_ = new ChatsManager(jid_, stanzaChannel_, iqRouter_, eventController_, chatWindowFactory_, nickResolver_, presenceOracle_, serverDiscoInfo_, presenceSender_, uiEventStream_, chatListWindowFactory_, true, NULL);
avatarManager_ = new MockAvatarManager();
manager_->setAvatarManager(avatarManager_);
};