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 MIXRegistry, Limber MIX Mock Server and MIX Example #88

Open
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

tarun018
Copy link
Contributor

Standard output shows:

Connecting...Connected
Request list of channels.
List of rooms at example.tg
1. coven - [email protected]

Request supported nodes for MIX channel: [email protected]
Nodes supported by channel [email protected]
1. urn:xmpp:mix:nodes:participants
2. urn:xmpp:mix:nodes:messages
3. urn:xmpp:mix:nodes:presence
4. urn:xmpp:mix:nodes:jidmap

Successfully joined channel [email protected]
[warning] Swiften/MIX/MIXRegistry.cpp:24 joinChannel: Channel already joined: [email protected]
Successfully left channel [email protected]

Warning is intentional to notify about asking to join an already joined channel.

Update roster encoding for MIX channel
Update roster parser and serializer

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

Test-Information:
Added tests for joining and leaving channels via MIXRegistry, which passes.
Tested on Ubuntu 16.04 LTS.

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

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

Change-Id: Ib85d6e866e09db060b9b2da428f074dfbfd26385
channels_.insert(channelJID);
}

std::unordered_set<std::string>& getChannels() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be a const reference?


//If request comes to domain service
if (stanza->getTo().isValid() && stanza->getTo() == session->getLocalJID()) {
if (std::shared_ptr<IQ> iq = std::dynamic_pointer_cast<IQ>(stanza)) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you masking the existing iq variable with the same contents deliberately here?

if(iq->getPayload<RosterPayload>()) {
SWIFT_LOG(debug) << "Query: Roster" << std::endl;
session->sendElement(IQ::createResult(iq->getFrom(), iq->getID(), std::make_shared<RosterPayload>()));
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Continuing processing the rest of handleElementReceived after this sendElement (and the ones below) isn't really useful, is it?

SimpleEventLoop eventLoop;
SimpleUserRegistry userRegistry;

userRegistry.addUser(JID("[email protected]"), "mixtest");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I would prefer it if you used example.com, which is guaranteed to never mask existing domains, instead of assuming there will never be a .tg TLD.

assert(mixRegistry_);
assert(mixRegistry_->getMIXInstance(jid));

cout << "Successfully joined channel " << jid << std::endl;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mixing prefixed and non-prefixed iostream bits looks odd. I think I'd prefer the prefixed ones here.

@@ -40,6 +40,15 @@ void XMPPRosterImpl::clear() {
onRosterCleared();
}

bool XMPPRosterImpl::isMIXChannel(const JID& jid) {
std::map<JID, XMPPRosterItem>::const_iterator i = entries_.find(jid.toBare());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assuming you really need the explicit const iterator here, I would prefer RosterMap::const_iterator instead.

if (iq) {
if(iq->getPayload<RosterPayload>()) {
SWIFT_LOG(debug) << "Query: Roster" << std::endl;
session->sendElement(IQ::createResult(iq->getFrom(), iq->getID(), std::make_shared<RosterPayload>()));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't the final version right? I would expect it to return the MIX channels which the user has joined.

@@ -191,6 +196,7 @@ namespace Swift {
PresenceOracle* presenceOracle;
DirectedPresenceSender* directedPresenceSender;
StanzaChannelPresenceSender* stanzaChannelPresenceSender;
MIXRegistry* mixRegistry;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be a unique_ptr, right?

@tarun018
Copy link
Contributor Author

tarun018 commented Aug 17, 2017

Updated based on feedback and added roster sync between clients:

Example (Client 1) :

./Swiften/Examples/MIXListAndJoin/MIXListAndJoin [email protected]/some mixtest2 example.com
Connecting...Connected
Request list of channels.
List of rooms at example.com
	1. coven - [email protected]

Request supported nodes for MIX channel: [email protected]
Nodes supported by channel [email protected]
	1. urn:xmpp:mix:nodes:participants
	2. urn:xmpp:mix:nodes:messages
	3. urn:xmpp:mix:nodes:presence
	4. urn:xmpp:mix:nodes:jidmap

Already Joined channels: 
No channels already joined.

Successfully joined channel [email protected]
[warning] Swiften/MIX/MIXRegistry.cpp:40 joinChannel: Channel already joined: [email protected]

However (client 2 of the same user):

./Swiften/Examples/MIXListAndJoin/MIXListAndJoin [email protected]/someelse mixtest2 example.com
Connecting...Connected
Request list of channels.
List of rooms at example.com
	1. coven - [email protected]

Request supported nodes for MIX channel: [email protected]
Nodes supported by channel [email protected]
	1. urn:xmpp:mix:nodes:participants
	2. urn:xmpp:mix:nodes:messages
	3. urn:xmpp:mix:nodes:presence
	4. urn:xmpp:mix:nodes:jidmap

Already Joined channels: 
	1. coven - [email protected]

[warning] Swiften/MIX/MIXRegistry.cpp:40 joinChannel: Channel already joined: [email protected]

@tarun018 tarun018 force-pushed the MIXLimber branch 3 times, most recently from d8ef76f to acb0f16 Compare August 18, 2017 14:31
Copy link
Collaborator

@tfar tfar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some feedback inline.

* All rights reserved.
* See the COPYING file for more information.
*/
/*
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please leave a newline between copyright blocks.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will udpate.

//If request comes to own local server
if (stanza->getTo().isValid() && stanza->getTo() == session->getRemoteJID().toBare()) {
if (auto incomingJoinPayload = iq->getPayload<MIXJoin>()) {
if (boost::optional<JID> channelJID = incomingJoinPayload->getChannel()) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why use auto in the line above, but not here?

}

if (auto incomingLeavePayload = iq->getPayload<MIXLeave>()) {
if (boost::optional<JID> channelJID = incomingLeavePayload->getChannel()) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why use auto in the line above, but not here?

return;
}
} else {
session->sendElement(IQ::createError(iq->getFrom(), iq->getID(), ErrorPayload::BadRequest, ErrorPayload::Cancel));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it's not an IQ you can't return an IQ error. You should probably just ignore it in this case.

stanza->setFrom(session->getRemoteJID());

auto iq = std::dynamic_pointer_cast<IQ>(stanza);
if (iq) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It probably makes more sense to test for !iq and handle the error case there and afterwards check if it's a roster payload.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, makes more sense.

}

//If request comes to domain service
if (stanza->getTo().isValid() && stanza->getTo() == session->getLocalJID()) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the isValid() test required here?

}
return;
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method is quite long. It probably makes sense to split it up in parts. Like handleElementReceivedForAccount, handleElementReceivedForMIXService, handleElementReceivedForMIXChannel, etc.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've split the function into the three cases now.

@@ -42,7 +42,16 @@ namespace Swift {
version_ = version;
}

bool hasMIXAnnotationSupport() const {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method name doesn't make it clear what it is for. You probably want to enable requesting MIX annotations for roster requests. How about hasRequestMIXAnnotations()? Similar for the following method.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

@tarun018 tarun018 force-pushed the MIXLimber branch 3 times, most recently from c6aa5b3 to 6c07dce Compare August 21, 2017 12:52
@tarun018
Copy link
Contributor Author

Updated based on feedback.


auto iq = std::dynamic_pointer_cast<IQ>(stanza);
if (!iq) {
session->sendElement(IQ::createError(iq->getFrom(), iq->getID(), ErrorPayload::BadRequest, ErrorPayload::Cancel));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it's not an IQ, you cannot respond with an IQ error!!!! IQ errors are when you receive an IQ but do not understand the content and cannot sensibly handle it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am now returning if element is not an iq.

}

//If request comes to domain service
if (stanza->getTo() == session->getLocalJID()) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These should probably be else if statements, or does it make sense to have an IQ handled by multiple handleELementReceivedForX methods?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.


void handleElementReceivedForAccount(IQ::ref iq, std::shared_ptr<ServerFromClientSession> session) {
if (auto incomingJoinPayload = iq->getPayload<MIXJoin>()) {
if (auto channelJID = incomingJoinPayload->getChannel()) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Creating two helper methods to handle join and leave would help to further improve readability of the code. You could then simply call them in here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added handleJoin and handleLeave methods.

session->sendElement(IQ::createError(iq->getFrom(), iq->getID(), ErrorPayload::BadRequest, ErrorPayload::Cancel));
}
}
return;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you remove return statements at the end of void methods. Thanks.

Copy link
Collaborator

@tfar tfar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR is slowly getting there. 👍

return;
}

if(iq->getPayload<RosterPayload>()) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this if be in the handleElementReceivedForAccount method? After all roster queries go to the account, not?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Roster queries does not have a "to" field, therefore if conditions down there won't pass.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

handleElementReceivedForAccount is handling all queries for user's own local server.

handleElementReceivedForMIXChannel(iq, session);
}

//If request comes to domain service
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you move the comments for these if inside the if block so that the else if (… directly follows the line with the }, thanks. The current style makes it appear an if statement has ended while the corresponding else is following up a couple lines below.


private:
IDGenerator idGenerator_;
PlatformXMLParserFactory xmlParserFactory;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent, should end with _ as well.

@tarun018 tarun018 force-pushed the MIXLimber branch 2 times, most recently from f4035e2 to 67238a4 Compare August 21, 2017 20:32
This allows different client of same user to connect to server and retrieves already connected channels.

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

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

Change-Id: I31d0b68528ea0653685e6bcbc66e8131cb2c0eb4
//If request comes to domain service
handleElementReceivedForMIXService(iq, session);
} else if (stanza->getTo() == session->getRemoteJID().toBare() || stanza->getTo() == JID()) {
//If request comes to own local server
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is to the account and not to the local server, the local server would be to='server domain'.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the comment.

}

std::shared_ptr<RosterPayload> getRosterPayloadFromXMPPRoster(std::shared_ptr<XMPPRosterImpl> roster) {
auto rosterPayload = std::make_shared<RosterPayload>();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getFoo() usually describes a getter method. This seems to be a) not a method to retrieve a member and b) a method that does not otherwise access members, so can be static, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes this can be a static method.


#include <iostream>
#include <memory>
#include <unordered_set>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are using assert() but do not #include cassert header.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Included.

int channelCount = 0;
std::cout << "Already Joined channels: " << std::endl;
for (auto item : xmppRoster->getItems()) {
channelCount++;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Channels are only those in the roster that have the mix annotation. However, the MIX registry should probably be used to fetch the already joined channels, which has access to the roster. This way if the user wants to do something MIX related, they can always go tho the MIXRegistry, right? Opinions, @tarun018 @Kev ?

Copy link
Contributor Author

@tarun018 tarun018 Aug 22, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like a sensible idea. I wanted to hold my client till onInitialRosterPopulated is emitted and syncRegistryWithRoster is successfully executed.

mixRegistry_->onChannelJoinFailed.connect(&handleChannelJoinFailed);
mixRegistry_->onChannelLeft.connect(&handleChannelLeft);

XMPPRoster* xmppRoster = client->getRoster();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use auto here.

}

static void handleRosterPopulated() {
XMPPRoster* xmppRoster = client->getRoster();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use auto here.

std::cout << "Connecting..." << std::flush;
client->connect(options);
{
Timer::ref timer = networkFactories.getTimerFactory()->createTimer(30000);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use `auto´ here.

Timer::ref timer = networkFactories.getTimerFactory()->createTimer(30000);
timer->onTick.connect(boost::bind(&SimpleEventLoop::stop, &eventLoop));

Timer::ref disconnectTimer = networkFactories.getTimerFactory()->createTimer(25000);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use auto here.

#pragma once

#include <memory>
#include <unordered_set>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing a #include <string>.

@@ -7,6 +7,7 @@
#include <iostream>
#include <memory>
#include <unordered_set>
#include <cassert>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sort headers, there is a script for this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

@@ -8,6 +8,7 @@

#include <memory>
#include <unordered_set>
#include <string>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sort headers.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

Change-Id: I98bda1d18847cd1fabffde2c0f92fced31d2a5f3
Change-Id: Iab547e141952b29f4c9703035d2a1808927554c4
namespace Swift {

MIXRegistry::MIXRegistry(const JID& ownJID, IQRouter* iqRouter, XMPPRoster* xmppRoster) : ownJID_(ownJID), iqRouter_(iqRouter), xmppRoster_(xmppRoster) {
scopedConnection_ = xmppRoster_->onInitialRosterPopulated.connect(boost::bind(&MIXRegistry::syncRegistryWithRoster, this));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

scopedConnection_ doesn't sensibly describe the variable. That it is a scoped connection is clear from the type. Maybe name it initialRosterPopulationConnection_ or something like that

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.


}

void MIXRegistry::syncRegistryWithRoster() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be a C++11 lambda instead in the ctor, as the method name doesn't match project convention anyway (handle*).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a lambda function.

SWIFT_LOG(debug) << "Syncing with roster items. " << std::endl;
for (auto item : xmppRoster_->getItems()) {
if (item.isMIXChannel()) {
SWIFT_LOG(debug) << item.getJID() << std::endl;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pretty detailed logging. Probably not needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

SWIFT_LOG(debug) << item.getJID() << std::endl;
auto i = entries_.find(item.getJID());
if (i == entries_.end()) {
SWIFT_LOG(debug) << "Adding " << item.getJID() << std::endl;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pretty detailed logging. Probably not needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

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

public:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please move those in front of the the private methods above.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

Change-Id: Ie4a349c6b9ef35f08f367885755446c316f1e20e
Copy link
Collaborator

@tfar tfar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks ok to me now. What do you think @Kev ?

@tfar tfar requested a review from Kev August 23, 2017 20:26
@tarun018 tarun018 changed the title Add Limber MIX Mock Server and MIX Example Add MIXRegistry, Limber MIX Mock Server and MIX Example Aug 28, 2017
@tfar
Copy link
Collaborator

tfar commented Sep 4, 2017

@Kev, both @intosi and I have reviewed this PR. If you can give it a review too I can go on and submit it to master.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants