Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add methods for requesting VCard of a MIX channel participant #80

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Add Channel Join and Leave capability to MIX
License:
This patch is BSD-licensed, see Documentation/Licenses/BSD-simplified.txt for details.

Test-Information:
Tests added for joining and leaving a channel as in XEP-0369, which passes.
Tested on Ubuntu 16.04 LTS.

Change-Id: Ibc2737f6154eeee1a85e98cb5f80c8bdbad35dcd
  • Loading branch information
tarun018 committed Jul 18, 2017
commit 7ea47f3786e1d84aa89e0dd89e2fa6962ee9a4d9
14 changes: 14 additions & 0 deletions Swiften/MIX/MIX.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* Copyright (c) 2017 Tarun Gupta
* Licensed under the simplified BSD license.
* See Documentation/Licenses/BSD-simplified.txt for more information.
*/

#include <Swiften/MIX/MIX.h>

namespace Swift {

MIX::~MIX() {
}

}
43 changes: 43 additions & 0 deletions Swiften/MIX/MIX.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright (c) 2017 Tarun Gupta
* Licensed under the simplified BSD license.
* See Documentation/Licenses/BSD-simplified.txt for more information.
*/

#pragma once

#include <memory>
#include <string>
#include <unordered_set>

#include <boost/signals2.hpp>

#include <Swiften/Base/API.h>
#include <Swiften/JID/JID.h>
#include <Swiften/Elements/MIXJoin.h>
#include <Swiften/Elements/MIXLeave.h>
#include <Swiften/Elements/ErrorPayload.h>

namespace Swift {
class SWIFTEN_API MIX {
public:
using ref = std::shared_ptr<MIX>;

public:
virtual ~MIX();

/**
* Join a MIX channel and subscribe to nodes.
*/
virtual void joinChannelAndSubscribe(const std::unordered_set<std::string>& nodes) = 0;
virtual void joinChannel() = 0;

virtual void leaveChannel() = 0;

public:
boost::signals2::signal<void (MIXJoin::ref /* joinResponse */)> onJoinComplete;
boost::signals2::signal<void (ErrorPayload::ref /* joinError */)> onJoinFailed;
boost::signals2::signal<void (MIXLeave::ref /* leaveResponse */)> onLeaveComplete;
boost::signals2::signal<void (ErrorPayload::ref /* leaveError */)> onLeaveFailed;
};
}
63 changes: 63 additions & 0 deletions Swiften/MIX/MIXImpl.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright (c) 2017 Tarun Gupta
* Licensed under the simplified BSD license.
* See Documentation/Licenses/BSD-simplified.txt for more information.
*/

#include <Swiften/MIX/MIXImpl.h>

#include <Swiften/Client/StanzaChannel.h>
#include <Swiften/Elements/IQ.h>
#include <Swiften/Queries/GenericRequest.h>
#include <Swiften/Queries/IQRouter.h>

namespace Swift {

MIXImpl::MIXImpl(const JID &ownJID, const JID &channelJID, IQRouter* iqRouter) : ownJID_(ownJID), channelJID_(channelJID), iqRouter_(iqRouter) {

}

MIXImpl::~MIXImpl() {

}

void MIXImpl::joinChannelAndSubscribe(const std::unordered_set<std::string>& nodes) {
auto joinPayload = std::make_shared<MIXJoin>();
joinPayload->setChannel(channelJID_);
for (auto node : nodes) {
joinPayload->addSubscription(node);
}
auto request = std::make_shared<GenericRequest<MIXJoin> >(IQ::Set, getJID(), joinPayload, iqRouter_);
request->onResponse.connect(boost::bind(&MIXImpl::handleJoinResponse, this, _1, _2));
request->send();
}

void MIXImpl::joinChannel() {
joinChannelAndSubscribe(std::unordered_set<std::string>());
}

void MIXImpl::handleJoinResponse(MIXJoin::ref payload, ErrorPayload::ref error) {
if (error) {
onJoinFailed(error);
} else {
onJoinComplete(payload);
}
}

void MIXImpl::leaveChannel() {
auto leavePayload = std::make_shared<MIXLeave>();
leavePayload->setChannel(channelJID_);
auto request = std::make_shared<GenericRequest<MIXLeave> >(IQ::Set, getJID(), leavePayload, iqRouter_);
request->onResponse.connect(boost::bind(&MIXImpl::handleLeaveResponse, this, _1, _2));
request->send();
}

void MIXImpl::handleLeaveResponse(MIXLeave::ref payload, ErrorPayload::ref error) {
if (error) {
onLeaveFailed(error);
} else {
onLeaveComplete(payload);
}
}

}
51 changes: 51 additions & 0 deletions Swiften/MIX/MIXImpl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright (c) 2017 Tarun Gupta
* Licensed under the simplified BSD license.
* See Documentation/Licenses/BSD-simplified.txt for more information.
*/

#pragma once

#include <Swiften/MIX/MIX.h>

namespace Swift {
class StanzaChannel;
class IQRouter;

class SWIFTEN_API MIXImpl : public MIX {
public:
using ref = std::shared_ptr<MIXImpl>;

public:
MIXImpl(const JID &ownJID, const JID &channelJID, IQRouter* iqRouter);
virtual ~MIXImpl();

/**
* Returns the (bare) JID of the user.
*/
virtual JID getJID() const {
return ownJID_.toBare();
}

/**
* Returns the JID of MIX channel.
*/
virtual JID getChannelJID() const {
return channelJID_;
}

virtual void joinChannelAndSubscribe(const std::unordered_set<std::string>& nodes) override;
virtual void joinChannel() override;

virtual void leaveChannel() override;

private:
void handleJoinResponse(MIXJoin::ref, ErrorPayload::ref);
void handleLeaveResponse(MIXLeave::ref, ErrorPayload::ref);

private:
JID ownJID_;
JID channelJID_;
IQRouter* iqRouter_;
};
}
184 changes: 184 additions & 0 deletions Swiften/MIX/UnitTest/MIXImplTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
/*
* Copyright (c) 2017 Tarun Gupta
* Licensed under the simplified BSD license.
* See Documentation/Licenses/BSD-simplified.txt for more information.
*/

#include <gtest/gtest.h>

#include <boost/bind.hpp>

#include <Swiften/Client/DummyStanzaChannel.h>
#include <Swiften/MIX/MIXImpl.h>
#include <Swiften/Queries/IQRouter.h>

using namespace Swift;

class MIXImplTest : public ::testing::Test {

protected:
void SetUp() {
ownJID_ = JID("hag66@shakespeare.example/UUID-a1j/7533");
channelJID_ = JID("coven@mix.shakespeare.example");
channel_ = new DummyStanzaChannel();
router_ = new IQRouter(channel_);
successfulJoins_ = 0;
}

void TearDown() {
delete router_;
delete channel_;
}

MIX::ref createMIXClient() {
auto mix = std::make_shared<MIXImpl>(ownJID_, channelJID_, router_);
mix->onJoinComplete.connect(boost::bind(&MIXImplTest::handleJoinComplete, this, _1));
mix->onLeaveComplete.connect(boost::bind(&MIXImplTest::handleLeaveComplete, this, _1));
return mix;
}

void handleJoinComplete(MIXJoin::ref joinPayload) {
ASSERT_TRUE(joinPayload);
ASSERT_TRUE(joinPayload->getJID());
ASSERT_EQ(*joinPayload->getJID(), JID("123456#coven@mix.shakespeare.example"));
++successfulJoins_;
subscribedNodes_ = joinPayload->getSubscriptions();
}

void handleLeaveComplete(MIXLeave::ref leavePayload) {
ASSERT_TRUE(leavePayload);
ASSERT_EQ(static_cast<int>(0), subscribedNodes_.size());
}

IQ::ref createJoinResult(const std::unordered_set<std::string>& nodes) {
auto joinResultPayload = std::make_shared<MIXJoin>();
for (auto node : nodes) {
joinResultPayload->addSubscription(node);
}
joinResultPayload->setJID(JID("123456#coven@mix.shakespeare.example"));
return IQ::createResult(ownJID_, channel_->sentStanzas[0]->getTo(), channel_->sentStanzas[0]->getID(), joinResultPayload);
}

IQ::ref createLeaveResult() {
auto leaveResultPayload = std::make_shared<MIXLeave>();
return IQ::createResult(ownJID_, channel_->sentStanzas[0]->getTo(), channel_->sentStanzas[0]->getID(), leaveResultPayload);
}

IQ::ref createJoinError() {
return IQ::createError(ownJID_, channel_->sentStanzas[0]->getTo(), channel_->sentStanzas[0]->getID());
}

bool hasSubscription(const std::string& value) {
return std::find(subscribedNodes_.begin(), subscribedNodes_.end(), value) != subscribedNodes_.end();
}

JID ownJID_;
JID channelJID_;
DummyStanzaChannel* channel_;
IQRouter* router_;
int successfulJoins_;
std::unordered_set<std::string> subscribedNodes_;
};

TEST_F(MIXImplTest, testJoinChannelOnly) {
MIX::ref testling = createMIXClient();
testling->joinChannel();

ASSERT_EQ(1, static_cast<int>(channel_->sentStanzas.size()));
ASSERT_TRUE(channel_->isRequestAtIndex<MIXJoin>(0, ownJID_.toBare(), IQ::Set));

auto iq = channel_->getStanzaAtIndex<IQ>(0);
ASSERT_TRUE(iq);
ASSERT_TRUE(iq->getPayload<MIXJoin>());
ASSERT_FALSE(iq->getPayload<MIXJoin>()->getForm());
ASSERT_EQ(static_cast<size_t>(0), iq->getPayload<MIXJoin>()->getSubscriptions().size());

channel_->onIQReceived(createJoinResult(std::unordered_set<std::string>()));
ASSERT_EQ(static_cast<int>(1), successfulJoins_);
ASSERT_EQ(static_cast<int>(0), subscribedNodes_.size());
}

TEST_F(MIXImplTest, testJoinError) {
MIX::ref testling = createMIXClient();
testling->joinChannel();

ASSERT_EQ(1, static_cast<int>(channel_->sentStanzas.size()));
ASSERT_TRUE(channel_->isRequestAtIndex<MIXJoin>(0, ownJID_.toBare(), IQ::Set));

auto iq = channel_->getStanzaAtIndex<IQ>(0);
ASSERT_TRUE(iq);
ASSERT_TRUE(iq->getPayload<MIXJoin>());
ASSERT_FALSE(iq->getPayload<MIXJoin>()->getForm());
ASSERT_EQ(static_cast<size_t>(0), iq->getPayload<MIXJoin>()->getSubscriptions().size());

channel_->onIQReceived(createJoinError());
ASSERT_EQ(static_cast<int>(0), successfulJoins_);
ASSERT_EQ(static_cast<int>(0), subscribedNodes_.size());
}

TEST_F(MIXImplTest, testJoinWithAllSubscriptions) {
MIX::ref testling = createMIXClient();
std::unordered_set<std::string> nodes;
nodes.insert(std::string("urn:xmpp:mix:nodes:messages"));
nodes.insert(std::string("urn:xmpp:mix:nodes:presence"));
nodes.insert(std::string("urn:xmpp:mix:nodes:participants"));
nodes.insert(std::string("urn:xmpp:mix:nodes:config"));

testling->joinChannelAndSubscribe(nodes);

ASSERT_EQ(1, static_cast<int>(channel_->sentStanzas.size()));
ASSERT_TRUE(channel_->isRequestAtIndex<MIXJoin>(0, ownJID_.toBare(), IQ::Set));

auto iq = channel_->getStanzaAtIndex<IQ>(0);
ASSERT_TRUE(iq);
ASSERT_TRUE(iq->getPayload<MIXJoin>());
ASSERT_FALSE(iq->getPayload<MIXJoin>()->getForm());
ASSERT_EQ(static_cast<size_t>(4), iq->getPayload<MIXJoin>()->getSubscriptions().size());

channel_->onIQReceived(createJoinResult(nodes));
ASSERT_EQ(static_cast<int>(1), successfulJoins_);
ASSERT_EQ(static_cast<int>(4), subscribedNodes_.size());
}

TEST_F(MIXImplTest, testJoinWithSomeSubscriptions) {
MIX::ref testling = createMIXClient();
std::unordered_set<std::string> nodes;
nodes.insert(std::string("urn:xmpp:mix:nodes:messages"));
nodes.insert(std::string("urn:xmpp:mix:nodes:presence"));
nodes.insert(std::string("urn:xmpp:mix:nodes:participants"));
nodes.insert(std::string("urn:xmpp:mix:nodes:config"));

testling->joinChannelAndSubscribe(nodes);

ASSERT_EQ(1, static_cast<int>(channel_->sentStanzas.size()));
ASSERT_TRUE(channel_->isRequestAtIndex<MIXJoin>(0, ownJID_.toBare(), IQ::Set));

auto iq = channel_->getStanzaAtIndex<IQ>(0);
ASSERT_TRUE(iq);
ASSERT_TRUE(iq->getPayload<MIXJoin>());
ASSERT_FALSE(iq->getPayload<MIXJoin>()->getForm());
ASSERT_EQ(static_cast<size_t>(4), iq->getPayload<MIXJoin>()->getSubscriptions().size());

std::unordered_set<std::string> subscribedTo;
subscribedTo.insert(std::string("urn:xmpp:mix:nodes:messages"));

channel_->onIQReceived(createJoinResult(subscribedTo));
ASSERT_EQ(static_cast<int>(1), successfulJoins_);
ASSERT_EQ(static_cast<int>(1), subscribedNodes_.size());
ASSERT_TRUE(hasSubscription(std::string("urn:xmpp:mix:nodes:messages")));
}


TEST_F(MIXImplTest, testLeaveChannel) {
MIX::ref testling = createMIXClient();
testling->leaveChannel();
ASSERT_EQ(1, static_cast<int>(channel_->sentStanzas.size()));
ASSERT_TRUE(channel_->isRequestAtIndex<MIXLeave>(0, ownJID_.toBare(), IQ::Set));

auto iq = channel_->getStanzaAtIndex<IQ>(0);
ASSERT_TRUE(iq);
ASSERT_TRUE(iq->getPayload<MIXLeave>());
ASSERT_TRUE(iq->getPayload<MIXLeave>()->getChannel());

channel_->onIQReceived(createLeaveResult());
}
3 changes: 3 additions & 0 deletions Swiften/SConscript
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,8 @@ if env["SCONS_STAGE"] == "build" :
"Elements/IsodeIQDelegation.cpp",
"Entity/Entity.cpp",
"Entity/PayloadPersister.cpp",
"MIX/MIX.cpp",
"MIX/MIXImpl.cpp",
"MUC/MUC.cpp",
"MUC/MUCImpl.cpp",
"MUC/MUCManager.cpp",
Expand Down Expand Up @@ -415,6 +417,7 @@ if env["SCONS_STAGE"] == "build" :
File("LinkLocal/UnitTest/LinkLocalServiceBrowserTest.cpp"),
File("LinkLocal/UnitTest/LinkLocalServiceInfoTest.cpp"),
File("LinkLocal/UnitTest/LinkLocalServiceTest.cpp"),
File("MIX/UnitTest/MIXImplTest.cpp"),
File("MUC/UnitTest/MUCTest.cpp"),
File("MUC/UnitTest/MockMUC.cpp"),
File("Network/UnitTest/HostAddressTest.cpp"),
Expand Down