Newer
Older
/*
* 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 "linphone/utils/utils.h"
Ghislain MARY
committed
#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"
Ghislain MARY
committed
#include "object/object-p.h"
// TODO: remove me.
#include "private.h"
Ghislain MARY
committed
// =============================================================================
Ghislain MARY
committed
LINPHONE_BEGIN_NAMESPACE
using namespace Xsd::ConferenceInfo;
Ghislain MARY
committed
// -----------------------------------------------------------------------------
Ghislain MARY
committed
void LocalConferenceEventHandlerPrivate::notifyFullState (const string ¬ify, const shared_ptr<ParticipantDevice> &device) {
notifyParticipantDevice(notify, device);
void LocalConferenceEventHandlerPrivate::notifyAllExcept (const string ¬ify, const shared_ptr<Participant> &exceptParticipant) {
for (const auto &participant : conf->getParticipants()) {
if (participant != exceptParticipant)
notifyParticipant(notify, participant);
void LocalConferenceEventHandlerPrivate::notifyAll (const string ¬ify) {
for (const auto &participant : conf->getParticipants())
notifyParticipant(notify, participant);
Ghislain MARY
committed
Ghislain MARY
committed
string LocalConferenceEventHandlerPrivate::createNotifyFullState (int notifyId, bool oneToOne) {
string entity = conf->getConferenceAddress().asString();
string subject = conf->getSubject();
ConferenceType confInfo = ConferenceType(entity);
UsersType users;
ConferenceDescriptionType confDescr = ConferenceDescriptionType();
confDescr.setSubject(subject);
Ghislain MARY
committed
if (oneToOne) {
KeywordsType keywords(sizeof(char), "one-to-one");
confDescr.setKeywords(keywords);
}
confInfo.setConferenceDescription((const ConferenceDescriptionType) confDescr);
for (const auto &participant : conf->getParticipants()) {
UserType user = UserType();
UserRolesType roles;
UserType::EndpointSequence endpoints;
user.setEndpoint(endpoints);
user.setEntity(participant->getAddress().asString());
Ghislain MARY
committed
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->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,
eventNotifyId
);
} break;
case EventLog::Type::ConferenceParticipantUnsetAdmin: {
shared_ptr<ConferenceParticipantEvent> unsetAdminEvent = static_pointer_cast<ConferenceParticipantEvent>(eventLog);
body = createNotifyParticipantAdminStatusChanged(
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
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;
UserType user = UserType();
UserRolesType roles;
UserType::EndpointSequence endpoints;
shared_ptr<Participant> p = conf->findParticipant(addr);
if (p) {
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);
}
}
Ghislain MARY
committed
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;
UserType user = UserType();
UserRolesType roles;
user.setRoles(roles);
Ghislain MARY
committed
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;
UserType user = UserType();
Ghislain MARY
committed
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);
}
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);
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
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);
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
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();
}
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 ¬ify, const shared_ptr<Participant> &participant) {
for (const auto &device : participant->getPrivate()->getDevices())
notifyParticipantDevice(notify, device);
}
void LocalConferenceEventHandlerPrivate::notifyParticipantDevice (const string ¬ify, 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);
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;
}
// -----------------------------------------------------------------------------
Ghislain MARY
committed
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);
}
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()
<< "], device sending subscribe [" << contactAddr << "] is not known, no NOTIFY sent";
linphone_event_deny_subscription(lev, LinphoneReasonDeclined);
}
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();
Ghislain MARY
committed
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->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->lastNotify,
addr
);
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->lastNotify,
addr
);
return event;
Ghislain MARY
committed
shared_ptr<ConferenceSubjectEvent> LocalConferenceEventHandler::notifySubjectChanged () {
L_D();
d->notifyAll(d->createNotifySubjectChanged());
shared_ptr<ConferenceSubjectEvent> event = make_shared<ConferenceSubjectEvent>(
time(nullptr),
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->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->lastNotify,
addr,
gruu
);
return event;
}
void LocalConferenceEventHandler::setLastNotify (unsigned int lastNotify) {
L_D();
d->lastNotify = lastNotify;
}
void LocalConferenceEventHandler::setConferenceId (const ConferenceId &conferenceId) {
ConferenceId LocalConferenceEventHandler::getConferenceId () const {
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>();
}
Ghislain MARY
committed
LINPHONE_END_NAMESPACE