diff --git a/backends/swiften/main.cpp b/backends/swiften/main.cpp index a9f9a7578cffa106247be4853f4984c8df3c78fa..93137352ebd82ebc499a85cb71585e30ab983247 100644 --- a/backends/swiften/main.cpp +++ b/backends/swiften/main.cpp @@ -3,6 +3,8 @@ #include "transport/networkplugin.h" #include "transport/logging.h" +#include "boost/date_time/posix_time/posix_time.hpp" + // Swiften #include "Swiften/Swiften.h" @@ -32,7 +34,83 @@ Swift::SimpleEventLoop *loop_; // Plugins class SwiftenPlugin; -SwiftenPlugin *np = NULL; +NetworkPlugin *np = NULL; + +class MUCController { + public: + MUCController(const std::string &user, boost::shared_ptr client, const std::string &room, const std::string &nickname, const std::string &password) { + m_user = user; + m_room = room; + muc = client->getMUCManager()->createMUC(room); + if (!password.empty()) { + muc->setPassword(password); + } + + 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)); + muc->onOccupantRoleChanged.connect(boost::bind(&MUCController::handleOccupantRoleChanged, this, _1, _2, _3)); + muc->onOccupantAffiliationChanged.connect(boost::bind(&MUCController::handleOccupantAffiliationChanged, this, _1, _2, _3)); + + muc->joinAs(nickname); + } + + virtual ~MUCController() { + muc->onJoinComplete.disconnect(boost::bind(&MUCController::handleJoinComplete, this, _1)); + muc->onJoinFailed.disconnect(boost::bind(&MUCController::handleJoinFailed, this, _1)); + muc->onOccupantJoined.disconnect(boost::bind(&MUCController::handleOccupantJoined, this, _1)); + muc->onOccupantPresenceChange.disconnect(boost::bind(&MUCController::handleOccupantPresenceChange, this, _1)); + muc->onOccupantLeft.disconnect(boost::bind(&MUCController::handleOccupantLeft, this, _1, _2, _3)); + muc->onOccupantRoleChanged.disconnect(boost::bind(&MUCController::handleOccupantRoleChanged, this, _1, _2, _3)); + muc->onOccupantAffiliationChanged.disconnect(boost::bind(&MUCController::handleOccupantAffiliationChanged, this, _1, _2, _3)); + } + + const std::string &getNickname() { + //return muc->getCurrentNick(); + return m_nick; + } + + void handleOccupantJoined(const Swift::MUCOccupant& occupant) { + np->handleParticipantChanged(m_user, occupant.getNick(), m_room, occupant.getRole() == Swift::MUCOccupant::Moderator, pbnetwork::STATUS_ONLINE); + } + + void handleOccupantLeft(const Swift::MUCOccupant& occupant, Swift::MUC::LeavingType type, const std::string& reason) { + np->handleParticipantChanged(m_user, occupant.getNick(), m_room, occupant.getRole() == Swift::MUCOccupant::Moderator, pbnetwork::STATUS_NONE); + } + + void handleOccupantPresenceChange(boost::shared_ptr presence) { + const Swift::MUCOccupant& occupant = muc->getOccupant(presence->getFrom().getResource()); + np->handleParticipantChanged(m_user, presence->getFrom().getResource(), m_room, (int) occupant.getRole() == Swift::MUCOccupant::Moderator, (pbnetwork::StatusType) presence->getShow(), presence->getStatus()); + } + + void handleOccupantRoleChanged(const std::string& nick, const Swift::MUCOccupant& occupant, const Swift::MUCOccupant::Role& oldRole) { + + } + + void handleOccupantAffiliationChanged(const std::string& nick, const Swift::MUCOccupant::Affiliation& affiliation, const Swift::MUCOccupant::Affiliation& oldAffiliation) { +// np->handleParticipantChanged(m_user, occupant->getNick(), m_room, (int) occupant.getRole() == Swift::MUCOccupant::Moderator, pbnetwork::STATUS_ONLINE); + } + + void handleJoinComplete(const std::string& nick) { + m_nick = nick; + } + + void handleJoinFailed(boost::shared_ptr error) { + + } + + void part() { + muc->part(); + } + + private: + Swift::MUC::ref muc; + std::string m_user; + std::string m_room; + std::string m_nick; +}; class SwiftenPlugin : public NetworkPlugin { public: @@ -106,6 +184,7 @@ class SwiftenPlugin : public NetworkPlugin { client->onDisconnected.disconnect(boost::bind(&SwiftenPlugin::handleSwiftDisconnected, this, user, _1)); client->onMessageReceived.disconnect(boost::bind(&SwiftenPlugin::handleSwiftMessageReceived, this, user, _1)); m_users.erase(user); + m_mucs.erase(user); } #ifndef WIN32 @@ -136,6 +215,11 @@ class SwiftenPlugin : public NetworkPlugin { } void handleSwiftPresenceChanged(const std::string &user, Swift::Presence::ref presence) { + boost::shared_ptr client = m_users[user]; + if (client->getMUCRegistry()->isMUC(presence->getFrom().toBare())) { + return; + } + LOG4CXX_INFO(logger, user << ": " << presence->getFrom().toBare().toString() << " presence changed"); std::string message = presence->getStatus(); @@ -160,7 +244,22 @@ class SwiftenPlugin : public NetworkPlugin { std::string body = message->getBody(); boost::shared_ptr client = m_users[user]; if (client) { - handleMessage(user, message->getFrom().toBare().toString(), body, "", ""); + if (message->getType() == Swift::Message::Groupchat) { + boost::shared_ptr delay = message->getPayload(); + std::string timestamp = ""; + if (delay) { + timestamp = boost::posix_time::to_iso_string(delay->getStamp()); + } + handleMessage(user, message->getFrom().toBare().toString(), body, message->getFrom().getResource(), "", timestamp); + } + else { + if (client->getMUCRegistry()->isMUC(message->getFrom().toBare())) { + handleMessage(user, message->getFrom().toBare().toString(), body, message->getFrom().getResource(), "", "", false, true); + } + else { + handleMessage(user, message->getFrom().toBare().toString(), body, "", ""); + } + } } } @@ -199,6 +298,8 @@ class SwiftenPlugin : public NetworkPlugin { client->getRoster()->onInitialRosterPopulated.disconnect(boost::bind(&SwiftenPlugin::handleSwiftRosterReceived, this, user)); client->getPresenceOracle()->onPresenceChange.disconnect(boost::bind(&SwiftenPlugin::handleSwiftPresenceChanged, this, user, _1)); client->disconnect(); + m_mucs.erase(user); + m_users.erase(user); } } @@ -210,6 +311,11 @@ class SwiftenPlugin : public NetworkPlugin { message->setTo(Swift::JID(legacyName)); message->setFrom(client->getJID()); message->setBody(msg); + if (client->getMUCRegistry()->isMUC(legacyName)) { + message->setType(Swift::Message::Groupchat); + boost::shared_ptr muc = m_mucs[user][legacyName]; + handleMessage(user, legacyName, msg, muc->getNickname(), xhtml); + } client->sendMessage(message); } @@ -267,9 +373,40 @@ class SwiftenPlugin : public NetworkPlugin { } } + void handleJoinRoomRequest(const std::string &user, const std::string &room, const std::string &nickname, const std::string &password) { + boost::shared_ptr client = m_users[user]; + if (client) { + if (client->getMUCRegistry()->isMUC(room)) { + return; + } + + boost::shared_ptr muc = boost::shared_ptr( new MUCController(user, client, room, nickname, password)); + m_mucs[user][room] = muc; + } + } + + void handleLeaveRoomRequest(const std::string &user, const std::string &room) { + boost::shared_ptr client = m_users[user]; + if (client) { + if (!client->getMUCRegistry()->isMUC(room)) { + return; + } + + boost::shared_ptr muc = m_mucs[user][room]; + if (!muc) { + m_mucs[user].erase(room); + return; + } + + muc->part(); + m_mucs[user].erase(room); + } + } + private: Config *config; std::map > m_users; + std::map > > m_mucs; }; #ifndef WIN32 diff --git a/include/transport/networkplugin.h b/include/transport/networkplugin.h index eece2e810916fb80e1beb3da38f1c5b4abea644a..279a3c5d4503cd09454668d1ab738d4d2927b755 100644 --- a/include/transport/networkplugin.h +++ b/include/transport/networkplugin.h @@ -113,7 +113,7 @@ class NetworkPlugin { /// \param message Plain text message. /// \param nickname Nickname of buddy in room. Empty if it's normal chat message. /// \param xhtml XHTML message. - void handleMessage(const std::string &user, const std::string &legacyName, const std::string &message, const std::string &nickname = "", const std::string &xhtml = "", const std::string ×tamp = "", bool headline = false); + void handleMessage(const std::string &user, const std::string &legacyName, const std::string &message, const std::string &nickname = "", const std::string &xhtml = "", const std::string ×tamp = "", bool headline = false, bool pm = false); void handleMessageAck(const std::string &user, const std::string &legacyName, const std::string &id); diff --git a/include/transport/protocol.proto b/include/transport/protocol.proto index 8eafcde8d419c9c4274a36d120e0f49e6443c069..796b656c3dbfb59ca17b977fd7be347d5051359b 100644 --- a/include/transport/protocol.proto +++ b/include/transport/protocol.proto @@ -72,6 +72,7 @@ message ConversationMessage { optional string timestamp = 6; optional bool headline = 7; optional string id = 8; + optional bool pm = 9; } message Room { diff --git a/plugin/cpp/networkplugin.cpp b/plugin/cpp/networkplugin.cpp index 773c47ff1bbfa80f8485bc712b4aab3f52a73d50..45e39e9999f84d7cd2292b068cbe9c0a6d1ebd98 100644 --- a/plugin/cpp/networkplugin.cpp +++ b/plugin/cpp/networkplugin.cpp @@ -85,7 +85,7 @@ void NetworkPlugin::sendConfig(const PluginConfig &cfg) { send(message); } -void NetworkPlugin::handleMessage(const std::string &user, const std::string &legacyName, const std::string &msg, const std::string &nickname, const std::string &xhtml, const std::string ×tamp, bool headline) { +void NetworkPlugin::handleMessage(const std::string &user, const std::string &legacyName, const std::string &msg, const std::string &nickname, const std::string &xhtml, const std::string ×tamp, bool headline, bool pm) { pbnetwork::ConversationMessage m; m.set_username(user); m.set_buddyname(legacyName); @@ -94,6 +94,7 @@ void NetworkPlugin::handleMessage(const std::string &user, const std::string &le m.set_xhtml(xhtml); m.set_timestamp(timestamp); m.set_headline(headline); + m.set_pm(pm); std::string message; m.SerializeToString(&message); diff --git a/src/conversation.cpp b/src/conversation.cpp index c9be3d3de82a98f86243b85c5e10fe5366aa5e32..4073d8e66ab82fea2cb7d8e1780c286a4af4d3b9 100644 --- a/src/conversation.cpp +++ b/src/conversation.cpp @@ -130,7 +130,11 @@ void Conversation::handleMessage(boost::shared_ptr &message, con message->setFrom(Swift::JID(n, m_conversationManager->getComponent()->getJID().toBare(), "user")); } else { - message->setFrom(Swift::JID(m_room, m_conversationManager->getComponent()->getJID().toBare(), n)); + std::string legacyName = m_room; + if (legacyName.find_last_of("@") != std::string::npos) { + legacyName.replace(legacyName.find_last_of("@"), 1, "%"); // OK + } + message->setFrom(Swift::JID(legacyName, m_conversationManager->getComponent()->getJID().toBare(), n)); } } diff --git a/src/networkpluginserver.cpp b/src/networkpluginserver.cpp index 019cfa6d8654bc6aa804d408826865119e1669fb..85747b23ac71d3e150be9b193eff3d46bcb105e7 100644 --- a/src/networkpluginserver.cpp +++ b/src/networkpluginserver.cpp @@ -686,7 +686,6 @@ void NetworkPluginServer::handleConvMessagePayload(const std::string &data, bool msg->addPayload(delay); } - NetworkConversation *conv = (NetworkConversation *) user->getConversationManager()->getConversation(payload.buddyname()); // We can't create Conversation for payload with nickname, because this means the message is from room, @@ -695,6 +694,19 @@ void NetworkPluginServer::handleConvMessagePayload(const std::string &data, bool return; } + if (conv && payload.pm()) { + conv = (NetworkConversation *) user->getConversationManager()->getConversation(payload.buddyname() + "/" + payload.nickname()); + if (!conv) { + conv = new NetworkConversation(user->getConversationManager(), payload.nickname()); + std::string name = payload.buddyname(); + conv->setRoom(name); + conv->setNickname(payload.buddyname() + "/" + payload.nickname()); + + user->getConversationManager()->addConversation(conv); + conv->onMessageToSend.connect(boost::bind(&NetworkPluginServer::handleMessageReceived, this, _1, _2)); + } + } + // Create new Conversation if it does not exist if (!conv) { conv = new NetworkConversation(user->getConversationManager(), payload.buddyname());