-
Notifications
You must be signed in to change notification settings - Fork 64
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add methods for requesting VCard of a channel participant
License: This patch is BSD-licensed, see Documentation/Licenses/BSD-simplified.txt for details. Test-Information: Tests added for requesting VCard as in XEP-0369, which passes. Tested on Ubuntu 16.04 LTS. Change-Id: I08f550c71066e373955439ca81d61a26b7c254d9
- Loading branch information
Showing
4 changed files
with
392 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
/* | ||
* 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/VCard.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; | ||
|
||
virtual void requestVCard(const JID &participant) = 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; | ||
boost::signals2::signal<void (VCard::ref /* vCard */)> onVCardReceived; | ||
boost::signals2::signal<void (ErrorPayload::ref /* vCardRequestError */)> onVCardRequestFailed; | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
/* | ||
* 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/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); | ||
} | ||
} | ||
|
||
void MIXImpl::requestVCard(const JID &participant) { | ||
auto vCardPayload = std::make_shared<VCard>(); | ||
auto request = std::make_shared<GenericRequest<VCard> >(IQ::Get, participant, vCardPayload, iqRouter_); | ||
request->onResponse.connect(boost::bind(&MIXImpl::handleVCardReceived, this, _1, _2)); | ||
request->send(); | ||
} | ||
|
||
void MIXImpl::handleVCardReceived(VCard::ref payload, ErrorPayload::ref error) { | ||
if (error) { | ||
onVCardRequestFailed(error); | ||
} else { | ||
onVCardReceived(payload); | ||
} | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
/* | ||
* 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; | ||
|
||
virtual void requestVCard(const JID &participant) override; | ||
|
||
private: | ||
void handleJoinResponse(MIXJoin::ref, ErrorPayload::ref); | ||
void handleLeaveResponse(MIXLeave::ref, ErrorPayload::ref); | ||
void handleVCardReceived(VCard::ref payload, ErrorPayload::ref error); | ||
|
||
private: | ||
JID ownJID_; | ||
JID channelJID_; | ||
IQRouter* iqRouter_; | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,213 @@ | ||
/* | ||
* 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)); | ||
mix->onVCardReceived.connect(boost::bind(&MIXImplTest::handleVCard, 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()); | ||
} | ||
|
||
void handleVCard(VCard::ref payload) { | ||
ASSERT_TRUE(payload); | ||
vCardResult_ = payload; | ||
} | ||
|
||
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(); | ||
} | ||
|
||
IQ::ref createVCardResult() { | ||
auto vCard = std::make_shared<VCard>(); | ||
vCard->setFullName("Peter Saint-Andre"); | ||
vCard->setFamilyName("Saint-Andre"); | ||
vCard->setGivenName("Peter"); | ||
vCard->setNickname("stpeter"); | ||
vCard->addURL("http://www.xmpp.org/xsf/people/stpeter.shtml"); | ||
return IQ::createResult(ownJID_, channel_->sentStanzas[0]->getTo(), channel_->sentStanzas[0]->getID(), vCard); | ||
} | ||
|
||
JID ownJID_; | ||
JID channelJID_; | ||
DummyStanzaChannel* channel_; | ||
IQRouter* router_; | ||
int successfulJoins_; | ||
std::unordered_set<std::string> subscribedNodes_; | ||
VCard::ref vCardResult_; | ||
}; | ||
|
||
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()); | ||
} | ||
|
||
} | ||
|
||
TEST_F(MIXImplTest, testRequestVCard) { | ||
MIX::ref testling = createMIXClient(ownJID_); | ||
testling->requestVCard(JID("989898#coven@mix.shakespeare.example")); | ||
ASSERT_EQ(1, static_cast<int>(channel_->sentStanzas.size())); | ||
ASSERT_TRUE(channel_->isRequestAtIndex<VCard>(0, JID("989898#coven@mix.shakespeare.example"), IQ::Get)); | ||
|
||
channel_->onIQReceived(createVCardResult()); | ||
ASSERT_TRUE(vCardResult_); | ||
ASSERT_EQ(vCardResult_->getFullName(), std::string("Peter Saint-Andre")); |