client-group-chat-room.cpp 20.8 KB
Newer Older
1 2
/*
 * client-group-chat-room.cpp
3
 * Copyright (C) 2010-2018 Belledonne Communications SARL
4
 *
Ghislain MARY's avatar
Ghislain MARY committed
5 6 7 8
 * 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.
9 10 11 12 13 14 15
 *
 * 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
Ghislain MARY's avatar
Ghislain MARY committed
16 17
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 19
 */

20 21
#include "linphone/utils/utils.h"

22
#include "address/address-p.h"
23
#include "basic-to-client-group-chat-room.h"
24
#include "c-wrapper/c-wrapper.h"
25
#include "client-group-chat-room-p.h"
26
#include "conference/handlers/remote-conference-event-handler.h"
27
#include "conference/participant-p.h"
28 29
#include "conference/remote-conference-p.h"
#include "conference/session/call-session-p.h"
30
#include "core/core-p.h"
31
#include "logger/logger.h"
32
#include "sal/refer-op.h"
33

34
// =============================================================================
35

36 37 38 39
using namespace std;

LINPHONE_BEGIN_NAMESPACE

40 41
// -----------------------------------------------------------------------------

42
list<IdentityAddress> ClientGroupChatRoomPrivate::cleanAddressesList (const list<IdentityAddress> &addresses) const {
43
	L_Q();
44
	list<IdentityAddress> cleanedList(addresses);
45 46 47
	cleanedList.sort();
	cleanedList.unique();
	for (auto it = cleanedList.begin(); it != cleanedList.end();) {
48
		if (q->findParticipant(*it) || (q->getMe()->getAddress() == *it))
49 50 51 52 53 54 55
			it = cleanedList.erase(it);
		else
			it++;
	}
	return cleanedList;
}

56 57
shared_ptr<CallSession> ClientGroupChatRoomPrivate::createSession () {
	L_Q();
58
	L_Q_T(RemoteConference, qConference);
59

60 61
	CallSessionParams csp;
	csp.addCustomHeader("Require", "recipient-list-invite");
62
	csp.addCustomContactParameter("text");
63 64
	if (capabilities & ClientGroupChatRoom::Capabilities::OneToOne)
		csp.addCustomHeader("One-To-One-Chat-Room", "true");
65

66 67 68 69 70
	ParticipantPrivate *dFocus = qConference->getPrivate()->focus->getPrivate();
	shared_ptr<CallSession> session = dFocus->createSession(*q, &csp, false, callSessionListener);
	Address myCleanedAddress(q->getMe()->getAddress());
	myCleanedAddress.removeUriParam("gr"); // Remove gr parameter for INVITE.
	session->configure(LinphoneCallOutgoing, nullptr, nullptr, myCleanedAddress, dFocus->getDevices().front()->getAddress());
71
	session->initiateOutgoing();
Ghislain MARY's avatar
Ghislain MARY committed
72
	session->getPrivate()->createOp();
73 74 75
	return session;
}

Ghislain MARY's avatar
Ghislain MARY committed
76
void ClientGroupChatRoomPrivate::notifyReceived (const string &body) {
77 78
	L_Q_T(RemoteConference, qConference);
	qConference->getPrivate()->eventHandler->notifyReceived(body);
79 80
}

81 82 83 84 85
void ClientGroupChatRoomPrivate::multipartNotifyReceived (const string &body) {
	L_Q_T(RemoteConference, qConference);
	qConference->getPrivate()->eventHandler->multipartNotifyReceived(body);
}

86 87
// -----------------------------------------------------------------------------

88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
void ClientGroupChatRoomPrivate::setCallSessionListener (CallSessionListener *listener) {
	L_Q();
	L_Q_T(RemoteConference, qConference);
	callSessionListener = listener;
	shared_ptr<CallSession> session = qConference->getPrivate()->focus->getPrivate()->getSession();
	if (session)
		session->getPrivate()->setCallSessionListener(listener);
	for (const auto &participant : q->getParticipants()) {
		session = participant->getPrivate()->getSession();
		if (session)
			session->getPrivate()->setCallSessionListener(listener);
	}
}

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

104 105 106 107 108 109 110 111 112 113
void ClientGroupChatRoomPrivate::onChatRoomInsertRequested (const shared_ptr<AbstractChatRoom> &chatRoom) {
	L_Q();
	q->getCore()->getPrivate()->insertChatRoom(chatRoom);
}

void ClientGroupChatRoomPrivate::onChatRoomInsertInDatabaseRequested (const shared_ptr<AbstractChatRoom> &chatRoom) {
	L_Q();
	q->getCore()->getPrivate()->insertChatRoomWithDb(chatRoom);
}

114 115 116 117 118 119
void ClientGroupChatRoomPrivate::onChatRoomDeleteRequested (const shared_ptr<AbstractChatRoom> &chatRoom) {
	L_Q();
	q->getCore()->deleteChatRoom(q->getSharedFromThis());
	setState(ClientGroupChatRoom::State::Deleted);
}

120 121
// -----------------------------------------------------------------------------

122
void ClientGroupChatRoomPrivate::onCallSessionSetReleased (const shared_ptr<CallSession> &session) {
123 124 125 126 127 128 129 130
	L_Q_T(RemoteConference, qConference);

	ParticipantPrivate *participantPrivate = qConference->getPrivate()->focus->getPrivate();
	if (session == participantPrivate->getSession())
		participantPrivate->removeSession();
}

void ClientGroupChatRoomPrivate::onCallSessionStateChanged (
131
	const shared_ptr<CallSession> &session,
132
	CallSession::State newState,
133 134 135 136 137
	const string &message
) {
	L_Q();
	L_Q_T(RemoteConference, qConference);

138
	if (newState == CallSession::State::Connected) {
139
		if (q->getState() == ChatRoom::State::CreationPending) {
140 141
			IdentityAddress addr(session->getRemoteContactAddress()->asStringUriOnly());
			q->onConferenceCreated(addr);
142 143
			if (session->getRemoteContactAddress()->hasParam("isfocus")){
				bgTask.start(q->getCore(), 32); /*it will be stopped when receiving the first notify*/
144
				qConference->getPrivate()->eventHandler->subscribe(q->getChatRoomId());
145
			}
146
		} else if (q->getState() == ChatRoom::State::TerminationPending)
147
			qConference->getPrivate()->focus->getPrivate()->getSession()->terminate();
148 149 150 151 152 153 154 155 156 157 158
	} else if (newState == CallSession::State::Released) {
		if (q->getState() == ChatRoom::State::TerminationPending) {
			if (session->getReason() == LinphoneReasonNone) {
				// Everything is fine, the chat room has been left on the server side
				q->onConferenceTerminated(q->getConferenceAddress());
			} else {
				// Go to state TerminationFailed and then back to Created since it has not been terminated
				setState(ChatRoom::State::TerminationFailed);
				setState(ChatRoom::State::Created);
			}
		}
159 160 161 162
	} else if (newState == CallSession::State::Error) {
		if (q->getState() == ChatRoom::State::CreationPending)
			setState(ChatRoom::State::CreationFailed);
		else if (q->getState() == ChatRoom::State::TerminationPending) {
163 164 165 166 167 168 169 170
			if (session->getReason() == LinphoneReasonNotFound) {
				// Somehow the chat room is no longer know on the server, so terminate it
				q->onConferenceTerminated(q->getConferenceAddress());
			} else {
				// Go to state TerminationFailed and then back to Created since it has not been terminated
				setState(ChatRoom::State::TerminationFailed);
				setState(ChatRoom::State::Created);
			}
171
		}
172 173 174
	}
}

175 176
// =============================================================================

177
ClientGroupChatRoom::ClientGroupChatRoom (
178
	const shared_ptr<Core> &core,
179
	const string &uri,
180
	const IdentityAddress &me,
181
	const string &subject
182
) : ChatRoom(*new ClientGroupChatRoomPrivate, core, ChatRoomId(IdentityAddress(), me)),
183
RemoteConference(core, me, nullptr) {
184
	L_D_T(RemoteConference, dConference);
185 186
	L_D();
	
187 188 189
	IdentityAddress focusAddr(uri);
	dConference->focus = make_shared<Participant>(focusAddr);
	dConference->focus->getPrivate()->addDevice(focusAddr);
190
	RemoteConference::setSubject(subject);
191
	d->bgTask.setName("Subscribe/notify of full state conference");
192 193
}

194 195
ClientGroupChatRoom::ClientGroupChatRoom (
	const shared_ptr<Core> &core,
196
	const ChatRoomId &chatRoomId,
Ronan's avatar
Ronan committed
197
	shared_ptr<Participant> &me,
198
	AbstractChatRoom::CapabilitiesMask capabilities,
199
	const string &subject,
200
	list<shared_ptr<Participant>> &&participants,
201
	unsigned int lastNotifyId
202
) : ChatRoom(*new ClientGroupChatRoomPrivate, core, chatRoomId),
Ronan's avatar
Ronan committed
203
RemoteConference(core, me->getAddress(), nullptr) {
204
	L_D();
205
	L_D_T(RemoteConference, dConference);
206

207
	d->capabilities |= capabilities & ClientGroupChatRoom::Capabilities::OneToOne;
208
	const IdentityAddress &peerAddress = chatRoomId.getPeerAddress();
209
	dConference->focus = make_shared<Participant>(peerAddress);
210
	dConference->focus->getPrivate()->addDevice(peerAddress);
211 212
	dConference->conferenceAddress = peerAddress;
	dConference->subject = subject;
213
	dConference->participants = move(participants);
214

Ronan's avatar
Ronan committed
215
	getMe()->getPrivate()->setAdmin(me->isAdmin());
216

217
	dConference->eventHandler->setLastNotify(lastNotifyId);
218
	dConference->eventHandler->subscribe(getChatRoomId());
219 220
}

221 222 223 224 225
ClientGroupChatRoom::~ClientGroupChatRoom () {
	L_D();
	d->setCallSessionListener(nullptr);
}

226 227 228 229
shared_ptr<Core> ClientGroupChatRoom::getCore () const {
	return ChatRoom::getCore();
}

Sylvain Berfini's avatar
Sylvain Berfini committed
230
void ClientGroupChatRoom::allowCpim (bool value) {
231

Sylvain Berfini's avatar
Sylvain Berfini committed
232 233 234
}

void ClientGroupChatRoom::allowMultipart (bool value) {
235

Sylvain Berfini's avatar
Sylvain Berfini committed
236 237
}

238 239 240 241 242 243 244 245
bool ClientGroupChatRoom::canHandleCpim () const {
	return true;
}

bool ClientGroupChatRoom::canHandleMultipart () const {
	return true;
}

246
ClientGroupChatRoom::CapabilitiesMask ClientGroupChatRoom::getCapabilities () const {
247 248
	L_D();
	return d->capabilities;
249 250
}

251
bool ClientGroupChatRoom::hasBeenLeft () const {
252
	return getState() != State::Created;
253 254
}

255 256 257 258
bool ClientGroupChatRoom::canHandleParticipants () const {
	return RemoteConference::canHandleParticipants();
}

259
const IdentityAddress &ClientGroupChatRoom::getConferenceAddress () const {
260
	return RemoteConference::getConferenceAddress();
261
}
262

263 264 265 266 267 268 269 270 271 272
void ClientGroupChatRoom::deleteFromDb () {
	L_D();
	if (!hasBeenLeft()) {
		d->deletionOnTerminationEnabled = true;
		leave();
		return;
	}
	d->chatRoomListener->onChatRoomDeleteRequested(getSharedFromThis());
}

273 274
void ClientGroupChatRoom::addParticipant (const IdentityAddress &addr, const CallSessionParams *params, bool hasMedia) {
	list<IdentityAddress> addresses;
275 276
	addresses.push_back(addr);
	addParticipants(addresses, params, hasMedia);
277 278
}

279
void ClientGroupChatRoom::addParticipants (
280
	const list<IdentityAddress> &addresses,
281 282 283
	const CallSessionParams *params,
	bool hasMedia
) {
284
	L_D();
285
	L_D_T(RemoteConference, dConference);
286

287
	list<IdentityAddress> addressesList = d->cleanAddressesList(addresses);
288
	if (addressesList.empty())
289
		return;
290

291
	if ((getState() != ChatRoom::State::Instantiated) && (getState() != ChatRoom::State::Created)) {
292 293 294
		lError() << "Cannot add participants to the ClientGroupChatRoom in a state other than Instantiated or Created";
		return;
	}
295

296 297 298 299 300 301 302 303 304 305 306 307 308
	if ((getState() == ChatRoom::State::Created) && (d->capabilities & ClientGroupChatRoom::Capabilities::OneToOne)) {
		lError() << "Cannot add more participants to a OneToOne ClientGroupChatRoom";
		return;
	}

	if ((getState() == ChatRoom::State::Instantiated)
		&& (addressesList.size() == 1)
		&& (linphone_config_get_bool(linphone_core_get_config(L_GET_C_BACK_PTR(getCore())),
			"misc", "one_to_one_chat_room_enabled", TRUE))
	) {
		d->capabilities |= ClientGroupChatRoom::Capabilities::OneToOne;
	}

309
	Content content;
310
	content.setBody(getResourceLists(addressesList));
311 312
	content.setContentType("application/resource-lists+xml");
	content.setContentDisposition("recipient-list");
313

314
	shared_ptr<CallSession> session = dConference->focus->getPrivate()->getSession();
315
	if (session)
316
		session->update(nullptr, getSubject(), &content);
317 318
	else {
		session = d->createSession();
319
		session->startInvite(nullptr, getSubject(), &content);
320
		if (getState() == ChatRoom::State::Instantiated)
321
			d->setState(ChatRoom::State::CreationPending);
322
	}
323 324
}

325
void ClientGroupChatRoom::removeParticipant (const shared_ptr<const Participant> &participant) {
326
	LinphoneCore *cCore = getCore()->getCCore();
327 328

	SalReferOp *referOp = new SalReferOp(cCore->sal);
329
	LinphoneAddress *lAddr = linphone_address_new(getConferenceAddress().asString().c_str());
330
	linphone_configure_op(cCore, referOp, lAddr, nullptr, false);
331 332 333 334 335 336
	linphone_address_unref(lAddr);
	Address referToAddr = participant->getAddress();
	referToAddr.setParam("text");
	referToAddr.setUriParam("method", "BYE");
	referOp->send_refer(referToAddr.getPrivate()->getInternalAddress());
	referOp->unref();
337 338
}

339
void ClientGroupChatRoom::removeParticipants (const list<shared_ptr<Participant>> &participants) {
340
	RemoteConference::removeParticipants(participants);
341
}
342

343
shared_ptr<Participant> ClientGroupChatRoom::findParticipant (const IdentityAddress &addr) const {
344 345 346 347 348 349 350
	return RemoteConference::findParticipant(addr);
}

shared_ptr<Participant> ClientGroupChatRoom::getMe () const {
	return RemoteConference::getMe();
}

351 352
int ClientGroupChatRoom::getParticipantCount () const {
	return RemoteConference::getParticipantCount();
353 354
}

355
const list<shared_ptr<Participant>> &ClientGroupChatRoom::getParticipants () const {
356 357 358
	return RemoteConference::getParticipants();
}

359
void ClientGroupChatRoom::setParticipantAdminStatus (const shared_ptr<Participant> &participant, bool isAdmin) {
360 361
	if (isAdmin == participant->isAdmin())
		return;
362 363

	if (!getMe()->isAdmin()) {
364 365 366
		lError() << "Cannot change the participant admin status because I am not admin";
		return;
	}
367

368
	LinphoneCore *cCore = getCore()->getCCore();
369 370

	SalReferOp *referOp = new SalReferOp(cCore->sal);
371
	LinphoneAddress *lAddr = linphone_address_new(getConferenceAddress().asString().c_str());
372
	linphone_configure_op(cCore, referOp, lAddr, nullptr, false);
373 374 375 376 377 378 379 380
	linphone_address_unref(lAddr);
	Address referToAddr = participant->getAddress();
	referToAddr.setParam("text");
	referToAddr.setParam("admin", Utils::toString(isAdmin));
	referOp->send_refer(referToAddr.getPrivate()->getInternalAddress());
	referOp->unref();
}

381 382 383 384
const string &ClientGroupChatRoom::getSubject () const {
	return RemoteConference::getSubject();
}

385 386
void ClientGroupChatRoom::setSubject (const string &subject) {
	L_D();
387
	L_D_T(RemoteConference, dConference);
388

389
	if (getState() != ChatRoom::State::Created) {
390 391 392
		lError() << "Cannot change the ClientGroupChatRoom subject in a state other than Created";
		return;
	}
393 394

	if (!getMe()->isAdmin()) {
395 396 397
		lError() << "Cannot change the ClientGroupChatRoom subject because I am not admin";
		return;
	}
398

399
	shared_ptr<CallSession> session = dConference->focus->getPrivate()->getSession();
400 401 402 403 404 405
	if (session)
		session->update(nullptr, subject);
	else {
		session = d->createSession();
		session->startInvite(nullptr, subject, nullptr);
	}
406 407
}

408 409 410 411 412
void ClientGroupChatRoom::join () {
	L_D();
	L_D_T(RemoteConference, dConference);

	shared_ptr<CallSession> session = dConference->focus->getPrivate()->getSession();
413
	if (!session && ((getState() == ChatRoom::State::Instantiated) || (getState() == ChatRoom::State::Terminated))) {
414
		session = d->createSession();
415 416 417 418
	}
	if (session) {
		if (getState() != ChatRoom::State::TerminationPending)
			session->startInvite(nullptr, "", nullptr);
419 420 421 422 423 424 425 426 427
		d->setState(ChatRoom::State::CreationPending);
	}
}

void ClientGroupChatRoom::leave () {
	L_D();
	L_D_T(RemoteConference, dConference);

	dConference->eventHandler->unsubscribe();
428

429 430 431 432 433 434 435
	shared_ptr<CallSession> session = dConference->focus->getPrivate()->getSession();
	if (session)
		session->terminate();
	else {
		session = d->createSession();
		session->startInvite(nullptr, "", nullptr);
	}
436

437
	d->setState(ChatRoom::State::TerminationPending);
438 439
}

440 441
// -----------------------------------------------------------------------------

442
void ClientGroupChatRoom::onConferenceCreated (const IdentityAddress &addr) {
443
	L_D();
444 445
	L_D_T(RemoteConference, dConference);
	dConference->conferenceAddress = addr;
446
	dConference->focus->getPrivate()->setAddress(addr);
447 448
	dConference->focus->getPrivate()->clearDevices();
	dConference->focus->getPrivate()->addDevice(addr);
449
	d->chatRoomId = ChatRoomId(addr, d->chatRoomId.getLocalAddress());
450
	d->chatRoomListener->onChatRoomInsertRequested(getSharedFromThis());
451 452
}

453 454 455 456 457
void ClientGroupChatRoom::onConferenceKeywordsChanged (const vector<string> &keywords) {
	L_D();
	d->capabilities |= ClientGroupChatRoom::Capabilities::OneToOne;
}

458
void ClientGroupChatRoom::onConferenceTerminated (const IdentityAddress &addr) {
459
	L_D();
460 461
	L_D_T(RemoteConference, dConference);
	dConference->eventHandler->resetLastNotify();
462
	d->setState(ChatRoom::State::Terminated);
463
	d->addEvent(make_shared<ConferenceEvent>(
464 465 466 467
		EventLog::Type::ConferenceTerminated,
		time(nullptr),
		d->chatRoomId
	));
468 469 470 471
	if (d->deletionOnTerminationEnabled) {
		d->deletionOnTerminationEnabled = false;
		d->chatRoomListener->onChatRoomDeleteRequested(getSharedFromThis());
	}
472 473
}

474
void ClientGroupChatRoom::onFirstNotifyReceived (const IdentityAddress &addr) {
475 476
	L_D();
	d->setState(ChatRoom::State::Created);
477
	
478 479 480 481 482
	if (getParticipantCount() == 1) {
		ChatRoomId id(getParticipants().front()->getAddress(), getMe()->getAddress());
		shared_ptr<AbstractChatRoom> chatRoom = getCore()->findChatRoom(id);
		if (chatRoom && (chatRoom->getCapabilities() & ChatRoom::Capabilities::Basic)) {
			BasicToClientGroupChatRoom::migrate(getSharedFromThis(), chatRoom);
483
			goto end;
484 485 486
		}
	}

487
	d->chatRoomListener->onChatRoomInsertInDatabaseRequested(getSharedFromThis());
488 489
end:
	d->bgTask.stop();
490 491 492
	// TODO: Bug. Event is inserted many times.
	// Avoid this in the future. Deal with signals/slots system.
	#if 0
493
	d->addEvent(make_shared<ConferenceEvent>(
494 495 496 497
		EventLog::Type::ConferenceCreated,
		time(nullptr),
		d->chatRoomId
	));
498
	#endif
499 500
}

501
void ClientGroupChatRoom::onParticipantAdded (const shared_ptr<ConferenceParticipantEvent> &event, bool isFullState) {
502
	L_D();
503 504
	L_D_T(RemoteConference, dConference);

505
	const IdentityAddress &addr = event->getParticipantAddress();
506 507
	if (isMe(addr))
		return;
508

509 510 511 512 513
	shared_ptr<Participant> participant = findParticipant(addr);
	if (participant) {
		lWarning() << "Participant " << participant << " added but already in the list of participants!";
		return;
	}
514

Ronan's avatar
Ronan committed
515
	participant = make_shared<Participant>(addr);
516
	dConference->participants.push_back(participant);
517 518 519 520

	if (isFullState)
		return;

521
	d->addEvent(event);
Benjamin REIS's avatar
Benjamin REIS committed
522

523
	LinphoneChatRoom *cr = d->getCChatRoom();
524
	_linphone_chat_room_notify_participant_added(cr, L_GET_C_BACK_PTR(event));
525 526
}

527
void ClientGroupChatRoom::onParticipantRemoved (const shared_ptr<ConferenceParticipantEvent> &event, bool isFullState) {
528
	(void)isFullState;
529

530
	L_D();
531 532
	L_D_T(RemoteConference, dConference);

533
	const IdentityAddress &addr = event->getParticipantAddress();
534 535
	shared_ptr<Participant> participant = findParticipant(addr);
	if (!participant) {
536
		lWarning() << "Participant " << addr.asString() << " removed but not in the list of participants!";
537 538
		return;
	}
539

540
	dConference->participants.remove(participant);
541
	d->addEvent(event);
542

543
	LinphoneChatRoom *cr = d->getCChatRoom();
544
	_linphone_chat_room_notify_participant_removed(cr, L_GET_C_BACK_PTR(event));
545 546
}

547
void ClientGroupChatRoom::onParticipantSetAdmin (const shared_ptr<ConferenceParticipantEvent> &event, bool isFullState) {
548 549
	L_D();

550
	const IdentityAddress &addr = event->getParticipantAddress();
551
	shared_ptr<Participant> participant;
552
	if (isMe(addr))
553
		participant = getMe();
554 555
	else
		participant = findParticipant(addr);
556
	if (!participant) {
557
		lWarning() << "Participant " << addr.asString() << " admin status has been changed but is not in the list of participants!";
558 559
		return;
	}
560

561 562 563 564 565
	bool isAdmin = event->getType() == EventLog::Type::ConferenceParticipantSetAdmin;
	if (participant->isAdmin() == isAdmin)
		return; // No change in the local admin status, do not notify
	participant->getPrivate()->setAdmin(isAdmin);

566 567 568
	if (isFullState)
		return;

569
	d->addEvent(event);
Benjamin REIS's avatar
Benjamin REIS committed
570

571
	LinphoneChatRoom *cr = d->getCChatRoom();
572
	_linphone_chat_room_notify_participant_admin_status_changed(cr, L_GET_C_BACK_PTR(event));
573 574
}

575
void ClientGroupChatRoom::onSubjectChanged (const shared_ptr<ConferenceSubjectEvent> &event, bool isFullState) {
576 577
	L_D();

578 579
	if (getSubject() == event->getSubject())
		return; // No change in the local subject, do not notify
580
	RemoteConference::setSubject(event->getSubject());
581

582 583 584
	if (isFullState)
		return;

585
	d->addEvent(event);
Benjamin REIS's avatar
Benjamin REIS committed
586

587
	LinphoneChatRoom *cr = d->getCChatRoom();
588
	_linphone_chat_room_notify_subject_changed(cr, L_GET_C_BACK_PTR(event));
589 590
}

591
void ClientGroupChatRoom::onParticipantDeviceAdded (const shared_ptr<ConferenceParticipantDeviceEvent> &event, bool isFullState) {
592 593
	L_D();

594
	const IdentityAddress &addr = event->getParticipantAddress();
595
	shared_ptr<Participant> participant;
596
	if (isMe(addr))
597
		participant = getMe();
598 599 600
	else
		participant = findParticipant(addr);
	if (!participant) {
601
		lWarning() << "Participant " << addr.asString() << " added a device but is not in the list of participants!";
602 603
		return;
	}
604
	participant->getPrivate()->addDevice(event->getDeviceAddress());
605 606 607 608

	if (isFullState)
		return;

609
	d->addEvent(event);
Benjamin REIS's avatar
Benjamin REIS committed
610

611
	LinphoneChatRoom *cr = d->getCChatRoom();
612
	_linphone_chat_room_notify_participant_device_added(cr, L_GET_C_BACK_PTR(event));
613 614
}

615
void ClientGroupChatRoom::onParticipantDeviceRemoved (const shared_ptr<ConferenceParticipantDeviceEvent> &event, bool isFullState) {
616 617
	L_D();

618
	(void)isFullState;
619

620
	const IdentityAddress &addr = event->getParticipantAddress();
621
	shared_ptr<Participant> participant;
622
	if (isMe(addr))
623
		participant = getMe();
624 625 626
	else
		participant = findParticipant(addr);
	if (!participant) {
627
		lWarning() << "Participant " << addr.asString() << " removed a device but is not in the list of participants!";
628 629
		return;
	}
630
	participant->getPrivate()->removeDevice(event->getDeviceAddress());
631
	d->addEvent(event);
Benjamin REIS's avatar
Benjamin REIS committed
632

633
	LinphoneChatRoom *cr = d->getCChatRoom();
634
	_linphone_chat_room_notify_participant_device_removed(cr, L_GET_C_BACK_PTR(event));
635 636
}

637
LINPHONE_END_NAMESPACE