remote-conference-list-event-handler.cpp 8.17 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/*
 * remote-conference-list-event-handler.cpp
 * Copyright (C) 2010-2018 Belledonne Communications SARL
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

Benjamin REIS's avatar
Benjamin REIS committed
20 21 22 23 24
#include "linphone/core.h"
#include "linphone/event.h"
#include "linphone/proxy_config.h"
#include "linphone/utils/utils.h"

25
#include "address/address.h"
Benjamin REIS's avatar
Benjamin REIS committed
26
#include "c-wrapper/c-wrapper.h"
27 28
#include "content/content-manager.h"
#include "content/content-type.h"
Benjamin REIS's avatar
Benjamin REIS committed
29
#include "core/core-p.h"
30
#include "logger/logger.h"
31 32
#include "remote-conference-event-handler.h"
#include "remote-conference-list-event-handler.h"
33
#include "xml/conference-info.h"
34 35 36
#include "xml/resource-lists.h"
#include "xml/rlmi.h"

Benjamin REIS's avatar
Benjamin REIS committed
37 38 39
// TODO: Remove me later.
#include "private.h"

40 41 42 43 44 45 46 47
// =============================================================================

using namespace std;

LINPHONE_BEGIN_NAMESPACE

// -----------------------------------------------------------------------------

48 49 50 51 52 53 54 55 56 57 58
RemoteConferenceListEventHandler::RemoteConferenceListEventHandler (const std::shared_ptr<Core> &core) : CoreAccessor(core) {
	getCore()->getPrivate()->registerListener(this);
}

RemoteConferenceListEventHandler::~RemoteConferenceListEventHandler () {
	try {
		getCore()->getPrivate()->unregisterListener(this);
	} catch (const bad_weak_ptr &) {
		// Unable to unregister listener here. Core is destroyed and the listener doesn't exist.
	}

59
	unsubscribe();
60 61 62 63
}

// -----------------------------------------------------------------------------

64
void RemoteConferenceListEventHandler::subscribe () {
Benjamin REIS's avatar
Benjamin REIS committed
65 66 67 68
	if (lev) {
		linphone_event_unref(lev);
		lev = nullptr;
	}
69

Benjamin REIS's avatar
Benjamin REIS committed
70 71 72
	if (handlers.size() == 0)
		return;

Benjamin REIS's avatar
Benjamin REIS committed
73
	Content content;
74 75 76 77 78
	content.setContentType(ContentType::ResourceLists);

	Xsd::ResourceLists::ResourceLists rl = Xsd::ResourceLists::ResourceLists();
	Xsd::ResourceLists::ListType l = Xsd::ResourceLists::ListType();
	for (const auto &handler : handlers) {
Benjamin REIS's avatar
Benjamin REIS committed
79 80 81 82 83 84 85 86 87
		const ChatRoomId &chatRoomId = handler->getChatRoomId();
		shared_ptr<AbstractChatRoom> cr = getCore()->findChatRoom(chatRoomId);
		if (!cr)
			continue;

		if (cr->hasBeenLeft())
			continue;

		Address addr = chatRoomId.getPeerAddress();
Benjamin REIS's avatar
Benjamin REIS committed
88
		addr.setUriParam("Last-Notify", Utils::toString(handler->getLastNotify()));
89
		Xsd::ResourceLists::EntryType entry = Xsd::ResourceLists::EntryType(addr.asStringUriOnly());
90 91 92 93 94 95 96 97 98 99
		l.getEntry().push_back(entry);
	}
	rl.getList().push_back(l);

	Xsd::XmlSchema::NamespaceInfomap map;
	stringstream xmlBody;
	serializeResourceLists(xmlBody, rl, map);
	content.setBody(xmlBody.str());

	LinphoneCore *lc = getCore()->getCCore();
Benjamin REIS's avatar
Benjamin REIS committed
100 101
	LinphoneProxyConfig *cfg = linphone_core_get_default_proxy_config(lc);
	if (!cfg || (linphone_proxy_config_get_state(cfg) != LinphoneRegistrationOk))
102
		return;
Benjamin REIS's avatar
Benjamin REIS committed
103 104

	LinphoneAddress *rlsAddr = linphone_address_new(linphone_proxy_config_get_conference_factory_uri(cfg));
105

106
	lev = linphone_core_create_subscribe(lc, rlsAddr, "conference", 600);
Benjamin REIS's avatar
Benjamin REIS committed
107
	char *from = linphone_address_as_string(linphone_proxy_config_get_contact(linphone_core_get_default_proxy_config(getCore()->getCCore())));
108
	lev->op->setFrom(from);
Benjamin REIS's avatar
Benjamin REIS committed
109
	bctbx_free(from);
110 111
	linphone_address_unref(rlsAddr);
	linphone_event_set_internal(lev, TRUE);
112 113 114 115 116 117 118
	linphone_event_add_custom_header(lev, "Require", "recipient-list-subscribe");
	linphone_event_add_custom_header(lev, "Accept", "multipart/related, application/conference-info+xml, application/rlmi+xml");
	linphone_event_add_custom_header(lev, "Content-Disposition", "recipient-list");
	if (linphone_core_content_encoding_supported(lc, "deflate")) {
		content.setContentEncoding("deflate");
		linphone_event_add_custom_header(lev, "Accept-Encoding", "deflate");
	}
119
	linphone_event_set_user_data(lev, this);
120 121
	LinphoneContent *cContent = L_GET_C_BACK_PTR(&content);
	linphone_event_send_subscribe(lev, cContent);
122 123 124
}

void RemoteConferenceListEventHandler::unsubscribe () {
125 126 127 128
	if (lev) {
		linphone_event_terminate(lev);
		lev = nullptr;
	}
129 130
}

131
void RemoteConferenceListEventHandler::notifyReceived (const Content *notifyContent) {
132
	char *from = linphone_address_as_string(linphone_event_get_from(lev));
133 134
	const IdentityAddress local(from);

Benjamin REIS's avatar
Benjamin REIS committed
135
	if (notifyContent->getContentType().weakEqual(ContentType::ConferenceInfo)) {
136 137 138 139 140 141 142 143 144 145 146 147
		// Simple notify received directly from a chat-room
		const string &xmlBody = notifyContent->getBodyAsString();
		istringstream data(xmlBody);
		unique_ptr<Xsd::ConferenceInfo::ConferenceType> confInfo = Xsd::ConferenceInfo::parseConferenceInfo(data, Xsd::XmlSchema::Flags::dont_validate);

		IdentityAddress entityAddress(confInfo->getEntity().c_str());
		ChatRoomId id(entityAddress, local);
		RemoteConferenceEventHandler *handler = findHandler(id);
		if (!handler)
			return;

		handler->notifyReceived(xmlBody);
148
		return;
149 150 151
	}

	list<Content> contents = ContentManager::multipartToContentList(*notifyContent);
152
	bctbx_free(from);
153
	map<string, IdentityAddress> addresses;
154 155
	for (const auto &content : contents) {
		const string &body = content.getBodyAsString();
Benjamin REIS's avatar
Benjamin REIS committed
156 157
		const ContentType &contentType = content.getContentType();
		if (contentType == ContentType::Rlmi) {
158 159 160 161
			addresses = parseRlmi(body);
			continue;
		}

162
		const string &cid = content.getHeader("Content-Id").getValue();
163
		if (cid.empty())
Benjamin REIS's avatar
Benjamin REIS committed
164 165
			continue;

Benjamin REIS's avatar
Benjamin REIS committed
166 167 168 169 170
		map<string, IdentityAddress>::const_iterator it = addresses.find(cid);
		if (it == addresses.cend())
			continue;

		IdentityAddress peer = it->second;
171
		ChatRoomId id(peer, local);
Benjamin REIS's avatar
Benjamin REIS committed
172
		RemoteConferenceEventHandler *handler = findHandler(id);
173 174 175
		if (!handler)
			continue;

Benjamin REIS's avatar
Benjamin REIS committed
176
		if (contentType.weakEqual(ContentType::Multipart))
177
			handler->multipartNotifyReceived(body);
Benjamin REIS's avatar
Benjamin REIS committed
178
		else if (contentType.weakEqual(ContentType::ConferenceInfo))
179 180 181 182
			handler->notifyReceived(body);
	}
}

183 184
// -----------------------------------------------------------------------------

Benjamin REIS's avatar
Benjamin REIS committed
185
RemoteConferenceEventHandler *RemoteConferenceListEventHandler::findHandler (const ChatRoomId &chatRoomId) const {
186 187 188 189 190 191 192 193
	for (const auto &handler : handlers) {
		if (handler->getChatRoomId() == chatRoomId)
			return handler;
	}

	return nullptr;
}

Benjamin REIS's avatar
Benjamin REIS committed
194
const list<RemoteConferenceEventHandler *> &RemoteConferenceListEventHandler::getHandlers () const {
195 196 197
	return handlers;
}

Benjamin REIS's avatar
Benjamin REIS committed
198 199 200 201 202 203 204 205
void RemoteConferenceListEventHandler::addHandler (RemoteConferenceEventHandler *handler) {
	if (handler)
		handlers.push_back(handler);
}

void RemoteConferenceListEventHandler::removeHandler (RemoteConferenceEventHandler *handler) {
	if (handler)
		handlers.remove(handler);
206 207
}

208
map<string, IdentityAddress> RemoteConferenceListEventHandler::parseRlmi (const string &xmlBody) const {
Benjamin REIS's avatar
Benjamin REIS committed
209 210
	istringstream data(xmlBody);
	unique_ptr<Xsd::Rlmi::List> rlmi(Xsd::Rlmi::parseList(
211 212 213
		data,
		Xsd::XmlSchema::Flags::dont_validate
	));
214
	map<string, IdentityAddress> addresses;
Benjamin REIS's avatar
Benjamin REIS committed
215 216 217 218
	for (const auto &resource : rlmi->getResource()) {
		if (resource.getInstance().empty())
			continue;

Benjamin REIS's avatar
Benjamin REIS committed
219 220 221 222 223
		const string &uri = string(resource.getUri());
		if (uri.empty())
			continue;

		IdentityAddress peer(uri);
Benjamin REIS's avatar
Benjamin REIS committed
224
		for (const auto &instance : resource.getInstance()) {
Benjamin REIS's avatar
Benjamin REIS committed
225 226
			const string &cid = string(instance.getId());
			if (cid.empty())
Benjamin REIS's avatar
Benjamin REIS committed
227 228
				continue;

Benjamin REIS's avatar
Benjamin REIS committed
229
			addresses.emplace(cid, peer);
230 231 232 233 234
		}
	}
	return addresses;
}

235
// -----------------------------------------------------------------------------
236

237 238 239
void RemoteConferenceListEventHandler::onNetworkReachable (bool sipNetworkReachable, bool mediaNetworkReachable) {
	if (!sipNetworkReachable)
		unsubscribe();
240 241
}

242 243 244
void RemoteConferenceListEventHandler::onRegistrationStateChanged (LinphoneProxyConfig *cfg, LinphoneRegistrationState state, const std::string &message) {
	if (state == LinphoneRegistrationOk)
		subscribe();
245 246
}

247 248 249 250 251 252 253 254
void RemoteConferenceListEventHandler::onEnteringBackground () {
	unsubscribe();
}

void RemoteConferenceListEventHandler::onEnteringForeground () {
	subscribe();
}

255
LINPHONE_END_NAMESPACE