summaryrefslogtreecommitdiffstats
blob: 95ce23bde659cbddc5ea4b9f5c56563234d149d6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
/*
 * Copyright (c) 2010 Kevin Smith
 * Licensed under the GNU General Public License v3.
 * See Documentation/Licenses/GPLv3.txt for more information.
 */

#include <Swift/Controllers/DiscoServiceWalker.h>

#include <boost/bind.hpp>

#include "Swiften/Disco/GetDiscoInfoRequest.h"
#include "Swiften/Disco/GetDiscoItemsRequest.h"

namespace Swift {

DiscoServiceWalker::DiscoServiceWalker(const JID& service, IQRouter* iqRouter, size_t maxSteps) : service_(service), iqRouter_(iqRouter), maxSteps_(maxSteps) {

}

void DiscoServiceWalker::beginWalk() {
	assert(servicesBeingSearched_.size() == 0);
	walkNode(service_);
}

void DiscoServiceWalker::walkNode(const JID& jid) {
	servicesBeingSearched_.push_back(jid);
	searchedServices_.push_back(jid);
	GetDiscoInfoRequest::ref discoInfoRequest = GetDiscoInfoRequest::create(jid, iqRouter_);
	discoInfoRequest->onResponse.connect(boost::bind(&DiscoServiceWalker::handleDiscoInfoResponse, this, _1, _2, jid));
	discoInfoRequest->send();
}

void DiscoServiceWalker::handleReceivedDiscoItem(const JID& item) {
	if (std::find(searchedServices_.begin(), searchedServices_.end(), item) != searchedServices_.end()) {
		/* Don't recurse infinitely */
		return;
	}
	walkNode(item);
}

void DiscoServiceWalker::handleDiscoInfoResponse(boost::shared_ptr<DiscoInfo> info, ErrorPayload::ref error, const JID& jid) {
	if (error) {
		handleDiscoError(jid, error);
		return;
	}

	bool couldContainServices = false;
	foreach (DiscoInfo::Identity identity, info->getIdentities()) {
		if (identity.getCategory() == "server") {
			couldContainServices = true;
		}
	}
	bool completed = false;
	if (couldContainServices) {
		GetDiscoItemsRequest::ref discoItemsRequest = GetDiscoItemsRequest::create(jid, iqRouter_);
		discoItemsRequest->onResponse.connect(boost::bind(&DiscoServiceWalker::handleDiscoItemsResponse, this, _1, _2, jid));
		discoItemsRequest->send();
	} else {
		completed = true;
	}
	onServiceFound(jid, info);
	if (completed) {
		markNodeCompleted(jid);
	}
}

void DiscoServiceWalker::handleDiscoItemsResponse(boost::shared_ptr<DiscoItems> items, ErrorPayload::ref error, const JID& jid) {
	if (error) {
		handleDiscoError(jid, error);
		return;
	}
	foreach (DiscoItems::Item item, items->getItems()) {
		if (item.getNode().isEmpty()) {
			/* Don't look at noded items. It's possible that this will exclude some services,
			 * but I've never seen one in the wild, and it's an easy fix for not looping.
			 */
			handleReceivedDiscoItem(item.getJID());
		}
	}
	markNodeCompleted(jid);
}

void DiscoServiceWalker::handleDiscoError(const JID& jid, ErrorPayload::ref /*error*/) {
	markNodeCompleted(jid);
}

void DiscoServiceWalker::markNodeCompleted(const JID& jid) {
	servicesBeingSearched_.erase(std::remove(servicesBeingSearched_.begin(), servicesBeingSearched_.end(), jid), servicesBeingSearched_.end());
	/* All results are in */
	if (servicesBeingSearched_.size() == 0) {
		onWalkComplete();
	}
	/* Check if we're on a rampage */
	if (searchedServices_.size() >= maxSteps_) {
		onWalkComplete();
	}
}

}