Skip to content

Commit

Permalink
Add joining a channel by invitation
Browse files Browse the repository at this point in the history
Add requesting invitation token, sending to invitee and acknowledging invitee's response.

License:
This patch is BSD-licensed, see Documentation/Licenses/BSD-simplified.txt for details.

Test-Information:
Added tests for requesting invitation token based on examples in XEP 0369, which passes.
Tested on Ubuntu 16.04 LTS.

Change-Id: Icd822b440cb633b12c6dd15798e97d2fc302cb40
  • Loading branch information
tarun018 committed Jul 22, 2017
1 parent 716063b commit 09e0124
Show file tree
Hide file tree
Showing 9 changed files with 213 additions and 17 deletions.
11 changes: 10 additions & 1 deletion Swiften/Elements/MIXJoin.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <Swiften/Base/API.h>
#include <Swiften/Elements/Payload.h>
#include <Swiften/Elements/Form.h>
#include <Swiften/Elements/MIXInvitation.h>
#include <Swiften/JID/JID.h>

namespace Swift {
Expand Down Expand Up @@ -67,11 +68,19 @@ namespace Swift {
return form_;
}

const boost::optional<MIXInvitation::ref>& getInvitation() const {
return invitation_;
}

void setInvitation(MIXInvitation::ref invitation) {
invitation_ = invitation;
}

private:
boost::optional<JID> jid_;
boost::optional<JID> channel_;
std::unordered_set<std::string> subscribeItems_;
std::shared_ptr<Form> form_;
// FIXME: MIXInvitation to be implemented. boost::optional<MIXInvitation> invitation_;
boost::optional<MIXInvitation::ref> invitation_;
};
}
31 changes: 29 additions & 2 deletions Swiften/MIX/MIX.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,14 @@
#include <Swiften/Base/API.h>
#include <Swiften/JID/JID.h>
#include <Swiften/Elements/Form.h>
#include <Swiften/Elements/Message.h>
#include <Swiften/Elements/MIXJoin.h>
#include <Swiften/Elements/MIXLeave.h>
#include <Swiften/Elements/MIXUpdateSubscription.h>
#include <Swiften/Elements/MIXUserPreference.h>
#include <Swiften/Elements/MIXInvite.h>
#include <Swiften/Elements/MIXInvitation.h>
#include <Swiften/Elements/MIXInvitationAck.h>
#include <Swiften/Elements/ErrorPayload.h>

namespace Swift {
Expand All @@ -29,16 +33,26 @@ namespace Swift {
public:
virtual ~MIX();

/**
* Join Channel with preferences and subscriptions to node with invitation.
*/
virtual void joinChannel(const std::unordered_set<std::string>& nodes, Form::ref form, MIXInvitation::ref invitation) = 0;

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

/**
* Join Channel with a set of preferences.
* Join Channel with preferences and subscriptions to node with invitation.
*/
virtual void joinChannelWithPreferences(const std::unordered_set<std::string>& nodes, Form::ref form) = 0;

/**
* Join channel with invite and subscribe to nodes.
*/
virtual void joinChannelWithInvite(const std::unordered_set<std::string>& nodes, MIXInvitation::ref invitation) = 0;

/**
* Update subscription of nodes.
*/
Expand All @@ -59,6 +73,16 @@ namespace Swift {
*/
virtual void updatePreferences(Form::ref form) = 0;

/**
* Request invitation payload along with token for invitee.
*/
virtual void requestInvitation(const JID& invitee) = 0;

/**
* Invite invitee based on received token from requestInvitation.
*/
virtual void sendInvitation(MIXInvitation::ref invitation, std::string invitationMessage) = 0;

public:
boost::signals2::signal<void (MIXJoin::ref /* joinResponse */)> onJoinComplete;
boost::signals2::signal<void (ErrorPayload::ref /* joinError */)> onJoinFailed;
Expand All @@ -68,5 +92,8 @@ namespace Swift {
boost::signals2::signal<void (ErrorPayload::ref /* updateError */)> onSubscriptionUpdateFailed;
boost::signals2::signal<void (Form::ref /* preferencesForm */)> onPreferencesFormReceived;
boost::signals2::signal<void (ErrorPayload::ref /* failedConfiguration */)> onPreferencesUpdateFailed;
boost::signals2::signal<void (MIXInvitation::ref /* inviteResponse */)> onInvitationReceived;
boost::signals2::signal<void (ErrorPayload::ref /* failedConfiguration */)> onInvitationRequestFailed;
boost::signals2::signal<void (Message::ref /* message */)> onMessageReceived;
};
}
55 changes: 48 additions & 7 deletions Swiften/MIX/MIXImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,15 @@

namespace Swift {

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

MIXImpl::MIXImpl(const JID& ownJID, const JID& channelJID, IQRouter* iqRouter, StanzaChannel* stanzaChannel) : ownJID_(ownJID), channelJID_(channelJID), iqRouter_(iqRouter), stanzaChannel_(stanzaChannel) {
stanzaChannel_->onMessageReceived.connect(boost::bind(&MIXImpl::handleIncomingMessage, this, _1));
}

MIXImpl::~MIXImpl() {

}

void MIXImpl::joinChannel(const std::unordered_set<std::string>& nodes) {
joinChannelWithPreferences(nodes, nullptr);
}

void MIXImpl::joinChannelWithPreferences(const std::unordered_set<std::string>& nodes, Form::ref form) {
void MIXImpl::joinChannel(const std::unordered_set<std::string>& nodes, Form::ref form, MIXInvitation::ref invitation) {
auto joinPayload = std::make_shared<MIXJoin>();
joinPayload->setChannel(channelJID_);
for (auto node : nodes) {
Expand All @@ -34,11 +30,26 @@ void MIXImpl::joinChannelWithPreferences(const std::unordered_set<std::string>&
if (form) {
joinPayload->setForm(form);
}
if (invitation) {
joinPayload->setInvitation(invitation);
}
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::joinChannelWithSubscriptions(const std::unordered_set<std::string>& nodes) {
joinChannel(nodes, nullptr, nullptr);
}

void MIXImpl::joinChannelWithPreferences(const std::unordered_set<std::string>& nodes, Form::ref form) {
joinChannel(nodes, form, nullptr);
}

void MIXImpl::joinChannelWithInvite(const std::unordered_set<std::string>& nodes, MIXInvitation::ref invitation) {
joinChannel(nodes, nullptr, invitation);
}

void MIXImpl::handleJoinResponse(MIXJoin::ref payload, ErrorPayload::ref error) {
if (error) {
onJoinFailed(error);
Expand Down Expand Up @@ -112,4 +123,34 @@ void MIXImpl::updatePreferences(Form::ref form) {
request->send();
}

void MIXImpl::handleIncomingMessage(Message::ref message) {
onMessageReceived(message);
}

void MIXImpl::requestInvitation(const JID& invitee) {
auto invitePayload = std::make_shared<MIXInvite>();
invitePayload->setInvitee(invitee);
auto request = std::make_shared<GenericRequest<MIXInvite>>(IQ::Get, channelJID_, invitePayload, iqRouter_);
request->onResponse.connect(boost::bind(&MIXImpl::handleInvitationReceived, this, _1, _2));
request->send();
}

void MIXImpl::handleInvitationReceived(MIXInvite::ref invite, ErrorPayload::ref error) {
if (error) {
onInvitationRequestFailed(error);
} else {
if (invite->getInvitation()) {
onInvitationReceived(*invite->getInvitation());
}
}
}

void MIXImpl::sendInvitation(MIXInvitation::ref invitation, std::string invitationMessage) {
auto message = std::make_shared<Message>();
message->setTo(invitation->getInvitee());
message->setBody(invitationMessage);
message->addPayload(invitation);
stanzaChannel_->sendMessage(message);
}

}
15 changes: 13 additions & 2 deletions Swiften/MIX/MIXImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ namespace Swift {
using ref = std::shared_ptr<MIXImpl>;

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

/**
Expand All @@ -34,10 +34,14 @@ namespace Swift {
return channelJID_;
}

virtual void joinChannel(const std::unordered_set<std::string>& nodes) override;
virtual void joinChannel(const std::unordered_set<std::string>& nodes, Form::ref form, MIXInvitation::ref invitation) override;

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

virtual void joinChannelWithPreferences(const std::unordered_set<std::string>& nodes, Form::ref form) override;

virtual void joinChannelWithInvite(const std::unordered_set<std::string>& nodes, MIXInvitation::ref invitation) override;

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

virtual void leaveChannel() override;
Expand All @@ -46,16 +50,23 @@ namespace Swift {

virtual void updatePreferences(Form::ref form) override;

virtual void requestInvitation(const JID& invitee) override;

virtual void sendInvitation(MIXInvitation::ref invitation, std::string invitationMessage) override;

private:
void handleJoinResponse(MIXJoin::ref, ErrorPayload::ref);
void handleLeaveResponse(MIXLeave::ref, ErrorPayload::ref);
void handleUpdateSubscriptionResponse(MIXUpdateSubscription::ref, ErrorPayload::ref);
void handlePreferencesFormReceived(MIXUserPreference::ref, ErrorPayload::ref);
void handlePreferencesResultReceived(MIXUserPreference::ref /*payload*/, ErrorPayload::ref error);
void handleInvitationReceived(MIXInvite::ref invite, ErrorPayload::ref error);
void handleIncomingMessage(Message::ref message);

private:
JID ownJID_;
JID channelJID_;
IQRouter* iqRouter_;
StanzaChannel* stanzaChannel_;
};
}
39 changes: 34 additions & 5 deletions Swiften/MIX/UnitTest/MIXImplTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,12 @@ class MIXImplTest : public ::testing::Test {
}

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

Expand All @@ -51,6 +52,10 @@ class MIXImplTest : public ::testing::Test {
subscribedNodes_ = joinPayload->getSubscriptions();
}

void handleInvitation(MIXInvitation::ref invitation) {
invitation_ = invitation;
}

void handleLeaveComplete(MIXLeave::ref leavePayload) {
ASSERT_TRUE(leavePayload);
ASSERT_EQ(static_cast<int>(0), subscribedNodes_.size());
Expand Down Expand Up @@ -98,12 +103,13 @@ class MIXImplTest : public ::testing::Test {
IQRouter* router_;
int successfulJoins_;
Form::ref preferenceForm_;
MIXInvitation::ref invitation_;
std::unordered_set<std::string> subscribedNodes_;
};

TEST_F(MIXImplTest, testJoinError) {
MIX::ref testling = createMIXClient();
testling->joinChannel(std::unordered_set<std::string>());
testling->joinChannelWithSubscriptions(std::unordered_set<std::string>());

ASSERT_EQ(1, static_cast<int>(channel_->sentStanzas.size()));
ASSERT_TRUE(channel_->isRequestAtIndex<MIXJoin>(0, ownJID_.toBare(), IQ::Set));
Expand All @@ -127,7 +133,7 @@ TEST_F(MIXImplTest, testJoinWithAllSubscriptions) {
nodes.insert(std::string("urn:xmpp:mix:nodes:participants"));
nodes.insert(std::string("urn:xmpp:mix:nodes:config"));

testling->joinChannel(nodes);
testling->joinChannelWithSubscriptions(nodes);

ASSERT_EQ(1, static_cast<int>(channel_->sentStanzas.size()));
ASSERT_TRUE(channel_->isRequestAtIndex<MIXJoin>(0, ownJID_.toBare(), IQ::Set));
Expand All @@ -143,6 +149,29 @@ TEST_F(MIXImplTest, testJoinWithAllSubscriptions) {
ASSERT_EQ(static_cast<int>(4), subscribedNodes_.size());
}

TEST_F(MIXImplTest, testInvite) {
MIX::ref testling = createMIXClient();
testling->requestInvitation(JID("cat@shakespeare.lit"));

ASSERT_EQ(1, static_cast<int>(channel_->sentStanzas.size()));
ASSERT_TRUE(channel_->isRequestAtIndex<MIXInvite>(0, channelJID_, IQ::Get));

//fake response
auto invite = std::make_shared<MIXInvite>();

auto invitation = std::make_shared<MIXInvitation>();
invitation->setInviter(JID("hag66@shakespeare.lit"));
invitation->setInvitee(JID("cat@shakespeare.lit"));
invitation->setChannel(JID("coven@mix.shakespeare.lit"));
invitation->setToken(std::string("ABCDEF"));

invite->setInvitation(invitation);

channel_->onIQReceived(IQ::createResult(ownJID_, channel_->sentStanzas[0]->getTo(), channel_->sentStanzas[0]->getID(), invite));
ASSERT_TRUE(invitation_);
ASSERT_EQ(invitation_->getInvitee(), JID("cat@shakespeare.lit"));
}

TEST_F(MIXImplTest, testJoinWithSomeSubscriptions) {
MIX::ref testling = createMIXClient();
std::unordered_set<std::string> nodes;
Expand All @@ -151,7 +180,7 @@ TEST_F(MIXImplTest, testJoinWithSomeSubscriptions) {
nodes.insert(std::string("urn:xmpp:mix:nodes:participants"));
nodes.insert(std::string("urn:xmpp:mix:nodes:config"));

testling->joinChannel(nodes);
testling->joinChannelWithSubscriptions(nodes);

ASSERT_EQ(1, static_cast<int>(channel_->sentStanzas.size()));
ASSERT_TRUE(channel_->isRequestAtIndex<MIXJoin>(0, ownJID_.toBare(), IQ::Set));
Expand Down Expand Up @@ -191,7 +220,7 @@ TEST_F(MIXImplTest, testUpdateSubscription) {
nodes.insert(std::string("urn:xmpp:mix:nodes:messages"));
nodes.insert(std::string("urn:xmpp:mix:nodes:presence"));

testling->joinChannel(nodes);
testling->joinChannelWithSubscriptions(nodes);

ASSERT_EQ(1, static_cast<int>(channel_->sentStanzas.size()));
ASSERT_TRUE(channel_->isRequestAtIndex<MIXJoin>(0, ownJID_.toBare(), IQ::Set));
Expand Down
7 changes: 7 additions & 0 deletions Swiften/Parser/PayloadParsers/MIXJoinParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

#include <Swiften/Parser/PayloadParserFactory.h>
#include <Swiften/Parser/PayloadParsers/FormParser.h>
#include <Swiften/Parser/PayloadParsers/MIXInvitationParser.h>

using namespace Swift;

Expand Down Expand Up @@ -42,6 +43,9 @@ void MIXJoinParser::handleStartElement(const std::string& element, const std::st
if (element == "x" && ns == "jabber:x:data") {
currentPayloadParser_ = std::make_shared<FormParser>();
}
if (element == "invitation" && ns == "urn:xmpp:mix:0") {
currentPayloadParser_ = std::make_shared<MIXInvitationParser>();
}
}

if (level_ >= 1 && currentPayloadParser_) {
Expand All @@ -61,6 +65,9 @@ void MIXJoinParser::handleEndElement(const std::string& element, const std::stri
if (element == "x" && ns == "jabber:x:data") {
getPayloadInternal()->setForm(std::dynamic_pointer_cast<Form>(currentPayloadParser_->getPayload()));
}
if (element == "invitation" && ns == "urn:xmpp:mix:0") {
getPayloadInternal()->setInvitation(std::dynamic_pointer_cast<MIXInvitation>(currentPayloadParser_->getPayload()));
}
currentPayloadParser_.reset();
}
}
Expand Down
Loading

0 comments on commit 09e0124

Please sign in to comment.