local-conference-event-handler.cpp 20.87 KiB
/*
 * local-conference-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.
#include <ctime>
#include "linphone/api/c-content.h"
#include "linphone/utils/utils.h"
#include "c-wrapper/c-wrapper.h"
#include "conference/local-conference.h"
#include "conference/participant-device.h"
#include "conference/participant-p.h"
#include "content/content-manager.h"
#include "content/content-type.h"
#include "content/content.h"
#include "core/core-p.h"
#include "db/main-db.h"
#include "event-log/events.h"
#include "local-conference-event-handler-p.h"
#include "logger/logger.h"
#include "object/object-p.h"
// TODO: remove me.
#include "private.h"
// =============================================================================
using namespace std;
LINPHONE_BEGIN_NAMESPACE
using namespace Xsd::ConferenceInfo;
// -----------------------------------------------------------------------------
void LocalConferenceEventHandlerPrivate::notifyFullState (const string &notify, const shared_ptr<ParticipantDevice> &device) {
	notifyParticipantDevice(notify, device);
void LocalConferenceEventHandlerPrivate::notifyAllExcept (const string &notify, const shared_ptr<Participant> &exceptParticipant) {
	for (const auto &participant : conf->getParticipants()) {
		if (participant != exceptParticipant)
			notifyParticipant(notify, participant);
void LocalConferenceEventHandlerPrivate::notifyAll (const string &notify) {
	for (const auto &participant : conf->getParticipants())
		notifyParticipant(notify, participant);
string LocalConferenceEventHandlerPrivate::createNotifyFullState (int notifyId, bool oneToOne) {
	string entity = conf->getConferenceAddress().asString();
	string subject = conf->getSubject();
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
ConferenceType confInfo = ConferenceType(entity); UsersType users; ConferenceDescriptionType confDescr = ConferenceDescriptionType(); confDescr.setSubject(subject); if (oneToOne) { KeywordsType keywords(sizeof(char), "one-to-one"); confDescr.setKeywords(keywords); } confInfo.setUsers(users); confInfo.setConferenceDescription((const ConferenceDescriptionType) confDescr); for (const auto &participant : conf->getParticipants()) { UserType user = UserType(); UserRolesType roles; UserType::EndpointSequence endpoints; user.setRoles(roles); user.setEndpoint(endpoints); user.setEntity(participant->getAddress().asString()); user.getRoles()->getEntry().push_back(participant->isAdmin() ? "admin" : "participant"); user.setState(StateType::full); for (const auto &device : participant->getPrivate()->getDevices()) { const string &gruu = device->getAddress().asString(); EndpointType endpoint = EndpointType(); endpoint.setEntity(gruu); endpoint.setState(StateType::full); user.getEndpoint().push_back(endpoint); } confInfo.getUsers()->getUser().push_back(user); } return createNotify(confInfo, notifyId, true); } string LocalConferenceEventHandlerPrivate::createNotifyMultipart (int notifyId) { list<shared_ptr<EventLog>> events = conf->getCore()->getPrivate()->mainDb->getConferenceNotifiedEvents( ConferenceId(conf->getConferenceAddress(), conf->getConferenceAddress()), static_cast<unsigned int>(notifyId) ); list<Content> contents; for (const auto &eventLog : events) { Content *content = new Content(); content->setContentType(ContentType::ConferenceInfo); string body; shared_ptr<ConferenceNotifiedEvent> notifiedEvent = static_pointer_cast<ConferenceNotifiedEvent>(eventLog); int eventNotifyId = static_cast<int>(notifiedEvent->getNotifyId()); switch (eventLog->getType()) { case EventLog::Type::ConferenceParticipantAdded: { shared_ptr<ConferenceParticipantEvent> addedEvent = static_pointer_cast<ConferenceParticipantEvent>(eventLog); body = createNotifyParticipantAdded( addedEvent->getParticipantAddress(), eventNotifyId ); } break; case EventLog::Type::ConferenceParticipantRemoved: { shared_ptr<ConferenceParticipantEvent> removedEvent = static_pointer_cast<ConferenceParticipantEvent>(eventLog); body = createNotifyParticipantRemoved( removedEvent->getParticipantAddress(), eventNotifyId ); } break; case EventLog::Type::ConferenceParticipantSetAdmin: { shared_ptr<ConferenceParticipantEvent> setAdminEvent = static_pointer_cast<ConferenceParticipantEvent>(eventLog); body = createNotifyParticipantAdminStatusChanged( setAdminEvent->getParticipantAddress(), true,
141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
eventNotifyId ); } break; case EventLog::Type::ConferenceParticipantUnsetAdmin: { shared_ptr<ConferenceParticipantEvent> unsetAdminEvent = static_pointer_cast<ConferenceParticipantEvent>(eventLog); body = createNotifyParticipantAdminStatusChanged( unsetAdminEvent->getParticipantAddress(), false, eventNotifyId ); } break; case EventLog::Type::ConferenceParticipantDeviceAdded: { shared_ptr<ConferenceParticipantDeviceEvent> deviceAddedEvent = static_pointer_cast<ConferenceParticipantDeviceEvent>(eventLog); body = createNotifyParticipantDeviceAdded( deviceAddedEvent->getParticipantAddress(), deviceAddedEvent->getDeviceAddress(), eventNotifyId ); } break; case EventLog::Type::ConferenceParticipantDeviceRemoved: { shared_ptr<ConferenceParticipantDeviceEvent> deviceRemovedEvent = static_pointer_cast<ConferenceParticipantDeviceEvent>(eventLog); body = createNotifyParticipantDeviceRemoved( deviceRemovedEvent->getParticipantAddress(), deviceRemovedEvent->getDeviceAddress(), eventNotifyId ); } break; case EventLog::Type::ConferenceSubjectChanged: { shared_ptr<ConferenceSubjectEvent> subjectEvent = static_pointer_cast<ConferenceSubjectEvent>(eventLog); body = createNotifySubjectChanged( subjectEvent->getSubject(), eventNotifyId ); } break; default: // We should never pass here! L_ASSERT(false); continue; } contents.emplace_back(Content()); contents.back().setContentType(ContentType::ConferenceInfo); contents.back().setBody(body); } if (contents.empty()) return Utils::getEmptyConstRefObject<string>(); list<Content *> contentPtrs; for (auto &content : contents) contentPtrs.push_back(&content); string multipart = ContentManager::contentListToMultipart(contentPtrs).getBodyAsUtf8String(); return multipart; } string LocalConferenceEventHandlerPrivate::createNotifyParticipantAdded (const Address &addr, int notifyId) { string entity = conf->getConferenceAddress().asString(); ConferenceType confInfo = ConferenceType(entity); UsersType users; confInfo.setUsers(users); UserType user = UserType(); UserRolesType roles; UserType::EndpointSequence endpoints; shared_ptr<Participant> p = conf->findParticipant(addr); if (p) {
211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
for (const auto &device : p->getPrivate()->getDevices()) { const string &gruu = device->getAddress().asString(); EndpointType endpoint = EndpointType(); endpoint.setEntity(gruu); endpoint.setState(StateType::full); user.getEndpoint().push_back(endpoint); } } user.setRoles(roles); user.setEntity(addr.asStringUriOnly()); user.getRoles()->getEntry().push_back("participant"); user.setState(StateType::full); confInfo.getUsers()->getUser().push_back(user); return createNotify(confInfo, notifyId); } string LocalConferenceEventHandlerPrivate::createNotifyParticipantAdminStatusChanged (const Address &addr, bool isAdmin, int notifyId) { string entity = conf->getConferenceAddress().asString(); ConferenceType confInfo = ConferenceType(entity); UsersType users; confInfo.setUsers(users); UserType user = UserType(); UserRolesType roles; user.setRoles(roles); user.setEntity(addr.asStringUriOnly()); user.getRoles()->getEntry().push_back(isAdmin ? "admin" : "participant"); user.setState(StateType::partial); confInfo.getUsers()->getUser().push_back(user); return createNotify(confInfo, notifyId); } string LocalConferenceEventHandlerPrivate::createNotifyParticipantRemoved (const Address &addr, int notifyId) { string entity = conf->getConferenceAddress().asString(); ConferenceType confInfo = ConferenceType(entity); UsersType users; confInfo.setUsers(users); UserType user = UserType(); user.setEntity(addr.asStringUriOnly()); user.setState(StateType::deleted); confInfo.getUsers()->getUser().push_back(user); return createNotify(confInfo, notifyId); } string LocalConferenceEventHandlerPrivate::createNotifyParticipantDeviceAdded (const Address &addr, const Address &gruu, int notifyId) { string entity = conf->getConferenceAddress().asString(); ConferenceType confInfo = ConferenceType(entity); UsersType users; confInfo.setUsers(users); UserType user = UserType(); UserType::EndpointSequence endpoints; user.setEntity(addr.asStringUriOnly()); user.setState(StateType::partial); EndpointType endpoint = EndpointType(); endpoint.setEntity(gruu.asStringUriOnly()); endpoint.setState(StateType::full); user.getEndpoint().push_back(endpoint); confInfo.getUsers()->getUser().push_back(user); return createNotify(confInfo, notifyId); }
281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
string LocalConferenceEventHandlerPrivate::createNotifyParticipantDeviceRemoved (const Address &addr, const Address &gruu, int notifyId) { string entity = conf->getConferenceAddress().asString(); ConferenceType confInfo = ConferenceType(entity); UsersType users; confInfo.setUsers(users); UserType user = UserType(); UserType::EndpointSequence endpoints; user.setEntity(addr.asStringUriOnly()); user.setState(StateType::partial); EndpointType endpoint = EndpointType(); endpoint.setEntity(gruu.asStringUriOnly()); endpoint.setState(StateType::deleted); user.getEndpoint().push_back(endpoint); confInfo.getUsers()->getUser().push_back(user); return createNotify(confInfo, notifyId); } string LocalConferenceEventHandlerPrivate::createNotifySubjectChanged (int notifyId) { return createNotifySubjectChanged(conf->getSubject(), notifyId); } // ----------------------------------------------------------------------------- void LocalConferenceEventHandlerPrivate::notifyResponseCb (const LinphoneEvent *ev) { LinphoneEventCbs *cbs = linphone_event_get_callbacks(ev); LocalConferenceEventHandlerPrivate *handler = reinterpret_cast<LocalConferenceEventHandlerPrivate *>( linphone_event_cbs_get_user_data(cbs) ); linphone_event_cbs_set_user_data(cbs, nullptr); linphone_event_cbs_set_notify_response(cbs, nullptr); if (linphone_event_get_reason(ev) != LinphoneReasonNone) return; for (const auto &p : handler->conf->getParticipants()) { for (const auto &d : p->getPrivate()->getDevices()) { if ((d->getConferenceSubscribeEvent() == ev) && (d->getState() == ParticipantDevice::State::Joining)) { handler->conf->onFirstNotifyReceived(d->getAddress()); return; } } } } // ----------------------------------------------------------------------------- string LocalConferenceEventHandlerPrivate::createNotify (ConferenceType confInfo, int notifyId, bool isFullState) { confInfo.setVersion(notifyId == -1 ? ++lastNotify : static_cast<unsigned int>(notifyId)); confInfo.setState(isFullState ? StateType::full : StateType::partial); if (!confInfo.getConferenceDescription()) { ConferenceDescriptionType description = ConferenceDescriptionType(); confInfo.setConferenceDescription(description); } time_t result = time(nullptr); confInfo.getConferenceDescription()->setFreeText(Utils::toString(static_cast<long>(result))); stringstream notify; Xsd::XmlSchema::NamespaceInfomap map; map[""].name = "urn:ietf:params:xml:ns:conference-info"; serializeConferenceInfo(notify, confInfo, map); return notify.str(); }
351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
string LocalConferenceEventHandlerPrivate::createNotifySubjectChanged (const string &subject, int notifyId) { string entity = conf->getConferenceAddress().asString(); ConferenceType confInfo = ConferenceType(entity); ConferenceDescriptionType confDescr = ConferenceDescriptionType(); confDescr.setSubject(subject); confInfo.setConferenceDescription((const ConferenceDescriptionType)confDescr); return createNotify(confInfo, notifyId); } void LocalConferenceEventHandlerPrivate::notifyParticipant (const string &notify, const shared_ptr<Participant> &participant) { for (const auto &device : participant->getPrivate()->getDevices()) notifyParticipantDevice(notify, device); } void LocalConferenceEventHandlerPrivate::notifyParticipantDevice (const string &notify, const shared_ptr<ParticipantDevice> &device, bool multipart) { if (!device->isSubscribedToConferenceEventPackage() || notify.empty()) return; LinphoneEvent *ev = device->getConferenceSubscribeEvent(); LinphoneEventCbs *cbs = linphone_event_get_callbacks(ev); linphone_event_cbs_set_user_data(cbs, this); linphone_event_cbs_set_notify_response(cbs, notifyResponseCb); Content content; content.setBodyFromUtf8(notify); ContentType contentType; if (multipart) { contentType = ContentType(ContentType::Multipart); contentType.addParameter("boundary", MultipartBoundary); } else contentType = ContentType(ContentType::ConferenceInfo); content.setContentType(contentType); if (linphone_core_content_encoding_supported(conf->getCore()->getCCore(), "deflate")) content.setContentEncoding("deflate"); LinphoneContent *cContent = L_GET_C_BACK_PTR(&content); linphone_event_notify(ev, cContent); } // ============================================================================= LocalConferenceEventHandler::LocalConferenceEventHandler (LocalConference *localConference, unsigned int notify) : Object(*new LocalConferenceEventHandlerPrivate) { L_D(); d->conf = localConference; d->lastNotify = notify; } // ----------------------------------------------------------------------------- void LocalConferenceEventHandler::subscribeReceived (LinphoneEvent *lev, bool oneToOne) { L_D(); const LinphoneAddress *lAddr = linphone_event_get_from(lev); char *addrStr = linphone_address_as_string(lAddr); shared_ptr<Participant> participant = d->conf->findParticipant(Address(addrStr)); bctbx_free(addrStr); if (!participant) { lError() << "Received SUBSCRIBE corresponds to no participant of the conference [" << d->conf->getConferenceAddress() << "], no NOTIFY sent"; linphone_event_deny_subscription(lev, LinphoneReasonDeclined); return; } const LinphoneAddress *lContactAddr = linphone_event_get_remote_contact(lev); char *contactAddrStr = linphone_address_as_string(lContactAddr); IdentityAddress contactAddr(contactAddrStr); bctbx_free(contactAddrStr); shared_ptr<ParticipantDevice> device = participant->getPrivate()->findDevice(contactAddr); if (!device || (device->getState() != ParticipantDevice::State::Present && device->getState() != ParticipantDevice::State::Joining)) { lError() << "Received SUBSCRIBE for conference [" << d->conf->getConferenceAddress()
421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
<< "], device sending subscribe [" << contactAddr << "] is not known, no NOTIFY sent"; linphone_event_deny_subscription(lev, LinphoneReasonDeclined); return; } linphone_event_accept_subscription(lev); if (linphone_event_get_subscription_state(lev) == LinphoneSubscriptionActive) { unsigned int lastNotify = static_cast<unsigned int>(Utils::stoi(linphone_event_get_custom_header(lev, "Last-Notify-Version"))); device->setConferenceSubscribeEvent(lev); if (lastNotify == 0 || (device->getState() == ParticipantDevice::State::Joining)) { lInfo() << "Sending initial notify of conference [" << d->conf->getConferenceAddress() << "] to: " << device->getAddress(); d->notifyFullState(d->createNotifyFullState(static_cast<int>(d->lastNotify), oneToOne), device); } else if (lastNotify < d->lastNotify) { lInfo() << "Sending all missed notify [" << lastNotify << "-" << d->lastNotify << "] for conference [" << d->conf->getConferenceAddress() << "] to: " << participant->getAddress(); d->notifyParticipantDevice(d->createNotifyMultipart(static_cast<int>(lastNotify)), device, true); } else if (lastNotify > d->lastNotify) { lError() << "Last notify received by client [" << lastNotify << "] for conference [" << d->conf->getConferenceAddress() << "] should not be higher than last notify sent by server [" << d->lastNotify << "]"; } } } void LocalConferenceEventHandler::subscriptionStateChanged (LinphoneEvent *lev, LinphoneSubscriptionState state) { L_D(); if (state == LinphoneSubscriptionTerminated) { const LinphoneAddress *lAddr = linphone_event_get_from(lev); char *addrStr = linphone_address_as_string(lAddr); shared_ptr<Participant> participant = d->conf->findParticipant(Address(addrStr)); bctbx_free(addrStr); if (!participant) return; const LinphoneAddress *lContactAddr = linphone_event_get_remote_contact(lev); char *contactAddrStr = linphone_address_as_string(lContactAddr); IdentityAddress contactAddr(contactAddrStr); bctbx_free(contactAddrStr); shared_ptr<ParticipantDevice> device = participant->getPrivate()->findDevice(contactAddr); if (!device) return; lInfo() << "End of subscription for device [" << device->getAddress() << "] of conference [" << d->conf->getConferenceAddress() << "]"; device->setConferenceSubscribeEvent(nullptr); } } shared_ptr<ConferenceParticipantEvent> LocalConferenceEventHandler::notifyParticipantAdded (const Address &addr) { L_D(); shared_ptr<Participant> participant = d->conf->findParticipant(addr); d->notifyAllExcept(d->createNotifyParticipantAdded(addr), participant); shared_ptr<ConferenceParticipantEvent> event = make_shared<ConferenceParticipantEvent>( EventLog::Type::ConferenceParticipantAdded, time(nullptr), d->conferenceId, d->lastNotify, addr ); return event; } shared_ptr<ConferenceParticipantEvent> LocalConferenceEventHandler::notifyParticipantRemoved (const Address &addr) { L_D(); shared_ptr<Participant> participant = d->conf->findParticipant(addr); d->notifyAllExcept(d->createNotifyParticipantRemoved(addr), participant); shared_ptr<ConferenceParticipantEvent> event = make_shared<ConferenceParticipantEvent>( EventLog::Type::ConferenceParticipantRemoved, time(nullptr), d->conferenceId, d->lastNotify, addr
491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
); return event; } shared_ptr<ConferenceParticipantEvent> LocalConferenceEventHandler::notifyParticipantSetAdmin (const Address &addr, bool isAdmin) { L_D(); d->notifyAll(d->createNotifyParticipantAdminStatusChanged(addr, isAdmin)); shared_ptr<ConferenceParticipantEvent> event = make_shared<ConferenceParticipantEvent>( isAdmin ? EventLog::Type::ConferenceParticipantSetAdmin : EventLog::Type::ConferenceParticipantUnsetAdmin, time(nullptr), d->conferenceId, d->lastNotify, addr ); return event; } shared_ptr<ConferenceSubjectEvent> LocalConferenceEventHandler::notifySubjectChanged () { L_D(); d->notifyAll(d->createNotifySubjectChanged()); shared_ptr<ConferenceSubjectEvent> event = make_shared<ConferenceSubjectEvent>( time(nullptr), d->conferenceId, d->lastNotify, d->conf->getSubject() ); return event; } shared_ptr<ConferenceParticipantDeviceEvent> LocalConferenceEventHandler::notifyParticipantDeviceAdded (const Address &addr, const Address &gruu) { L_D(); d->notifyAll(d->createNotifyParticipantDeviceAdded(addr, gruu)); shared_ptr<ConferenceParticipantDeviceEvent> event = make_shared<ConferenceParticipantDeviceEvent>( EventLog::Type::ConferenceParticipantDeviceAdded, time(nullptr), d->conferenceId, d->lastNotify, addr, gruu ); return event; } shared_ptr<ConferenceParticipantDeviceEvent> LocalConferenceEventHandler::notifyParticipantDeviceRemoved (const Address &addr, const Address &gruu) { L_D(); d->notifyAll(d->createNotifyParticipantDeviceRemoved(addr, gruu)); shared_ptr<ConferenceParticipantDeviceEvent> event = make_shared<ConferenceParticipantDeviceEvent>( EventLog::Type::ConferenceParticipantDeviceRemoved, time(nullptr), d->conferenceId, d->lastNotify, addr, gruu ); return event; } void LocalConferenceEventHandler::setLastNotify (unsigned int lastNotify) { L_D(); d->lastNotify = lastNotify; } void LocalConferenceEventHandler::setConferenceId (const ConferenceId &conferenceId) { L_D(); d->conferenceId = conferenceId; } ConferenceId LocalConferenceEventHandler::getConferenceId () const { L_D(); return d->conferenceId;
561562563564565566567568569570571572573574
} string LocalConferenceEventHandler::getNotifyForId (int notifyId, bool oneToOne) { L_D(); if (notifyId == 0) return d->createNotifyFullState(static_cast<int>(d->lastNotify), oneToOne); else if (notifyId < static_cast<int>(d->lastNotify)) return d->createNotifyMultipart(notifyId); return Utils::getEmptyConstRefObject<string>(); } LINPHONE_END_NAMESPACE