diff --git a/coreapi/bellesip_sal/sal_sdp.c b/coreapi/bellesip_sal/sal_sdp.c index 0fef5bcbb9c4b0abc018d62ba7f4edbdacb47a14..97f59a3e479d883be50478e48a5fed54277957e0 100644 --- a/coreapi/bellesip_sal/sal_sdp.c +++ b/coreapi/bellesip_sal/sal_sdp.c @@ -17,7 +17,6 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "c-wrapper/internal/c-tools.h" #include "sal_impl.h" inline OrtpRtcpXrStatSummaryFlag operator|=(OrtpRtcpXrStatSummaryFlag a, OrtpRtcpXrStatSummaryFlag b) { diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c index 94994bf71469a9d063532e65eb5816ded387c60a..c2518aa389c1e7dfdee45a4689fb1ee6dc9d57ca 100644 --- a/coreapi/callbacks.c +++ b/coreapi/callbacks.c @@ -49,6 +49,7 @@ #include "chat/chat-room/client-group-chat-room-p.h" #include "chat/chat-room/server-group-chat-room-p.h" #endif +#include "conference/participant-info.h" #include "conference/participant.h" #include "conference/session/call-session-p.h" #include "conference/session/call-session.h" @@ -125,8 +126,8 @@ static void call_received(SalCallOp *h) { return; } std::shared_ptr<Address> fromOp = Address::create(h->getFrom()); - list<std::shared_ptr<Address>> identAddresses = Utils::parseResourceLists(h->getRemoteBody()); - if (identAddresses.size() != 1) { + const auto participantList = Utils::parseResourceLists(h->getRemoteBody()); + if (participantList.size() != 1) { h->decline(SalReasonNotAcceptable); h->release(); return; @@ -134,10 +135,10 @@ static void call_received(SalCallOp *h) { const char *endToEndEncryptedStr = sal_custom_header_find(h->getRecvCustomHeaders(), "End-To-End-Encrypted"); bool encrypted = endToEndEncryptedStr && strcmp(endToEndEncryptedStr, "true") == 0; - + const auto &participant = (*participantList.begin())->getAddress(); std::shared_ptr<Address> confAddr = L_GET_PRIVATE_FROM_C_OBJECT(lc)->mainDb->findOneToOneConferenceChatRoomAddress( - fromOp, identAddresses.front(), encrypted); + fromOp, participant, encrypted); if (confAddr && confAddr->isValid()) { shared_ptr<AbstractChatRoom> chatRoom = L_GET_CPP_PTR_FROM_C_OBJECT(lc)->findChatRoom(ConferenceId(confAddr, confAddr)); @@ -164,9 +165,9 @@ static void call_received(SalCallOp *h) { const char *oneToOneChatRoomStr = sal_custom_header_find(h->getRecvCustomHeaders(), "One-To-One-Chat-Room"); if (oneToOneChatRoomStr && (strcmp(oneToOneChatRoomStr, "true") == 0)) { - list<std::shared_ptr<Address>> participantList = Utils::parseResourceLists(h->getRemoteBody()); + const auto participantList = Utils::parseResourceLists(h->getRemoteBody()); if (participantList.size() == 1) { - std::shared_ptr<Address> participant = participantList.front(); + const auto &participant = (*participantList.begin())->getAddress(); shared_ptr<AbstractChatRoom> chatRoom = L_GET_PRIVATE_FROM_C_OBJECT(lc)->findExhumableOneToOneChatRoom(to, participant, endToEndEncrypted == "true"); diff --git a/coreapi/conference.cpp b/coreapi/conference.cpp index 39d38a2d0f42934119c4c992a424d3de57882ac5..947a6b9a753eded63e9bc14a41ad81359a55bf14 100644 --- a/coreapi/conference.cpp +++ b/coreapi/conference.cpp @@ -21,9 +21,11 @@ #include "conference.h" #include "call/call.h" #include "conference/notify-conference-listener.h" +#include "conference/participant-info.h" #include "conference/participant.h" #include "conference/session/media-session-p.h" #include "core/core.h" +#include "factory/factory.h" #include "linphone/api/c-call.h" #include "linphone/core.h" @@ -212,6 +214,40 @@ bool Conference::isConferenceStarted() const { return conferenceStarted; } +void Conference::fillParticipantAttributes(std::shared_ptr<Participant> &p) { + const auto &pAddress = p->getAddress(); + const auto participantInfo = + std::find_if(mInvitedParticipants.cbegin(), mInvitedParticipants.cend(), + [&pAddress](const auto &info) { return pAddress->weakEqual(*info->getAddress()); }); + const auto conferenceAddressStr = + (getConferenceAddress() ? getConferenceAddress()->toString() : std::string("<address-not-defined>")); + + if (participantInfo == mInvitedParticipants.cend()) { + if (mInvitedParticipants.empty()) { + // It is a conference created on the fly, therefore all participants are speakers + p->setRole(Participant::Role::Speaker); + lInfo() << "Conference " << this << " (address " << conferenceAddressStr + << ") has been created on the fly, either by inviting addresses or by merging existing calls " + "therefoe participant " + << *pAddress << " is given the role of " << p->getRole(); + } else { + lInfo() << "Unable to find participant " << *pAddress + << " in the list of invited participants. Assuming its role to be " << p->getRole() + << " in conference " << this << " (address " << conferenceAddressStr << ")"; + } + } else { + const auto &role = (*participantInfo)->getRole(); + if (role == Participant::Role::Unknown) { + p->setRole(Participant::Role::Speaker); + lInfo() << "No role was given to participant " << *pAddress << " when the conference " << this + << " (address " << conferenceAddressStr << ") was created. Assuming its role to be " + << p->getRole(); + } else { + p->setRole(role); + } + } +} + bool Conference::addParticipant(const std::shared_ptr<Address> &participantAddress) { bool success = LinphonePrivate::Conference::addParticipant(participantAddress); @@ -222,6 +258,7 @@ bool Conference::addParticipant(const std::shared_ptr<Address> &participantAddre << conferenceAddressStr; time_t creationTime = time(nullptr); std::shared_ptr<LinphonePrivate::Participant> p = findParticipant(participantAddress); + fillParticipantAttributes(p); notifyParticipantAdded(creationTime, false, p); } else { lError() << "Unable to add participant with address " << *participantAddress << " to conference " @@ -239,6 +276,7 @@ bool Conference::addParticipant(std::shared_ptr<LinphonePrivate::Call> call) { if (p == nullptr) { auto session = call->getActiveSession(); p = Participant::create(this, remoteAddress); + fillParticipantAttributes(p); p->setFocus(false); std::shared_ptr<Address> toAddr; if (session) { @@ -444,27 +482,6 @@ void Conference::setState(LinphonePrivate::ConferenceInterface::State state) { } } -std::shared_ptr<ConferenceInfo> -Conference::createConferenceInfoWithOrganizer(const std::shared_ptr<Address> &organizer) const { - - std::list<std::shared_ptr<Address>> participantAddresses; - if (!invitedAddresses.empty()) { - participantAddresses = invitedAddresses; - } - - // Add participants that are not part of the invitees'list - for (const auto &p : getParticipants()) { - const auto &pAddress = p->getAddress(); - auto pIt = std::find_if(participantAddresses.begin(), participantAddresses.end(), - [&pAddress](const auto &address) { return (pAddress->weakEqual(*address)); }); - if (pIt == participantAddresses.end()) { - participantAddresses.push_back(pAddress); - } - } - - return createConferenceInfoWithCustomParticipantList(organizer, participantAddresses); -} - void Conference::notifyStateChanged(LinphonePrivate::ConferenceInterface::State state) { // Call listeners LinphonePrivate::Conference::notifyStateChanged(state); @@ -515,6 +532,30 @@ void Conference::transferStateChanged(LinphoneCore *lc, LinphoneCall *transfered } } +std::list<std::shared_ptr<Address>> Conference::getInvitedAddresses() const { + list<std::shared_ptr<Address>> addresses; + for (auto &participant : mInvitedParticipants) { + addresses.push_back(participant->getAddress()); + } + return addresses; +} + +ConferenceInfo::participant_list_t Conference::getFullParticipantList() const { + auto participantList = mInvitedParticipants; + // Add participants that are not part of the invitees'list + for (const auto &p : getParticipants()) { + const auto &pAddress = p->getAddress(); + auto pIt = std::find_if(participantList.begin(), participantList.end(), [&pAddress](const auto &participant) { + return (pAddress->weakEqual(*participant->getAddress())); + }); + if (pIt == participantList.end()) { + auto participantInfo = Factory::get()->createParticipantInfo(pAddress); + participantList.push_back(participantInfo); + } + } + return participantList; +} + } // end of namespace MediaConference LINPHONE_END_NAMESPACE diff --git a/coreapi/conference.h b/coreapi/conference.h index 78370947b35bb6abc6768c07ca87d058f308e57f..c4cc8e6cfaa7ef4f6831b1932210cdf6be7c25a8 100644 --- a/coreapi/conference.h +++ b/coreapi/conference.h @@ -24,6 +24,8 @@ #include "belle-sip/object++.hh" #include "conference-cbs.h" +#include "conference/conference-interface.h" +#include "conference/conference.h" #include "linphone/conference.h" #include "linphone/utils/general.h" @@ -63,8 +65,19 @@ void linphone_conference_set_state_changed_callback(LinphoneConference *obj, LINPHONE_BEGIN_NAMESPACE +class AudioControlInterface; +class VideoControlInterface; +class MixerSession; +class ConferenceParams; +class Call; +class CallSession; +class CallSessionListener; +class ParticipantDevice; +class AudioDevice; +class ConferenceId; + namespace MediaConference { // They are in a special namespace because of conflict of generic Conference classes in - // src/conference/* + // src/conference/* /* * Base class for audio/video conference. @@ -180,7 +193,7 @@ public: virtual void notifyStateChanged(LinphonePrivate::ConferenceInterface::State state) override; protected: - std::list<std::shared_ptr<Address>> invitedAddresses; + ConferenceInfo::participant_list_t mInvitedParticipants; // Legacy member std::string mConferenceID; @@ -196,9 +209,9 @@ protected: callStateChangedCb(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cstate, const char *message) = 0; virtual void transferStateChangedCb(LinphoneCore *lc, LinphoneCall *transfered, LinphoneCallState new_call_state) = 0; - - virtual std::shared_ptr<ConferenceInfo> - createConferenceInfoWithOrganizer(const std::shared_ptr<Address> &organizer) const override; + std::list<std::shared_ptr<Address>> getInvitedAddresses() const; + ConferenceInfo::participant_list_t getFullParticipantList() const; + void fillParticipantAttributes(std::shared_ptr<Participant> &p); }; } // end of namespace MediaConference diff --git a/coreapi/linphoneconference.c b/coreapi/linphoneconference.c index d0c93a4075eaf0e802cf3a2fb355091ed0feef2d..a62c037bc7088dfcbaa6420b5409c822b93e1e25 100644 --- a/coreapi/linphoneconference.c +++ b/coreapi/linphoneconference.c @@ -41,6 +41,7 @@ #include "linphone/conference.h" #include "local_conference.h" #include "remote_conference.h" +#include "linphone/utils/utils.h" using namespace std; diff --git a/coreapi/local_conference.cpp b/coreapi/local_conference.cpp index 82b9bf0dba4da8cfb49f8e19148a6ad6aa6985d1..c85c66cbf6ff4f9fe6805b0c70cb1d762d405fe5 100644 --- a/coreapi/local_conference.cpp +++ b/coreapi/local_conference.cpp @@ -18,14 +18,16 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "core/core-p.h" -#include "call/call-log.h" -#include "db/main-db.h" #include "local_conference.h" +#include "call/call-log.h" +#include "conference/params/media-session-params-p.h" +#include "conference/participant-info.h" #include "conference/participant.h" -#include "conference/session/mixers.h" #include "conference/session/media-session-p.h" -#include "conference/params/media-session-params-p.h" +#include "conference/session/mixers.h" +#include "core/core-p.h" +#include "db/main-db.h" +#include "factory/factory.h" #ifdef HAVE_ADVANCED_IM #include "conference/handlers/local-audio-video-conference-event-handler.h" #endif // HAVE_ADVANCED_IM @@ -140,9 +142,10 @@ void LocalConference::updateConferenceInformation(SalCallOp *op) { if (salAddress) { ms_free(salAddress); } - auto invited = std::find_if(invitedAddresses.begin(), invitedAddresses.end(), [&address](const auto &invitee) { - return address->weakEqual(*invitee); - }) != invitedAddresses.end(); + auto invited = + std::find_if(mInvitedParticipants.begin(), mInvitedParticipants.end(), [&address](const auto &invitee) { + return address->weakEqual(*invitee->getAddress()); + }) != mInvitedParticipants.end(); std::shared_ptr<Address> remoteAddress = Address::create((op->getDir() == SalOp::Dir::Incoming) ? op->getFrom() : op->getTo()); @@ -202,13 +205,10 @@ void LocalConference::updateConferenceInformation(SalCallOp *op) { getMe()->setAdmin(true); getMe()->setFocus(true); - invitedAddresses.clear(); const auto resourceList = op->getContentInRemote(ContentType::ResourceLists); - if (!resourceList.isEmpty()) { - auto invitees = Utils::parseResourceLists(resourceList); - invitedAddresses.insert(invitedAddresses.begin(), invitees.begin(), invitees.end()); - } - const auto &conferenceInfo = createConferenceInfoWithCustomParticipantList(organizer, invitedAddresses); + fillInvitedParticipantList(op, resourceList.isEmpty()); + + const auto &conferenceInfo = createConferenceInfoWithCustomParticipantList(organizer, mInvitedParticipants); auto infoState = ConferenceInfo::State::New; if (resourceList.isEmpty()) { infoState = ConferenceInfo::State::Cancelled; @@ -242,6 +242,27 @@ void LocalConference::updateConferenceInformation(SalCallOp *op) { } } +void LocalConference::fillInvitedParticipantList(SalCallOp *op, bool cancelling) { + mInvitedParticipants.clear(); + const auto resourceList = op->getContentInRemote(ContentType::ResourceLists); + if (!resourceList.isEmpty()) { + auto invitees = Utils::parseResourceLists(resourceList); + mInvitedParticipants = invitees; + if (!cancelling) { + auto organizerNotFound = + std::find_if(mInvitedParticipants.begin(), mInvitedParticipants.end(), [this](const auto &invitee) { + return organizer->weakEqual(*invitee->getAddress()); + }) == mInvitedParticipants.end(); + if (organizerNotFound && organizer) { + Participant::Role role = Participant::Role::Speaker; + lInfo() << "Setting role of organizer " << *organizer << " to " << role; + auto organizerInfo = Factory::get()->createParticipantInfo(organizer); + organizerInfo->setRole(role); + mInvitedParticipants.push_back(organizerInfo); + } + } + } +} void LocalConference::configure(SalCallOp *op) { LinphoneCore *lc = getCore()->getCCore(); bool admin = ((sal_address_has_param(op->getRemoteContactAddress(), "admin") && @@ -310,12 +331,7 @@ void LocalConference::configure(SalCallOp *op) { if (endTime <= 0) { endTime = -1; } - - const auto resourceList = op->getContentInRemote(ContentType::ResourceLists); - if (!resourceList.isEmpty()) { - auto invitees = Utils::parseResourceLists(resourceList); - invitedAddresses.insert(invitedAddresses.begin(), invitees.begin(), invitees.end()); - } + fillInvitedParticipantList(op, false); } else if (info) { subject = info->getSubject(); organizer = info->getOrganizerAddress(); @@ -327,11 +343,7 @@ void LocalConference::configure(SalCallOp *op) { } else { endTime = -1; } - - const auto &participants = info->getParticipants(); - for (const auto &p : participants) { - invitedAddresses.push_back(p.first); - } + mInvitedParticipants = info->getParticipants(); } auto videoEnabled = linphone_core_video_enabled(lc); @@ -399,10 +411,12 @@ void LocalConference::configure(SalCallOp *op) { } std::list<std::shared_ptr<Address>> LocalConference::getAllowedAddresses() const { - auto allowedAddresses = invitedAddresses; - auto organizerIt = std::find_if(invitedAddresses.begin(), invitedAddresses.end(), - [this](const auto &address) { return address->weakEqual(*organizer); }); - if (organizerIt == invitedAddresses.end()) { + auto allowedAddresses = getInvitedAddresses(); + ; + auto organizerIt = + std::find_if(mInvitedParticipants.begin(), mInvitedParticipants.end(), + [this](const auto &participant) { return participant->getAddress()->weakEqual(*organizer); }); + if (organizerIt == mInvitedParticipants.end()) { allowedAddresses.push_back(organizer); } return allowedAddresses; @@ -477,7 +491,7 @@ void LocalConference::confirmCreation() { } std::shared_ptr<ConferenceInfo> LocalConference::createConferenceInfo() const { - return createConferenceInfoWithOrganizer(organizer); + return createConferenceInfoWithCustomParticipantList(organizer, getFullParticipantList()); } void LocalConference::finalizeCreation() { @@ -772,10 +786,10 @@ int LocalConference::participantDeviceJoined( BCTBX_UNUSED(const std::shared_ptr<LinphonePrivate::Participant> &participant), const std::shared_ptr<LinphonePrivate::ParticipantDevice> &device) { int success = -1; - if ((device->updateMediaCapabilities() || (device->getState() != ParticipantDevice::State::Present)) && + const auto mediaCapabilitiesChanged = device->updateMediaCapabilities(); + if ((!mediaCapabilitiesChanged.empty() || (device->getState() != ParticipantDevice::State::Present)) && (getState() == ConferenceInterface::State::Created)) { lInfo() << "Device " << *device->getAddress() << " joined conference " << *getConferenceAddress(); - device->updateMediaCapabilities(); device->updateStreamAvailabilities(); device->setState(ParticipantDevice::State::Present); return 0; @@ -803,7 +817,8 @@ int LocalConference::participantDeviceLeft( BCTBX_UNUSED(const std::shared_ptr<LinphonePrivate::Participant> &participant), const std::shared_ptr<LinphonePrivate::ParticipantDevice> &device) { int success = -1; - if ((device->updateMediaCapabilities() || (device->getState() != ParticipantDevice::State::OnHold)) && + const auto mediaCapabilitiesChanged = device->updateMediaCapabilities(); + if ((!mediaCapabilitiesChanged.empty() || (device->getState() != ParticipantDevice::State::OnHold)) && (getState() == ConferenceInterface::State::Created)) { lInfo() << "Device " << *device->getAddress() << " left conference " << *getConferenceAddress(); device->updateStreamAvailabilities(); @@ -843,7 +858,8 @@ int LocalConference::participantDeviceMediaCapabilityChanged( const std::shared_ptr<LinphonePrivate::Participant> &participant, const std::shared_ptr<LinphonePrivate::ParticipantDevice> &device) { int success = -1; - if (device->updateMediaCapabilities() && + const auto mediaCapabilitiesChanged = device->updateMediaCapabilities(); + if (!mediaCapabilitiesChanged.empty() && ((getState() == ConferenceInterface::State::CreationPending) || (getState() == ConferenceInterface::State::Created)) && (device->getState() == ParticipantDevice::State::Present)) { @@ -935,11 +951,7 @@ bool LocalConference::dialOutAddresses(const std::list<std::shared_ptr<Address>> const string &confId = conferenceAddress->getUriParamValue("conf-id"); linphone_call_params_set_conference_id(new_params, confId.c_str()); - std::list<std::shared_ptr<Address>> addresses; - if (!invitedAddresses.empty()) { - addresses = invitedAddresses; - } - + std::list<std::shared_ptr<Address>> addresses = getInvitedAddresses(); // Add participants already in the conference to the list of addresses if they are not part of the invitees for (const auto &p : getParticipants()) { const auto &pAddress = p->getAddress(); @@ -1179,6 +1191,7 @@ bool LocalConference::addParticipant(std::shared_ptr<LinphonePrivate::Call> call // therefore there is no way to know if the remote client already knew that the call was in a conference or not. auto contactAddress = session->getContactAddress(); tryAddMeDevice(); + // Add participant to the conference participant list switch (state) { case LinphoneCallOutgoingInit: @@ -1188,7 +1201,7 @@ bool LocalConference::addParticipant(std::shared_ptr<LinphonePrivate::Call> call case LinphoneCallPausing: case LinphoneCallPaused: case LinphoneCallResuming: - case LinphoneCallStreamsRunning: + case LinphoneCallStreamsRunning: { if (call->toC() == linphone_core_get_current_call(getCore()->getCCore())) L_GET_PRIVATE_FROM_C_OBJECT(getCore()->getCCore())->setCurrentCall(nullptr); mMixerSession->joinStreamsGroup(session->getStreamsGroup()); @@ -1218,7 +1231,27 @@ bool LocalConference::addParticipant(std::shared_ptr<LinphonePrivate::Call> call Conference::addParticipant(call); - break; + const auto &participant = findParticipant(session->getRemoteAddress()); + LinphoneMediaDirection audioDirection = LinphoneMediaDirectionInactive; + if (participant) { + const auto &role = participant->getRole(); + switch (role) { + case Participant::Role::Speaker: + audioDirection = LinphoneMediaDirectionSendRecv; + break; + case Participant::Role::Listener: + audioDirection = LinphoneMediaDirectionSendOnly; + break; + case Participant::Role::Unknown: + audioDirection = LinphoneMediaDirectionInactive; + break; + } + } + + const_cast<LinphonePrivate::MediaSessionParams *>(call->getParams())->setAudioDirection(audioDirection); + } + + break; default: lError() << "Call " << call << " (local address " << call->getLocalAddress()->toString() << " remote address " << (remoteAddress ? remoteAddress->toString() : "Unknown") @@ -1260,9 +1293,9 @@ bool LocalConference::addParticipant(std::shared_ptr<LinphonePrivate::Call> call } } break; default: - lError() << "Call " << call << " (local address " << call->getLocalAddress()->toString() - << " remote address " << (remoteAddress ? remoteAddress->toString() : "Unknown") - << ") is in state " << Utils::toString(call->getState()) + lError() << "Call " << call << " (local address " << *call->getLocalAddress() << " remote address " + << (remoteAddress ? remoteAddress->toString() : "Unknown") << ") is in state " + << Utils::toString(call->getState()) << ", hence the call cannot be updated following it becoming part of the conference"; return false; break; @@ -1286,7 +1319,8 @@ bool LocalConference::addParticipant(std::shared_ptr<LinphonePrivate::Call> call // If no resource list is provided in the INVITE, there is not need to call participants if ((initialState == ConferenceInterface::State::CreationPending) && dialout && !resourceList.isEmpty()) { list<std::shared_ptr<Address>> addresses; - for (auto &addr : invitedAddresses) { + for (auto &participant : mInvitedParticipants) { + const auto &addr = participant->getAddress(); // Do not invite organizer as it is already dialing in if (*addr != *organizer) { addresses.push_back(addr); @@ -1318,7 +1352,10 @@ bool LocalConference::addParticipant(const std::shared_ptr<Address> &participant return (participantAddress->weakEqual(*address)); }); if (p == allowedAddresses.end()) { - invitedAddresses.push_back(participantAddress); + // Participants invited after the start of a conference can only listen to it + auto participantInfo = Factory::get()->createParticipantInfo(participantAddress); + participantInfo->setRole(Participant::Role::Listener); + mInvitedParticipants.push_back(participantInfo); } std::list<std::shared_ptr<Address>> addressesList{participantAddress}; @@ -2026,7 +2063,7 @@ void LocalConference::callStateChangedCb(LinphoneCore *lc, } break; case LinphoneCallStateEnd: case LinphoneCallStateError: - lInfo() << "Removing terminated call (local address " << session->getLocalAddress()->toString() + lInfo() << "Removing terminated call (local address " << *session->getLocalAddress() << " remote address " << *remoteAddress << ") from conference " << this << " (" << *getConferenceAddress() << ")"; if (session->getErrorInfo() && diff --git a/coreapi/local_conference.h b/coreapi/local_conference.h index b03285cc80ff71cc6daf00bd3e88540cea0d1540..e004259f5432436b40a5392479f38d66d8273e53 100644 --- a/coreapi/local_conference.h +++ b/coreapi/local_conference.h @@ -33,7 +33,7 @@ class EventSubscribe; class EventPublish; namespace MediaConference { // They are in a special namespace because of conflict of generic Conference classes in - // src/conference/* + // src/conference/* /* * Class for an audio/video conference running locally. @@ -172,6 +172,9 @@ private: bool mIsIn = false; std::shared_ptr<Address> organizer; static constexpr int confIdLength = 10; +#ifdef HAVE_ADVANCED_IM + std::shared_ptr<LocalAudioVideoConferenceEventHandler> eventHandler; +#endif // HAVE_ADVANCED_IM bool validateNewParameters(const LinphonePrivate::ConferenceParams &newConfParams) const; bool updateAllParticipantSessionsExcept(const std::shared_ptr<CallSession> &session); @@ -184,12 +187,10 @@ private: void checkIfTerminated(); std::list<std::shared_ptr<Address>> getAllowedAddresses() const; void configure(SalCallOp *op); + void fillInvitedParticipantList(SalCallOp *op, bool cancelling); void addLocalEndpoint(); void removeLocalEndpoint(); -#ifdef HAVE_ADVANCED_IM - std::shared_ptr<LocalAudioVideoConferenceEventHandler> eventHandler; -#endif // HAVE_ADVANCED_IM virtual std::shared_ptr<ConferenceInfo> createConferenceInfo() const override; bool tryAddMeDevice(); diff --git a/coreapi/remote_conference.cpp b/coreapi/remote_conference.cpp index 59b3c6ef7459623d7239d7992f02a362752bd29e..cda3328b240e5aa1f384498549d319b3ad330908 100644 --- a/coreapi/remote_conference.cpp +++ b/coreapi/remote_conference.cpp @@ -45,7 +45,7 @@ RemoteConference::RemoteConference(const shared_ptr<Core> &core, const std::shared_ptr<LinphonePrivate::CallSession> &focusSession, const std::shared_ptr<Address> &confAddr, const ConferenceId &conferenceId, - const std::list<std::shared_ptr<Address>> &invitees, + const ConferenceInfo::participant_list_t &invitees, CallSessionListener *listener, const std::shared_ptr<LinphonePrivate::ConferenceParams> params) : Conference(core, conferenceId.getLocalAddress(), listener, params) { @@ -68,7 +68,7 @@ RemoteConference::RemoteConference(const shared_ptr<Core> &core, #endif getMe()->setAdmin((organizer == nullptr) || organizer->weakEqual(*(getMe()->getAddress()))); - invitedAddresses = invitees; + mInvitedParticipants = invitees; setState(ConferenceInterface::State::Instantiated); @@ -168,8 +168,16 @@ void RemoteConference::finalizeCreation() { std::shared_ptr<ConferenceInfo> RemoteConference::createConferenceInfo() const { auto session = static_pointer_cast<MediaSession>(getMainSession()); const auto referer = (session ? L_GET_PRIVATE(session->getMediaParams())->getReferer() : nullptr); - const auto organizer = (referer) ? referer->getRemoteAddress() : getMe()->getAddress(); - return createConferenceInfoWithOrganizer(organizer); + const auto guessedOrganizer = getOrganizer(); + std::shared_ptr<Address> organizer = nullptr; + if (guessedOrganizer) { + organizer = guessedOrganizer; + } else if (referer) { + organizer = referer->getRemoteAddress(); + } else { + organizer = getMe()->getAddress(); + } + return createConferenceInfoWithCustomParticipantList(organizer, getFullParticipantList()); } void RemoteConference::setMainSession(const std::shared_ptr<LinphonePrivate::CallSession> &session) { @@ -1335,7 +1343,14 @@ void RemoteConference::onParticipantDeviceRemoved( auto session = static_pointer_cast<MediaSession>(getMainSession()); const MediaSessionParams *params = session->getMediaParams(); - if (confParams->videoEnabled() && params->videoEnabled() && (getState() == ConferenceInterface::State::Created) && + const auto &confSecurityLevel = confParams->getSecurityLevel(); + const auto &audioAvailable = device->getStreamAvailability(LinphoneStreamTypeAudio); + const auto audioNeedsReInvite = ((confSecurityLevel == ConferenceParams::SecurityLevel::EndToEnd) && + confParams->audioEnabled() && params->audioEnabled() && audioAvailable); + + const auto videoNeedsReInvite = (confParams->videoEnabled() && params->videoEnabled()); + + if ((audioNeedsReInvite || videoNeedsReInvite) && (getState() == ConferenceInterface::State::Created) && !isMe(device->getAddress()) && (device->getTimeOfJoining() >= 0)) { auto updateSession = [this, device]() -> LinphoneStatus { lInfo() << "Sending re-INVITE in order to update streams because participant device " @@ -1368,16 +1383,12 @@ void RemoteConference::onParticipantDeviceStateChanged( return (*devAddr == contactAddress); }); - const auto &audioDir = device->getStreamCapability(LinphoneStreamTypeAudio); + const auto &audioAvailable = device->getStreamAvailability(LinphoneStreamTypeAudio); const auto &confSecurityLevel = confParams->getSecurityLevel(); - const auto audioNeedsReInvite = - ((confSecurityLevel == ConferenceParams::SecurityLevel::EndToEnd) && confParams->audioEnabled() && - params->audioEnabled() && - ((audioDir == LinphoneMediaDirectionSendOnly) || (audioDir == LinphoneMediaDirectionSendRecv))); - const auto &videoDir = device->getStreamCapability(LinphoneStreamTypeVideo); - const auto videoNeedsReInvite = - (confParams->videoEnabled() && params->videoEnabled() && - ((videoDir == LinphoneMediaDirectionSendOnly) || (videoDir == LinphoneMediaDirectionSendRecv))); + const auto audioNeedsReInvite = ((confSecurityLevel == ConferenceParams::SecurityLevel::EndToEnd) && + confParams->audioEnabled() && params->audioEnabled() && audioAvailable); + const auto &videoAvailable = device->getStreamAvailability(LinphoneStreamTypeVideo); + const auto videoNeedsReInvite = (confParams->videoEnabled() && params->videoEnabled() && videoAvailable); if ((getState() == ConferenceInterface::State::Created) && (callIt == m_pendingCalls.cend()) && isIn() && (device->getState() == ParticipantDevice::State::Present) && ((videoNeedsReInvite || audioNeedsReInvite))) { auto updateSession = [this, device]() -> LinphoneStatus { diff --git a/coreapi/remote_conference.h b/coreapi/remote_conference.h index 6df2219373f692294bc9c9a47a805f02dd22948c..35fc7deae7387347cf0da85cadb21f3b4ce76d64 100644 --- a/coreapi/remote_conference.h +++ b/coreapi/remote_conference.h @@ -53,7 +53,7 @@ public: const std::shared_ptr<LinphonePrivate::CallSession> &focusSession, const std::shared_ptr<Address> &confAddr, const ConferenceId &conferenceId, - const std::list<std::shared_ptr<Address>> &invitees, + const ConferenceInfo::participant_list_t &invitees, CallSessionListener *listener, const std::shared_ptr<LinphonePrivate::ConferenceParams> params); virtual ~RemoteConference(); diff --git a/coreapi/tester_utils.cpp b/coreapi/tester_utils.cpp index a12ef932f6fb9986ba887ba9d4e26de923c06c53..cb4e3b4462b2d800069b8e7fe5b0af32e8866327 100644 --- a/coreapi/tester_utils.cpp +++ b/coreapi/tester_utils.cpp @@ -236,7 +236,7 @@ void linphone_core_reset_tone_manager_stats(LinphoneCore *lc) { L_GET_PRIVATE_FROM_C_OBJECT(lc)->getToneManager().resetStats(); } -const char *linphone_core_get_tone_file(LinphoneCore *lc, LinphoneToneID id){ +const char *linphone_core_get_tone_file(LinphoneCore *lc, LinphoneToneID id) { LinphoneToneDescription *tone = L_GET_PRIVATE_FROM_C_OBJECT(lc)->getToneManager().getTone(id); return tone ? tone->audiofile : NULL; } diff --git a/coreapi/tester_utils.h b/coreapi/tester_utils.h index 07c4447482aacad79746b7e89aa596682d022474..7a73faf8dca3e7a2422d9796ed28e5983fb79e76 100644 --- a/coreapi/tester_utils.h +++ b/coreapi/tester_utils.h @@ -304,7 +304,8 @@ LINPHONE_PUBLIC const char *linphone_core_get_groupchat_version(const LinphoneCo LINPHONE_PUBLIC const char *linphone_core_get_ephemeral_version(const LinphoneCore *lc); LINPHONE_PUBLIC size_t linphone_chat_room_get_previouses_conference_ids_count(LinphoneChatRoom *cr); -LINPHONE_PUBLIC void linphone_conference_info_set_uri(LinphoneConferenceInfo *conference_info, LinphoneAddress *uri); +LINPHONE_PUBLIC void linphone_conference_info_set_uri(LinphoneConferenceInfo *conference_info, + const LinphoneAddress *uri); LINPHONE_PUBLIC bool_t linphone_participant_preserve_session(const LinphoneParticipant *participant); LINPHONE_PUBLIC bool_t linphone_conference_params_is_static(const LinphoneConferenceParams *params); diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt index 372d6be358053e293f50b9396f30eba4ab6aa64f..575238b1ce804f7920055c99bf739100bde63b57 100644 --- a/include/CMakeLists.txt +++ b/include/CMakeLists.txt @@ -106,6 +106,7 @@ set(C_API_HEADER_FILES c-magic-search.h c-magic-search-cbs.h c-participant.h + c-participant-info.h c-participant-device.h c-participant-device-cbs.h c-participant-device-identity.h @@ -128,6 +129,7 @@ set(ENUMS_HEADER_FILES chat-message-enums.h chat-room-enums.h conference-enums.h + participant-enums.h participant-device-enums.h encryption-engine-enums.h event-log-enums.h diff --git a/include/linphone/api/c-api.h b/include/linphone/api/c-api.h index 2f10cdb9b89832c0ce32d9905dec5862f20ae5d8..631c4b0e6fe55fc11e09e1132294d0ddeaf1df80 100644 --- a/include/linphone/api/c-api.h +++ b/include/linphone/api/c-api.h @@ -60,6 +60,7 @@ #include "linphone/api/c-participant-device-identity.h" #include "linphone/api/c-participant-device.h" #include "linphone/api/c-participant-imdn-state.h" +#include "linphone/api/c-participant-info.h" #include "linphone/api/c-participant.h" #include "linphone/api/c-push-notification-config.h" #include "linphone/api/c-push-notification-message.h" diff --git a/include/linphone/api/c-conference-info.h b/include/linphone/api/c-conference-info.h index 21b284ca354fd59e4338ac1789187716375b978a..06d371ae001a6ed98d4d2087705af6d5a5180186 100644 --- a/include/linphone/api/c-conference-info.h +++ b/include/linphone/api/c-conference-info.h @@ -72,14 +72,23 @@ linphone_conference_info_get_organizer(const LinphoneConferenceInfo *conference_ * @param organizer The #LinphoneAddress of the conference's organizer. @maybenil */ LINPHONE_PUBLIC void linphone_conference_info_set_organizer(LinphoneConferenceInfo *conference_info, - LinphoneAddress *organizer); + const LinphoneAddress *organizer); /** - * Retrieve the list of participants. + * Retrieve the list of participants as list of addresses. * @param conference_info The #LinphoneConferenceInfo object. @notnil * @return The list of participants. \bctbx_list{LinphoneAddress} @maybenil */ -LINPHONE_PUBLIC bctbx_list_t *linphone_conference_info_get_participants(const LinphoneConferenceInfo *conference_info); +LINPHONE_PUBLIC LINPHONE_DEPRECATED const bctbx_list_t * +linphone_conference_info_get_participants(const LinphoneConferenceInfo *conference_info); + +/** + * Retrieve the list of participants as list of participant infos. + * @param conference_info The #LinphoneConferenceInfo object. @notnil + * @return The list of participants. \bctbx_list{LinphoneParticipantInfo} @maybenil + */ +LINPHONE_PUBLIC const bctbx_list_t * +linphone_conference_info_get_participant_infos(const LinphoneConferenceInfo *conference_info); /** * Set the list of participants. @@ -87,7 +96,15 @@ LINPHONE_PUBLIC bctbx_list_t *linphone_conference_info_get_participants(const Li * @param participants The list of participants to set. \bctbx_list{LinphoneAddress} @maybenil */ LINPHONE_PUBLIC void linphone_conference_info_set_participants(LinphoneConferenceInfo *conference_info, - bctbx_list_t *participants); + const bctbx_list_t *participants); + +/** + * Set the list of participants. + * @param conference_info The #LinphoneConferenceInfo object. @notnil + * @param participant_infos The list of participant informations to set. \bctbx_list{LinphoneParticipantInfo} @maybenil + */ +LINPHONE_PUBLIC void linphone_conference_info_set_participants_2(LinphoneConferenceInfo *conference_info, + const bctbx_list_t *participant_infos); /** * Add a participant to the conference. @@ -95,7 +112,25 @@ LINPHONE_PUBLIC void linphone_conference_info_set_participants(LinphoneConferenc * @param participant The participant (#LinphoneAddress) to add. @notnil */ LINPHONE_PUBLIC void linphone_conference_info_add_participant(LinphoneConferenceInfo *conference_info, - LinphoneAddress *participant); + const LinphoneAddress *participant); + +/** + * Add a participant to the conference. + * @param conference_info The #LinphoneConferenceInfo object. @notnil + * @param participant_info The participant information (#LinphoneParticipantInfo) to add. This method can be called to + * set attributes such as the role to the organizer of the conference @notnil + */ +LINPHONE_PUBLIC void linphone_conference_info_add_participant_2(LinphoneConferenceInfo *conference_info, + const LinphoneParticipantInfo *participant_info); + +/** + * Update the participant information in the conference informations + * @param conference_info The #LinphoneConferenceInfo object. @notnil + * @param participant_info The participant information (#LinphoneParticipantInfo) to update. This method can be called + * to change attributes such as the role to the organizer of the conference @notnil + */ +LINPHONE_PUBLIC void linphone_conference_info_update_participant(LinphoneConferenceInfo *conference_info, + const LinphoneParticipantInfo *participant_info); /** * Remove a participant from the conference. @@ -103,7 +138,16 @@ LINPHONE_PUBLIC void linphone_conference_info_add_participant(LinphoneConference * @param participant The participant (#LinphoneAddress) to remove. @notnil */ LINPHONE_PUBLIC void linphone_conference_info_remove_participant(LinphoneConferenceInfo *conference_info, - LinphoneAddress *participant); + const LinphoneAddress *participant); + +/** + * Find a participant information in the conference information. + * @param conference_info The #LinphoneConferenceInfo object. @notnil + * @param participant The participant (#LinphoneAddress) to search. @notnil + * @return The participant information (#LinphoneParticipantInfo). @maybenil + */ +LINPHONE_PUBLIC const LinphoneParticipantInfo * +linphone_conference_info_find_participant(LinphoneConferenceInfo *conference_info, const LinphoneAddress *participant); /** * Retrieve the URI of the conference. @@ -184,7 +228,7 @@ linphone_conference_info_get_security_level(const LinphoneConferenceInfo *confer * @param security_level The desired security level of the conference. */ LINPHONE_PUBLIC void linphone_conference_info_set_security_level(LinphoneConferenceInfo *conference_info, - LinphoneConferenceSecurityLevel security_level); + LinphoneConferenceSecurityLevel security_level); /** * Retrieve the conference as an Icalendar string. diff --git a/include/linphone/api/c-factory.h b/include/linphone/api/c-factory.h index af0268fc16ff01b610620f6f70af0d9bf16eaf54..593fd9779b1e49866ea6b3ce0210039377ba9433 100644 --- a/include/linphone/api/c-factory.h +++ b/include/linphone/api/c-factory.h @@ -792,6 +792,15 @@ LINPHONE_PUBLIC LinphoneConferenceInfo *linphone_factory_create_conference_info( LINPHONE_PUBLIC LinphoneConferenceInfo * linphone_factory_create_conference_info_from_icalendar_content(LinphoneFactory *factory, LinphoneContent *content); +/** + * Creates an object #LinphoneConferenceInfo from an Icalendar #LinphoneContent + * @param factory the #LinphoneFactory @notnil + * @param address the #LinphoneAddress of the participant @notnil + * @return a #LinphoneParticipantInfo @maybenil + */ +LINPHONE_PUBLIC LinphoneParticipantInfo *linphone_factory_create_participant_info(LinphoneFactory *factory, + LinphoneAddress *address); + /** * Creates an object #LinphoneConferenceSchedulerCbs * @param factory the #LinphoneFactory @notnil diff --git a/include/linphone/api/c-participant-info.h b/include/linphone/api/c-participant-info.h new file mode 100644 index 0000000000000000000000000000000000000000..7266c17f10aaf62e999e0a5e4653c2f33671ead7 --- /dev/null +++ b/include/linphone/api/c-participant-info.h @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2010-2022 Belledonne Communications SARL. + * + * This file is part of Liblinphone + * (see https://gitlab.linphone.org/BC/public/liblinphone). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef LINPHONE_PARTICIPANT_INFO_H +#define LINPHONE_PARTICIPANT_INFO_H + +#include "linphone/api/c-types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @addtogroup conference + * @{ + */ + +/** + * Create a new #LinphoneParticipantInfo object. + * @return The newly created #LinphoneParticipantInfo object. @notnil + */ +LINPHONE_PUBLIC LinphoneParticipantInfo *linphone_participant_info_new(LinphoneAddress *address); + +/** + * Take a reference on a #LinphoneParticipantInfo. + * @param participant_info The #LinphoneParticipantInfo object. @notnil + * @return the same #LinphoneParticipantInfo object. @notnil + */ +LINPHONE_PUBLIC LinphoneParticipantInfo *linphone_participant_info_ref(LinphoneParticipantInfo *participant_info); + +/** + * Clone an object #LinphoneParticipantInfo. + * @param participant_info The #LinphoneParticipantInfo object. @notnil + * @return the cloned #LinphoneParticipantInfo object. @notnil + */ +LINPHONE_PUBLIC LinphoneParticipantInfo * +linphone_participant_info_clone(const LinphoneParticipantInfo *participant_info); + +/** + * Release a #LinphoneParticipantInfo. + * @param participant_info The #LinphoneParticipantInfo object. @notnil + */ +LINPHONE_PUBLIC void linphone_participant_info_unref(LinphoneParticipantInfo *participant_info); + +/** + * Get the address of the object #LinphoneParticipantInfo. + * @param participant_info The #LinphoneParticipantInfo object. @notnil + * @return the #LiphoneAddress of the #LinphoneParticipantInfo object. @notnil + */ +LINPHONE_PUBLIC const LinphoneAddress * +linphone_participant_info_get_address(const LinphoneParticipantInfo *participant_info); + +/** + * Set the role of the object #LinphoneParticipantInfo. + * @param participant_info The #LinphoneParticipantInfo object. @notnil + * @param role the #LiphoneParticipantRole of the #LinphoneParticipantInfo object. @notnil + */ +LINPHONE_PUBLIC void linphone_participant_info_set_role(LinphoneParticipantInfo *participant_info, + LinphoneParticipantRole role); + +/** + * Get the role of the object #LinphoneParticipantInfo. + * @param participant_info The #LinphoneParticipantInfo object. @notnil + * @return the #LiphoneParticipantRole of the #LinphoneParticipantInfo object. @notnil + */ +LINPHONE_PUBLIC LinphoneParticipantRole +linphone_participant_info_get_role(const LinphoneParticipantInfo *participant_info); + +/** + * Set the a custom parameter to object #LinphoneParticipantInfo. + * @param participant_info The #LinphoneParticipantInfo object. @notnil + * @param name the name of the parameter. @notnil + * @param value the value of the parameter. @notnil + */ +LINPHONE_PUBLIC void +linphone_participant_info_add_parameter(LinphoneParticipantInfo *participant_info, const char *name, const char *value); + +/** + * Get the value of a custom parameter of the object #LinphoneParticipantInfo. + * @param participant_info The #LinphoneParticipantInfo object. @notnil + * @param name the name of the parameter. @notnil + * @return value the value of the parameter. @notnil + */ +LINPHONE_PUBLIC const char * +linphone_participant_info_get_parameter_value(const LinphoneParticipantInfo *participant_info, const char *name); + +/** + * Find whether a #LinphoneParticipantInfo has a parameter + * @param participant_info The #LinphoneParticipantInfo object. @notnil + * @param name the name of the parameter. @notnil + * @return TRUE if the parameter is present, FALSE otherwise + */ +LINPHONE_PUBLIC bool_t linphone_participant_info_has_parameter(const LinphoneParticipantInfo *participant_info, + const char *name); + +/** + * Find the value of a custom parameter of the object #LinphoneParticipantInfo. + * @param participant_info The #LinphoneParticipantInfo object. @notnil + * @param name the name of the parameter. @notnil + */ +LINPHONE_PUBLIC void linphone_participant_info_remove_parameter(LinphoneParticipantInfo *participant_info, + const char *name); + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif // LINPHONE_PARTICIPANT_INFO_H diff --git a/include/linphone/api/c-participant.h b/include/linphone/api/c-participant.h index 09637f41045d94ebd13d6de13d85961daa872d70..f065acf54beac672484706d868edefef5d735d7d 100644 --- a/include/linphone/api/c-participant.h +++ b/include/linphone/api/c-participant.h @@ -124,6 +124,13 @@ LINPHONE_PUBLIC LinphoneParticipantDevice *linphone_participant_find_device(cons */ LINPHONE_PUBLIC time_t linphone_participant_get_creation_time(const LinphoneParticipant *participant); +/** + * Get the role of the participant within the conference + * @param participant A #LinphoneParticipant object @notnil + * @return role within the conference #LinphoneParticipantRole + */ +LINPHONE_PUBLIC LinphoneParticipantRole linphone_participant_get_role(const LinphoneParticipant *participant); + /** * @} */ diff --git a/include/linphone/api/c-types.h b/include/linphone/api/c-types.h index baec1b69e9d8753094570726f8077c1dd8fcea77..de442e0af690f6370050c6d3f74e79cfe40db0cb 100644 --- a/include/linphone/api/c-types.h +++ b/include/linphone/api/c-types.h @@ -31,6 +31,7 @@ #include "linphone/enums/encryption-engine-enums.h" #include "linphone/enums/event-log-enums.h" #include "linphone/enums/participant-device-enums.h" +#include "linphone/enums/participant-enums.h" #include "linphone/enums/security-event-enums.h" #include "linphone/utils/enum-generator.h" @@ -197,6 +198,13 @@ typedef struct _LinphoneConferenceSchedulerCbs LinphoneConferenceSchedulerCbs; */ typedef struct _LinphoneParticipant LinphoneParticipant; +/** + * @brief Object defining all information related to a participant + * + * @ingroup conference + */ +typedef struct _LinphoneParticipantInfo LinphoneParticipantInfo; + /** * @brief This object represents a unique device for a member of a #LinphoneConference or #LinphoneChatRoom. * diff --git a/include/linphone/enums/participant-enums.h b/include/linphone/enums/participant-enums.h new file mode 100644 index 0000000000000000000000000000000000000000..cf439f66632034267ab833d93b8a4229630b18ea --- /dev/null +++ b/include/linphone/enums/participant-enums.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2010-2023 Belledonne Communications SARL. + * + * This file is part of Liblinphone + * (see https://gitlab.linphone.org/BC/public/liblinphone). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef _L_PARTICIPANT_ENUMS_H_ +#define _L_PARTICIPANT_ENUMS_H_ +// ============================================================================= + +/** + * #LinphoneParticipantRole is used to define a role of a participant within a conference + * @ingroup conference + */ +typedef enum _LinphoneParticipantRole { + LinphoneParticipantRoleSpeaker = 0, /**< participant is a speaker in the conference */ + LinphoneParticipantRoleListener = 1, /**< participant is a listener in the conference. He/She cannot speak */ + LinphoneParticipantRoleUnknown = 2 /**< participant role is unknown */ +} LinphoneParticipantRole; + +#endif // ifndef _L_PARTICIPANT_ENUMS_H_ diff --git a/include/linphone/misc.h b/include/linphone/misc.h index 328564eb4d2a4826d3816f28d7b23f0a952e0269..bc015334128d5c073400acb06a807fed09cabcf0 100644 --- a/include/linphone/misc.h +++ b/include/linphone/misc.h @@ -109,6 +109,8 @@ linphone_core_log_collection_upload_state_to_string(const LinphoneCoreLogCollect LINPHONE_PUBLIC const char *linphone_call_state_to_string(LinphoneCallState cs); +LINPHONE_PUBLIC const char *linphone_participant_role_to_string(LinphoneParticipantRole role); + /** * Converts a #LinphoneConfiguringState enum to a string. * @param state #LinphoneConfiguringState the value for which we want a string representation diff --git a/include/linphone/utils/utils.h b/include/linphone/utils/utils.h index 8c1c9272cd080239f5dbddd8184b5aa7c9298c8e..893847af4cdd656f47a8cd3ad692d0fa13a7e173 100644 --- a/include/linphone/utils/utils.h +++ b/include/linphone/utils/utils.h @@ -31,8 +31,7 @@ #include "bctoolbox/utils.hh" -#include "address/address.h" -#include "conference/session/streams.h" +#include "conference/conference-info.h" #include "linphone/utils/enum-generator.h" // ============================================================================= @@ -261,7 +260,7 @@ private: LINPHONE_PUBLIC std::map<std::string, Version> parseCapabilityDescriptor(const std::string &descriptor); std::string getSipFragAddress(const Content &content); std::string getResourceLists(const std::list<std::shared_ptr<Address>> &addresses); -std::list<std::shared_ptr<Address>> parseResourceLists(const Content &content); +ConferenceInfo::participant_list_t parseResourceLists(const Content &content); std::shared_ptr<ConferenceInfo> createConferenceInfoFromOp(SalCallOp *op, bool remote); std::string computeHa1ForAlgorithm(const std::string &userId, const std::string &password, diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 80dd69d9171d04d18b513f09aa81f5351229fb4a..21af77fa4b9e8127ca9f89cf631ba9b1225ab896 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -111,6 +111,7 @@ set(LINPHONE_CXX_OBJECTS_PRIVATE_HEADER_FILES auth-info/auth-info.h auth-info/auth-stack.h c-wrapper/c-wrapper.h + c-wrapper/list-holder.h c-wrapper/internal/c-sal.h c-wrapper/internal/c-tools.h call/call-log.h @@ -168,6 +169,7 @@ set(LINPHONE_CXX_OBJECTS_PRIVATE_HEADER_FILES conference/params/call-session-params.h conference/params/media-session-params-p.h conference/params/media-session-params.h + conference/participant-info.h conference/participant-device.h conference/participant-imdn-state-p.h conference/participant-imdn-state.h @@ -374,6 +376,7 @@ set(LINPHONE_CXX_OBJECTS_SOURCE_FILES c-wrapper/api/c-magic-search.cpp c-wrapper/api/c-magic-search-cbs.cpp c-wrapper/api/c-participant.cpp + c-wrapper/api/c-participant-info.cpp c-wrapper/api/c-participant-device.cpp c-wrapper/api/c-participant-device-cbs.cpp c-wrapper/api/c-participant-device-identity.cpp @@ -420,6 +423,7 @@ set(LINPHONE_CXX_OBJECTS_SOURCE_FILES conference/local-conference.cpp conference/params/call-session-params.cpp conference/params/media-session-params.cpp + conference/participant-info.cpp conference/participant-device.cpp conference/participant-imdn-state.cpp conference/participant.cpp diff --git a/src/alert/alert.h b/src/alert/alert.h index 256a3134f63cda84b06d2ded63b04669639dc49a..e238eccbf640aa98ce1a74355e27b2fbbf3be91e 100644 --- a/src/alert/alert.h +++ b/src/alert/alert.h @@ -20,12 +20,15 @@ #ifndef ALERT_H #define ALERT_H +#include <memory> +#include <unordered_map> + +#include <belle-sip/object++.hh> + #include "call/call.h" +#include "conference/session/streams.h" #include "dictionary/dictionary.h" #include "linphone/api/c-types.h" -#include <belle-sip/object++.hh> -#include <memory> -#include <unordered_map> using namespace std; @@ -151,4 +154,4 @@ private: }; LINPHONE_END_NAMESPACE -#endif \ No newline at end of file +#endif diff --git a/src/c-wrapper/api/c-account-params.cpp b/src/c-wrapper/api/c-account-params.cpp index 53f7303fb76764060223fdc74dbbcf321e13d2b1..4dd82e9c9a6e1123be88845827125a013c7f5c98 100644 --- a/src/c-wrapper/api/c-account-params.cpp +++ b/src/c-wrapper/api/c-account-params.cpp @@ -25,6 +25,7 @@ #include "linphone/api/c-address.h" #include "linphone/core.h" #include "linphone/lpconfig.h" +#include "linphone/utils/utils.h" #include "nat/nat-policy.h" #include "push-notification/push-notification-config.h" diff --git a/src/c-wrapper/api/c-conference-info.cpp b/src/c-wrapper/api/c-conference-info.cpp index b11308f58ec7aee915fb0c656459f4ea5f74ab33..2b9b55867ee2b50335977b7a8c8e465b2cfd1a91 100644 --- a/src/c-wrapper/api/c-conference-info.cpp +++ b/src/c-wrapper/api/c-conference-info.cpp @@ -22,6 +22,7 @@ #include "c-wrapper/c-wrapper.h" #include "c-wrapper/internal/c-tools.h" #include "conference/conference-info.h" +#include "conference/participant-info.h" #include "linphone/api/c-address.h" // ============================================================================= @@ -50,46 +51,68 @@ const LinphoneAddress *linphone_conference_info_get_organizer(const LinphoneConf return address && address->isValid() ? address->toC() : nullptr; } -void linphone_conference_info_set_organizer(LinphoneConferenceInfo *conference_info, LinphoneAddress *organizer) { +void linphone_conference_info_set_organizer(LinphoneConferenceInfo *conference_info, const LinphoneAddress *organizer) { ConferenceInfo::toCpp(conference_info)->setOrganizer(Address::toCpp(organizer)->getSharedFromThis()); } -bctbx_list_t *linphone_conference_info_get_participants(const LinphoneConferenceInfo *conference_info) { - const auto &participants = ConferenceInfo::toCpp(conference_info)->getParticipants(); - bctbx_list_t *participant_addresses = NULL; - for (const auto &participant : participants) { - const auto &address = participant.first; - participant_addresses = bctbx_list_append(participant_addresses, address->toC()); - } - return participant_addresses; +const bctbx_list_t *linphone_conference_info_get_participants(const LinphoneConferenceInfo *conference_info) { + return ConferenceInfo::toCpp(conference_info)->getParticipantAddressCList(); +} + +const bctbx_list_t *linphone_conference_info_get_participant_infos(const LinphoneConferenceInfo *conference_info) { + return ConferenceInfo::toCpp(conference_info)->getParticipantsCList(); } -void linphone_conference_info_set_participants(LinphoneConferenceInfo *conference_info, bctbx_list_t *participants) { +void linphone_conference_info_set_participants(LinphoneConferenceInfo *conference_info, + const bctbx_list_t *participants) { const std::list<std::shared_ptr<LinphonePrivate::Address>> participantsList = LinphonePrivate::Utils::bctbxListToCppSharedPtrList<LinphoneAddress, LinphonePrivate::Address>(participants); - ConferenceInfo::participant_list_t participantsMap; - ConferenceInfo::participant_params_t participantsParams; - for (const auto &p : participantsList) { - participantsMap[p] = participantsParams; - } - ConferenceInfo::toCpp(conference_info)->setParticipants(participantsMap); + ConferenceInfo::toCpp(conference_info)->setParticipants(participantsList); +} + +void linphone_conference_info_set_participants_2(LinphoneConferenceInfo *conference_info, + const bctbx_list_t *participant_infos) { + const std::list<std::shared_ptr<LinphonePrivate::ParticipantInfo>> participantInfos = + LinphonePrivate::Utils::bctbxListToCppSharedPtrList<LinphoneParticipantInfo, LinphonePrivate::ParticipantInfo>( + participant_infos); + ConferenceInfo::toCpp(conference_info)->setParticipants(participantInfos); } -void linphone_conference_info_add_participant(LinphoneConferenceInfo *conference_info, LinphoneAddress *participant) { +void linphone_conference_info_add_participant(LinphoneConferenceInfo *conference_info, + const LinphoneAddress *participant) { ConferenceInfo::toCpp(conference_info)->addParticipant(Address::toCpp(participant)->getSharedFromThis()); } +void linphone_conference_info_add_participant_2(LinphoneConferenceInfo *conference_info, + const LinphoneParticipantInfo *participant_info) { + ConferenceInfo::toCpp(conference_info) + ->addParticipant(ParticipantInfo::toCpp(participant_info)->getSharedFromThis()); +} + +void linphone_conference_info_update_participant(LinphoneConferenceInfo *conference_info, + const LinphoneParticipantInfo *participant_info) { + ConferenceInfo::toCpp(conference_info) + ->updateParticipant(ParticipantInfo::toCpp(participant_info)->getSharedFromThis()); +} + void linphone_conference_info_remove_participant(LinphoneConferenceInfo *conference_info, - LinphoneAddress *participant) { + const LinphoneAddress *participant) { ConferenceInfo::toCpp(conference_info)->removeParticipant(Address::toCpp(participant)->getSharedFromThis()); } +const LinphoneParticipantInfo *linphone_conference_info_find_participant(LinphoneConferenceInfo *conference_info, + const LinphoneAddress *participant) { + const auto &participant_info = + ConferenceInfo::toCpp(conference_info)->findParticipant(Address::toCpp(participant)->getSharedFromThis()); + return (participant_info) ? participant_info->toC() : NULL; +} + const LinphoneAddress *linphone_conference_info_get_uri(const LinphoneConferenceInfo *conference_info) { const auto &address = ConferenceInfo::toCpp(conference_info)->getUri(); return address && address->isValid() ? address->toC() : nullptr; } -void linphone_conference_info_set_uri(LinphoneConferenceInfo *conference_info, LinphoneAddress *uri) { +void linphone_conference_info_set_uri(LinphoneConferenceInfo *conference_info, const LinphoneAddress *uri) { ConferenceInfo::toCpp(conference_info)->setUri(Address::toCpp(uri)->getSharedFromThis()); } @@ -139,7 +162,7 @@ linphone_conference_info_get_security_level(const LinphoneConferenceInfo *confer } void linphone_conference_info_set_security_level(LinphoneConferenceInfo *conference_info, - LinphoneConferenceSecurityLevel security_level) { + LinphoneConferenceSecurityLevel security_level) { ConferenceInfo::toCpp(conference_info)->setSecurityLevel((ConferenceParamsInterface::SecurityLevel)security_level); } diff --git a/src/c-wrapper/api/c-factory.cpp b/src/c-wrapper/api/c-factory.cpp index 19561f730aa9c4a451d204507a3897592515ba20..b0feacc514d849b4979efee865855dcc8028b343 100644 --- a/src/c-wrapper/api/c-factory.cpp +++ b/src/c-wrapper/api/c-factory.cpp @@ -21,6 +21,7 @@ #include <bctoolbox/defs.h> #include "c-wrapper/c-wrapper.h" +#include "conference/participant-info.h" #include "factory/factory.h" #include "linphone/api/c-factory.h" @@ -466,6 +467,12 @@ LinphoneConferenceInfo *linphone_factory_create_conference_info_from_icalendar_c return conferenceInfo ? linphone_conference_info_ref(conferenceInfo->toC()) : nullptr; } +LinphoneParticipantInfo *linphone_factory_create_participant_info(LinphoneFactory *factory, LinphoneAddress *address) { + std::shared_ptr<LinphonePrivate::ParticipantInfo> participantInfo = + Factory::toCpp(factory)->createParticipantInfo(Address::toCpp(address)->getSharedFromThis()); + return participantInfo ? linphone_participant_info_ref(participantInfo->toC()) : nullptr; +} + LinphoneConferenceSchedulerCbs *linphone_factory_create_conference_scheduler_cbs(LinphoneFactory *factory) { return Factory::toCpp(factory)->createConferenceSchedulerCbs(); } diff --git a/src/c-wrapper/api/c-participant-info.cpp b/src/c-wrapper/api/c-participant-info.cpp new file mode 100644 index 0000000000000000000000000000000000000000..53a143576936b4f06aba3d01970cc1c0035aedc8 --- /dev/null +++ b/src/c-wrapper/api/c-participant-info.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2010-2023 Belledonne Communications SARL. + * + * This file is part of Liblinphone + * (see https://gitlab.linphone.org/BC/public/liblinphone). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "linphone/api/c-participant-info.h" +#include "c-wrapper/c-wrapper.h" +#include "c-wrapper/internal/c-tools.h" +#include "conference/participant-info.h" +#include "linphone/api/c-address.h" + +// ============================================================================= + +using namespace LinphonePrivate; + +LinphoneParticipantInfo *linphone_participant_info_new(LinphoneAddress *address) { + return ParticipantInfo::createCObject(Address::toCpp(address)->getSharedFromThis()); +} + +LinphoneParticipantInfo *linphone_participant_info_ref(LinphoneParticipantInfo *participant_info) { + ParticipantInfo::toCpp(participant_info)->ref(); + return participant_info; +} + +LinphoneParticipantInfo *linphone_participant_info_clone(const LinphoneParticipantInfo *info) { + return static_cast<ParticipantInfo *>(ParticipantInfo::toCpp(info)->clone())->toC(); +} + +void linphone_participant_info_unref(LinphoneParticipantInfo *participant_info) { + ParticipantInfo::toCpp(participant_info)->unref(); +} + +const LinphoneAddress *linphone_participant_info_get_address(const LinphoneParticipantInfo *participant_info) { + const auto &addr = ParticipantInfo::toCpp(participant_info)->getAddress(); + return addr->toC(); +} + +void linphone_participant_info_set_role(LinphoneParticipantInfo *participant_info, LinphoneParticipantRole role) { + return ParticipantInfo::toCpp(participant_info)->setRole((Participant::Role)role); +} + +LinphoneParticipantRole linphone_participant_info_get_role(const LinphoneParticipantInfo *participant_info) { + return (LinphoneParticipantRole)ParticipantInfo::toCpp(participant_info)->getRole(); +} + +void linphone_participant_info_add_parameter(LinphoneParticipantInfo *participant_info, + const char *name, + const char *value) { + ParticipantInfo::toCpp(participant_info)->addParameter(L_C_TO_STRING(name), L_C_TO_STRING(value)); +} + +const char *linphone_participant_info_get_parameter_value(const LinphoneParticipantInfo *participant_info, + const char *name) { + return L_STRING_TO_C(ParticipantInfo::toCpp(participant_info)->getParameterValue(L_C_TO_STRING(name))); +} + +bool_t linphone_participant_info_has_parameter(const LinphoneParticipantInfo *participant_info, const char *name) { + return (bool_t)ParticipantInfo::toCpp(participant_info)->hasParameter(L_C_TO_STRING(name)); +} + +LINPHONE_PUBLIC void linphone_participant_info_remove_parameter(LinphoneParticipantInfo *participant_info, + const char *name) { + ParticipantInfo::toCpp(participant_info)->removeParameter(L_C_TO_STRING(name)); +} diff --git a/src/c-wrapper/api/c-participant.cpp b/src/c-wrapper/api/c-participant.cpp index 528136a90c878247835f0e361bf40dc9dd01cffb..f2bfd2397152f9f1d403074ee7946700893c6050 100644 --- a/src/c-wrapper/api/c-participant.cpp +++ b/src/c-wrapper/api/c-participant.cpp @@ -98,3 +98,19 @@ time_t linphone_participant_get_creation_time(const LinphoneParticipant *partici bool_t linphone_participant_preserve_session(const LinphoneParticipant *participant) { return LinphonePrivate::Participant::toCpp(participant)->getPreserveSession(); } + +LinphoneParticipantRole linphone_participant_get_role(const LinphoneParticipant *participant) { + return (LinphoneParticipantRole)LinphonePrivate::Participant::toCpp(participant)->getRole(); +} + +const char *linphone_participant_role_to_string(LinphoneParticipantRole role) { + switch (role) { + case LinphoneParticipantRoleSpeaker: + return "LinphoneParticipantRoleSpeaker"; + case LinphoneParticipantRoleListener: + return "LinphoneParticipantRoleListener"; + case LinphoneParticipantRoleUnknown: + return "LinphoneParticipantRoleUnknown"; + } + return NULL; +} diff --git a/src/c-wrapper/c-wrapper.h b/src/c-wrapper/c-wrapper.h index 987221812508e8f610af5262693360b5ced2afc8..f62f81d5801e5183cee2a127f90c685b952ac5b3 100644 --- a/src/c-wrapper/c-wrapper.h +++ b/src/c-wrapper/c-wrapper.h @@ -129,85 +129,6 @@ private: bool mIsActive = true; }; -/* - * Template for easy conversion from std::list to bctbx_list_t. - * The authority list is the C++ one, the C one being only a const view of it. - * _T must be an HybridObject; so that conversion from C++ type to C type is done automatically. - */ -template <typename _T> -class ListHolder { -public: - ListHolder() = default; - ListHolder(const ListHolder<_T> &other) : mList(other.mList), mCList(nullptr) { - } - // The STL list is a public member, directly accessible. - std::list<std::shared_ptr<_T>> mList; - // Return a C list from the STL list. - const bctbx_list_t *getCList() const { - if (mCList) bctbx_list_free(mCList); - mCList = _T::getCListFromCppList(mList, false); - return mCList; - } - // Assign a C list. This replaces the STL list. - void setCList(const bctbx_list_t *clist) { - mList = _T::getCppListFromCList(clist); - } - ~ListHolder() { - if (mCList) bctbx_list_free(mCList); - } - -private: - mutable bctbx_list_t *mCList = nullptr; -}; - -/* - * Template class for classes that hold callbacks (such as LinphoneCallCbs, LinphoneAccountCbs etc. - * The invocation of callbacks can be done with the LINPHONE_HYBRID_OBJECT_INVOKE_CBS() macro. - */ -template <typename _CppCbsType> -class LINPHONE_PUBLIC CallbacksHolder { -public: - void addCallbacks(const std::shared_ptr<_CppCbsType> &callbacks) { - if (find(mCallbacksList.mList.begin(), mCallbacksList.mList.end(), callbacks) == mCallbacksList.mList.end()) { - mCallbacksList.mList.push_back(callbacks); - callbacks->setActive(true); - } else { - lError() << "Rejected Callbacks " << typeid(_CppCbsType).name() << " [" << (void *)callbacks.get() - << "] added twice."; - } - } - void removeCallbacks(const std::shared_ptr<_CppCbsType> &callbacks) { - auto it = find(mCallbacksList.mList.begin(), mCallbacksList.mList.end(), callbacks); - if (it != mCallbacksList.mList.end()) { - mCallbacksList.mList.erase(it); - callbacks->setActive(false); - } else { - lError() << "Attempt to remove " << typeid(_CppCbsType).name() << " [" << (void *)callbacks.get() - << "] that does not exist."; - } - } - void setCurrentCallbacks(const std::shared_ptr<_CppCbsType> &callbacks) { - mCurrentCallbacks = callbacks; - } - std::shared_ptr<_CppCbsType> getCurrentCallbacks() const { - return mCurrentCallbacks; - } - const std::list<std::shared_ptr<_CppCbsType>> &getCallbacksList() const { - return mCallbacksList.mList; - } - const bctbx_list_t *getCCallbacksList() const { - return mCallbacksList.getCList(); - } - void clearCallbacksList() { - mCallbacksList.mList.clear(); - mCurrentCallbacks = nullptr; - } - -private: - ListHolder<_CppCbsType> mCallbacksList; - std::shared_ptr<_CppCbsType> mCurrentCallbacks; -}; - LINPHONE_END_NAMESPACE #include "internal/c-tools.h" diff --git a/src/c-wrapper/list-holder.h b/src/c-wrapper/list-holder.h new file mode 100644 index 0000000000000000000000000000000000000000..84124a8a1cf46e2cd4173639ba9b3fc68430a11f --- /dev/null +++ b/src/c-wrapper/list-holder.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2010-2023 Belledonne Communications SARL. + * + * This file is part of Liblinphone + * (see https://gitlab.linphone.org/BC/public/liblinphone). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/* + * Template for easy conversion from std::list to bctbx_list_t. + * The authority list is the C++ one, the C one being only a const view of it. + * _T must be an HybridObject; so that conversion from C++ type to C type is done automatically. + */ + +#include "logger/logger.h" + +LINPHONE_BEGIN_NAMESPACE + +template <typename _T> +class ListHolder { +public: + ListHolder() = default; + ListHolder(const ListHolder<_T> &other) : mList(other.mList), mCList(nullptr) { + } + // The STL list is a public member, directly accessible. + std::list<std::shared_ptr<_T>> mList; + // Return a C list from the STL list. + const bctbx_list_t *getCList() const { + if (mCList) bctbx_list_free(mCList); + mCList = _T::getCListFromCppList(mList, false); + return mCList; + } + // Assign a C list. This replaces the STL list. + void setCList(const bctbx_list_t *clist) { + mList = _T::getCppListFromCList(clist); + } + ~ListHolder() { + if (mCList) bctbx_list_free(mCList); + } + +private: + mutable bctbx_list_t *mCList = nullptr; +}; + +/* + * Template class for classes that hold callbacks (such as LinphoneCallCbs, LinphoneAccountCbs etc. + * The invocation of callbacks can be done with the LINPHONE_HYBRID_OBJECT_INVOKE_CBS() macro. + */ +template <typename _CppCbsType> +class LINPHONE_PUBLIC CallbacksHolder { +public: + void addCallbacks(const std::shared_ptr<_CppCbsType> &callbacks) { + if (find(mCallbacksList.mList.begin(), mCallbacksList.mList.end(), callbacks) == mCallbacksList.mList.end()) { + mCallbacksList.mList.push_back(callbacks); + callbacks->setActive(true); + } else { + lError() << "Rejected Callbacks " << typeid(_CppCbsType).name() << " [" << (void *)callbacks.get() + << "] added twice."; + } + } + void removeCallbacks(const std::shared_ptr<_CppCbsType> &callbacks) { + auto it = find(mCallbacksList.mList.begin(), mCallbacksList.mList.end(), callbacks); + if (it != mCallbacksList.mList.end()) { + mCallbacksList.mList.erase(it); + callbacks->setActive(false); + } else { + lError() << "Attempt to remove " << typeid(_CppCbsType).name() << " [" << (void *)callbacks.get() + << "] that does not exist."; + } + } + void setCurrentCallbacks(const std::shared_ptr<_CppCbsType> &callbacks) { + mCurrentCallbacks = callbacks; + } + std::shared_ptr<_CppCbsType> getCurrentCallbacks() const { + return mCurrentCallbacks; + } + const std::list<std::shared_ptr<_CppCbsType>> &getCallbacksList() const { + return mCallbacksList.mList; + } + const bctbx_list_t *getCCallbacksList() const { + return mCallbacksList.getCList(); + } + void clearCallbacksList() { + mCallbacksList.mList.clear(); + mCurrentCallbacks = nullptr; + } + +private: + ListHolder<_CppCbsType> mCallbacksList; + std::shared_ptr<_CppCbsType> mCurrentCallbacks; +}; + +LINPHONE_END_NAMESPACE diff --git a/src/call/call.cpp b/src/call/call.cpp index 67b609bbec305fd64ec8eb79daab3530e90e9dd0..4d05081e109dd041dc30c8c567b7a0454e20eb51 100644 --- a/src/call/call.cpp +++ b/src/call/call.cpp @@ -542,10 +542,9 @@ void Call::createRemoteConference(const shared_ptr<CallSession> &session) { time_t endTime = startTime + static_cast<time_t>(duration) * 60; confParams->setEndTime(endTime); } - std::list<std::shared_ptr<Address>> invitees{conferenceInfo->getOrganizerAddress()}; - for (const auto &participant : conferenceInfo->getParticipants()) { - invitees.push_back(participant.first); - } + auto invitees = conferenceInfo->getParticipants(); + const auto &organizer = conferenceInfo->getOrganizer(); + invitees.push_back(organizer); const std::shared_ptr<Address> confAddr = conferenceInfo->getUri(); const ConferenceId confId(confAddr, session->getLocalAddress()); @@ -558,9 +557,10 @@ void Call::createRemoteConference(const shared_ptr<CallSession> &session) { const auto &remoteParams = static_pointer_cast<MediaSession>(session)->getRemoteParams(); confParams->setStartTime(remoteParams->getPrivate()->getStartTime()); confParams->setEndTime(remoteParams->getPrivate()->getEndTime()); - auto organizer = Utils::getSipFragAddress(sipfrag); auto invitees = Utils::parseResourceLists(resourceList); - invitees.push_back(Address::create(organizer)); + const auto organizer = Utils::getSipFragAddress(sipfrag); + auto organizerInfo = Factory::get()->createParticipantInfo(Address::create(organizer)); + invitees.push_back(organizerInfo); remoteConference = std::shared_ptr<MediaConference::RemoteConference>( new MediaConference::RemoteConference(getCore(), session, remoteContactAddress, conferenceId, invitees, nullptr, confParams), diff --git a/src/chat/chat-room/client-group-chat-room.cpp b/src/chat/chat-room/client-group-chat-room.cpp index 21ad93b09877668a3745f4070161b0cfab5c4cd2..d8e7385e687c624797442ff04988e1c105456696 100644 --- a/src/chat/chat-room/client-group-chat-room.cpp +++ b/src/chat/chat-room/client-group-chat-room.cpp @@ -33,6 +33,7 @@ #include "conference/handlers/remote-conference-event-handler.h" #include "conference/handlers/remote-conference-list-event-handler.h" #include "conference/participant-device.h" +#include "conference/participant-info.h" #include "conference/participant.h" #include "conference/remote-conference.h" #include "conference/session/call-session-p.h" @@ -190,11 +191,12 @@ void ClientGroupChatRoomPrivate::confirmJoining(SalCallOp *op) { if (!previousSession && !found) { q->setState(ConferenceInterface::State::CreationPending); // Handle participants addition - list<std::shared_ptr<Address>> identAddresses = Utils::parseResourceLists(op->getRemoteBody()); - for (const auto &addr : identAddresses) { - auto participant = q->findParticipant(addr); + const auto participantList = Utils::parseResourceLists(op->getRemoteBody()); + for (const auto &participantInfo : participantList) { + const auto &address = participantInfo->getAddress(); + auto participant = q->findParticipant(address); if (!participant) { - participant = Participant::create(q->getConference().get(), addr); + participant = Participant::create(q->getConference().get(), address); q->getConference()->participants.push_back(participant); } } @@ -417,8 +419,11 @@ ClientGroupChatRoom::ClientGroupChatRoom(const shared_ptr<Core> &core, [](BCTBX_UNUSED(ConferenceListenerInterface * p)) {})); getConference()->setSubject(subject); - for (const auto &addr : Utils::parseResourceLists(content)) - getConference()->participants.push_back(Participant::create(getConference().get(), addr)); + const auto participantList = Utils::parseResourceLists(content); + for (const auto &participantInfo : participantList) { + getConference()->participants.push_back( + Participant::create(getConference().get(), participantInfo->getAddress())); + } if (params->getEphemeralMode() == AbstractChatRoom::EphemeralMode::AdminManaged) { d->capabilities |= ClientGroupChatRoom::Capabilities::Ephemeral; diff --git a/src/chat/chat-room/server-group-chat-room.cpp b/src/chat/chat-room/server-group-chat-room.cpp index 422fde411fa99aa0e2b8f38ffd9bdb75cda8acf1..38cd2f93fb9c3ef958f053ca6f798b7606fee35f 100644 --- a/src/chat/chat-room/server-group-chat-room.cpp +++ b/src/chat/chat-room/server-group-chat-room.cpp @@ -30,6 +30,7 @@ #include "conference/handlers/local-conference-event-handler.h" #include "conference/handlers/local-conference-list-event-handler.h" #include "conference/local-conference.h" +#include "conference/participant-info.h" #include "conference/participant.h" #include "conference/session/call-session-p.h" #include "content/content-disposition.h" @@ -624,18 +625,18 @@ void ServerGroupChatRoomPrivate::unSubscribeRegistrationForParticipant(const std } bool ServerGroupChatRoomPrivate::initializeParticipants(const shared_ptr<Participant> &initiator, SalCallOp *op) { - handleSubjectChange(op); // Handle participants addition - list<std::shared_ptr<Address>> identAddresses = Utils::parseResourceLists(op->getRemoteBody()); + const auto participantList = Utils::parseResourceLists(op->getRemoteBody()); + std::list<std::shared_ptr<Address>> identAddresses; // DO not try to add participants with invalid address - for (auto it = identAddresses.begin(); it != identAddresses.end();) { - if (!((*it)->isValid())) { - lError() << "ServerGroupChatRoomPrivate::initializeParticipants(): removing invalid address " - << ((*it)->toString()) << " at position " << std::distance(it, identAddresses.begin()); - it = identAddresses.erase(it); + for (auto it = participantList.begin(); it != participantList.end(); ++it) { + const auto &address = (*it)->getAddress(); + if (!(address->isValid())) { + lError() << "ServerGroupChatRoomPrivate::initializeParticipants(): removing invalid address " << *address + << " at position " << std::distance(it, participantList.begin()); } else { - ++it; + identAddresses.push_back(address); } } if (identAddresses.empty()) { diff --git a/src/chat/ics/ics.cpp b/src/chat/ics/ics.cpp index 6e240203824645fb14ad642a7df2c271950af448..9a865500c93070160ba6bac1962f45320e11f315 100644 --- a/src/chat/ics/ics.cpp +++ b/src/chat/ics/ics.cpp @@ -22,10 +22,12 @@ #include <iomanip> #include <sstream> -#include "ics.h" - #include "bctoolbox/utils.hh" + #include "chat/ics/parser/ics-parser.h" +#include "conference/conference-params.h" +#include "conference/participant-info.h" +#include "ics.h" // ============================================================================= @@ -46,7 +48,7 @@ void Ics::Event::setOrganizer(const std::string &organizer) { setOrganizer(organizer, params); } -void Ics::Event::setOrganizer(const std::string &organizer, const attendee_params_t ¶ms) { +void Ics::Event::setOrganizer(const std::string &organizer, const Ics::Event::attendee_params_t ¶ms) { mOrganizer = std::make_pair(organizer, params); } @@ -59,7 +61,7 @@ void Ics::Event::addAttendee(const std::string &attendee) { addAttendee(attendee, params); } -void Ics::Event::addAttendee(const std::string &attendee, const attendee_params_t ¶ms) { +void Ics::Event::addAttendee(const std::string &attendee, const Ics::Event::attendee_params_t ¶ms) { mAttendees.insert(std::make_pair(attendee, params)); } @@ -280,6 +282,14 @@ std::string Ics::Icalendar::asString() const { return bctoolbox::Utils::fold(output.str()); } +std::shared_ptr<ParticipantInfo> +Ics::Icalendar::fillParticipantInfo(const std::shared_ptr<Address> &address, + const Ics::Event::attendee_params_t ¶ms) const { + auto participantInfo = ParticipantInfo::create(address); + participantInfo->setParameters(params); + return participantInfo; +} + std::shared_ptr<ConferenceInfo> Ics::Icalendar::toConferenceInfo() const { if (mEvents.empty()) return nullptr; @@ -287,11 +297,11 @@ std::shared_ptr<ConferenceInfo> Ics::Icalendar::toConferenceInfo() const { const auto &event = mEvents.front(); // It should always be one event if (!event->getOrganizerAddress().empty()) { - const auto &org = event->getOrganizer(); - const auto &orgAddress = Address::create(org.first); - const auto &orgParams = org.second; + const auto &[orgAddressStr, orgParams] = event->getOrganizer(); + const auto &orgAddress = Address::create(orgAddressStr); if (orgAddress && orgAddress->isValid()) { - confInfo->setOrganizer(orgAddress, orgParams); + const auto organizerInfo = fillParticipantInfo(orgAddress, orgParams); + confInfo->setOrganizer(organizerInfo); } else { lWarning() << "Could not parse organizer's address: " << event->getOrganizerAddress() << " because it is not a valid address"; @@ -302,7 +312,8 @@ std::shared_ptr<ConferenceInfo> Ics::Icalendar::toConferenceInfo() const { if (!address.empty()) { const auto addr = Address::create(address); if (addr && addr->isValid()) { - confInfo->addParticipant(addr, params); + const auto participantInfo = fillParticipantInfo(addr, params); + confInfo->addParticipant(participantInfo); } else { lWarning() << "Could not parse attendee's address: " << address << " because it is not a valid address"; } diff --git a/src/chat/ics/ics.h b/src/chat/ics/ics.h index cf35f58870168049d3b72da74b69691d9f3916a7..e67b25a7e1ca5fb21fab9a43e1846b3b513fd674 100644 --- a/src/chat/ics/ics.h +++ b/src/chat/ics/ics.h @@ -122,6 +122,9 @@ public: void setCreationTime(time_t time); private: + std::shared_ptr<ParticipantInfo> fillParticipantInfo(const std::shared_ptr<Address> &address, + const Ics::Event::attendee_params_t ¶ms) const; + Method mMethod = Method::Request; std::list<std::shared_ptr<Event>> mEvents; }; diff --git a/src/conference/conference-info.cpp b/src/conference/conference-info.cpp index b60ffcc0e5764e3a00250121938b6887e8f2157a..e111c985895c541cd6e7cf450de98ed5a33bb628 100644 --- a/src/conference/conference-info.cpp +++ b/src/conference/conference-info.cpp @@ -21,8 +21,10 @@ #include "conference-info.h" #include "chat/ics/ics.h" +#include "factory/factory.h" #include "linphone/api/c-address.h" #include "linphone/types.h" +#include "participant-info.h" #include "private.h" #include "c-wrapper/c-wrapper.h" @@ -33,8 +35,6 @@ using namespace std; LINPHONE_BEGIN_NAMESPACE -const std::string ConferenceInfo::sequenceParam = "X-SEQ"; - ConferenceInfo::ConferenceInfo() { } @@ -43,84 +43,115 @@ const ConferenceInfo::organizer_t &ConferenceInfo::getOrganizer() const { } const std::shared_ptr<Address> &ConferenceInfo::getOrganizerAddress() const { - return getOrganizer().first; + const auto &organizer = getOrganizer(); + mOrganizerAddress = organizer ? organizer->getAddress() : nullptr; + return mOrganizerAddress; } -void ConferenceInfo::setOrganizer(const std::shared_ptr<Address> &organizer, const participant_params_t ¶ms) { - mOrganizer = std::make_pair(Address::create(organizer->getUri()), params); +void ConferenceInfo::setOrganizer(const std::shared_ptr<const ParticipantInfo> &organizer) { + mOrganizer = organizer->clone()->toSharedPtr(); } -void ConferenceInfo::setOrganizer(const std::shared_ptr<Address> &organizer) { - ConferenceInfo::participant_params_t params; - setOrganizer(organizer, params); +void ConferenceInfo::setOrganizer(const std::shared_ptr<const Address> &organizer) { + auto organizerInfo = Factory::get()->createParticipantInfo(Address::create(organizer->getUri())); + for (const auto &[name, value] : organizer->getParams()) { + organizerInfo->addParameter(name, value); + } + setOrganizer(organizerInfo); } -void ConferenceInfo::addOrganizerParam(const std::string ¶m, const std::string &value) { - mOrganizer.second[param] = value; +const ConferenceInfo::participant_list_t &ConferenceInfo::getParticipants() const { + return mParticipants; } -const std::string ConferenceInfo::getOrganizerParam(const std::string ¶m) const { - try { - const auto ¶ms = mOrganizer.second; - return params.at(param); - } catch (std::out_of_range &) { - return std::string(); +const bctbx_list_t *ConferenceInfo::getParticipantsCList() const { + mParticipantList.mList = mParticipants; + return mParticipantList.getCList(); +} + +const std::list<std::shared_ptr<Address>> &ConferenceInfo::getParticipantAddressList() const { + updateParticipantAddresses(); + return mParticipantAddresses.mList; +} + +const bctbx_list_t *ConferenceInfo::getParticipantAddressCList() const { + updateParticipantAddresses(); + return mParticipantAddresses.getCList(); +} + +void ConferenceInfo::updateParticipantAddresses() const { + std::list<std::shared_ptr<Address>> addressList; + for (const auto &participantInfo : mParticipants) { + addressList.push_back(participantInfo->getAddress()); } + mParticipantAddresses.mList = addressList; } -const ConferenceInfo::participant_list_t &ConferenceInfo::getParticipants() const { - return mParticipants; +void ConferenceInfo::setParticipants(const std::list<std::shared_ptr<Address>> &participants) { + for (const auto &info : participants) { + addParticipant(info); + } } -void ConferenceInfo::setParticipants(const participant_list_t &participants) { - for (const auto &p : participants) { - addParticipant(p.first, p.second); +void ConferenceInfo::setParticipants(const ConferenceInfo::participant_list_t &participants) { + for (const auto &info : participants) { + addParticipant(info); + } +} + +void ConferenceInfo::addParticipant(const std::shared_ptr<const ParticipantInfo> &participantInfo) { + const auto &address = participantInfo->getAddress(); + if (hasParticipant(address)) { + mParticipants.push_back(participantInfo->clone()->toSharedPtr()); + } else { + lInfo() << "Participant with address " << *address << " is already in the list of conference info " << this + << " (address " << (getUri() ? getUri()->toString() : std::string("<unknown address>")) << ")"; } } -void ConferenceInfo::addParticipant(const std::shared_ptr<Address> &participant) { - ConferenceInfo::participant_params_t params; - addParticipant(participant, params); +void ConferenceInfo::addParticipant(const std::shared_ptr<const Address> &participant) { + auto participantInfo = Factory::get()->createParticipantInfo(participant->clone()->toSharedPtr()); + addParticipant(participantInfo); } -void ConferenceInfo::addParticipant(const std::shared_ptr<Address> &participant, const participant_params_t ¶ms) { - mParticipants.insert(std::make_pair(Address::create(participant->getUri()), params)); +void ConferenceInfo::removeParticipant(const std::shared_ptr<const ParticipantInfo> &participantInfo) { + removeParticipant(participantInfo->getAddress()); } -void ConferenceInfo::removeParticipant(const std::shared_ptr<Address> &participant) { - auto it = std::find_if(mParticipants.begin(), mParticipants.end(), - [&participant](const auto &p) { return (participant->weakEqual(*p.first)); }); +void ConferenceInfo::removeParticipant(const std::shared_ptr<const Address> &participant) { + auto it = findParticipantIt(participant); if (it == mParticipants.cend()) { - lInfo() << "Unable to find participant with address " << participant << " in conference info " << this - << " (address " << *getUri() << ")"; + lInfo() << "Unable to remove participant with address " << *participant << " in conference info " << this + << " (address " << (getUri() ? getUri()->toString() : std::string("<unknown address>")) << ")"; } else { mParticipants.erase(it); } } -void ConferenceInfo::addParticipantParam(const std::shared_ptr<Address> &participant, - const std::string ¶m, - const std::string &value) { - auto it = std::find_if(mParticipants.begin(), mParticipants.end(), - [&participant](const auto &p) { return (participant->weakEqual(*p.first)); }); - if (it != mParticipants.end()) { - auto ¶ms = (*it).second; - params[param] = value; - } +void ConferenceInfo::updateParticipant(const std::shared_ptr<const ParticipantInfo> &participantInfo) { + removeParticipant(participantInfo); + addParticipant(participantInfo); } -const std::string ConferenceInfo::getParticipantParam(const std::shared_ptr<Address> &participant, - const std::string ¶m) const { - try { - auto it = std::find_if(mParticipants.begin(), mParticipants.end(), - [&participant](const auto &p) { return (participant->weakEqual(*p.first)); }); - if (it != mParticipants.cend()) { - const auto ¶ms = (*it).second; - return params.at(param); - } - } catch (std::out_of_range &) { - } - return std::string(); +ConferenceInfo::participant_list_t::const_iterator +ConferenceInfo::findParticipantIt(const std::shared_ptr<const Address> &address) const { + return std::find_if(mParticipants.begin(), mParticipants.end(), + [&address](const auto &p) { return (address->weakEqual(*p->getAddress())); }); +} + +bool ConferenceInfo::hasParticipant(const std::shared_ptr<const Address> &address) const { + return (findParticipantIt(address) == mParticipants.end()); +} + +const std::shared_ptr<ParticipantInfo> +ConferenceInfo::findParticipant(const std::shared_ptr<const Address> &address) const { + auto it = findParticipantIt(address); + if (it != mParticipants.end()) { + return *it; + }; + lInfo() << "Unable to find participant with address " << *address << " in conference info " << this << " (address " + << (getUri() ? getUri()->toString() : std::string("<unknown address>")) << ")"; + return nullptr; } bool ConferenceInfo::isValidUri() const { @@ -131,7 +162,7 @@ const std::shared_ptr<Address> &ConferenceInfo::getUri() const { return mUri; } -void ConferenceInfo::setUri(const std::shared_ptr<Address> uri) { +void ConferenceInfo::setUri(const std::shared_ptr<const Address> uri) { mUri = Address::create(uri->getUri()); } @@ -232,14 +263,17 @@ void ConferenceInfo::updateFrom(const std::shared_ptr<ConferenceInfo> &info) { setIcsSequence(info->getIcsSequence() + 1); const auto &participants = info->getParticipants(); - for (auto &participant : mParticipants) { - const auto &otherParticipant = + const auto &pAddress = participant->getAddress(); + const auto &otherParticipantIt = std::find_if(participants.cbegin(), participants.cend(), - [&participant](const auto &p) { return (p.first == participant.first); }); + [&pAddress](const auto &p) { return (pAddress->weakEqual(*p->getAddress())); }); - if (otherParticipant != participants.cend()) { - participant.second = otherParticipant->second; + if (otherParticipantIt != participants.cend()) { + const auto &otherParticipant = (*otherParticipantIt); + // Copy sequence number in order to keep it inceasing. + // IF this is not done, the sequence number may be arbitrarly changed and clients could get out of sync + participant->setSequenceNumber(otherParticipant->getSequenceNumber()); } } } @@ -272,7 +306,7 @@ const string ConferenceInfo::toIcsString(bool cancel, int sequence) const { const auto &organizerAddress = getOrganizerAddress(); if (organizerAddress && organizerAddress->isValid()) { const auto uri = organizerAddress->asStringUriOnly(); - event->setOrganizer(uri, mOrganizer.second); + event->setOrganizer(uri, mOrganizer->getAllParameters()); } event->setSummary(mSubject); @@ -282,10 +316,11 @@ const string ConferenceInfo::toIcsString(bool cancel, int sequence) const { const auto uri = mUri->asStringUriOnly(); event->setXConfUri(uri); } - for (const auto &[address, params] : mParticipants) { + for (const auto &participantInfo : mParticipants) { + const auto &address = participantInfo->getAddress(); if (address->isValid()) { const auto uri = address->asStringUriOnly(); - event->addAttendee(uri, params); + event->addAttendee(uri, participantInfo->getAllParameters()); } } @@ -330,32 +365,6 @@ void ConferenceInfo::setCreationTime(time_t time) { mCreationTime = time; } -const std::string ConferenceInfo::memberParametersToString(const ConferenceInfo::participant_params_t ¶ms) { - std::string str; - for (const auto &[name, value] : params) { - if (!str.empty()) { - str.append(";"); - } - str.append(name + "=" + value); - } - return str; -} - -const ConferenceInfo::participant_params_t ConferenceInfo::stringToMemberParameters(const std::string ¶msString) { - ConferenceInfo::participant_params_t params; - if (!paramsString.empty()) { - const auto &splittedValue = bctoolbox::Utils::split(Utils::trim(paramsString), ";"); - for (const auto ¶m : splittedValue) { - auto equal = param.find("="); - string name = param.substr(0, equal); - string value = param.substr(equal + 1, param.size()); - params.insert(std::make_pair(name, value)); - } - } - - return params; -} - std::ostream &operator<<(std::ostream &lhs, ConferenceInfo::State s) { switch (s) { case ConferenceInfo::State::New: diff --git a/src/conference/conference-info.h b/src/conference/conference-info.h index ba9fdc27ead355da1beda1186556d909771adb9e..2028907888ed54bcdc4a67320771db9eb02d290a 100644 --- a/src/conference/conference-info.h +++ b/src/conference/conference-info.h @@ -28,6 +28,7 @@ #include <belle-sip/object++.hh> #include "address/address.h" +#include "c-wrapper/list-holder.h" #include "conference/conference-params-interface.h" #include "linphone/api/c-types.h" #include "linphone/types.h" @@ -35,6 +36,8 @@ // ============================================================================= LINPHONE_BEGIN_NAMESPACE + +class ParticipantInfo; class LINPHONE_PUBLIC ConferenceInfo : public bellesip::HybridObject<LinphoneConferenceInfo, ConferenceInfo> { public: struct AddressCmp { @@ -43,10 +46,8 @@ public: } }; - static const std::string sequenceParam; - using participant_params_t = std::map<std::string, std::string>; - using participant_list_t = std::map<std::shared_ptr<Address>, participant_params_t, AddressCmp>; - using organizer_t = std::pair<std::shared_ptr<Address>, participant_params_t>; + using participant_list_t = std::list<std::shared_ptr<ParticipantInfo>>; + using organizer_t = std::shared_ptr<ParticipantInfo>; enum class State { New = LinphoneConferenceInfoStateNew, @@ -60,31 +61,33 @@ public: return new ConferenceInfo(*this); } - static const std::string memberParametersToString(const participant_params_t ¶ms); - static const participant_params_t stringToMemberParameters(const std::string ¶ms); - const organizer_t &getOrganizer() const; const std::shared_ptr<Address> &getOrganizerAddress() const; - void setOrganizer(const std::shared_ptr<Address> &organizer, const participant_params_t ¶ms); - void setOrganizer(const std::shared_ptr<Address> &organizer); - - void addOrganizerParam(const std::string ¶m, const std::string &value); - const std::string getOrganizerParam(const std::string ¶m) const; + void setOrganizer(const std::shared_ptr<const ParticipantInfo> &organizer); + void setOrganizer(const std::shared_ptr<const Address> &organizer); + const std::list<std::shared_ptr<Address>> &getParticipantAddressList() const; + const bctbx_list_t *getParticipantAddressCList() const; const participant_list_t &getParticipants() const; + const bctbx_list_t *getParticipantsCList() const; + + void setParticipants(const std::list<std::shared_ptr<Address>> &participants); void setParticipants(const participant_list_t &participants); - void addParticipant(const std::shared_ptr<Address> &participant); - void addParticipant(const std::shared_ptr<Address> &participant, const participant_params_t ¶ms); - void removeParticipant(const std::shared_ptr<Address> &participant); - void addParticipantParam(const std::shared_ptr<Address> &participant, - const std::string ¶m, - const std::string &value); - const std::string getParticipantParam(const std::shared_ptr<Address> &participant, const std::string ¶m) const; + void addParticipant(const std::shared_ptr<const Address> &participant); + void addParticipant(const std::shared_ptr<const ParticipantInfo> &participantInfo); + + void removeParticipant(const std::shared_ptr<const Address> &participant); + void removeParticipant(const std::shared_ptr<const ParticipantInfo> &participantInfo); + + bool hasParticipant(const std::shared_ptr<const Address> &address) const; + const std::shared_ptr<ParticipantInfo> findParticipant(const std::shared_ptr<const Address> &address) const; + + void updateParticipant(const std::shared_ptr<const ParticipantInfo> &participantInfo); bool isValidUri() const; const std::shared_ptr<Address> &getUri() const; - void setUri(const std::shared_ptr<Address> uri); + void setUri(const std::shared_ptr<const Address> uri); time_t getDateTime() const; void setDateTime(time_t dateTime); @@ -124,8 +127,14 @@ public: void setCreationTime(time_t time); private: + void updateParticipantAddresses() const; + participant_list_t::const_iterator findParticipantIt(const std::shared_ptr<const Address> &address) const; + organizer_t mOrganizer; + mutable std::shared_ptr<Address> mOrganizerAddress; participant_list_t mParticipants; + mutable ListHolder<Address> mParticipantAddresses; + mutable ListHolder<ParticipantInfo> mParticipantList; std::shared_ptr<Address> mUri; time_t mDateTime = (time_t)-1; unsigned int mDuration = 0; diff --git a/src/conference/conference-interface.h b/src/conference/conference-interface.h index 6304531fa92895471e401b7c5d0a688639f5f146..e2c28568e72a12fb771e74013040d3c914148345 100644 --- a/src/conference/conference-interface.h +++ b/src/conference/conference-interface.h @@ -119,10 +119,10 @@ public: virtual const std::string &getSubject() const = 0; /* - * Get the subject of this conference - * @return The subject of the chat room in UTF8 - */ - virtual const std::string &getUtf8Subject () const = 0; + * Get the subject of this conference + * @return The subject of the chat room in UTF8 + */ + virtual const std::string &getUtf8Subject() const = 0; /* * Set the subject of this conference. If not focus, this operation is only available if the local participant diff --git a/src/conference/conference-scheduler.cpp b/src/conference/conference-scheduler.cpp index fc1e9900ba21403f0207ca8630b6ce3d11e5eef4..d9f2990dda4e0aaf01774e3958b6ba4c8f057a78 100644 --- a/src/conference/conference-scheduler.cpp +++ b/src/conference/conference-scheduler.cpp @@ -23,8 +23,9 @@ #include "account/account.h" #include "c-wrapper/c-wrapper.h" #include "chat/chat-message/chat-message-p.h" -#include "conference-scheduler.h" +#include "conference/conference-scheduler.h" #include "conference/params/call-session-params-p.h" +#include "conference/participant-info.h" #include "conference/session/media-session.h" #include "content/file-content.h" #include "core/core-p.h" @@ -85,21 +86,19 @@ const std::shared_ptr<ConferenceInfo> ConferenceScheduler::getInfo() const { void ConferenceScheduler::fillCancelList(const ConferenceInfo::participant_list_t &oldList, const ConferenceInfo::participant_list_t &newList) { mCancelToSend.clear(); - for (const auto &participant : oldList) { - const bool participantFound = (std::find_if(newList.cbegin(), newList.cend(), [&participant](const auto &e) { - return (e.first->weakEqual(*participant.first)); - }) != newList.cend()); + for (const auto &oldParticipantInfo : oldList) { + const auto &address = oldParticipantInfo->getAddress(); + // Workaroud for CLang issue: https://github.com/llvm/llvm-project/issues/52720 + const bool participantFound = + (std::find_if(newList.cbegin(), newList.cend(), [&address = address](const auto &e) { + return (address->weakEqual(*e->getAddress())); + }) != newList.cend()); if (!participantFound) { - auto sequence = -1; - try { - const auto params = participant.second; - const auto &value = params.at(ConferenceInfo::sequenceParam); - if (!value.empty()) { - sequence = std::atoi(value.c_str()) + 1; - } - } catch (std::out_of_range &) { + auto sequence = oldParticipantInfo->getSequenceNumber(); + if (sequence >= 0) { + sequence++; } - mCancelToSend.insert(std::make_pair(participant.first, sequence)); + mCancelToSend.insert(std::make_pair(address, sequence)); } } } @@ -109,13 +108,21 @@ void ConferenceScheduler::cancelConference(const std::shared_ptr<ConferenceInfo> auto clone = info->clone()->toSharedPtr(); while (!clone->getParticipants().empty()) { const auto &participants = clone->getParticipants(); - const auto &participant = *(participants.begin()); - clone->removeParticipant(participant.first); + clone->removeParticipant(*(participants.begin())); } setInfo(clone); } } +std::shared_ptr<Address> +ConferenceScheduler::createParticipantAddress(const ConferenceInfo::participant_list_t::value_type &p) const { + std::shared_ptr<Address> address = Address::create(p->getAddress()->getUri()); + for (const auto &[name, value] : p->getAllParameters()) { + address->setParam(name, value); + } + return address; +} + void ConferenceScheduler::setInfo(const std::shared_ptr<ConferenceInfo> &info) { if (!info) { lWarning() << "[Conference Scheduler] [" << this @@ -146,7 +153,7 @@ void ConferenceScheduler::setInfo(const std::shared_ptr<ConferenceInfo> &info) { const auto &participants = clone->getParticipants(); const auto participantListEmpty = participants.empty(); const bool participantFound = (std::find_if(participants.cbegin(), participants.cend(), [&creator](const auto &p) { - return (creator->weakEqual(*(p.first))); + return (creator->weakEqual(*p->getAddress())); }) != participants.cend()); if (!creator->weakEqual(*organizer) && !participantFound) { lWarning() << "[Conference Scheduler] [" << this << "] Address " << creator->toString() @@ -232,7 +239,7 @@ void ConferenceScheduler::setInfo(const std::shared_ptr<ConferenceInfo> &info) { std::list<std::shared_ptr<Address>> invitees; for (const auto &p : mConferenceInfo->getParticipants()) { - invitees.push_back(p.first); + invitees.push_back(createParticipantAddress(p)); } if (isUpdate) { @@ -353,10 +360,9 @@ void ConferenceScheduler::onCallSessionSetTerminated(const shared_ptr<CallSessio // Participant with the focus call is admin L_GET_CPP_PTR_FROM_C_OBJECT(new_params)->addCustomContactParameter("admin", Utils::toString(true)); std::list<std::shared_ptr<Address>> addressesList; - for (const auto &p : mConferenceInfo->getParticipants()) { - addressesList.push_back(p.first); + for (const auto &participantInfo : mConferenceInfo->getParticipants()) { + addressesList.push_back(participantInfo->getAddress()); } - addressesList.sort([](const auto &addr1, const auto &addr2) { return *addr1 < *addr2; }); addressesList.unique([](const auto &addr1, const auto &addr2) { return addr1->weakEqual(*addr2); }); @@ -374,8 +380,8 @@ void ConferenceScheduler::onCallSessionSetTerminated(const shared_ptr<CallSessio LinphoneVideoActivationPolicy *pol = linphone_core_get_video_activation_policy(getCore()->getCCore()); bool_t initiate_video = !!linphone_video_activation_policy_get_automatically_initiate(pol); linphone_call_params_enable_video( - new_params, - static_pointer_cast<MediaSession>(session)->getMediaParams()->videoEnabled() && initiate_video); + new_params, + static_pointer_cast<MediaSession>(session)->getMediaParams()->videoEnabled() && initiate_video); linphone_video_activation_policy_unref(pol); linphone_core_invite_address_with_params_2(getCore()->getCCore(), remoteAddress->toC(), new_params, @@ -415,10 +421,9 @@ shared_ptr<ChatMessage> ConferenceScheduler::createInvitationChatMessage(shared_ std::find_if(mCancelToSend.cbegin(), mCancelToSend.cend(), [&participant](const auto &p) { return (participant->weakEqual(*p.first)); }); if (cancelParticipant == mCancelToSend.cend()) { - const auto participantSequence = - mConferenceInfo->getParticipantParam(participant, ConferenceInfo::sequenceParam); - if (!participantSequence.empty()) { - sequence = std::atoi(participantSequence.c_str()); + const auto &participantInfo = mConferenceInfo->findParticipant(participant); + if (participantInfo) { + sequence = participantInfo->getSequenceNumber(); } } else { sequence = (*cancelParticipant).second; @@ -467,12 +472,12 @@ void ConferenceScheduler::sendInvitations(shared_ptr<ChatRoomParams> chatRoomPar const auto &participants = mConferenceInfo->getParticipants(); const bool participantFound = (std::find_if(participants.cbegin(), participants.cend(), [&sender](const auto &p) { - return (sender->weakEqual(*p.first)); + return (sender->weakEqual(*p->getAddress())); }) != participants.cend()); - const auto &organizer = mConferenceInfo->getOrganizerAddress(); + const auto organizer = mConferenceInfo->getOrganizerAddress(); if (!sender->weakEqual(*organizer) && !participantFound) { - lWarning() << "[Conference Scheduler] [" << this << "] Unable to find the address " << sender - << " sending invitations among the list of participants or the organizer (" << organizer + lWarning() << "[Conference Scheduler] [" << this << "] Unable to find the address " << *sender + << " sending invitations among the list of participants or the organizer (" << *organizer << ") of conference " << *mConferenceInfo->getUri(); return; } @@ -489,12 +494,20 @@ void ConferenceScheduler::sendInvitations(shared_ptr<ChatRoomParams> chatRoomPar } std::list<std::shared_ptr<Address>> invitees; - for (const auto &p : participants) { - invitees.push_back(p.first); + for (const auto &participantInfo : participants) { + invitees.push_back(participantInfo->getAddress()); } - for (const auto &p : mCancelToSend) { - invitees.push_back(p.first); +// https://gcc.gnu.org/bugzilla/show_bug.cgi?format=multiple&id=81767 +#if __GNUC__ == 7 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-variable" +#endif // __GNUC__ == 7 + for (const auto &[address, value] : mCancelToSend) { + invitees.push_back(address); } +#if __GNUC__ == 7 +#pragma GCC diagnostic pop +#endif // __GNUC__ == 7 mInvitationsToSend.clear(); for (auto participant : invitees) { @@ -505,14 +518,24 @@ void ConferenceScheduler::sendInvitations(shared_ptr<ChatRoomParams> chatRoomPar << "] from chat room participants as it is ourselves"; } - const std::string &sequence = mConferenceInfo->getParticipantParam(participant, ConferenceInfo::sequenceParam); - const int newSequence = (sequence.empty()) ? 0 : std::atoi(sequence.c_str()) + 1; - mConferenceInfo->addParticipantParam(participant, ConferenceInfo::sequenceParam, std::to_string(newSequence)); + const auto &participantInfo = mConferenceInfo->findParticipant(participant); + if (participantInfo) { + auto newParticipantInfo = participantInfo->clone()->toSharedPtr(); + const auto &sequence = newParticipantInfo->getSequenceNumber(); + const auto newSequence = (sequence < 0) ? 0 : sequence + 1; + newParticipantInfo->setSequenceNumber(newSequence); + mConferenceInfo->updateParticipant(newParticipantInfo); + } } - const std::string &organizerSequence = mConferenceInfo->getOrganizerParam(ConferenceInfo::sequenceParam); - const int newOrganizerSequence = (organizerSequence.empty()) ? 0 : std::atoi(organizerSequence.c_str()) + 1; - mConferenceInfo->addOrganizerParam(ConferenceInfo::sequenceParam, std::to_string(newOrganizerSequence)); + const auto &organizerInfo = mConferenceInfo->getOrganizer(); + if (organizerInfo) { + auto newOrganizerInfo = organizerInfo->clone()->toSharedPtr(); + const auto &sequence = newOrganizerInfo->getSequenceNumber(); + const auto newSequence = (sequence < 0) ? 0 : sequence + 1; + newOrganizerInfo->setSequenceNumber(newSequence); + mConferenceInfo->setOrganizer(newOrganizerInfo); + } if (!sender->weakEqual(*organizer)) { const bool organizerFound = diff --git a/src/conference/conference-scheduler.h b/src/conference/conference-scheduler.h index b3365820a47457d5f0564c28816afbee2924e899..7a5290070e56df8e1ef5ea706c227a3303608abd 100644 --- a/src/conference/conference-scheduler.h +++ b/src/conference/conference-scheduler.h @@ -80,6 +80,7 @@ private: void setState(State newState); std::string stateToString(State state); + std::shared_ptr<Address> createParticipantAddress(const ConferenceInfo::participant_list_t::value_type &p) const; std::shared_ptr<ChatMessage> createInvitationChatMessage(std::shared_ptr<AbstractChatRoom> chatRoom, const std::shared_ptr<Address> participant, bool cancel); diff --git a/src/conference/conference.cpp b/src/conference/conference.cpp index fa7d368c91d0eebc8c8a4778b5f9ca26d28f229d..0cf6a1a25ede5d65b2706c8f519e3fe6999216b6 100644 --- a/src/conference/conference.cpp +++ b/src/conference/conference.cpp @@ -23,6 +23,7 @@ #include "conference.h" #include "conference/params/media-session-params-p.h" #include "conference/participant-device.h" +#include "conference/participant-info.h" #include "conference/session/call-session-p.h" #include "conference/session/media-session.h" #include "content/content-disposition.h" @@ -715,22 +716,14 @@ std::shared_ptr<ConferenceInfo> Conference::createConferenceInfo() const { return nullptr; } -std::shared_ptr<ConferenceInfo> -Conference::createConferenceInfoWithOrganizer(const std::shared_ptr<Address> &organizer) const { - // Add participants that are currently in the conference - std::list<std::shared_ptr<Address>> participantAddresses; - for (const auto &p : getParticipants()) { - const auto &pAddress = p->getAddress(); - participantAddresses.push_back(pAddress); - } - - return createConferenceInfoWithCustomParticipantList(organizer, participantAddresses); -} - std::shared_ptr<ConferenceInfo> Conference::createConferenceInfoWithCustomParticipantList( - const std::shared_ptr<Address> &organizer, const std::list<std::shared_ptr<Address>> invitedParticipants) const { + const std::shared_ptr<Address> &organizer, const ConferenceInfo::participant_list_t invitedParticipants) const { std::shared_ptr<ConferenceInfo> info = ConferenceInfo::create(); - info->setOrganizer(organizer); + auto organizerInfo = ParticipantInfo::create(Address::create(organizer->getUri())); + for (const auto &[name, value] : organizer->getParams()) { + organizerInfo->addParameter(name, value); + } + info->setOrganizer(organizerInfo); for (const auto &participant : invitedParticipants) { info->addParticipant(participant); } @@ -823,9 +816,9 @@ void Conference::updateParticipantsInConferenceInfo(const std::shared_ptr<Addres auto info = createOrGetConferenceInfo(); if (info) { const auto ¤tParticipants = info->getParticipants(); - const auto participantAddressIt = - std::find_if(currentParticipants.begin(), currentParticipants.end(), - [&participantAddress](const auto &p) { return (*p.first == *participantAddress); }); + const auto participantAddressIt = std::find_if( + currentParticipants.begin(), currentParticipants.end(), + [&participantAddress](const auto &p) { return (participantAddress->weakEqual(*p->getAddress())); }); if (participantAddressIt == currentParticipants.end()) { info->addParticipant(participantAddress); diff --git a/src/conference/conference.h b/src/conference/conference.h index ec346119b44206545d31cb02fc0bf6065210d291..2f19adba716da4a33398e21f9fb873882eba9620 100644 --- a/src/conference/conference.h +++ b/src/conference/conference.h @@ -247,9 +247,7 @@ protected: virtual std::shared_ptr<ConferenceInfo> createConferenceInfo() const; virtual std::shared_ptr<ConferenceInfo> createConferenceInfoWithCustomParticipantList(const std::shared_ptr<Address> &organizer, - const std::list<std::shared_ptr<Address>> invitedParticipants) const; - virtual std::shared_ptr<ConferenceInfo> - createConferenceInfoWithOrganizer(const std::shared_ptr<Address> &organizer) const; + const ConferenceInfo::participant_list_t invitedParticipants) const; private: L_DISABLE_COPY(Conference); diff --git a/src/conference/handlers/local-conference-event-handler.cpp b/src/conference/handlers/local-conference-event-handler.cpp index dbb13b371c0df546da949800699d0164c8d46dcb..b0e5b814068dbfe5c7adc432d9ab635c514f4b6c 100644 --- a/src/conference/handlers/local-conference-event-handler.cpp +++ b/src/conference/handlers/local-conference-event-handler.cpp @@ -195,6 +195,7 @@ Content LocalConferenceEventHandler::createNotifyFullState(const shared_ptr<Even user.setEndpoint(endpoints); user.setEntity(participant->getAddress()->asStringUriOnly()); user.getRoles()->getEntry().push_back(participant->isAdmin() ? "admin" : "participant"); + user.getRoles()->getEntry().push_back(Participant::roleToText(participant->getRole())); user.setState(StateType::full); for (const auto &device : participant->getDevices()) { @@ -539,6 +540,9 @@ string LocalConferenceEventHandler::createNotifyParticipantAdded(const std::shar user.setRoles(roles); user.setEntity(pAddress->asStringUriOnly()); user.getRoles()->getEntry().push_back((participant && participant->isAdmin()) ? "admin" : "participant"); + if (participant) { + user.getRoles()->getEntry().push_back(Participant::roleToText(participant->getRole())); + } user.setState(StateType::full); confInfo.getUsers()->getUser().push_back(user); diff --git a/src/conference/handlers/remote-conference-event-handler.cpp b/src/conference/handlers/remote-conference-event-handler.cpp index 2b61c7884220bf01b58c327443b22065e9a73111..4d9c980f5b86cac70de63540858710c74f15be12 100644 --- a/src/conference/handlers/remote-conference-event-handler.cpp +++ b/src/conference/handlers/remote-conference-event-handler.cpp @@ -324,18 +324,26 @@ void RemoteConferenceEventHandler::conferenceInfoNotifyReceived(const string &xm auto &roles = user.getRoles(); if (roles) { - auto &entry = roles->getEntry(); - bool isAdmin = (find(entry, "admin") != entry.end() ? true : false); + // Admin + bool isAdmin = (find(entry, "admin") != entry.end() ? true : false); if (participant->isAdmin() != isAdmin) { - participant->setAdmin(isAdmin); - if (!isFullState) { conf->notifyParticipantSetAdmin(creationTime, isFullState, participant, isAdmin); } } + + // Role + bool isSpeaker = (find(entry, "speaker") != entry.end() ? true : false); + bool isListener = (find(entry, "listener") != entry.end() ? true : false); + if (isListener) { + participant->setRole(Participant::Role::Listener); + } else if (isSpeaker || (state == StateType::full)) { + // When a participant is added, then set its role to speaker by default to be backward compatible + participant->setRole(Participant::Role::Speaker); + } } for (const auto &endpoint : user.getEndpoint()) { @@ -425,7 +433,7 @@ void RemoteConferenceEventHandler::conferenceInfoNotifyReceived(const string &xm } } */ - bool mediaCapabilityChanged = false; + std::set<LinphoneStreamType> mediaCapabilityChanged; for (const auto &media : endpoint.getMedia()) { const std::string mediaType = media.getType().get(); LinphoneStreamType streamType = LinphoneStreamTypeUnknown; @@ -441,7 +449,9 @@ void RemoteConferenceEventHandler::conferenceInfoNotifyReceived(const string &xm LinphoneMediaDirection mediaDirection = RemoteConferenceEventHandler::mediaStatusToMediaDirection(media.getStatus().get()); - mediaCapabilityChanged |= device->setStreamCapability(mediaDirection, streamType); + if (device->setStreamCapability(mediaDirection, streamType)) { + mediaCapabilityChanged.insert(streamType); + } if (media.getLabel()) { const std::string label = media.getLabel().get(); if (!label.empty()) { @@ -459,19 +469,19 @@ void RemoteConferenceEventHandler::conferenceInfoNotifyReceived(const string &xm } } - bool mediaAvailabilityChanged = device->updateStreamAvailabilities(); + auto mediaAvailabilityChanged = device->updateStreamAvailabilities(); // Do not notify availability changed during full states and participant addition because it is // already done by the listener method onFullStateReceived if (!isFullState && (state != StateType::full) && (previousDeviceState != ParticipantDevice::State::ScheduledForJoining) && (previousDeviceState != ParticipantDevice::State::Joining) && (previousDeviceState != ParticipantDevice::State::Alerting)) { - if (mediaAvailabilityChanged) { + if (!mediaAvailabilityChanged.empty()) { conf->notifyParticipantDeviceMediaAvailabilityChanged(creationTime, isFullState, participant, device); } - if (mediaCapabilityChanged) { + if (!mediaCapabilityChanged.empty()) { conf->notifyParticipantDeviceMediaCapabilityChanged(creationTime, isFullState, participant, device); } diff --git a/src/conference/participant-device.cpp b/src/conference/participant-device.cpp index 724088abecbf9762e73fd201b5b3ebea1e83c149..1f3a49d6b47392e590aead2026d3a69f59203b1b 100644 --- a/src/conference/participant-device.cpp +++ b/src/conference/participant-device.cpp @@ -429,10 +429,14 @@ LinphoneMediaDirection ParticipantDevice::computeDeviceMediaDirection(const bool return LinphoneMediaDirectionInactive; } -bool ParticipantDevice::updateMediaCapabilities() { - bool mediaCapabilityChanged = false; +std::set<LinphoneStreamType> ParticipantDevice::updateMediaCapabilities() { + std::set<LinphoneStreamType> mediaCapabilityChanged; const auto &conference = getConference(); + auto resultAudioDir = LinphoneMediaDirectionInactive; + auto resultVideoDir = LinphoneMediaDirectionInactive; + auto resultTextDir = LinphoneMediaDirectionInactive; + if (conference) { const auto &isMe = conference->isMe(getAddress()); const auto &conferenceParams = conference->getCurrentParams(); @@ -485,26 +489,42 @@ bool ParticipantDevice::updateMediaCapabilities() { if (mSession->getPrivate()->isInConference()) { const auto audioSsrc = mMediaSession->getSsrc(LinphoneStreamTypeAudio); - mediaCapabilityChanged |= setSsrc(LinphoneStreamTypeAudio, audioSsrc); + if (setSsrc(LinphoneStreamTypeAudio, audioSsrc)) { + mediaCapabilityChanged.insert(LinphoneStreamTypeAudio); + } const auto videoSsrc = mMediaSession->getSsrc(LinphoneStreamTypeVideo); - mediaCapabilityChanged |= setSsrc(LinphoneStreamTypeVideo, videoSsrc); + if (setSsrc(LinphoneStreamTypeVideo, videoSsrc)) { + mediaCapabilityChanged.insert(LinphoneStreamTypeVideo); + } } } - mediaCapabilityChanged |= setStreamCapability( - computeDeviceMediaDirection(conferenceAudioEnabled, audioEnabled, audioDir), LinphoneStreamTypeAudio); - mediaCapabilityChanged |= setStreamCapability( - computeDeviceMediaDirection(conferenceVideoEnabled, videoEnabled, videoDir), LinphoneStreamTypeVideo); - mediaCapabilityChanged |= setStreamCapability( - computeDeviceMediaDirection(conferenceTextEnabled, textEnabled, textDir), LinphoneStreamTypeText); + resultAudioDir = computeDeviceMediaDirection(conferenceAudioEnabled, audioEnabled, audioDir); + resultVideoDir = computeDeviceMediaDirection(conferenceVideoEnabled, videoEnabled, videoDir); + resultTextDir = computeDeviceMediaDirection(conferenceTextEnabled, textEnabled, textDir); } else { - mediaCapabilityChanged |= setStreamCapability(LinphoneMediaDirectionInactive, LinphoneStreamTypeAudio); - mediaCapabilityChanged |= setStreamCapability(LinphoneMediaDirectionInactive, LinphoneStreamTypeVideo); - mediaCapabilityChanged |= setStreamCapability(LinphoneMediaDirectionInactive, LinphoneStreamTypeText); - mediaCapabilityChanged |= setSsrc(LinphoneStreamTypeAudio, 0); - mediaCapabilityChanged |= setSsrc(LinphoneStreamTypeVideo, 0); + if (setSsrc(LinphoneStreamTypeAudio, 0)) { + mediaCapabilityChanged.insert(LinphoneStreamTypeAudio); + } + + if (setSsrc(LinphoneStreamTypeVideo, 0)) { + mediaCapabilityChanged.insert(LinphoneStreamTypeVideo); + } + + resultAudioDir = LinphoneMediaDirectionInactive; + resultVideoDir = LinphoneMediaDirectionInactive; + resultTextDir = LinphoneMediaDirectionInactive; } + if (setStreamCapability(resultAudioDir, LinphoneStreamTypeAudio)) { + mediaCapabilityChanged.insert(LinphoneStreamTypeAudio); + } + if (setStreamCapability(resultVideoDir, LinphoneStreamTypeVideo)) { + mediaCapabilityChanged.insert(LinphoneStreamTypeVideo); + } + if (setStreamCapability(resultTextDir, LinphoneStreamTypeText)) { + mediaCapabilityChanged.insert(LinphoneStreamTypeText); + } return mediaCapabilityChanged; } @@ -515,9 +535,9 @@ bool ParticipantDevice::computeStreamAvailable(const bool conferenceEnable, return ((resultDir == LinphoneMediaDirectionSendOnly) || (resultDir == LinphoneMediaDirectionSendRecv)); } -bool ParticipantDevice::updateStreamAvailabilities() { +std::set<LinphoneStreamType> ParticipantDevice::updateStreamAvailabilities() { const auto &conference = getConference(); - auto streamAvailabilityChanged = false; + std::set<LinphoneStreamType> streamAvailabilityChanged; const auto session = getSession() ? getSession() : (conference ? conference->getMainSession() : nullptr); if (conference) { @@ -526,20 +546,14 @@ bool ParticipantDevice::updateStreamAvailabilities() { const auto &conferenceVideoEnabled = conferenceParams.videoEnabled(); const auto &conferenceTextEnabled = conferenceParams.chatEnabled(); if (session) { + auto audioEnabled = false; + auto videoEnabled = false; + auto textEnabled = false; if (session->getPrivate()->isInConference() && conferenceParams.localParticipantEnabled()) { if (conference->isMe(getAddress())) { - streamAvailabilityChanged |= - setStreamAvailability(computeStreamAvailable(conferenceAudioEnabled, conferenceAudioEnabled, - getStreamCapability(LinphoneStreamTypeAudio)), - LinphoneStreamTypeAudio); - streamAvailabilityChanged |= - setStreamAvailability(computeStreamAvailable(conferenceVideoEnabled, conferenceVideoEnabled, - getStreamCapability(LinphoneStreamTypeVideo)), - LinphoneStreamTypeVideo); - streamAvailabilityChanged |= - setStreamAvailability(computeStreamAvailable(conferenceTextEnabled, conferenceTextEnabled, - getStreamCapability(LinphoneStreamTypeText)), - LinphoneStreamTypeText); + audioEnabled = conferenceAudioEnabled; + videoEnabled = conferenceVideoEnabled; + textEnabled = conferenceTextEnabled; } else { std::shared_ptr<ParticipantDevice> meDev = nullptr; if (conferenceParams.getAccount()) { @@ -548,24 +562,12 @@ bool ParticipantDevice::updateStreamAvailabilities() { meDev = conference->getMe()->findDevice(devAddr); } } - auto audioEnabled = + audioEnabled = (meDev) ? meDev->getStreamAvailability(LinphoneStreamTypeAudio) : conferenceAudioEnabled; - auto videoEnabled = + videoEnabled = (meDev) ? meDev->getStreamAvailability(LinphoneStreamTypeVideo) : conferenceVideoEnabled; - auto textEnabled = + textEnabled = (meDev) ? meDev->getStreamAvailability(LinphoneStreamTypeText) : conferenceTextEnabled; - streamAvailabilityChanged |= - setStreamAvailability(computeStreamAvailable(conferenceAudioEnabled, audioEnabled, - getStreamCapability(LinphoneStreamTypeAudio)), - LinphoneStreamTypeAudio); - streamAvailabilityChanged |= - setStreamAvailability(computeStreamAvailable(conferenceVideoEnabled, videoEnabled, - getStreamCapability(LinphoneStreamTypeVideo)), - LinphoneStreamTypeVideo); - streamAvailabilityChanged |= - setStreamAvailability(computeStreamAvailable(conferenceTextEnabled, textEnabled, - getStreamCapability(LinphoneStreamTypeText)), - LinphoneStreamTypeText); } } else { // A conference server is a passive element, therefore it may happen that the negotiated capabilities @@ -575,42 +577,43 @@ bool ParticipantDevice::updateStreamAvailabilities() { const MediaSessionParams *params = (session->getPrivate()->isInConference()) ? static_pointer_cast<MediaSession>(session)->getRemoteParams() : static_pointer_cast<MediaSession>(session)->getMediaParams(); - - auto audioEnabled = false; - auto videoEnabled = false; - auto textEnabled = false; if (params) { audioEnabled = params->audioEnabled(); videoEnabled = params->videoEnabled(); textEnabled = params->realtimeTextEnabled(); } - - streamAvailabilityChanged |= - setStreamAvailability(computeStreamAvailable(conferenceAudioEnabled, audioEnabled, - getStreamCapability(LinphoneStreamTypeAudio)), - LinphoneStreamTypeAudio); - streamAvailabilityChanged |= - setStreamAvailability(computeStreamAvailable(conferenceVideoEnabled, videoEnabled, - getStreamCapability(LinphoneStreamTypeVideo)), - LinphoneStreamTypeVideo); - streamAvailabilityChanged |= - setStreamAvailability(computeStreamAvailable(conferenceTextEnabled, textEnabled, - getStreamCapability(LinphoneStreamTypeText)), - LinphoneStreamTypeText); + } + if (setStreamAvailability(computeStreamAvailable(conferenceAudioEnabled, audioEnabled, + getStreamCapability(LinphoneStreamTypeAudio)), + LinphoneStreamTypeAudio)) { + streamAvailabilityChanged.insert(LinphoneStreamTypeAudio); + } + if (setStreamAvailability(computeStreamAvailable(conferenceVideoEnabled, videoEnabled, + getStreamCapability(LinphoneStreamTypeVideo)), + LinphoneStreamTypeVideo)) { + streamAvailabilityChanged.insert(LinphoneStreamTypeVideo); + } + if (setStreamAvailability(computeStreamAvailable(conferenceTextEnabled, textEnabled, + getStreamCapability(LinphoneStreamTypeText)), + LinphoneStreamTypeText)) { + streamAvailabilityChanged.insert(LinphoneStreamTypeText); } } else if (conference->isMe(getAddress()) && conferenceParams.localParticipantEnabled()) { - streamAvailabilityChanged |= - setStreamAvailability(computeStreamAvailable(conferenceAudioEnabled, conferenceAudioEnabled, + if (setStreamAvailability(computeStreamAvailable(conferenceAudioEnabled, conferenceAudioEnabled, getStreamCapability(LinphoneStreamTypeAudio)), - LinphoneStreamTypeAudio); - streamAvailabilityChanged |= - setStreamAvailability(computeStreamAvailable(conferenceVideoEnabled, conferenceVideoEnabled, + LinphoneStreamTypeAudio)) { + streamAvailabilityChanged.insert(LinphoneStreamTypeAudio); + } + if (setStreamAvailability(computeStreamAvailable(conferenceVideoEnabled, conferenceVideoEnabled, getStreamCapability(LinphoneStreamTypeVideo)), - LinphoneStreamTypeVideo); - streamAvailabilityChanged |= - setStreamAvailability(computeStreamAvailable(conferenceTextEnabled, conferenceTextEnabled, + LinphoneStreamTypeVideo)) { + streamAvailabilityChanged.insert(LinphoneStreamTypeVideo); + } + if (setStreamAvailability(computeStreamAvailable(conferenceTextEnabled, conferenceTextEnabled, getStreamCapability(LinphoneStreamTypeText)), - LinphoneStreamTypeText); + LinphoneStreamTypeText)) { + streamAvailabilityChanged.insert(LinphoneStreamTypeText); + } } } diff --git a/src/conference/participant-device.h b/src/conference/participant-device.h index a0e27cd9df7b9fcf8af11de0b13928322d15b056..ca6947bde878c320ca59ab0db46523a1efd95ac8 100644 --- a/src/conference/participant-device.h +++ b/src/conference/participant-device.h @@ -22,6 +22,7 @@ #define _L_PARTICIPANT_DEVICE_H_ #include <ctime> +#include <set> #include <string> #include <belle-sip/object++.hh> @@ -50,9 +51,8 @@ class Participant; class ParticipantDeviceCbs; class LINPHONE_PUBLIC ParticipantDevice : public bellesip::HybridObject<LinphoneParticipantDevice, ParticipantDevice>, - public UserDataAccessor, - public CallbacksHolder<ParticipantDeviceCbs> - { + public UserDataAccessor, + public CallbacksHolder<ParticipantDeviceCbs> { public: enum class State { Joining = LinphoneParticipantDeviceStateJoining, @@ -172,17 +172,18 @@ public: void *getUserData() const; void setUserData(void *ud); - // Media getters and setters - bool updateMediaCapabilities(); - bool updateStreamAvailabilities(); + std::set<LinphoneStreamType> updateMediaCapabilities(); + std::set<LinphoneStreamType> updateStreamAvailabilities(); bool adminModeSupported() const; void enableAdminModeSupport(bool support); + // Media getters and setters void *createWindowId() const; void setWindowId(void *newWindowId); void *getWindowId() const; + // Media getters and setters bool setLabel(const std::string &label, const LinphoneStreamType type); const std::string &getLabel(const LinphoneStreamType type) const; diff --git a/src/conference/participant-info.cpp b/src/conference/participant-info.cpp new file mode 100644 index 0000000000000000000000000000000000000000..09ec6728e3add007b9b1785f6f8cbe4ec7ea392a --- /dev/null +++ b/src/conference/participant-info.cpp @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2010-2023 Belledonne Communications SARL. + * + * This file is part of Liblinphone + * (see https://gitlab.linphone.org/BC/public/liblinphone). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "participant-info.h" +#include "address/address.h" + +using namespace std; + +LINPHONE_BEGIN_NAMESPACE + +const std::string ParticipantInfo::sequenceParameter = "X-SEQ"; +const std::string ParticipantInfo::roleParameter = "X-ROLE"; + +ParticipantInfo::ParticipantInfo(const std::shared_ptr<Address> &address) { + mAddress = Address::create(address->getUri()); +} + +ParticipantInfo::~ParticipantInfo() { +} + +ParticipantInfo *ParticipantInfo::clone() const { + return new ParticipantInfo(*this); +} + +ParticipantInfo::ParticipantInfo(const ParticipantInfo &other) : HybridObject(other) { + mAddress = other.mAddress; + mRole = other.mRole; + mSequence = other.mSequence; + mParameters = other.mParameters; +} + +const std::shared_ptr<Address> &ParticipantInfo::getAddress() const { + return mAddress; +} + +void ParticipantInfo::setRole(Participant::Role role) { + mRole = role; +}; + +Participant::Role ParticipantInfo::getRole() const { + return mRole; +}; + +void ParticipantInfo::setSequenceNumber(const int &nb) { + mSequence = nb; +}; + +const int &ParticipantInfo::getSequenceNumber() const { + return mSequence; +}; + +void ParticipantInfo::setParameters(const ParticipantInfo::participant_params_t ¶ms) { + mParameters.clear(); + addParameters(params); +} + +void ParticipantInfo::addParameter(const std::string &name, const std::string &value) { + if (name.compare(ParticipantInfo::sequenceParameter) == 0) { + setSequenceNumber(std::stoi(value)); + } else if (name.compare(ParticipantInfo::roleParameter) == 0) { + setRole(Participant::textToRole(value)); + } else { + mParameters[name] = value; + } +} + +void ParticipantInfo::addParameters(const ParticipantInfo::participant_params_t ¶ms) { + for (const auto &[name, value] : params) { + addParameter(name, value); + } +} + +bool ParticipantInfo::hasParameter(const std::string &name) const { + return (mParameters.find(name) == mParameters.end()); +} + +const std::string &ParticipantInfo::getParameterValue(const std::string &name) const { + try { + return mParameters.at(name); + } catch (std::out_of_range &) { + lInfo() << "Unable to find parameter " << name << " associated to participant info " << this << " with address " + << *getAddress(); + return Utils::getEmptyConstRefObject<string>(); + } +} + +void ParticipantInfo::removeParameter(const std::string &name) { + auto it = mParameters.find(name); + if (it == mParameters.end()) { + lInfo() << "Unable to remove parameter " << name << " associated to participant info " << this + << " with address " << *getAddress(); + } else { + mParameters.erase(it); + } +} + +ParticipantInfo::participant_params_t ParticipantInfo::getAllParameters() const { + auto params = mParameters; + if (mSequence >= 0) { + params[ParticipantInfo::sequenceParameter] = std::to_string(mSequence); + } + const auto &role = getRole(); + if (role != Participant::Role::Unknown) { + params[ParticipantInfo::roleParameter] = Participant::roleToText(role); + } + return params; +} + +const std::string ParticipantInfo::memberParametersToString(const ParticipantInfo::participant_params_t ¶ms) { + std::string str; + for (const auto &[name, value] : params) { + if (!str.empty()) { + str.append(";"); + } + str.append(name + "=" + value); + } + return str; +} + +const ParticipantInfo::participant_params_t ParticipantInfo::stringToMemberParameters(const std::string ¶msString) { + ParticipantInfo::participant_params_t params; + if (!paramsString.empty()) { + const auto &splittedValue = bctoolbox::Utils::split(Utils::trim(paramsString), ";"); + for (const auto ¶m : splittedValue) { + auto equal = param.find("="); + string name = param.substr(0, equal); + string value = param.substr(equal + 1, param.size()); + params.insert(std::make_pair(name, value)); + } + } + + return params; +} + +LINPHONE_END_NAMESPACE diff --git a/src/conference/participant-info.h b/src/conference/participant-info.h new file mode 100644 index 0000000000000000000000000000000000000000..df9be9bcaf014823a82bd9a17bdb78e7cae89adc --- /dev/null +++ b/src/conference/participant-info.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2010-2023 Belledonne Communications SARL. + * + * This file is part of Liblinphone + * (see https://gitlab.linphone.org/BC/public/liblinphone). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef _L_PARTICIPANT_INFO_H_ +#define _L_PARTICIPANT_INFO_H_ + +#include <belle-sip/object++.hh> + +#include "conference/participant.h" + +// ============================================================================= + +LINPHONE_BEGIN_NAMESPACE + +class Address; + +class LINPHONE_PUBLIC ParticipantInfo : public bellesip::HybridObject<LinphoneParticipantInfo, ParticipantInfo> { +public: + using participant_params_t = std::map<std::string, std::string>; + + static const std::string sequenceParameter; + static const std::string roleParameter; + + ParticipantInfo(const std::shared_ptr<Address> &address); + virtual ~ParticipantInfo(); + + ParticipantInfo(const ParticipantInfo &other); + + ParticipantInfo *clone() const override; + + const std::shared_ptr<Address> &getAddress() const; + + void setRole(Participant::Role role); + Participant::Role getRole() const; + + void setSequenceNumber(const int &nb); + const int &getSequenceNumber() const; + + void setParameters(const participant_params_t ¶ms); + void addParameter(const std::string &name, const std::string &value); + void addParameters(const participant_params_t ¶ms); + const std::string &getParameterValue(const std::string ¶m) const; + bool hasParameter(const std::string &name) const; + void removeParameter(const std::string &name); + + ParticipantInfo::participant_params_t getAllParameters() const; + + static const std::string memberParametersToString(const participant_params_t ¶ms); + static const participant_params_t stringToMemberParameters(const std::string ¶ms); + +private: + std::shared_ptr<Address> mAddress; + Participant::Role mRole = Participant::Role::Unknown; + int mSequence = -1; + participant_params_t mParameters; +}; + +LINPHONE_END_NAMESPACE + +#endif // _L_PARTICIPANT_INFO_H_ diff --git a/src/conference/participant.cpp b/src/conference/participant.cpp index 6f7cc7df8e9832654d23f19fdb68a582dd207482..4bea9e919aaec6d04cb749aafeabf54b9e4c7a35 100644 --- a/src/conference/participant.cpp +++ b/src/conference/participant.cpp @@ -20,6 +20,8 @@ #include <algorithm> +#include "participant.h" + #include "core/core.h" #include "params/media-session-params.h" #include "participant.h" @@ -140,13 +142,13 @@ std::shared_ptr<ParticipantDevice> Participant::addDevice(const std::shared_ptr< * we cannot afford to call Address:toString() for nothing when logs are disabled */ if (getCore() && (linphone_core_get_global_state(getCore()->getCCore()) == LinphoneGlobalOn)) { if (bctbx_log_level_enabled(BCTBX_LOG_DOMAIN, BCTBX_LOG_MESSAGE)) { - lInfo() << "Add device " << (name.empty() ? "<no-name>" : name) << " with address " << gruu->toString() - << " to participant " << getAddress()->toString(); + lInfo() << "Add device " << (name.empty() ? "<no-name>" : name) << " with address " << *gruu + << " to participant " << *getAddress(); } } else { if (bctbx_log_level_enabled(BCTBX_LOG_DOMAIN, BCTBX_LOG_DEBUG)) { - lDebug() << "Add device " << (name.empty() ? "<no-name>" : name) << " with address " << gruu->toString() - << " to participant " << getAddress()->toString(); + lDebug() << "Add device " << (name.empty() ? "<no-name>" : name) << " with address " << *gruu + << " to participant " << *getAddress(); } } device = ParticipantDevice::create(getSharedFromThis(), gruu, name); @@ -297,4 +299,37 @@ void Participant::setUserData(void *ud) { mUserData = ud; } +string Participant::roleToText(const Participant::Role &role) { + std::string roleText = std::string(); + switch (role) { + case Participant::Role::Speaker: + roleText = "speaker"; + break; + case Participant::Role::Listener: + roleText = "listener"; + break; + case Participant::Role::Unknown: + roleText = "unknown"; + break; + } + return roleText; +} + +Participant::Role Participant::textToRole(const string &str) { + Participant::Role role = Participant::Role::Speaker; + if (str.compare("speaker") == 0) { + role = Participant::Role::Speaker; + } else if (str.compare("listener") == 0) { + role = Participant::Role::Listener; + } else if (str.compare("unknown") == 0) { + role = Participant::Role::Unknown; + } + return role; +} + +ostream &operator<<(ostream &stream, Participant::Role role) { + const auto &str = Participant::roleToText(role); + return stream << str; +} + LINPHONE_END_NAMESPACE diff --git a/src/conference/participant.h b/src/conference/participant.h index 970928c0388dbb3e329791ff2b29b5ab985b31a7..d9add46b6cf16bd72ba09691215ca73a7525eebc 100644 --- a/src/conference/participant.h +++ b/src/conference/participant.h @@ -40,9 +40,9 @@ class LocalConferenceTester; namespace LinphoneTest { - class LocalConferenceTester; - class ClientConference; -} +class LocalConferenceTester; +class ClientConference; +} // namespace LinphoneTest LINPHONE_BEGIN_NAMESPACE @@ -82,6 +82,15 @@ class LINPHONE_PUBLIC Participant : public bellesip::HybridObject<LinphonePartic friend class ::LocalConferenceTester; public: + enum class Role { + Speaker = LinphoneParticipantRoleSpeaker, + Listener = LinphoneParticipantRoleListener, + Unknown = LinphoneParticipantRoleUnknown + }; + + static std::string roleToText(const Participant::Role &role); + static Participant::Role textToRole(const std::string &str); + explicit Participant(Conference *conference, const std::shared_ptr<Address> &address, std::shared_ptr<CallSession> callSession); @@ -132,6 +141,13 @@ public: void *getUserData() const; void setUserData(void *ud); + inline void setRole(Role role) { + mRole = role; + }; + inline Role getRole() const { + return mRole; + }; + protected: std::shared_ptr<Core> getCore() const { return mConference ? mConference->getCore() : nullptr; @@ -184,6 +200,7 @@ private: std::list<std::shared_ptr<ParticipantDevice>> devices; time_t creationTime; bool preserveSession = false; + Role mRole = Role::Listener; void *mUserData = nullptr; @@ -195,6 +212,8 @@ inline std::ostream &operator<<(std::ostream &os, const Participant &participant return os; } +std::ostream &operator<<(std::ostream &stream, Participant::Role role); + LINPHONE_END_NAMESPACE #endif // ifndef _L_PARTICIPANT_H_ diff --git a/src/conference/session/media-session-p.h b/src/conference/session/media-session-p.h index 52bab2ec2a8e56b7004c7fb5658d9cf43bbf0c57..1b0d7acd4ba509ba76b482ca74426f80fb8aa2b6 100644 --- a/src/conference/session/media-session-p.h +++ b/src/conference/session/media-session-p.h @@ -338,11 +338,18 @@ private: SalStreamDescription &addStreamToMd(std::shared_ptr<SalMediaDescription> md, int streamIdx, const std::shared_ptr<SalMediaDescription> &oldMd); + std::list<unsigned int> getProtectedStreamNumbers(std::shared_ptr<SalMediaDescription> md); static const std::string ecStateStore; static const int ecStateMaxLen; static constexpr const int rtpExtHeaderMidNumber = RTP_EXTENSION_MID; + static const std::string DTXAudioContentAttribute; + static const std::string EncryptedActiveSpeakerVideoContentAttribute; + static const std::string ActiveSpeakerVideoContentAttribute; + static const std::string GridVideoContentAttribute; + static const std::string ThumbnailVideoContentAttribute; + std::weak_ptr<Participant> me; std::unique_ptr<StreamsGroup> streamsGroup; diff --git a/src/conference/session/media-session.cpp b/src/conference/session/media-session.cpp index c37af9bff144b2dd4a2c0086cba53acdf54e9e87..b4b792313528fbaa9168c03cc547dcd46537aa67 100644 --- a/src/conference/session/media-session.cpp +++ b/src/conference/session/media-session.cpp @@ -77,6 +77,12 @@ inline OrtpRtcpXrStatSummaryFlag operator|(OrtpRtcpXrStatSummaryFlag a, OrtpRtcp const string MediaSessionPrivate::ecStateStore = ".linphone.ecstate"; const int MediaSessionPrivate::ecStateMaxLen = 1048576; /* 1Mo */ +const string MediaSessionPrivate::DTXAudioContentAttribute = "DTX"; +const string MediaSessionPrivate::EncryptedActiveSpeakerVideoContentAttribute = "only-active-speaker"; +const string MediaSessionPrivate::ActiveSpeakerVideoContentAttribute = "speaker"; +const string MediaSessionPrivate::GridVideoContentAttribute = "main"; +const string MediaSessionPrivate::ThumbnailVideoContentAttribute = "thumbnail"; + // ============================================================================= void MediaSessionPrivate::setDtlsFingerprint(const std::string &fingerPrint) { dtlsCertificateFingerprint = fingerPrint; @@ -277,6 +283,15 @@ void MediaSessionPrivate::accepted() { const auto conferenceInfo = (log) ? log->getConferenceInfo() : nullptr; bool updatingConference = conferenceInfo && (conferenceInfo->getState() == ConferenceInfo::State::Updated); + std::shared_ptr<MediaConference::Conference> conference = nullptr; + if (listener) { + conference = listener->getCallSessionConference(q->getSharedFromThis()); + } + // Paused by remote state is not allowed when the call is in a conference. In fact, a conference server is not + // allowed to paused a call unilaterally. This assumption also aims at simplifying the management of the + // PausedByRemote state as it is simply triggered by SIP messages without really knowing the will of the other party + bool pausedByRemoteNotAllowed = ((conference && !isInConference()) || updatingConference); + // Do not reject media session if the client is trying to update a conference if (rejectMediaSession(rmd, md) && !updatingConference) { lInfo() << "Rejecting media session"; @@ -303,7 +318,7 @@ void MediaSessionPrivate::accepted() { // The call always enters state PausedByRemote if all streams are rejected. This is done to support // some clients who accept to stop the streams by setting the RTP port to 0 If the call is part of a // conference, then it shouldn't be paused if it is just trying to update the conference - if (!updatingConference && !localDesc->hasDir(SalStreamInactive) && + if (!pausedByRemoteNotAllowed && !localDesc->hasDir(SalStreamInactive) && (md->hasDir(SalStreamRecvOnly) || md->hasDir(SalStreamInactive) || md->isEmpty())) { nextState = CallSession::State::PausedByRemote; nextStateMsg = "Call paused by remote"; @@ -1264,7 +1279,9 @@ void MediaSessionPrivate::runStunTestsIfNeeded() { : getParams()->getConferenceVideoLayout(); isConferenceLayoutActiveSpeaker = (confLayout == ConferenceLayout::ActiveSpeaker); } - const auto mainStreamAttrValue = isConferenceLayoutActiveSpeaker ? "speaker" : "main"; + const auto mainStreamAttrValue = isConferenceLayoutActiveSpeaker + ? MediaSessionPrivate::ActiveSpeakerVideoContentAttribute + : MediaSessionPrivate::GridVideoContentAttribute; const auto videoStreamIndex = conference ? md->findIdxStreamWithContent(mainStreamAttrValue) : md->findIdxBestStream(SalVideo); int videoPort = portFromStreamIndex(videoStreamIndex); @@ -1613,6 +1630,8 @@ void MediaSessionPrivate::fillConferenceParticipantStream(SalStreamDescription & // A participant device can only send a video stream if its video direction has the send component (i.e. // SendOnly or SendRecv) if (conference && (participantDevice == dev)) { + // If the core is holding the conference, then the device is the one that is active in the session. + // Otherwise it is me dir = (isInLocalConference) ? SalStreamRecvOnly : (((getParams()->getPrivate()->getSalVideoDirection() == SalStreamSendOnly) || @@ -1620,7 +1639,7 @@ void MediaSessionPrivate::fillConferenceParticipantStream(SalStreamDescription & ? SalStreamSendOnly : SalStreamInactive); } else { - if (content.compare("main") == 0) { + if (content.compare(MediaSessionPrivate::GridVideoContentAttribute) == 0) { dir = (isInLocalConference) ? SalStreamRecvOnly : SalStreamSendOnly; } else { const auto &mediaDir = dev->getStreamCapability(sal_stream_type_to_linphone(type)); @@ -1632,7 +1651,6 @@ void MediaSessionPrivate::fillConferenceParticipantStream(SalStreamDescription & case LinphoneMediaDirectionRecvOnly: case LinphoneMediaDirectionInactive: case LinphoneMediaDirectionInvalid: - dir = SalStreamInactive; dir = (label.empty()) ? SalStreamInactive : SalStreamRecvOnly; break; } @@ -1648,7 +1666,8 @@ void MediaSessionPrivate::fillConferenceParticipantStream(SalStreamDescription & if (type == SalVideo) { validateVideoStreamDirection(cfg); } - if (getParams()->rtpBundleEnabled()) addStreamToBundle(md, newStream, cfg, mid); + if (getParams()->rtpBundleEnabled() && (dir != SalStreamInactive)) + addStreamToBundle(md, newStream, cfg, mid); cfg.replacePayloads(l); newStream.addActualConfiguration(cfg); newStream.setSupportedEncryptions(encs); @@ -1723,48 +1742,73 @@ void MediaSessionPrivate::fillLocalStreamDescription(SalStreamDescription &strea if (customSdpAttributes) stream.custom_sdp_attributes = sal_custom_sdp_attribute_clone(customSdpAttributes); } -SalStreamDescription &MediaSessionPrivate::addStreamToMd(std::shared_ptr<SalMediaDescription> md, - int streamIdx, - const std::shared_ptr<SalMediaDescription> &oldMd) { +std::list<unsigned int> MediaSessionPrivate::getProtectedStreamNumbers(std::shared_ptr<SalMediaDescription> md) { L_Q(); - const auto currentMdSize = md->streams.size(); - std::list<unsigned int> protectedStreamNumbers; - std::list<unsigned int> protectedStreamNumbersOldMd; + std::list<unsigned int> streamNumbers; // Protected streams are only meaningful when the call is in a conference - auto conference = listener ? listener->getCallSessionConference(q->getSharedFromThis()) : nullptr; + const auto conference = listener ? listener->getCallSessionConference(q->getSharedFromThis()) : nullptr; // Protected streams are the first audio stream and the first 2 video streams as they handle local participant // medias - auto firstAudioStream = md->findFirstStreamIdxOfType(SalAudio); - if (firstAudioStream > -1) { - protectedStreamNumbers.push_back(static_cast<unsigned int>(firstAudioStream)); - } - auto firstVideoStream = md->findFirstStreamIdxOfType(SalVideo); - if (firstVideoStream > -1) { - protectedStreamNumbers.push_back(static_cast<unsigned int>(firstVideoStream)); - if (conference) { - auto secondVideoStream = md->findFirstStreamIdxOfType(SalVideo, firstVideoStream + 1); - if (secondVideoStream > -1) { - protectedStreamNumbers.push_back(static_cast<unsigned int>(secondVideoStream)); - } + if (conference) { + auto firstAudioStream = md->findFirstStreamIdxOfType(SalAudio); + if (firstAudioStream > -1) { + streamNumbers.push_back(static_cast<unsigned int>(firstAudioStream)); } - } - if (oldMd) { - auto firstAudioStreamOldMd = oldMd->findFirstStreamIdxOfType(SalAudio); - if (firstAudioStreamOldMd > -1) { - protectedStreamNumbersOldMd.push_back(static_cast<unsigned int>(firstAudioStreamOldMd)); + const auto ¤tConfParams = conference->getCurrentParams(); + const auto &confSecurityLevel = currentConfParams.getSecurityLevel(); + const std::string activeSpeakerAttribute((confSecurityLevel == ConferenceParams::SecurityLevel::EndToEnd) + ? MediaSessionPrivate::EncryptedActiveSpeakerVideoContentAttribute + : MediaSessionPrivate::ActiveSpeakerVideoContentAttribute); + const std::string gridAttribute(MediaSessionPrivate::GridVideoContentAttribute); + + bool isInLocalConference = getParams()->getPrivate()->getInConference(); + auto participantDevice = isInLocalConference ? conference->findParticipantDevice(q->getSharedFromThis()) + : conference->getMe()->findDevice(q->getSharedFromThis()); + + std::string deviceLabel; + if (participantDevice) { + deviceLabel = participantDevice->getLabel(LinphoneStreamTypeVideo); } - auto firstVideoStreamOldMd = oldMd->findFirstStreamIdxOfType(SalVideo); - if (firstVideoStreamOldMd > -1) { - protectedStreamNumbersOldMd.push_back(static_cast<unsigned int>(firstVideoStreamOldMd)); - if (conference) { - auto secondVideoStreamOldMd = oldMd->findFirstStreamIdxOfType(SalVideo, firstVideoStreamOldMd + 1); - if (secondVideoStreamOldMd > -1) { - protectedStreamNumbersOldMd.push_back(static_cast<unsigned int>(secondVideoStreamOldMd)); - } + + const auto mainActiveSpeakerVideoStreamLabel = + md->findIdxStreamWithContent(activeSpeakerAttribute, deviceLabel); + const auto mainGridVideoStreamLabel = md->findIdxStreamWithContent(gridAttribute, deviceLabel); + const auto mainActiveSpeakerVideoStreamNoLabel = md->findIdxStreamWithContent(activeSpeakerAttribute); + const auto mainGridVideoStreamNoLabel = md->findIdxStreamWithContent(gridAttribute); + const auto mainVideoStreamLabel = + (mainActiveSpeakerVideoStreamLabel > -1) ? mainActiveSpeakerVideoStreamLabel : mainGridVideoStreamLabel; + const auto mainVideoStreamNoLabel = (mainActiveSpeakerVideoStreamNoLabel > -1) + ? mainActiveSpeakerVideoStreamNoLabel + : mainGridVideoStreamNoLabel; + const auto mainVideoStream = (mainVideoStreamLabel > -1) ? mainVideoStreamLabel : mainVideoStreamNoLabel; + if (mainVideoStream > -1) { + streamNumbers.push_back(static_cast<unsigned int>(mainVideoStream)); + const auto thumbnailVideoStreamLabel = + md->findIdxStreamWithContent(MediaSessionPrivate::ThumbnailVideoContentAttribute, deviceLabel); + const auto thumbnailVideoStreamNoLabel = + md->findIdxStreamWithContent(MediaSessionPrivate::ThumbnailVideoContentAttribute); + const auto thumbnailVideoStream = + (thumbnailVideoStreamLabel > -1) ? thumbnailVideoStreamLabel : thumbnailVideoStreamNoLabel; + if (thumbnailVideoStream > -1) { + streamNumbers.push_back(static_cast<unsigned int>(thumbnailVideoStream)); } } } + return streamNumbers; +} + +SalStreamDescription &MediaSessionPrivate::addStreamToMd(std::shared_ptr<SalMediaDescription> md, + int streamIdx, + const std::shared_ptr<SalMediaDescription> &oldMd) { + L_Q(); + const auto currentMdSize = md->streams.size(); + const auto conference = listener ? listener->getCallSessionConference(q->getSharedFromThis()) : nullptr; + std::list<unsigned int> protectedStreamNumbers = getProtectedStreamNumbers(md); + std::list<unsigned int> protectedStreamNumbersOldMd; + if (oldMd) { + protectedStreamNumbersOldMd = getProtectedStreamNumbers(oldMd); + } // Search for a free slot int freeSlot = -1; @@ -1799,7 +1843,7 @@ SalStreamDescription &MediaSessionPrivate::addStreamToMd(std::shared_ptr<SalMedi (std::find(protectedStreamNumbersOldMd.cbegin(), protectedStreamNumbersOldMd.cend(), mdStreamIdx) != protectedStreamNumbersOldMd.cend()); auto oldStream = oldMd->getStreamIdx(static_cast<unsigned int>(mdStreamIdx)); - if (!protectedIdx && (oldStream.getLabel() == stream.getLabel())) { + if (conference && !protectedIdx && (oldStream.getLabel() == stream.getLabel())) { idxOldMd = static_cast<int>(mdStreamIdx); break; } @@ -1858,7 +1902,8 @@ void MediaSessionPrivate::addConferenceLocalParticipantStreams(bool add, if (((type == SalVideo) && isVideoConferenceEnabled) || (type == SalAudio)) { std::list<OrtpPayloadType *> emptyList; bool isInLocalConference = getParams()->getPrivate()->getInConference(); - const std::string content((type == SalAudio) ? "DTX" : "thumbnail"); + const std::string content((type == SalAudio) ? MediaSessionPrivate::DTXAudioContentAttribute + : MediaSessionPrivate::ThumbnailVideoContentAttribute); const auto &participantDevice = isInLocalConference ? conference->findParticipantDevice(q->getSharedFromThis()) : conference->getMe()->findDevice(q->getSharedFromThis()); @@ -1963,9 +2008,13 @@ void MediaSessionPrivate::addConferenceParticipantStreams(std::shared_ptr<SalMed (isInLocalConference) ? remoteContactAddress : q->getContactAddress(); if (localIsOfferer && !linphone_core_conference_server_enabled(q->getCore()->getCCore())) { - const std::string participantContent( - (type == SalAudio) ? "DTX" : ((isConferenceLayoutActiveSpeaker) ? "thumbnail" : "")); - const std::string HDParticipantVideoContent("only-active-speaker"); + const std::string participantContent((type == SalAudio) + ? MediaSessionPrivate::DTXAudioContentAttribute + : ((isConferenceLayoutActiveSpeaker) + ? MediaSessionPrivate::ThumbnailVideoContentAttribute + : "")); + const std::string HDParticipantVideoContent( + MediaSessionPrivate::EncryptedActiveSpeakerVideoContentAttribute); const std::string bundleNameStreamPrefix((type == SalVideo) ? "vs" : "as"); const std::string bundleNameHDStreamPrefix("vsHD"); @@ -2330,9 +2379,11 @@ void MediaSessionPrivate::makeLocalMediaDescription(bool localIsOfferer, } } - const std::string activeSpeakerAttribute( - (confSecurityLevel == ConferenceParams::SecurityLevel::EndToEnd) ? "only-active-speaker" : "speaker"); - const std::string mainStreamAttrValue(isConferenceLayoutActiveSpeaker ? activeSpeakerAttribute : "main"); + const std::string activeSpeakerAttribute((confSecurityLevel == ConferenceParams::SecurityLevel::EndToEnd) + ? MediaSessionPrivate::EncryptedActiveSpeakerVideoContentAttribute + : MediaSessionPrivate::ActiveSpeakerVideoContentAttribute); + const std::string mainStreamAttrValue( + isConferenceLayoutActiveSpeaker ? activeSpeakerAttribute : MediaSessionPrivate::GridVideoContentAttribute); auto callVideoEnabled = (!conferenceCreated && conference) ? getCurrentParams()->videoEnabled() : getParams()->videoEnabled(); @@ -2342,7 +2393,8 @@ void MediaSessionPrivate::makeLocalMediaDescription(bool localIsOfferer, // It is necessary to check for both Grid and ActiveSpeaker layout in order to cover the case when the layout is // changed const SalStreamDescription &oldGridLayoutMainVideoStream = - refMd ? refMd->findStreamWithContent("main") : Utils::getEmptyConstRefObject<SalStreamDescription>(); + refMd ? refMd->findStreamWithContent(MediaSessionPrivate::GridVideoContentAttribute) + : Utils::getEmptyConstRefObject<SalStreamDescription>(); const SalStreamDescription &oldActiveSpeakerLayoutMainVideoStream = refMd ? refMd->findStreamWithContent(activeSpeakerAttribute) : Utils::getEmptyConstRefObject<SalStreamDescription>(); @@ -3265,18 +3317,17 @@ bool MediaSessionPrivate::canSoundResourcesBeFreed() const { LinphoneStatus MediaSessionPrivate::pause() { L_Q(); if (state == CallSession::State::Paused) { - lWarning() << "Media session (local address " << q->getLocalAddress()->toString() << " remote address " - << q->getRemoteAddress()->toString() << ") is in state " << Utils::toString(state) - << " is already paused"; + lWarning() << "Media session (local address " << *q->getLocalAddress() << " remote address " + << *q->getRemoteAddress() << ") is in state " << Utils::toString(state) << " is already paused"; return 0; } else if (state == CallSession::State::Pausing) { - lWarning() << "Media session (local address " << q->getLocalAddress()->toString() << " remote address " - << q->getRemoteAddress()->toString() << ") is in state " << Utils::toString(state) + lWarning() << "Media session (local address " << *q->getLocalAddress() << " remote address " + << *q->getRemoteAddress() << ") is in state " << Utils::toString(state) << " is already in the process of being paused"; return 0; } else if (!canSoundResourcesBeFreed()) { - lWarning() << "Media session (local address " << q->getLocalAddress()->toString() << " remote address " - << q->getRemoteAddress()->toString() << ") is in state " << Utils::toString(state) + lWarning() << "Media session (local address " << *q->getLocalAddress() << " remote address " + << *q->getRemoteAddress() << ") is in state " << Utils::toString(state) << " hence it cannot be paused"; return -1; } @@ -3292,9 +3343,8 @@ LinphoneStatus MediaSessionPrivate::pause() { if (listener) { if (conference) { if (conference->findParticipantDevice(q->getSharedFromThis())) { - lWarning() << "Unable to pause media session (local address " - << q->getLocalAddress()->toString() << " remote address " - << q->getRemoteAddress()->toString() + lWarning() << "Unable to pause media session (local address " << *q->getLocalAddress() + << " remote address " << *q->getRemoteAddress() << ") because it is part of a conference. Please use the dedicated conference API " "to execute the desired actions"; return -1; @@ -3460,9 +3510,10 @@ int MediaSessionPrivate::getMainVideoStreamIdx(const std::shared_ptr<SalMediaDes const auto &confSecurityLevel = currentConfParams.getSecurityLevel(); const auto mainStreamAttrValue = isConferenceLayoutActiveSpeaker - ? ((confSecurityLevel == ConferenceParams::SecurityLevel::EndToEnd) ? "only-active-speaker" - : "speaker") - : "main"; + ? ((confSecurityLevel == ConferenceParams::SecurityLevel::EndToEnd) + ? MediaSessionPrivate::EncryptedActiveSpeakerVideoContentAttribute + : MediaSessionPrivate::ActiveSpeakerVideoContentAttribute) + : MediaSessionPrivate::GridVideoContentAttribute; streamIdx = md->findIdxStreamWithContent(mainStreamAttrValue); } else { streamIdx = md->findIdxBestStream(SalVideo); @@ -3496,7 +3547,7 @@ int MediaSessionPrivate::getThumbnailStreamIdx(const std::shared_ptr<SalMediaDes : meDevices.front()->getLabel(LinphoneStreamTypeVideo); if (!label.empty()) { if (md) { - const auto content = "thumbnail"; + const auto content = MediaSessionPrivate::ThumbnailVideoContentAttribute; streamIdx = md->findIdxStreamWithContent(content, label); } } @@ -4018,10 +4069,11 @@ MediaSession::~MediaSession() { ConferenceLayout MediaSession::computeConferenceLayout(const std::shared_ptr<SalMediaDescription> &md) { ConferenceLayout layout = ConferenceLayout::ActiveSpeaker; if (md) { - if (md->findIdxStreamWithContent("main") != -1) { + if (md->findIdxStreamWithContent(MediaSessionPrivate::GridVideoContentAttribute) != -1) { layout = ConferenceLayout::Grid; - } else if ((md->findIdxStreamWithContent("only-active-speaker") != -1) || - (md->findIdxStreamWithContent("speaker") != -1)) { + } else if ((md->findIdxStreamWithContent(MediaSessionPrivate::EncryptedActiveSpeakerVideoContentAttribute) != + -1) || + (md->findIdxStreamWithContent(MediaSessionPrivate::ActiveSpeakerVideoContentAttribute) != -1)) { layout = ConferenceLayout::ActiveSpeaker; } else { lDebug() << "Unable to deduce layout from media description " << md; diff --git a/src/core/core-chat-room.cpp b/src/core/core-chat-room.cpp index 6a29d5014877129248e8008bc4dfd3f6999938d2..0a55569f66993f9851f1b676eb0ce66867d7ea03 100644 --- a/src/core/core-chat-room.cpp +++ b/src/core/core-chat-room.cpp @@ -439,7 +439,6 @@ void CorePrivate::loadChatRooms() { #ifdef HAVE_ADVANCED_IM if (remoteListEventHandler) remoteListEventHandler->clearHandlers(); #endif - if (!mainDb->isInitialized()) return; for (auto &chatRoom : mainDb->getChatRooms()) { insertChatRoom(chatRoom); diff --git a/src/core/core.cpp b/src/core/core.cpp index ceed7a1f33eee534862470101621a8c3ae2d74f1..b7decff4337a68b7e23b913df5187f1885b6f398 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -878,7 +878,7 @@ bool Core::hasSpec(const std::string &spec) const { #if __GNUC__ == 7 #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-variable" -#endif +#endif // __GNUC__ == 7 const auto [name, version] = Core::getSpecNameVersion(spec); #if __GNUC__ == 7 #pragma GCC diagnostic pop @@ -893,7 +893,7 @@ void Core::removeSpec(const std::string &spec) { #if __GNUC__ == 7 #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-variable" -#endif +#endif // __GNUC__ == 7 const auto [name, version] = Core::getSpecNameVersion(spec); #if __GNUC__ == 7 #pragma GCC diagnostic pop diff --git a/src/db/main-db-p.h b/src/db/main-db-p.h index 6f618f086870495bdfad0e669c8fd17147cacfee..2a1f08847c4d17f2022be996213546f1e7f91725 100644 --- a/src/db/main-db-p.h +++ b/src/db/main-db-p.h @@ -26,6 +26,7 @@ #include "linphone/utils/utils.h" #include "abstract/abstract-db-p.h" +#include "conference/participant-info.h" #include "containers/lru-cache.h" #include "event-log/event-log.h" #include "main-db.h" @@ -73,10 +74,10 @@ private: long long insertOrUpdateConferenceInfoParticipant(long long conferenceInfoId, long long participantSipAddressId, bool deleted, - const ConferenceInfo::participant_params_t params); + const ParticipantInfo::participant_params_t params); long long insertOrUpdateConferenceInfoOrganizer(long long conferenceInfoId, long long organizerSipAddressId, - const ConferenceInfo::participant_params_t params); + const ParticipantInfo::participant_params_t params); long long insertOrUpdateConferenceCall(const std::shared_ptr<CallLog> &callLog, const std::shared_ptr<ConferenceInfo> &conferenceInfo = nullptr); diff --git a/src/db/main-db.cpp b/src/db/main-db.cpp index cad6893d4124a91490527685ee6760b876451ced..2243dd8391afd06f0e83be5215332a66ec4374f6 100644 --- a/src/db/main-db.cpp +++ b/src/db/main-db.cpp @@ -634,22 +634,23 @@ long long MainDbPrivate::insertConferenceInfo(const std::shared_ptr<ConferenceIn } insertOrUpdateConferenceInfoOrganizer(conferenceInfoId, organizerSipAddressId, - conferenceInfo->getOrganizer().second); + conferenceInfo->getOrganizer()->getAllParameters()); const auto &participantList = conferenceInfo->getParticipants(); for (const auto &participantAddress : participantList) { - insertOrUpdateConferenceInfoParticipant(conferenceInfoId, insertSipAddress(participantAddress.first), false, - participantAddress.second); + insertOrUpdateConferenceInfoParticipant(conferenceInfoId, insertSipAddress(participantAddress->getAddress()), + false, participantAddress->getAllParameters()); } for (const auto &oldParticipantAddress : dbParticipantList) { const bool deleted = (std::find_if(participantList.cbegin(), participantList.cend(), [&oldParticipantAddress](const auto &p) { - return (p.first->weakEqual(*oldParticipantAddress.first)); + return (p->getAddress()->weakEqual(*oldParticipantAddress->getAddress())); }) == participantList.cend()); if (deleted) { - insertOrUpdateConferenceInfoParticipant(conferenceInfoId, insertSipAddress(oldParticipantAddress.first), - true, oldParticipantAddress.second); + insertOrUpdateConferenceInfoParticipant(conferenceInfoId, + insertSipAddress(oldParticipantAddress->getAddress()), true, + oldParticipantAddress->getAllParameters()); } } @@ -663,10 +664,10 @@ long long MainDbPrivate::insertConferenceInfo(const std::shared_ptr<ConferenceIn long long MainDbPrivate::insertOrUpdateConferenceInfoOrganizer(long long conferenceInfoId, long long organizerSipAddressId, - const ConferenceInfo::participant_params_t params) { + const ParticipantInfo::participant_params_t params) { #ifdef HAVE_DB_STORAGE long long conferenceInfoOrganizerId = selectConferenceInfoOrganizerId(conferenceInfoId); - auto paramsStr = ConferenceInfo::memberParametersToString(params); + auto paramsStr = ParticipantInfo::memberParametersToString(params); if (conferenceInfoOrganizerId >= 0) { *dbSession.getBackendSession() << "UPDATE conference_info_organizer SET" " organizer_sip_address_id = :organizerSipAddressId, params = :paramsStr" @@ -690,11 +691,11 @@ long long MainDbPrivate::insertOrUpdateConferenceInfoOrganizer(long long confere long long MainDbPrivate::insertOrUpdateConferenceInfoParticipant(long long conferenceInfoId, long long participantSipAddressId, bool deleted, - const ConferenceInfo::participant_params_t params) { + const ParticipantInfo::participant_params_t params) { #ifdef HAVE_DB_STORAGE long long conferenceInfoParticipantId = selectConferenceInfoParticipantId(conferenceInfoId, participantSipAddressId); - auto paramsStr = ConferenceInfo::memberParametersToString(params); + auto paramsStr = ParticipantInfo::memberParametersToString(params); int participantDeleted = deleted ? 1 : 0; if (conferenceInfoParticipantId >= 0) { *dbSession.getBackendSession() << "UPDATE conference_info_participant SET" @@ -1142,17 +1143,16 @@ shared_ptr<EventLog> MainDbPrivate::selectConferenceCallEvent(const soci::row &r (session->prepare << participantsQuery, soci::use(conferenceInfoId)); for (const auto &participantRow : participantRows) { std::shared_ptr<Address> participant = Address::create(participantRow.get<string>(0)); - ConferenceInfo::participant_params_t participantParams; - conferenceInfo->addParticipant(participant, participantParams); + auto participantInfo = ParticipantInfo::create(participant); + conferenceInfo->addParticipant(participantInfo); } // For backward compability purposes, get the organizer from conference_info table and set the sequence // number to that of the conference info stored in the db It may be overridden if the conference organizer // has been stored in table conference_info_organizer. - std::shared_ptr<Address> organizer = Address::create(row.get<string>(16)); - ConferenceInfo::participant_params_t defaultOrganizerParams; - defaultOrganizerParams.insert(std::make_pair(ConferenceInfo::sequenceParam, std::to_string(icsSequence))); - conferenceInfo->setOrganizer(organizer, defaultOrganizerParams); + std::shared_ptr<Address> organizerAddress = Address::create(row.get<string>(16)); + ParticipantInfo::participant_params_t organizerParams; + organizerParams.insert(std::make_pair(ParticipantInfo::sequenceParameter, std::to_string(icsSequence))); static const string organizerQuery = "SELECT sip_address.value, conference_info_organizer.params" " FROM sip_address, conference_info, conference_info_organizer" @@ -1162,12 +1162,13 @@ shared_ptr<EventLog> MainDbPrivate::selectConferenceCallEvent(const soci::row &r soci::rowset<soci::row> organizerRows = (session->prepare << organizerQuery, soci::use(conferenceInfoId)); for (const auto &organizerRow : organizerRows) { - std::shared_ptr<Address> organizerAddress = Address::create(organizerRow.get<string>(0)); + organizerAddress = Address::create(organizerRow.get<string>(0)); const string organizerParamsStr = organizerRow.get<string>(1); - ConferenceInfo::participant_params_t organizerParams = - ConferenceInfo::stringToMemberParameters(organizerParamsStr); - conferenceInfo->setOrganizer(organizerAddress, organizerParams); + organizerParams = ParticipantInfo::stringToMemberParameters(organizerParamsStr); } + auto organizerInfo = ParticipantInfo::create(organizerAddress); + organizerInfo->setParameters(organizerParams); + conferenceInfo->setOrganizer(organizerInfo); cache(conferenceInfo, conferenceInfoId); } @@ -1936,20 +1937,20 @@ shared_ptr<ConferenceInfo> MainDbPrivate::selectConferenceInfo(const soci::row & if (deleted == 0) { std::shared_ptr<Address> participantAddress = Address::create(participantRow.get<string>(0)); const string participantParamsStr = participantRow.get<string>(2); - ConferenceInfo::participant_params_t participantParams = - ConferenceInfo::stringToMemberParameters(participantParamsStr); - conferenceInfo->addParticipant(participantAddress, participantParams); + ParticipantInfo::participant_params_t participantParams = + ParticipantInfo::stringToMemberParameters(participantParamsStr); + auto participantInfo = ParticipantInfo::create(participantAddress); + participantInfo->setParameters(participantParams); + conferenceInfo->addParticipant(participantInfo); } } // For backward compability purposes, get the organizer from conference_info table and set the sequence number to // that of the conference info stored in the db It may be overridden if the conference organizer has been stored in // table conference_info_organizer. - std::shared_ptr<Address> organizer = Address::create(row.get<string>(1)); - ConferenceInfo::participant_params_t defaultOrganizerParams; - defaultOrganizerParams.insert(std::make_pair(ConferenceInfo::sequenceParam, std::to_string(icsSequence))); - conferenceInfo->setOrganizer(organizer, defaultOrganizerParams); - + std::shared_ptr<Address> organizerAddress = Address::create(row.get<string>(1)); + ParticipantInfo::participant_params_t organizerParams; + organizerParams.insert(std::make_pair(ParticipantInfo::sequenceParameter, std::to_string(icsSequence))); static const string organizerQuery = "SELECT sip_address.value, conference_info_organizer.params" " FROM sip_address, conference_info, conference_info_organizer" " WHERE conference_info.id = :conferenceInfoId" @@ -1958,12 +1959,13 @@ shared_ptr<ConferenceInfo> MainDbPrivate::selectConferenceInfo(const soci::row & soci::rowset<soci::row> organizerRows = (session->prepare << organizerQuery, soci::use(dbConferenceInfoId)); for (const auto &organizerRow : organizerRows) { - std::shared_ptr<Address> organizerAddress = Address::create(organizerRow.get<string>(0)); + organizerAddress = Address::create(organizerRow.get<string>(0)); const string organizerParamsStr = organizerRow.get<string>(1); - ConferenceInfo::participant_params_t organizerParams = - ConferenceInfo::stringToMemberParameters(organizerParamsStr); - conferenceInfo->setOrganizer(organizerAddress, organizerParams); + organizerParams = ParticipantInfo::stringToMemberParameters(organizerParamsStr); } + auto organizerInfo = ParticipantInfo::create(organizerAddress); + organizerInfo->setParameters(organizerParams); + conferenceInfo->setOrganizer(organizerInfo); cache(conferenceInfo, dbConferenceInfoId); @@ -4652,7 +4654,7 @@ list<shared_ptr<AbstractChatRoom>> MainDb::getChatRooms() const { soci::session *session = d->dbSession.getBackendSession(); soci::rowset<soci::row> rows = (session->prepare << query); - // SOCI use a hack for sqlite3: + // SOCI uses a hack for sqlite3: // "sqlite3 type system does not have a date or time field. Also it does not reliably id other data types. // It has a tendency to see everything as text. // sqlite3_column_decltype returns the text that is used in the create table statement" diff --git a/src/factory/factory.cpp b/src/factory/factory.cpp index 14155aa561e3fd3ce1dc005704f32ed5e5fe60f4..b82652c8a3981888147cff80f4c98a7b3b25cdd3 100644 --- a/src/factory/factory.cpp +++ b/src/factory/factory.cpp @@ -55,6 +55,7 @@ #include "alert/alert.h" #include "chat/ics/ics.h" #include "conference/conference-info.h" +#include "conference/participant-info.h" #include "content/file-content.h" #include "core/paths/paths.h" #include "dictionary/dictionary.h" @@ -821,6 +822,10 @@ std::shared_ptr<ConferenceInfo> Factory::createConferenceInfoFromIcalendarConten #pragma GCC diagnostic pop #endif // _MSC_VER +std::shared_ptr<ParticipantInfo> Factory::createParticipantInfo(const std::shared_ptr<Address> &address) const { + return ParticipantInfo::create(address); +} + #ifndef _MSC_VER #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" diff --git a/src/factory/factory.h b/src/factory/factory.h index 95314db407f87439a7df8fdcde82395fa9283cb5..10ced792dd4da095147207bb26121f4a8e515e8b 100644 --- a/src/factory/factory.h +++ b/src/factory/factory.h @@ -217,9 +217,10 @@ public: void setVfsEncryption(const uint16_t encryptionModule, const uint8_t *secret, const size_t secretSize); std::shared_ptr<ConferenceInfo> createConferenceInfo() const; - std::shared_ptr<ConferenceInfo> createConferenceInfoFromIcalendarContent(LinphoneContent *content) const; + std::shared_ptr<ParticipantInfo> createParticipantInfo(const std::shared_ptr<Address> &address) const; + LinphoneConferenceSchedulerCbs *createConferenceSchedulerCbs() const; LinphoneContent *createQRCode(const std::string &code, diff --git a/src/sal/sal_media_description.cpp b/src/sal/sal_media_description.cpp index a1e013833815fb6c63bdad1bf4c9682919eb499d..569c31e5d744c60b9e4eb3179d1977113b3d6949 100644 --- a/src/sal/sal_media_description.cpp +++ b/src/sal/sal_media_description.cpp @@ -161,29 +161,24 @@ SalMediaDescription::SalMediaDescription(belle_sdp_session_description_t *sdp) /* Get ICE remote ufrag and remote pwd, and ice_lite flag */ value = belle_sdp_session_description_get_attribute_value(sdp, "ice-ufrag"); - if (value) - ice_ufrag = L_C_TO_STRING(value); + if (value) ice_ufrag = L_C_TO_STRING(value); value = belle_sdp_session_description_get_attribute_value(sdp, "ice-pwd"); - if (value) - ice_pwd = L_C_TO_STRING(value); + if (value) ice_pwd = L_C_TO_STRING(value); value = belle_sdp_session_description_get_attribute_value(sdp, "ice-lite"); - if (value) - ice_lite = true; + if (value) ice_lite = true; /* Get session RTCP-XR attributes if any */ sdp_parse_session_rtcp_xr_parameters(sdp, &rtcp_xr); /* Do we have Lime Ik attribute */ value = belle_sdp_session_description_get_attribute_value(sdp, "Ik"); - if (value) - haveLimeIk = true; + if (value) haveLimeIk = true; /* get ready to parse also lime-Ik */ value = belle_sdp_session_description_get_attribute_value(sdp, "lime-Ik"); - if (value) - haveLimeIk = true; + if (value) haveLimeIk = true; value = belle_sdp_session_description_get_attribute_value(sdp, "record"); if (value) { @@ -259,8 +254,7 @@ bool SalMediaDescription::hasDir(const SalStreamDir &stream_dir) const { if (containsStreamWithDir(SalStreamSendOnly) || containsStreamWithDir(SalStreamSendRecv) || containsStreamWithDir(SalStreamRecvOnly)) return false; - else - return true; + else return true; } return false; } @@ -272,8 +266,7 @@ const SalMediaDescriptionParams &SalMediaDescription::getParams() const { bool SalMediaDescription::containsStreamWithDir(const SalStreamDir &stream_dir, const SalStreamType &type) const { /* we are looking for at least one stream with requested direction, inactive streams are ignored*/ for (auto &stream : streams) { - if (!stream.enabled()) - continue; + if (!stream.enabled()) continue; if ((stream.getType() == type) && (stream.getDirection() == stream_dir)) { return true; } @@ -289,8 +282,7 @@ bool SalMediaDescription::containsStreamWithDir(const SalStreamDir &stream_dir, bool SalMediaDescription::containsStreamWithDir(const SalStreamDir &stream_dir) const { /* we are looking for at least one stream with requested direction, inactive streams are ignored*/ for (auto &stream : streams) { - if (!stream.enabled()) - continue; + if (!stream.enabled()) continue; if (stream.getDirection() == stream_dir) { return true; } @@ -352,8 +344,7 @@ std::list<int> SalMediaDescription::getTransportOwnerIndexes() const { int SalMediaDescription::getIndexOfTransportOwner(const SalStreamDescription &sd) const { std::string master_mid; int index; - if (sd.getChosenConfiguration().getMid().empty() == true) - return -1; /* not part of any bundle */ + if (sd.getChosenConfiguration().getMid().empty() == true) return -1; /* not part of any bundle */ /* lookup the mid in the bundle descriptions */ const auto &bundle = getBundleFromMid(sd.getChosenConfiguration().getMid()); if (bundle == Utils::getEmptyConstRefObject<SalStreamBundle>()) { @@ -407,7 +398,7 @@ SalMediaDescription::findStreamItWithLabel(SalStreamType type, const std::string } const SalStreamDescription &SalMediaDescription::findStreamWithLabel(SalStreamType type, - const std::string label) const { + const std::string label) const { const auto &streamIt = findStreamItWithLabel(type, label); if (streamIt != streams.end()) { return *streamIt; @@ -477,7 +468,7 @@ std::vector<SalStreamDescription>::const_iterator SalMediaDescription::findStreamItWithContent(const std::string content, const std::string label) const { const auto &streamIt = std::find_if(streams.cbegin(), streams.cend(), [&content, &label](const auto &stream) { return ((content.empty() && stream.getContent().empty()) || stream.getContent().compare(content) == 0) && - (stream.getLabel().compare(label) == 0); + ((label.empty() && stream.getLabel().empty()) || (stream.getLabel().compare(label) == 0)); }); return streamIt; } @@ -502,8 +493,7 @@ int SalMediaDescription::findIdxStreamWithContent(const std::string content, con unsigned int SalMediaDescription::nbStreamsOfType(SalStreamType type) const { unsigned int nb = 0; for (const auto &stream : streams) { - if (stream.getType() == type) - nb++; + if (stream.getType() == type) nb++; } return nb; } @@ -511,8 +501,7 @@ unsigned int SalMediaDescription::nbStreamsOfType(SalStreamType type) const { unsigned int SalMediaDescription::nbActiveStreamsOfType(SalStreamType type) const { unsigned int nb = 0; for (const auto &stream : streams) { - if (stream.enabled() && (stream.getType() == type)) - nb++; + if (stream.enabled() && (stream.getType() == type)) nb++; } return nb; } @@ -529,8 +518,7 @@ const SalStreamDescription &SalMediaDescription::getActiveStreamOfType(SalStream const SalStreamDescription &SalMediaDescription::findSecureStreamOfType(SalStreamType type) const { auto idx = findIdxStream(SalProtoRtpSavpf, type); - if (idx == -1) - idx = findIdxStream(SalProtoRtpSavp, type); + if (idx == -1) idx = findIdxStream(SalProtoRtpSavp, type); if (idx != -1) { return getStreamIdx(static_cast<unsigned int>(idx)); } @@ -547,16 +535,11 @@ const SalStreamDescription &SalMediaDescription::findBestStream(SalStreamType ty int SalMediaDescription::findIdxBestStream(SalStreamType type) const { auto idx = findIdxStream(SalProtoUdpTlsRtpSavpf, type); - if (idx == -1) - idx = findIdxStream(SalProtoUdpTlsRtpSavp, type); - if (idx == -1) - idx = findIdxStream(SalProtoRtpSavpf, type); - if (idx == -1) - idx = findIdxStream(SalProtoRtpSavp, type); - if (idx == -1) - idx = findIdxStream(SalProtoRtpAvpf, type); - if (idx == -1) - idx = findIdxStream(SalProtoRtpAvp, type); + if (idx == -1) idx = findIdxStream(SalProtoUdpTlsRtpSavp, type); + if (idx == -1) idx = findIdxStream(SalProtoRtpSavpf, type); + if (idx == -1) idx = findIdxStream(SalProtoRtpSavp, type); + if (idx == -1) idx = findIdxStream(SalProtoRtpAvpf, type); + if (idx == -1) idx = findIdxStream(SalProtoRtpAvp, type); return idx; } @@ -597,7 +580,7 @@ const SalStreamDescription &SalMediaDescription::findStreamWithSdpAttribute( } std::vector<SalStreamDescription>::const_iterator SalMediaDescription::findStreamItWithSdpAttribute( - const std::vector<std::pair<std::string, std::string>> &attributes) const { + const std::vector<std::pair<std::string, std::string>> &attributes) const { return std::find_if(streams.cbegin(), streams.cend(), [&attributes](const auto &stream) { bool found = true; for (const auto &[attrName, attrValue] : attributes) { @@ -613,7 +596,7 @@ std::vector<SalStreamDescription>::const_iterator SalMediaDescription::findStrea } std::vector<SalStreamDescription>::const_iterator SalMediaDescription::findStreamItWithSdpAttribute( - const SalStreamType type, const std::vector<std::pair<std::string, std::string>> &attributes) const { + const SalStreamType type, const std::vector<std::pair<std::string, std::string>> &attributes) const { return std::find_if(streams.cbegin(), streams.cend(), [&type, &attributes](const auto &stream) { bool found = true; for (const auto &[attrName, attrValue] : attributes) { @@ -629,11 +612,11 @@ std::vector<SalStreamDescription>::const_iterator SalMediaDescription::findStrea } std::vector<SalStreamDescription>::const_iterator SalMediaDescription::findFirstStreamItOfType(SalStreamType type, - int startingIdx) const { + int startingIdx) const { auto streamSize = static_cast<int>(streams.size()); auto idx = (startingIdx < 0) ? 0 : ((startingIdx >= streamSize) ? (streamSize - 1) : startingIdx); const auto &streamIt = std::find_if(streams.cbegin() + idx, streams.cend(), - [&type](const auto &stream) { return (stream.getType() == type); }); + [&type](const auto &stream) { return (stream.getType() == type); }); return streamIt; } @@ -664,23 +647,20 @@ const std::list<SalStreamDescription> SalMediaDescription::findAllStreamsOfType( } bool SalMediaDescription::isEmpty() const { - if (getNbActiveStreams() > 0) - return false; + if (getNbActiveStreams() > 0) return false; return true; } bool SalMediaDescription::isAcceptable() const { for (auto &stream : streams) { - if (!stream.isAcceptable()) - return false; + if (!stream.isAcceptable()) return false; } return true; } void SalMediaDescription::setDir(SalStreamDir stream_dir) { for (auto &stream : streams) { - if (!stream.enabled()) - continue; + if (!stream.enabled()) continue; stream.setDirection(stream_dir); } } @@ -688,8 +668,7 @@ void SalMediaDescription::setDir(SalStreamDir stream_dir) { int SalMediaDescription::getNbActiveStreams() const { int nb = 0; for (auto &stream : streams) { - if (stream.enabled()) - nb++; + if (stream.enabled()) nb++; } return nb; } @@ -699,8 +678,7 @@ bool SalMediaDescription::hasIceParams() const { bool foundIceCandidates = true; bool foundIceStreamDescParams = true; for (const auto &stream : streams) { - if (!stream.enabled()) - continue; + if (!stream.enabled()) continue; foundIceCandidates &= stream.hasIceCandidates(); foundIceStreamDescParams &= stream.hasIceParams(); } @@ -710,61 +688,46 @@ bool SalMediaDescription::hasIceParams() const { } bool SalMediaDescription::hasAvpf() const { - if (streams.empty()) - return false; + if (streams.empty()) return false; for (const auto &stream : streams) { - if (!stream.enabled()) - continue; - if (stream.hasAvpf() != true) - return false; + if (!stream.enabled()) continue; + if (stream.hasAvpf() != true) return false; } return true; } bool SalMediaDescription::hasImplicitAvpf() const { - if (streams.empty()) - return false; + if (streams.empty()) return false; for (const auto &stream : streams) { - if (!stream.enabled()) - continue; - if (stream.hasImplicitAvpf() != true) - return false; + if (!stream.enabled()) continue; + if (stream.hasImplicitAvpf() != true) return false; } return true; } bool SalMediaDescription::hasSrtp() const { - if (streams.empty()) - return false; + if (streams.empty()) return false; for (const auto &stream : streams) { - if (!stream.enabled()) - continue; - if (stream.hasSrtp() != true) - return false; + if (!stream.enabled()) continue; + if (stream.hasSrtp() != true) return false; } return true; } bool SalMediaDescription::hasDtls() const { - if (streams.empty()) - return false; + if (streams.empty()) return false; for (const auto &stream : streams) { - if (!stream.enabled()) - continue; - if (stream.hasDtls() != true) - return false; + if (!stream.enabled()) continue; + if (stream.hasDtls() != true) return false; } return true; } bool SalMediaDescription::hasZrtp() const { - if (streams.empty()) - return false; + if (streams.empty()) return false; for (const auto &stream : streams) { - if (!stream.enabled()) - continue; - if (stream.hasZrtp() != true) - return false; + if (!stream.enabled()) continue; + if (stream.hasZrtp() != true) return false; } return true; } @@ -774,17 +737,13 @@ bool SalMediaDescription::hasLimeIk() const { } bool SalMediaDescription::hasIpv6() const { - if (streams.empty()) - return false; + if (streams.empty()) return false; for (const auto &stream : streams) { - if (!stream.enabled()) - continue; + if (!stream.enabled()) continue; if (stream.getRtpAddress().empty() == false) { - if (!stream.hasIpv6()) - return false; + if (!stream.hasIpv6()) return false; } else { - if (addr.find(':') == std::string::npos) - return false; + if (addr.find(':') == std::string::npos) return false; } } return true; @@ -801,9 +760,8 @@ bool SalMediaDescription::operator!=(const SalMediaDescription &other) const { int SalMediaDescription::compareToActualConfiguration(const SalMediaDescription &otherMd) const { int result = globalEqual(otherMd); for (auto stream1 = streams.cbegin(), stream2 = otherMd.streams.cbegin(); - (stream1 != streams.cend() && stream2 != otherMd.streams.cend()); ++stream1, ++stream2) { - if (!stream1->enabled() && !stream2->enabled()) - continue; + (stream1 != streams.cend() && stream2 != otherMd.streams.cend()); ++stream1, ++stream2) { + if (!stream1->enabled() && !stream2->enabled()) continue; result |= stream1->compareToActualConfiguration(*stream2); } return result; @@ -812,9 +770,8 @@ int SalMediaDescription::compareToActualConfiguration(const SalMediaDescription int SalMediaDescription::compareToChosenConfiguration(const SalMediaDescription &otherMd) const { int result = globalEqual(otherMd); for (auto stream1 = streams.cbegin(), stream2 = otherMd.streams.cbegin(); - (stream1 != streams.cend() && stream2 != otherMd.streams.cend()); ++stream1, ++stream2) { - if (!stream1->enabled() && !stream2->enabled()) - continue; + (stream1 != streams.cend() && stream2 != otherMd.streams.cend()); ++stream1, ++stream2) { + if (!stream1->enabled() && !stream2->enabled()) continue; result |= stream1->compareToChosenConfiguration(*stream2); } return result; @@ -823,9 +780,8 @@ int SalMediaDescription::compareToChosenConfiguration(const SalMediaDescription int SalMediaDescription::equal(const SalMediaDescription &otherMd) const { int result = globalEqual(otherMd); for (auto stream1 = streams.cbegin(), stream2 = otherMd.streams.cbegin(); - (stream1 != streams.cend() && stream2 != otherMd.streams.cend()); ++stream1, ++stream2) { - if (!stream1->enabled() && !stream2->enabled()) - continue; + (stream1 != streams.cend() && stream2 != otherMd.streams.cend()); ++stream1, ++stream2) { + if (!stream1->enabled() && !stream2->enabled()) continue; result |= stream1->equal(*stream2); } return result; @@ -834,15 +790,12 @@ int SalMediaDescription::equal(const SalMediaDescription &otherMd) const { int SalMediaDescription::globalEqual(const SalMediaDescription &otherMd) const { int result = SAL_MEDIA_DESCRIPTION_UNCHANGED; - if (addr.compare(otherMd.addr) != 0) - result |= SAL_MEDIA_DESCRIPTION_NETWORK_CHANGED; + if (addr.compare(otherMd.addr) != 0) result |= SAL_MEDIA_DESCRIPTION_NETWORK_CHANGED; if (addr.empty() == false && otherMd.addr.empty() == false && - ms_is_multicast(L_STRING_TO_C(addr)) != ms_is_multicast(L_STRING_TO_C(otherMd.addr))) + ms_is_multicast(L_STRING_TO_C(addr)) != ms_is_multicast(L_STRING_TO_C(otherMd.addr))) result |= SAL_MEDIA_DESCRIPTION_NETWORK_XXXCAST_CHANGED; - if (streams.size() != otherMd.streams.size()) - result |= SAL_MEDIA_DESCRIPTION_STREAMS_CHANGED; - if (bandwidth != otherMd.bandwidth) - result |= SAL_MEDIA_DESCRIPTION_BANDWIDTH_CHANGED; + if (streams.size() != otherMd.streams.size()) result |= SAL_MEDIA_DESCRIPTION_STREAMS_CHANGED; + if (bandwidth != otherMd.bandwidth) result |= SAL_MEDIA_DESCRIPTION_BANDWIDTH_CHANGED; /* ICE */ if (ice_ufrag.compare(otherMd.ice_ufrag) != 0 && !otherMd.ice_ufrag.empty()) @@ -926,8 +879,7 @@ const std::string SalMediaDescription::printDifferences(int result) { if (result) { ms_fatal("There are unhandled result bitmasks in SalMediaDescription::printDifferences(), fix it"); } - if (out.empty()) - out = "NONE"; + if (out.empty()) out = "NONE"; return out; } diff --git a/src/utils/utils.cpp b/src/utils/utils.cpp index 8525230455bec62fa9bc4b90c52cfbd5a2c71b62..314ca423155de67cd68da8a628b5cb6a2c8e75c0 100644 --- a/src/utils/utils.cpp +++ b/src/utils/utils.cpp @@ -29,6 +29,7 @@ #include "c-wrapper/internal/c-tools.h" #include "conference/conference-info.h" +#include "conference/participant-info.h" #include "linphone/utils/utils.h" #include "logger/logger.h" #include "private.h" @@ -371,7 +372,8 @@ std::string Utils::getResourceLists(const std::list<std::shared_ptr<Address>> &a #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" #endif // _MSC_VER -std::list<std::shared_ptr<Address>> Utils::parseResourceLists(const Content &content) { +ConferenceInfo::participant_list_t Utils::parseResourceLists(const Content &content) { + ConferenceInfo::participant_list_t resources; #ifdef HAVE_ADVANCED_IM if ((content.getContentType() == ContentType::ResourceLists) && ((content.getContentDisposition().weakEqual(ContentDisposition::RecipientList)) || @@ -379,19 +381,22 @@ std::list<std::shared_ptr<Address>> Utils::parseResourceLists(const Content &con std::istringstream data(content.getBodyAsString()); std::unique_ptr<Xsd::ResourceLists::ResourceLists> rl( Xsd::ResourceLists::parseResourceLists(data, Xsd::XmlSchema::Flags::dont_validate)); - std::list<std::shared_ptr<Address>> addresses; for (const auto &l : rl->getList()) { for (const auto &entry : l.getEntry()) { - std::shared_ptr<Address> addr = Address::create(entry.getUri()); - addresses.push_back(addr); + Address address(entry.getUri()); + auto participantInfo = ParticipantInfo::create(Address::create(address.getUri())); + for (const auto &[name, value] : address.getParams()) { + participantInfo->addParameter(name, value); + } + resources.push_back(participantInfo); } } - return addresses; + return resources; } - return std::list<std::shared_ptr<Address>>(); + return resources; #else lWarning() << "Advanced IM such as group chat is disabled!"; - return std::list<std::shared_ptr<Address>>(); + return resources; #endif } #ifndef _MSC_VER @@ -405,14 +410,19 @@ std::shared_ptr<ConferenceInfo> Utils::createConferenceInfoFromOp(SalCallOp *op, const auto resourceList = op->getContentInRemote(ContentType::ResourceLists); if (!sipfrag.isEmpty()) { - auto organizer = Utils::getSipFragAddress(sipfrag); - info->setOrganizer(Address::create(organizer)); + auto organizerStr = Utils::getSipFragAddress(sipfrag); + auto organizer = Address::create(organizerStr); + auto organizerInfo = ParticipantInfo::create(Address::create(organizer->getUri())); + for (const auto &[name, value] : organizer->getParams()) { + organizerInfo->addParameter(name, value); + } + info->setOrganizer(organizerInfo); } if (!resourceList.isEmpty()) { auto invitees = Utils::parseResourceLists(resourceList); - for (const auto &i : invitees) { - info->addParticipant(i); + for (const auto &invitee : invitees) { + info->addParticipant(invitee); } } diff --git a/tester/audio_video_conference_tester.c b/tester/audio_video_conference_tester.c index 4820515ab891c74cbaaba55ac6ef154fd0f09ceb..d024c33517f235c214ed84ea770bf06a5821dadf 100644 --- a/tester/audio_video_conference_tester.c +++ b/tester/audio_video_conference_tester.c @@ -421,23 +421,27 @@ static void check_conference_volumes(LinphoneCall *call) { for (bctbx_list_t *it_d = devices; it_d != NULL; it_d = it_d->next) { LinphoneParticipantDevice *d = (LinphoneParticipantDevice *)it_d->data; - if (linphone_participant_device_get_stream_availability(d, LinphoneStreamTypeAudio)) { - BC_ASSERT_NOT_EQUAL((unsigned long)linphone_participant_device_get_ssrc(d, LinphoneStreamTypeAudio), - 0, unsigned long, "%0lu"); - BC_ASSERT_NOT_EQUAL(linphone_conference_get_participant_device_volume(conference, d), - AUDIOSTREAMVOLUMES_NOT_FOUND, int, "%d"); - BC_ASSERT_GREATER(linphone_conference_get_participant_device_volume(conference, d), - MS_VOLUME_DB_LOWEST, int, "%d"); - } else { + if (linphone_participant_device_get_stream_capability(d, LinphoneStreamTypeAudio) == + LinphoneMediaDirectionInactive) { BC_ASSERT_EQUAL((unsigned long)linphone_participant_device_get_ssrc(d, LinphoneStreamTypeAudio), 0, unsigned long, "%0lu"); - } - if (linphone_participant_device_get_stream_availability(d, LinphoneStreamTypeVideo)) { - BC_ASSERT_NOT_EQUAL((unsigned long)linphone_participant_device_get_ssrc(d, LinphoneStreamTypeVideo), - 0, unsigned long, "%0lu"); } else { + BC_ASSERT_NOT_EQUAL((unsigned long)linphone_participant_device_get_ssrc(d, LinphoneStreamTypeAudio), + 0, unsigned long, "%0lu"); + if (linphone_participant_device_get_stream_availability(d, LinphoneStreamTypeAudio)) { + BC_ASSERT_NOT_EQUAL(linphone_conference_get_participant_device_volume(conference, d), + AUDIOSTREAMVOLUMES_NOT_FOUND, int, "%d"); + BC_ASSERT_GREATER(linphone_conference_get_participant_device_volume(conference, d), + MS_VOLUME_DB_LOWEST, int, "%d"); + } + } + if (linphone_participant_device_get_stream_capability(d, LinphoneStreamTypeVideo) == + LinphoneMediaDirectionInactive) { BC_ASSERT_EQUAL((unsigned long)linphone_participant_device_get_ssrc(d, LinphoneStreamTypeVideo), 0, unsigned long, "%0lu"); + } else { + BC_ASSERT_NOT_EQUAL((unsigned long)linphone_participant_device_get_ssrc(d, LinphoneStreamTypeVideo), + 0, unsigned long, "%0lu"); } } bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); @@ -542,13 +546,15 @@ static void simple_conference_base(LinphoneCoreManager *marie, linphone_call_terminate(marie_call_pauline); linphone_call_terminate(marie_call_laure); BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallEnd, - initial_marie_stat.number_of_LinphoneCallEnd + 2, 10000)); + initial_marie_stat.number_of_LinphoneCallEnd + 2, + liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallEnd, initial_pauline_stat.number_of_LinphoneCallEnd + 1, 5000)); BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallEnd, initial_laure_stat.number_of_LinphoneCallEnd + 1, 5000)); BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallReleased, - initial_marie_stat.number_of_LinphoneCallReleased + 3, 10000)); + initial_marie_stat.number_of_LinphoneCallReleased + 3, + liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallReleased, initial_pauline_stat.number_of_LinphoneCallReleased + 1, 5000)); BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallReleased, @@ -682,7 +688,8 @@ static void simple_conference_base(LinphoneCoreManager *marie, BC_ASSERT_PTR_NULL(pauline_conference); linphone_address_unref(pauline_uri); BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneSubscriptionTerminated, - pauline_stat.number_of_LinphoneSubscriptionTerminated + 1, 10000)); + pauline_stat.number_of_LinphoneSubscriptionTerminated + 1, + liblinphone_tester_sip_timeout)); LinphoneAddress *laure_uri = linphone_address_new(linphone_core_get_identity(laure->lc)); LinphoneConference *laure_conference = @@ -690,7 +697,8 @@ static void simple_conference_base(LinphoneCoreManager *marie, BC_ASSERT_PTR_NULL(laure_conference); linphone_address_unref(laure_uri); BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneSubscriptionTerminated, - laure_stat.number_of_LinphoneSubscriptionTerminated + 1, 10000)); + laure_stat.number_of_LinphoneSubscriptionTerminated + 1, + liblinphone_tester_sip_timeout)); LinphoneAddress *marie_uri = linphone_address_new(linphone_core_get_identity(marie->lc)); LinphoneConference *marie_conference = @@ -702,11 +710,14 @@ static void simple_conference_base(LinphoneCoreManager *marie, } else { // Call between Marie and Laure BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallPausing, - marie_stat.number_of_LinphoneCallPausing + 1, 10000)); + marie_stat.number_of_LinphoneCallPausing + 1, + liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallPaused, - marie_stat.number_of_LinphoneCallPaused + 1, 10000)); + marie_stat.number_of_LinphoneCallPaused + 1, + liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallPausedByRemote, - laure_stat.number_of_LinphoneCallPausedByRemote + 1, 10000)); + laure_stat.number_of_LinphoneCallPausedByRemote + 1, + liblinphone_tester_sip_timeout)); // Conference on Laure's side BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneConferenceStateTerminationPending, @@ -769,19 +780,26 @@ static void simple_conference_base(LinphoneCoreManager *marie, } linphone_core_terminate_call(marie->lc, marie_call_laure); - BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallEnd, 1, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallEnd, 1, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallReleased, 1, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallReleased, 1, 10000)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &laure->stat.number_of_LinphoneCallEnd, 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &marie->stat.number_of_LinphoneCallEnd, 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &laure->stat.number_of_LinphoneCallReleased, 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &marie->stat.number_of_LinphoneCallReleased, 1, liblinphone_tester_sip_timeout)); BC_ASSERT_PTR_NOT_NULL(linphone_core_get_current_call(marie->lc)); linphone_call_terminate(marie_call_pauline); } else { linphone_core_pause_call(marie->lc, marie_call_laure); - BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallPaused, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallPausedByRemote, 1, 10000)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &marie->stat.number_of_LinphoneCallPaused, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallPausedByRemote, 1, + liblinphone_tester_sip_timeout)); linphone_core_terminate_call(marie->lc, marie_call_laure); - BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallEnd, is_remote_conf ? 2 : 1, 10000)); + BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallEnd, is_remote_conf ? 2 : 1, + liblinphone_tester_sip_timeout)); } } else { bctbx_list_t *participants = bctbx_list_append(NULL, laure); @@ -793,15 +811,24 @@ static void simple_conference_base(LinphoneCoreManager *marie, } bctbx_list_free(participants); } - BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallEnd, is_remote_conf ? 2 : 1, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallEnd, is_remote_conf ? 3 : 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallEnd, is_remote_conf ? 2 : 1, 10000)); - if (is_remote_conf) BC_ASSERT_TRUE(wait_for_list(lcs, &focus->stat.number_of_LinphoneCallEnd, 3, 10000)); - - BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallReleased, is_remote_conf ? 2 : 1, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallReleased, is_remote_conf ? 3 : 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallReleased, is_remote_conf ? 2 : 1, 10000)); - if (is_remote_conf) BC_ASSERT_TRUE(wait_for_list(lcs, &focus->stat.number_of_LinphoneCallReleased, 3, 10000)); + BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallEnd, is_remote_conf ? 2 : 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallEnd, is_remote_conf ? 3 : 2, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallEnd, is_remote_conf ? 2 : 1, + liblinphone_tester_sip_timeout)); + if (is_remote_conf) + BC_ASSERT_TRUE(wait_for_list(lcs, &focus->stat.number_of_LinphoneCallEnd, 3, liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallReleased, is_remote_conf ? 2 : 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallReleased, is_remote_conf ? 3 : 2, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallReleased, is_remote_conf ? 2 : 1, + liblinphone_tester_sip_timeout)); + if (is_remote_conf) + BC_ASSERT_TRUE( + wait_for_list(lcs, &focus->stat.number_of_LinphoneCallReleased, 3, liblinphone_tester_sip_timeout)); end: BC_ASSERT_EQUAL(marie->stat.number_of_LinphoneCoreLastCallEnded, 1, int, "%d"); @@ -962,17 +989,20 @@ static void simple_conference_notify_speaking_device(void) { BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneParticipantDeviceStopSpeaking, 2, 20000)); // No need to wait as much this time as pauline should also be notified at the same time - BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneParticipantDeviceStartSpeaking, 1, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneParticipantDeviceStopSpeaking, 1, 10000)); + BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneParticipantDeviceStartSpeaking, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneParticipantDeviceStopSpeaking, 1, + liblinphone_tester_sip_timeout)); terminate_conference(new_participants, marie, NULL, NULL); - BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallEnd, 1, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallEnd, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallEnd, 1, 10000)); + BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallEnd, 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallEnd, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallEnd, 1, liblinphone_tester_sip_timeout)); - BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallReleased, 1, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallReleased, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallReleased, 1, 10000)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallReleased, 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallReleased, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallReleased, 1, liblinphone_tester_sip_timeout)); bctbx_list_free(new_participants); @@ -1088,13 +1118,14 @@ static void simple_conference_notify_muted_device(void) { BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneParticipantDeviceMuted, 2, 5000)); terminate_conference(new_participants, marie, NULL, NULL); - BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallEnd, 1, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallEnd, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallEnd, 1, 10000)); + BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallEnd, 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallEnd, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallEnd, 1, liblinphone_tester_sip_timeout)); - BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallReleased, 1, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallReleased, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallReleased, 1, 10000)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallReleased, 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallReleased, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallReleased, 1, liblinphone_tester_sip_timeout)); bctbx_list_free(new_participants); @@ -1319,10 +1350,13 @@ static void simple_conference_with_admin_changed(void) { // Pauline ends all calls therefore Pauline exits the conference // A new admin must be designated linphone_core_terminate_all_calls(pauline->lc); - BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallEnd, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallReleased, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &((LinphoneCoreManager *)focus)->stat.number_of_LinphoneCallEnd, 1, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &((LinphoneCoreManager *)focus)->stat.number_of_LinphoneCallReleased, 1, 10000)); + BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallEnd, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallReleased, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(lcs, &((LinphoneCoreManager *)focus)->stat.number_of_LinphoneCallEnd, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(lcs, &((LinphoneCoreManager *)focus)->stat.number_of_LinphoneCallReleased, 1, + liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneConferenceStateTerminationPending, 1, 5000)); BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneConferenceStateTerminated, 1, 5000)); @@ -1630,9 +1664,14 @@ static void simple_conference_with_participant_addition_from_non_admin(void) { LinphoneParticipantDevice *d = (LinphoneParticipantDevice *)bctbx_list_get_data(d_it); BC_ASSERT_PTR_NOT_NULL(d); if (d) { - BC_ASSERT_TRUE( - !!linphone_participant_device_get_is_muted(d) == - (linphone_address_weak_equal(linphone_participant_device_get_address(d), michelle->identity))); + bool_t is_muted = + linphone_address_weak_equal(linphone_participant_device_get_address(d), michelle->identity); + int part_counter = 0; + do { + part_counter++; + wait_for_list(lcs, NULL, 0, 100); + } while ((part_counter < 100) && ((!!linphone_participant_device_get_is_muted(d)) != is_muted)); + BC_ASSERT_TRUE((!!linphone_participant_device_get_is_muted(d)) == (!!is_muted)); } } bctbx_list_free_with_data(participant_device_list, (void (*)(void *))linphone_participant_device_unref); @@ -1896,7 +1935,8 @@ static void simple_conference_with_subject_change_from_non_admin(void) { for (bctbx_list_t *it = all_manangers_in_conf; it; it = bctbx_list_next(it)) { LinphoneCoreManager *m = (LinphoneCoreManager *)bctbx_list_get_data(it); BC_ASSERT_TRUE(wait_for_list(lcs, &m->stat.number_of_subject_changed, - (initial_participants_stats[idx].number_of_subject_changed + 1), 10000)); + (initial_participants_stats[idx].number_of_subject_changed + 1), + liblinphone_tester_sip_timeout)); LinphoneAddress *uri = linphone_address_new(linphone_core_get_identity(m->lc)); LinphoneConference *conference = @@ -2087,14 +2127,18 @@ static void simple_conference_with_one_participant_base(bool_t local) { int marie_call_no = (int)bctbx_list_size(linphone_core_get_calls(marie->lc)); linphone_core_terminate_all_calls(marie->lc); - BC_ASSERT_TRUE(wait_for_list(lcs, &michelle->stat.number_of_LinphoneCallEnd, 1, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &michelle->stat.number_of_LinphoneCallReleased, 1, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallEnd, 1, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallReleased, 1, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallEnd, 1, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallReleased, 1, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallEnd, marie_call_no, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallReleased, marie_call_no, 10000)); + BC_ASSERT_TRUE(wait_for_list(lcs, &michelle->stat.number_of_LinphoneCallEnd, 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &michelle->stat.number_of_LinphoneCallReleased, 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallEnd, 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallReleased, 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallEnd, 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallReleased, 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &marie->stat.number_of_LinphoneCallEnd, marie_call_no, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &marie->stat.number_of_LinphoneCallReleased, marie_call_no, liblinphone_tester_sip_timeout)); destroy_mgr_in_conference(pauline); destroy_mgr_in_conference(laure); @@ -2647,20 +2691,27 @@ static void simple_conference_with_user_defined_layout(const LinphoneConferenceL int focus_call_no = (int)bctbx_list_size(linphone_core_get_calls(focus_mgr->lc)); linphone_core_terminate_all_calls(focus_mgr->lc); - BC_ASSERT_TRUE(wait_for_list(lcs, &michelle->stat.number_of_LinphoneCallEnd, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &michelle->stat.number_of_LinphoneCallReleased, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallEnd, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallReleased, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallEnd, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallReleased, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallEnd, (add_participant ? 5 : 4), 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallReleased, (add_participant ? 5 : 4), 10000)); + BC_ASSERT_TRUE(wait_for_list(lcs, &michelle->stat.number_of_LinphoneCallEnd, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &michelle->stat.number_of_LinphoneCallReleased, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallEnd, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallReleased, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallEnd, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallReleased, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallEnd, (add_participant ? 5 : 4), + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallReleased, (add_participant ? 5 : 4), + liblinphone_tester_sip_timeout)); if (add_participant) { - BC_ASSERT_TRUE(wait_for_list(lcs, &chloe->stat.number_of_LinphoneCallEnd, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &chloe->stat.number_of_LinphoneCallReleased, 2, 10000)); + BC_ASSERT_TRUE(wait_for_list(lcs, &chloe->stat.number_of_LinphoneCallEnd, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &chloe->stat.number_of_LinphoneCallReleased, 2, liblinphone_tester_sip_timeout)); } - BC_ASSERT_TRUE(wait_for_list(lcs, &focus_mgr->stat.number_of_LinphoneCallEnd, focus_call_no, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &focus_mgr->stat.number_of_LinphoneCallReleased, focus_call_no, 10000)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &focus_mgr->stat.number_of_LinphoneCallEnd, focus_call_no, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(lcs, &focus_mgr->stat.number_of_LinphoneCallReleased, focus_call_no, + liblinphone_tester_sip_timeout)); focus_call_no = (int)bctbx_list_size(linphone_core_get_calls(focus_mgr->lc)); BC_ASSERT_EQUAL(focus_call_no, 0, int, "%0d"); @@ -2847,14 +2898,18 @@ static void simple_conference_with_one_participant(void) { int marie_call_no = (int)bctbx_list_size(linphone_core_get_calls(marie->lc)); linphone_core_terminate_all_calls(marie->lc); - BC_ASSERT_TRUE(wait_for_list(lcs, &michelle->stat.number_of_LinphoneCallEnd, 1, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &michelle->stat.number_of_LinphoneCallReleased, 1, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallEnd, 1, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallReleased, 1, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallEnd, 1, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallReleased, 1, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallEnd, marie_call_no, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallReleased, marie_call_no, 10000)); + BC_ASSERT_TRUE(wait_for_list(lcs, &michelle->stat.number_of_LinphoneCallEnd, 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &michelle->stat.number_of_LinphoneCallReleased, 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallEnd, 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallReleased, 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallEnd, 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallReleased, 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &marie->stat.number_of_LinphoneCallEnd, marie_call_no, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &marie->stat.number_of_LinphoneCallReleased, marie_call_no, liblinphone_tester_sip_timeout)); linphone_conference_unref(conf); destroy_mgr_in_conference(pauline); @@ -3048,11 +3103,13 @@ static void simple_conference_with_subject_change_from_admin_base(bool_t enable_ for (bctbx_list_t *it = all_manangers_in_conf; it; it = bctbx_list_next(it)) { LinphoneCoreManager *m = (LinphoneCoreManager *)bctbx_list_get_data(it); BC_ASSERT_TRUE(wait_for_list(lcs, &m->stat.number_of_subject_changed, - (initial_participants_stats[idx].number_of_subject_changed + 1), 10000)); + (initial_participants_stats[idx].number_of_subject_changed + 1), + liblinphone_tester_sip_timeout)); if (enable_video) { BC_ASSERT_TRUE(wait_for_list( lcs, &m->stat.number_of_participant_devices_media_capability_changed, - (initial_participants_stats[idx].number_of_participant_devices_media_capability_changed + 1), 10000)); + (initial_participants_stats[idx].number_of_participant_devices_media_capability_changed + 1), + liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list( lcs, &m->stat.number_of_LinphoneCallUpdating, initial_participants_stats[idx].number_of_LinphoneCallUpdating + ((m == marie) ? 2 : 1), 3000)); @@ -3270,11 +3327,14 @@ static void simple_conference_through_inviting_participants(bool_t check_for_pro linphone_call_accept(pauline_call); linphone_call_accept(laure_call); - BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallConnected, 3, 10000)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &marie->stat.number_of_LinphoneCallConnected, 3, liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallStreamsRunning, 3, 3000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallConnected, 1, 10000)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallConnected, 1, liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallStreamsRunning, 1, 3000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallConnected, 1, 10000)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &laure->stat.number_of_LinphoneCallConnected, 1, liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallStreamsRunning, 1, 3000)); // Conference may have already been created of call was paused before hence initial stats can lead to false @@ -3317,14 +3377,17 @@ static void simple_conference_through_inviting_participants(bool_t check_for_pro BC_ASSERT_PTR_NOT_NULL(conf_call); linphone_core_remove_from_conference(marie->lc, conf_call); BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallEnd, - initial_marie_stat.number_of_LinphoneCallEnd + 1, 10000)); + initial_marie_stat.number_of_LinphoneCallEnd + 1, liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallReleased, - initial_marie_stat.number_of_LinphoneCallReleased + 1, 10000)); + initial_marie_stat.number_of_LinphoneCallReleased + 1, + liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallEnd, - initial_pauline_stat.number_of_LinphoneCallEnd + 1, 10000)); + initial_pauline_stat.number_of_LinphoneCallEnd + 1, + liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallReleased, - initial_pauline_stat.number_of_LinphoneCallReleased + 1, 10000)); + initial_pauline_stat.number_of_LinphoneCallReleased + 1, + liblinphone_tester_sip_timeout)); // Wait for all conferences to be terminated BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneConferenceStateTerminationPending, @@ -3379,7 +3442,8 @@ static void simple_conference_through_inviting_participants(bool_t check_for_pro initial_marie_stat.number_of_LinphoneSubscriptionTerminated + 1, 3000)); BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_NotifyReceived, - (initial_laure_stat.number_of_NotifyReceived + 2), 10000)); + (initial_laure_stat.number_of_NotifyReceived + 2), + liblinphone_tester_sip_timeout)); // CHeck that conference is not destroyed BC_ASSERT_FALSE(wait_for_list(lcs, &laure->stat.number_of_LinphoneConferenceStateTerminationPending, @@ -3413,17 +3477,20 @@ static void simple_conference_through_inviting_participants(bool_t check_for_pro linphone_core_remove_from_conference(marie->lc, conf_call); BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallEnd, - initial_marie_stat.number_of_LinphoneCallEnd + 1, 10000)); + initial_marie_stat.number_of_LinphoneCallEnd + 1, liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallReleased, - initial_marie_stat.number_of_LinphoneCallReleased + 1, 10000)); + initial_marie_stat.number_of_LinphoneCallReleased + 1, + liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallEnd, - initial_laure_stat.number_of_LinphoneCallEnd + 1, 10000)); + initial_laure_stat.number_of_LinphoneCallEnd + 1, liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallReleased, - initial_laure_stat.number_of_LinphoneCallReleased + 1, 10000)); + initial_laure_stat.number_of_LinphoneCallReleased + 1, + liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneSubscriptionTerminated, - initial_marie_stat.number_of_LinphoneSubscriptionTerminated + 1, 10000)); + initial_marie_stat.number_of_LinphoneSubscriptionTerminated + 1, + liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneConferenceStateTerminationPending, (initial_laure_stat.number_of_LinphoneConferenceStateTerminationPending + 1), @@ -3437,9 +3504,11 @@ static void simple_conference_through_inviting_participants(bool_t check_for_pro (initial_marie_stat.number_of_LinphoneConferenceStateTerminationPending + 1), 5000)); BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneConferenceStateTerminated, - (initial_marie_stat.number_of_LinphoneConferenceStateTerminated + 1), 10000)); + (initial_marie_stat.number_of_LinphoneConferenceStateTerminated + 1), + liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneConferenceStateDeleted, - (initial_marie_stat.number_of_LinphoneConferenceStateDeleted + 1), 10000)); + (initial_marie_stat.number_of_LinphoneConferenceStateDeleted + 1), + liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneSubscriptionTerminated, initial_laure_stat.number_of_LinphoneSubscriptionTerminated + 1, 3000)); @@ -3539,12 +3608,15 @@ static void _simple_conference_from_scratch(bool_t with_video) { linphone_call_accept(pauline_call); linphone_call_accept(laure_call); - BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallConnected, 2, 10000)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &marie->stat.number_of_LinphoneCallConnected, 2, liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallStreamsRunning, 2, 3000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallConnected, 1, 10000)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallConnected, 1, liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallStreamsRunning, 1, 3000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallConnected, 1, 10000)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &laure->stat.number_of_LinphoneCallConnected, 1, liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallStreamsRunning, 1, 3000)); // Conference may have already been created of call was paused before hence initial stats can lead to false @@ -3677,8 +3749,10 @@ static void video_conference_by_merging_calls(void) { pauline_call = linphone_core_invite_address_with_params(marie->lc, pauline->identity, params); linphone_call_params_unref(params); - BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallOutgoingProgress, 1, 10000)); - if (BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallIncomingReceived, 1, 10000))) { + BC_ASSERT_TRUE( + wait_for_list(lcs, &marie->stat.number_of_LinphoneCallOutgoingProgress, 1, liblinphone_tester_sip_timeout)); + if (BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallIncomingReceived, 1, + liblinphone_tester_sip_timeout))) { linphone_call_accept(linphone_core_get_current_call(pauline->lc)); } else goto end; BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallStreamsRunning, 1, 5000)); @@ -3690,8 +3764,10 @@ static void video_conference_by_merging_calls(void) { laure_call = linphone_core_invite_address_with_params(marie->lc, laure->identity, params); linphone_call_params_unref(params); - BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallOutgoingProgress, 2, 10000)); - if (BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallIncomingReceived, 1, 10000))) { + BC_ASSERT_TRUE( + wait_for_list(lcs, &marie->stat.number_of_LinphoneCallOutgoingProgress, 2, liblinphone_tester_sip_timeout)); + if (BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallIncomingReceived, 1, + liblinphone_tester_sip_timeout))) { linphone_call_accept(linphone_core_get_current_call(laure->lc)); } else goto end; BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallStreamsRunning, 2, 5000)); @@ -3871,16 +3947,18 @@ static void simple_conference_from_scratch_no_answer(void) { // Pauline immediately declines the call. linphone_call_decline(pauline_call, LinphoneReasonDeclined); } - BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallEnd, 1, 10000)); + BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallEnd, 1, liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallEnd, 1, 5000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallReleased, 1, 10000)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallReleased, 1, liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallReleased, 1, 5000)); wait_for_list(lcs, NULL, 0, 1000); lcs = bctbx_list_append(lcs, laure->lc); - BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallIncomingReceived, 1, 10000)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &laure->stat.number_of_LinphoneCallIncomingReceived, 1, liblinphone_tester_sip_timeout)); laure_call = linphone_core_get_current_call(laure->lc); BC_ASSERT_PTR_NOT_NULL(laure_call); @@ -3891,7 +3969,8 @@ static void simple_conference_from_scratch_no_answer(void) { // Laure accepts. linphone_call_accept(laure_call); - BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallStreamsRunning, 1, 10000)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &laure->stat.number_of_LinphoneCallStreamsRunning, 1, liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallStreamsRunning, 1, 5000)); wait_for_list(lcs, NULL, 0, 1000); @@ -3900,7 +3979,7 @@ static void simple_conference_from_scratch_no_answer(void) { // Terminate the call, simply. linphone_call_terminate(laure_call); - BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallEnd, 1, 10000)); + BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallEnd, 1, liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallEnd, 2, 5000)); BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallReleased, 2, 1000)); BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallReleased, 1, 1000)); @@ -4138,9 +4217,9 @@ static void eject_from_3_participants_conference(LinphoneCoreManager *marie, end_call(laure, marie); end_call(pauline, marie); - BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallEnd, 1, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallEnd, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallEnd, 1, 10000)); + BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallEnd, 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallEnd, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallEnd, 1, liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallReleased, initial_laure_stat.number_of_LinphoneCallReleased + 1, 3000)); @@ -4330,13 +4409,13 @@ static void eject_from_4_participants_conference_call_terminated_one_by_one(Linp linphone_call_terminate(marie_call_laure); BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallEnd, - initial_pauline_stat.number_of_LinphoneCallEnd + 1, 10000)); + initial_pauline_stat.number_of_LinphoneCallEnd + 1, liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &michelle->stat.number_of_LinphoneCallEnd, - initial_michelle_stat.number_of_LinphoneCallEnd + 1, 10000)); + initial_michelle_stat.number_of_LinphoneCallEnd + 1, liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallEnd, - initial_laure_stat.number_of_LinphoneCallEnd + 1, 10000)); + initial_laure_stat.number_of_LinphoneCallEnd + 1, liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallEnd, - initial_marie_stat.number_of_LinphoneCallEnd + 3, 10000)); + initial_marie_stat.number_of_LinphoneCallEnd + 3, liblinphone_tester_sip_timeout)); // Wait for conferences to be terminated BC_ASSERT_TRUE(wait_for_list(lcs, &michelle->stat.number_of_LinphoneConferenceStateTerminationPending, @@ -4369,13 +4448,17 @@ static void eject_from_4_participants_conference_call_terminated_one_by_one(Linp (initial_marie_stat.number_of_LinphoneConferenceStateDeleted + 1), 5000)); BC_ASSERT_TRUE(wait_for_list(lcs, &michelle->stat.number_of_LinphoneSubscriptionTerminated, - initial_michelle_stat.number_of_LinphoneSubscriptionTerminated + 1, 10000)); + initial_michelle_stat.number_of_LinphoneSubscriptionTerminated + 1, + liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneSubscriptionTerminated, - initial_pauline_stat.number_of_LinphoneSubscriptionTerminated + 1, 10000)); + initial_pauline_stat.number_of_LinphoneSubscriptionTerminated + 1, + liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneSubscriptionTerminated, - initial_laure_stat.number_of_LinphoneSubscriptionTerminated + 1, 10000)); + initial_laure_stat.number_of_LinphoneSubscriptionTerminated + 1, + liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneSubscriptionTerminated, - initial_marie_stat.number_of_LinphoneSubscriptionTerminated + 3, 10000)); + initial_marie_stat.number_of_LinphoneSubscriptionTerminated + 3, + liblinphone_tester_sip_timeout)); BC_ASSERT_PTR_NULL(linphone_core_get_conference(marie->lc)); @@ -4465,10 +4548,14 @@ static void eject_from_4_participants_conference_base(const LinphoneConferenceLa add_calls_to_local_conference(lcs, marie, NULL, new_participants, FALSE); /* Wait that the three participants are joined to the local conference, by checking the StreamsRunning states*/ - BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallStreamsRunning, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &michelle->stat.number_of_LinphoneCallStreamsRunning, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallStreamsRunning, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallStreamsRunning, 6, 10000)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &laure->stat.number_of_LinphoneCallStreamsRunning, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &michelle->stat.number_of_LinphoneCallStreamsRunning, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallStreamsRunning, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &marie->stat.number_of_LinphoneCallStreamsRunning, 6, liblinphone_tester_sip_timeout)); LinphoneConference *l_conference = linphone_core_get_conference(marie->lc); BC_ASSERT_PTR_NOT_NULL(l_conference); @@ -4501,8 +4588,10 @@ static void eject_from_4_participants_conference_base(const LinphoneConferenceLa BC_ASSERT_PTR_NULL(linphone_core_get_conference(marie->lc)); - BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallEnd, marie_call_no, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallReleased, marie_call_no, 10000)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &marie->stat.number_of_LinphoneCallEnd, marie_call_no, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &marie->stat.number_of_LinphoneCallReleased, marie_call_no, liblinphone_tester_sip_timeout)); end: @@ -4568,10 +4657,14 @@ static void participants_exit_conference_after_pausing(void) { bctbx_list_free(new_participants); // Wait that the three participants are joined to the local conference, by checking the StreamsRunning states - BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallStreamsRunning, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &michelle->stat.number_of_LinphoneCallStreamsRunning, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallStreamsRunning, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallStreamsRunning, 6, 10000)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &laure->stat.number_of_LinphoneCallStreamsRunning, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &michelle->stat.number_of_LinphoneCallStreamsRunning, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallStreamsRunning, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &marie->stat.number_of_LinphoneCallStreamsRunning, 6, liblinphone_tester_sip_timeout)); LinphoneConference *marie_conference = linphone_core_get_conference(marie->lc); BC_ASSERT_PTR_NOT_NULL(marie_conference); @@ -4623,11 +4716,11 @@ static void participants_exit_conference_after_pausing(void) { // Pausing participant calls causes a NOTIFY to be sent to the participants to notify of the change in media // capabilities BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallPausing, - laure_stats.number_of_LinphoneCallPausing + 1, 10000)); + laure_stats.number_of_LinphoneCallPausing + 1, liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallPaused, - laure_stats.number_of_LinphoneCallPaused + 1, 10000)); + laure_stats.number_of_LinphoneCallPaused + 1, liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallPausedByRemote, - marie_stats.number_of_LinphoneCallPausedByRemote + 1, 10000)); + marie_stats.number_of_LinphoneCallPausedByRemote + 1, liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_NotifyReceived, (pauline_stats.number_of_NotifyReceived + 1), 5000)); @@ -4650,11 +4743,11 @@ static void participants_exit_conference_after_pausing(void) { linphone_core_pause_call(pauline->lc, pauline_call_marie); BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallPausing, - pauline_stats.number_of_LinphoneCallPausing + 1, 10000)); + pauline_stats.number_of_LinphoneCallPausing + 1, liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallPaused, - pauline_stats.number_of_LinphoneCallPaused + 1, 10000)); + pauline_stats.number_of_LinphoneCallPaused + 1, liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallPausedByRemote, - marie_stats.number_of_LinphoneCallPausedByRemote + 2, 10000)); + marie_stats.number_of_LinphoneCallPausedByRemote + 2, liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE( wait_for_list(lcs, &laure->stat.number_of_NotifyReceived, (laure_stats.number_of_NotifyReceived + 1), 5000)); @@ -4700,16 +4793,20 @@ static void participants_exit_conference_after_pausing(void) { (michelle_stats.number_of_LinphoneConferenceStateDeleted + 1), 5000)); BC_ASSERT_TRUE(wait_for_list(lcs, &michelle->stat.number_of_LinphoneSubscriptionTerminated, - michelle_stats.number_of_LinphoneSubscriptionTerminated + 1, 10000)); + michelle_stats.number_of_LinphoneSubscriptionTerminated + 1, + liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneSubscriptionTerminated, - marie_stats.number_of_LinphoneSubscriptionTerminated + 1, 10000)); + marie_stats.number_of_LinphoneSubscriptionTerminated + 1, + liblinphone_tester_sip_timeout)); linphone_core_terminate_all_calls(marie->lc); BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneSubscriptionTerminated, - pauline_stats.number_of_LinphoneSubscriptionTerminated + 1, 10000)); + pauline_stats.number_of_LinphoneSubscriptionTerminated + 1, + liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneSubscriptionTerminated, - laure_stats.number_of_LinphoneSubscriptionTerminated + 1, 10000)); + laure_stats.number_of_LinphoneSubscriptionTerminated + 1, + liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneConferenceStateTerminationPending, (laure_stats.number_of_LinphoneConferenceStateTerminationPending + 1), 5000)); @@ -4733,24 +4830,27 @@ static void participants_exit_conference_after_pausing(void) { (marie_stats.number_of_LinphoneConferenceStateDeleted + 1), 5000)); BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneSubscriptionTerminated, - marie_stats.number_of_LinphoneSubscriptionTerminated + 3, 10000)); + marie_stats.number_of_LinphoneSubscriptionTerminated + 3, + liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallEnd, - marie_stats.number_of_LinphoneCallEnd + marie_call_no, 10000)); - BC_ASSERT_TRUE( - wait_for_list(lcs, &laure->stat.number_of_LinphoneCallEnd, laure_stats.number_of_LinphoneCallEnd + 1, 10000)); + marie_stats.number_of_LinphoneCallEnd + marie_call_no, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallEnd, laure_stats.number_of_LinphoneCallEnd + 1, + liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallEnd, - pauline_stats.number_of_LinphoneCallEnd + 1, 10000)); + pauline_stats.number_of_LinphoneCallEnd + 1, liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &michelle->stat.number_of_LinphoneCallEnd, - michelle_stats.number_of_LinphoneCallEnd + 1, 10000)); + michelle_stats.number_of_LinphoneCallEnd + 1, liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallReleased, - marie_stats.number_of_LinphoneCallReleased + marie_call_no, 10000)); + marie_stats.number_of_LinphoneCallReleased + marie_call_no, + liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallReleased, - laure_stats.number_of_LinphoneCallReleased + 1, 10000)); + laure_stats.number_of_LinphoneCallReleased + 1, liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallReleased, - pauline_stats.number_of_LinphoneCallReleased + 1, 10000)); + pauline_stats.number_of_LinphoneCallReleased + 1, liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &michelle->stat.number_of_LinphoneCallReleased, - michelle_stats.number_of_LinphoneCallReleased + 1, 10000)); + michelle_stats.number_of_LinphoneCallReleased + 1, liblinphone_tester_sip_timeout)); BC_ASSERT_PTR_NULL(linphone_core_get_conference(marie->lc)); BC_ASSERT_EQUAL((unsigned int)bctbx_list_size(linphone_core_get_calls(marie->lc)), 0, unsigned int, "%u"); @@ -4843,10 +4943,14 @@ static void add_participant_after_conference_started_base(bool_t pause_all_calls bctbx_list_free(additional_participants); // Wait that the three participants are joined to the local conference, by checking the StreamsRunning states - BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallStreamsRunning, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &michelle->stat.number_of_LinphoneCallStreamsRunning, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallStreamsRunning, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallStreamsRunning, 6, 10000)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &laure->stat.number_of_LinphoneCallStreamsRunning, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &michelle->stat.number_of_LinphoneCallStreamsRunning, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallStreamsRunning, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &marie->stat.number_of_LinphoneCallStreamsRunning, 6, liblinphone_tester_sip_timeout)); // If Laure's call was paused before creating the conference, local participant is not added BC_ASSERT_FALSE(linphone_conference_is_in(l_conference) == pause_all_calls); @@ -4973,10 +5077,14 @@ static void conference_with_last_call_paused(void) { BC_ASSERT_EQUAL(linphone_conference_get_participant_count(l_conference), 3, int, "%d"); // Wait that the three participants are joined to the local conference, by checking the StreamsRunning states - BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallStreamsRunning, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &michelle->stat.number_of_LinphoneCallStreamsRunning, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallStreamsRunning, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallStreamsRunning, 6, 10000)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &laure->stat.number_of_LinphoneCallStreamsRunning, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &michelle->stat.number_of_LinphoneCallStreamsRunning, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallStreamsRunning, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &marie->stat.number_of_LinphoneCallStreamsRunning, 6, liblinphone_tester_sip_timeout)); BC_ASSERT_FALSE(linphone_conference_is_in(l_conference)); BC_ASSERT_EQUAL(linphone_conference_get_participant_count(l_conference), 3, int, "%d"); @@ -5039,10 +5147,14 @@ static void add_all_calls_to_conference(void) { linphone_core_add_all_to_conference(marie->lc); // Wait that the three participants are joined to the local conference, by checking the StreamsRunning states - BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallStreamsRunning, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &michelle->stat.number_of_LinphoneCallStreamsRunning, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallStreamsRunning, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallStreamsRunning, 6, 10000)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &laure->stat.number_of_LinphoneCallStreamsRunning, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &michelle->stat.number_of_LinphoneCallStreamsRunning, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallStreamsRunning, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &marie->stat.number_of_LinphoneCallStreamsRunning, 6, liblinphone_tester_sip_timeout)); // SUBSCRIBEs BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneSubscriptionActive, 1, 5000)); @@ -5162,7 +5274,8 @@ static void add_call_not_accepted_to_conference_remote(void) { BC_ASSERT_PTR_NOT_NULL(pauline_call_marie); if (!pauline_call_marie) goto end; BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallOutgoingInit, 1, 5000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallIncomingReceived, 2, 10000)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &marie->stat.number_of_LinphoneCallIncomingReceived, 2, liblinphone_tester_sip_timeout)); marie_called_by_pauline = linphone_core_get_call_by_remote_address2(marie->lc, pauline->identity); BC_ASSERT_PTR_NOT_NULL(marie_called_by_pauline); @@ -5562,10 +5675,14 @@ static void remove_participant_from_video_conference_base(const LinphoneConferen BC_ASSERT_PTR_NOT_NULL(current_conf_params); BC_ASSERT_TRUE(linphone_conference_params_video_enabled(current_conf_params)); - BC_ASSERT_TRUE(wait_for_list(lcs, &michelle->stat.number_of_LinphoneCallStreamsRunning, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallStreamsRunning, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallStreamsRunning, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallStreamsRunning, 6, 10000)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &michelle->stat.number_of_LinphoneCallStreamsRunning, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallStreamsRunning, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &laure->stat.number_of_LinphoneCallStreamsRunning, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &marie->stat.number_of_LinphoneCallStreamsRunning, 6, liblinphone_tester_sip_timeout)); // Check that video is still on negotiated_call_params = linphone_call_get_current_params(marie_call_laure); @@ -5826,14 +5943,14 @@ static void conference_created_by_merging_video_calls_base(bool_t event_package_ BC_ASSERT_PTR_NOT_NULL(current_conf_params); BC_ASSERT_TRUE(linphone_conference_params_video_enabled(current_conf_params) == enable_video); - BC_ASSERT_TRUE( - wait_for_list(lcs, &michelle->stat.number_of_LinphoneCallStreamsRunning, 2 + ((enable_ice) ? 1 : 0), 10000)); - BC_ASSERT_TRUE( - wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallStreamsRunning, 2 + ((enable_ice) ? 1 : 0), 10000)); - BC_ASSERT_TRUE( - wait_for_list(lcs, &laure->stat.number_of_LinphoneCallStreamsRunning, 2 + ((enable_ice) ? 1 : 0), 10000)); - BC_ASSERT_TRUE( - wait_for_list(lcs, &marie->stat.number_of_LinphoneCallStreamsRunning, 4 + ((enable_ice) ? 6 : 0), 10000)); + BC_ASSERT_TRUE(wait_for_list(lcs, &michelle->stat.number_of_LinphoneCallStreamsRunning, 2 + ((enable_ice) ? 1 : 0), + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallStreamsRunning, 2 + ((enable_ice) ? 1 : 0), + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallStreamsRunning, 2 + ((enable_ice) ? 1 : 0), + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallStreamsRunning, 4 + ((enable_ice) ? 6 : 0), + liblinphone_tester_sip_timeout)); // Check that conference capabilities hasn't changed current_conf_params = linphone_conference_get_current_params(l_conference); @@ -6084,8 +6201,10 @@ static void ice_video_conference_one_participant(const LinphoneConferenceLayout BC_ASSERT_PTR_NOT_NULL(current_conf_params); BC_ASSERT_TRUE(linphone_conference_params_video_enabled(current_conf_params)); - BC_ASSERT_TRUE(wait_for_list(lcs, &michelle->stat.number_of_LinphoneCallStreamsRunning, 3, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallStreamsRunning, 3, 10000)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &michelle->stat.number_of_LinphoneCallStreamsRunning, 3, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &marie->stat.number_of_LinphoneCallStreamsRunning, 3, liblinphone_tester_sip_timeout)); // Check that conference capabilities hasn't changed current_conf_params = linphone_conference_get_current_params(conf); @@ -6349,14 +6468,14 @@ static void audio_calls_added_to_video_conference_base(const LinphoneConferenceL BC_ASSERT_PTR_NOT_NULL(current_conf_params); BC_ASSERT_TRUE(linphone_conference_params_video_enabled(current_conf_params)); - BC_ASSERT_TRUE( - wait_for_list(lcs, &michelle->stat.number_of_LinphoneCallStreamsRunning, 2 + ((enable_ice) ? 2 : 0), 10000)); - BC_ASSERT_TRUE( - wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallStreamsRunning, 2 + ((enable_ice) ? 2 : 0), 10000)); - BC_ASSERT_TRUE( - wait_for_list(lcs, &laure->stat.number_of_LinphoneCallStreamsRunning, 2 + ((enable_ice) ? 2 : 0), 10000)); - BC_ASSERT_TRUE( - wait_for_list(lcs, &marie->stat.number_of_LinphoneCallStreamsRunning, 6 + ((enable_ice) ? 6 : 0), 10000)); + BC_ASSERT_TRUE(wait_for_list(lcs, &michelle->stat.number_of_LinphoneCallStreamsRunning, 2 + ((enable_ice) ? 2 : 0), + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallStreamsRunning, 2 + ((enable_ice) ? 2 : 0), + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallStreamsRunning, 2 + ((enable_ice) ? 2 : 0), + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallStreamsRunning, 6 + ((enable_ice) ? 6 : 0), + liblinphone_tester_sip_timeout)); // Check that conference capabilities hasn't changed current_conf_params = linphone_conference_get_current_params(l_conference); @@ -6472,9 +6591,12 @@ static void simple_participant_leaves_conference_base(bool_t remote_participant_ BC_ASSERT_TRUE(linphone_conference_is_in(l_conference)); BC_ASSERT_EQUAL(linphone_conference_get_participant_count(l_conference), 2, int, "%d"); - BC_ASSERT_TRUE(wait_for_list(lcs, &michelle->stat.number_of_LinphoneCallStreamsRunning, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallStreamsRunning, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallStreamsRunning, 4, 10000)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &michelle->stat.number_of_LinphoneCallStreamsRunning, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallStreamsRunning, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &marie->stat.number_of_LinphoneCallStreamsRunning, 4, liblinphone_tester_sip_timeout)); LinphoneConference *marie_conference = linphone_core_get_conference(marie->lc); BC_ASSERT_PTR_NOT_NULL(marie_conference); @@ -6692,9 +6814,12 @@ participant_leaves_conference_base(bool_t remote_participant_leaves, bool_t add_ BC_ASSERT_TRUE(linphone_conference_is_in(l_conference)); BC_ASSERT_EQUAL(linphone_conference_get_participant_count(l_conference), 2, int, "%d"); - BC_ASSERT_TRUE(wait_for_list(lcs, &michelle->stat.number_of_LinphoneCallStreamsRunning, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallStreamsRunning, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallStreamsRunning, 4, 10000)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &michelle->stat.number_of_LinphoneCallStreamsRunning, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallStreamsRunning, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &marie->stat.number_of_LinphoneCallStreamsRunning, 4, liblinphone_tester_sip_timeout)); LinphoneConference *marie_conference = linphone_core_get_conference(marie->lc); BC_ASSERT_PTR_NOT_NULL(marie_conference); @@ -6916,9 +7041,11 @@ participant_leaves_conference_base(bool_t remote_participant_leaves, bool_t add_ // Wait that Laure joins the conference BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallStreamsRunning, - laure_stats.number_of_LinphoneCallStreamsRunning + 1, 10000)); + laure_stats.number_of_LinphoneCallStreamsRunning + 1, + liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallStreamsRunning, - marie_stats.number_of_LinphoneCallStreamsRunning + 1, 10000)); + marie_stats.number_of_LinphoneCallStreamsRunning + 1, + liblinphone_tester_sip_timeout)); } conf_parts_no = 2 + add_participant; @@ -7101,10 +7228,14 @@ static void all_temporarely_leave_conference_base(bool_t local_enters_first) { BC_ASSERT_TRUE(linphone_conference_is_in(l_conference)); BC_ASSERT_EQUAL(linphone_conference_get_participant_count(l_conference), 3, int, "%d"); - BC_ASSERT_TRUE(wait_for_list(lcs, &michelle->stat.number_of_LinphoneCallStreamsRunning, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallStreamsRunning, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallStreamsRunning, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallStreamsRunning, 6, 10000)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &michelle->stat.number_of_LinphoneCallStreamsRunning, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallStreamsRunning, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &laure->stat.number_of_LinphoneCallStreamsRunning, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &marie->stat.number_of_LinphoneCallStreamsRunning, 6, liblinphone_tester_sip_timeout)); int no_parts = (int)bctbx_list_size(participants); @@ -7472,17 +7603,18 @@ static void focus_takes_call_after_conference_started_and_participants_leave(voi BC_ASSERT_EQUAL(pauline_call_no, 1, unsigned int, "%u"); linphone_core_terminate_all_calls(marie->lc); BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallEnd, - marie_stat.number_of_LinphoneCallEnd + marie_call_no, 10000)); - BC_ASSERT_TRUE( - wait_for_list(lcs, &laure->stat.number_of_LinphoneCallEnd, laure_stat.number_of_LinphoneCallEnd + 1, 10000)); + marie_stat.number_of_LinphoneCallEnd + marie_call_no, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallEnd, laure_stat.number_of_LinphoneCallEnd + 1, + liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallEnd, - pauline_stat.number_of_LinphoneCallEnd + 1, 10000)); + pauline_stat.number_of_LinphoneCallEnd + 1, liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallReleased, - marie_stat.number_of_LinphoneCallReleased + marie_call_no, 10000)); + marie_stat.number_of_LinphoneCallReleased + marie_call_no, + liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallReleased, - laure_stat.number_of_LinphoneCallReleased + 1, 10000)); + laure_stat.number_of_LinphoneCallReleased + 1, liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallReleased, - pauline_stat.number_of_LinphoneCallReleased + 1, 10000)); + pauline_stat.number_of_LinphoneCallReleased + 1, liblinphone_tester_sip_timeout)); end: @@ -7526,9 +7658,12 @@ static void remote_participant_leaves_and_conference_ends_base(bool_t local_ends BC_ASSERT_TRUE(linphone_conference_is_in(l_conference)); BC_ASSERT_EQUAL(linphone_conference_get_participant_count(l_conference), 2, int, "%d"); - BC_ASSERT_TRUE(wait_for_list(lcs, &michelle->stat.number_of_LinphoneCallStreamsRunning, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallStreamsRunning, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallStreamsRunning, 4, 10000)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &michelle->stat.number_of_LinphoneCallStreamsRunning, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallStreamsRunning, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &marie->stat.number_of_LinphoneCallStreamsRunning, 4, liblinphone_tester_sip_timeout)); LinphoneConference *marie_conference = linphone_core_get_conference(marie->lc); BC_ASSERT_PTR_NOT_NULL(marie_conference); @@ -7607,13 +7742,15 @@ static void remote_participant_leaves_and_conference_ends_base(bool_t local_ends pauline_stats = pauline->stat; linphone_core_terminate_call(michelle->lc, michelle_called_by_marie); BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallEnd, - marie_stats.number_of_LinphoneCallEnd + 1, 10000)); + marie_stats.number_of_LinphoneCallEnd + 1, liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &michelle->stat.number_of_LinphoneCallEnd, - michelle_stats.number_of_LinphoneCallEnd + 1, 10000)); + michelle_stats.number_of_LinphoneCallEnd + 1, liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallReleased, - marie_stats.number_of_LinphoneCallReleased + 1, 10000)); + marie_stats.number_of_LinphoneCallReleased + 1, + liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &michelle->stat.number_of_LinphoneCallReleased, - michelle_stats.number_of_LinphoneCallReleased + 1, 10000)); + michelle_stats.number_of_LinphoneCallReleased + 1, + liblinphone_tester_sip_timeout)); // Wait for conferences to be terminated BC_ASSERT_TRUE(wait_for_list(lcs, &michelle->stat.number_of_LinphoneConferenceStateTerminationPending, @@ -7731,10 +7868,14 @@ static void participant_call_terminated_after_leaving_conference_base(bool_t loc BC_ASSERT_TRUE(linphone_conference_is_in(l_conference)); BC_ASSERT_EQUAL(linphone_conference_get_participant_count(l_conference), 3, int, "%d"); - BC_ASSERT_TRUE(wait_for_list(lcs, &michelle->stat.number_of_LinphoneCallStreamsRunning, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallStreamsRunning, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &chloe->stat.number_of_LinphoneCallStreamsRunning, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallStreamsRunning, 6, 10000)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &michelle->stat.number_of_LinphoneCallStreamsRunning, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallStreamsRunning, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &chloe->stat.number_of_LinphoneCallStreamsRunning, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &marie->stat.number_of_LinphoneCallStreamsRunning, 6, liblinphone_tester_sip_timeout)); LinphoneConference *marie_conference = linphone_core_get_conference(marie->lc); BC_ASSERT_PTR_NOT_NULL(marie_conference); @@ -7934,9 +8075,10 @@ static void participant_call_terminated_after_leaving_conference_base(bool_t loc // Wait that Pauline joins the conference BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallStreamsRunning, - pauline_stats.number_of_LinphoneCallStreamsRunning + 1, 10000)); + pauline_stats.number_of_LinphoneCallStreamsRunning + 1, + liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallStreamsRunning, - marie_stats.number_of_LinphoneCallStreamsRunning + 1, 10000)); + marie_stats.number_of_LinphoneCallStreamsRunning + 1, liblinphone_tester_sip_timeout)); for (bctbx_list_t *it = lcs; it; it = bctbx_list_next(it)) { LinphoneCore *c = (LinphoneCore *)bctbx_list_get_data(it); @@ -9250,11 +9392,14 @@ static void participants_take_call_after_conference_started_and_rejoins_conferen BC_ASSERT_TRUE(wait_for(marie->lc, laure->lc, &marie->stat.number_of_LinphoneCallReleased, marie_initial_stats.number_of_LinphoneCallReleased + 1)); BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneSubscriptionTerminated, - laure_initial_stats.number_of_LinphoneSubscriptionTerminated + 1, 10000)); + laure_initial_stats.number_of_LinphoneSubscriptionTerminated + 1, + liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &chloe->stat.number_of_LinphoneSubscriptionTerminated, - chloe_initial_stats.number_of_LinphoneSubscriptionTerminated + 1, 10000)); + chloe_initial_stats.number_of_LinphoneSubscriptionTerminated + 1, + liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneSubscriptionTerminated, - marie_initial_stats.number_of_LinphoneSubscriptionTerminated + 2, 10000)); + marie_initial_stats.number_of_LinphoneSubscriptionTerminated + 2, + liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneConferenceStateTerminationPending, (laure_initial_stats.number_of_LinphoneConferenceStateTerminationPending + 1), 5000)); @@ -9394,9 +9539,11 @@ static void participant_takes_call_after_conference_started_and_rejoins_conferen wait_for_list(lcs, NULL, 0, 2000); BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneSubscriptionTerminated, - laure_initial_stats.number_of_LinphoneSubscriptionTerminated + 1, 10000)); + laure_initial_stats.number_of_LinphoneSubscriptionTerminated + 1, + liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneSubscriptionTerminated, - marie_initial_stats.number_of_LinphoneSubscriptionTerminated + 2, 10000)); + marie_initial_stats.number_of_LinphoneSubscriptionTerminated + 2, + liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneConferenceStateTerminationPending, (laure_initial_stats.number_of_LinphoneConferenceStateTerminationPending + 1), 5000)); @@ -9587,11 +9734,14 @@ static void toggle_video_settings_during_conference_base(bool_t automatically_vi BC_ASSERT_EQUAL(linphone_conference_get_participant_count(conference), no_participants, int, "%d"); // Wait that the three participants have joined the local conference, by checking the StreamsRunning states - BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallStreamsRunning, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &michelle->stat.number_of_LinphoneCallStreamsRunning, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallStreamsRunning, 2, 10000)); BC_ASSERT_TRUE( - wait_for_list(lcs, &marie->stat.number_of_LinphoneCallStreamsRunning, 2 * no_participants, 10000)); + wait_for_list(lcs, &laure->stat.number_of_LinphoneCallStreamsRunning, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(lcs, &michelle->stat.number_of_LinphoneCallStreamsRunning, 2, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallStreamsRunning, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallStreamsRunning, 2 * no_participants, + liblinphone_tester_sip_timeout)); for (bctbx_list_t *it = lcs; it; it = bctbx_list_next(it)) { LinphoneCore *c = (LinphoneCore *)bctbx_list_get_data(it); @@ -9796,11 +9946,14 @@ static void simultaneous_toggle_video_settings_during_conference(void) { BC_ASSERT_EQUAL(linphone_conference_get_participant_count(conference), no_participants, int, "%d"); // Wait that the three participants have joined the local conference, by checking the StreamsRunning states - BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallStreamsRunning, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &michelle->stat.number_of_LinphoneCallStreamsRunning, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallStreamsRunning, 2, 10000)); BC_ASSERT_TRUE( - wait_for_list(lcs, &marie->stat.number_of_LinphoneCallStreamsRunning, 2 * no_participants, 10000)); + wait_for_list(lcs, &laure->stat.number_of_LinphoneCallStreamsRunning, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(lcs, &michelle->stat.number_of_LinphoneCallStreamsRunning, 2, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallStreamsRunning, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallStreamsRunning, 2 * no_participants, + liblinphone_tester_sip_timeout)); bool_t enable_video = FALSE; @@ -9976,10 +10129,12 @@ static void update_conf_params_during_conference(void) { BC_ASSERT_EQUAL(linphone_conference_get_participant_count(marie_conference),3, int, "%d"); // Wait that the three participants have joined the local conference, by checking the StreamsRunning states - BC_ASSERT_TRUE(wait_for_list(lcs,&laure->stat.number_of_LinphoneCallStreamsRunning, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs,&michelle->stat.number_of_LinphoneCallStreamsRunning, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallStreamsRunning, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneCallStreamsRunning, 6, 10000)); + BC_ASSERT_TRUE(wait_for_list(lcs,&laure->stat.number_of_LinphoneCallStreamsRunning, 2, +liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs,&michelle->stat.number_of_LinphoneCallStreamsRunning, +2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallStreamsRunning, 2, +liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneCallStreamsRunning, 6, +liblinphone_tester_sip_timeout)); // Enable video bool_t video_enabled = TRUE; @@ -10189,13 +10344,14 @@ static void focus_takes_quick_call_after_conference_started_base(bool_t toggle_v BC_ASSERT_EQUAL(laure_call_no, 1, unsigned int, "%u"); linphone_core_terminate_all_calls(marie->lc); BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallEnd, - marie_stat.number_of_LinphoneCallEnd + marie_call_no, 10000)); - BC_ASSERT_TRUE( - wait_for_list(lcs, &laure->stat.number_of_LinphoneCallEnd, laure_stat.number_of_LinphoneCallEnd + 1, 10000)); + marie_stat.number_of_LinphoneCallEnd + marie_call_no, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallEnd, laure_stat.number_of_LinphoneCallEnd + 1, + liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallReleased, - marie_stat.number_of_LinphoneCallReleased + marie_call_no, 10000)); + marie_stat.number_of_LinphoneCallReleased + marie_call_no, + liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallReleased, - laure_stat.number_of_LinphoneCallReleased + 1, 10000)); + laure_stat.number_of_LinphoneCallReleased + 1, liblinphone_tester_sip_timeout)); end: @@ -10274,10 +10430,14 @@ static void try_to_update_call_params_during_conference(void) { add_calls_to_local_conference(lcs, marie, NULL, new_participants, TRUE); // Wait that the three participants are joined to the local conference, by checking the StreamsRunning states - BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallStreamsRunning, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &michelle->stat.number_of_LinphoneCallStreamsRunning, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallStreamsRunning, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallStreamsRunning, 6, 10000)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &laure->stat.number_of_LinphoneCallStreamsRunning, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &michelle->stat.number_of_LinphoneCallStreamsRunning, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallStreamsRunning, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &marie->stat.number_of_LinphoneCallStreamsRunning, 6, liblinphone_tester_sip_timeout)); LinphoneConference *marie_conference = linphone_core_get_conference(marie->lc); BC_ASSERT_PTR_NOT_NULL(marie_conference); @@ -10431,10 +10591,14 @@ static void register_again_during_conference(void) { BC_ASSERT_EQUAL(linphone_conference_get_participant_count(marie_conference), 3, int, "%d"); // Wait that the three participants are joined to the local conference, by checking the StreamsRunning states - BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneCallStreamsRunning, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &michelle->stat.number_of_LinphoneCallStreamsRunning, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallStreamsRunning, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallStreamsRunning, 6, 10000)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &laure->stat.number_of_LinphoneCallStreamsRunning, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &michelle->stat.number_of_LinphoneCallStreamsRunning, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallStreamsRunning, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &marie->stat.number_of_LinphoneCallStreamsRunning, 6, liblinphone_tester_sip_timeout)); LinphoneProxyConfig *proxyConfig = linphone_core_get_default_proxy_config(laure->lc); linphone_proxy_config_edit(proxyConfig); @@ -10450,8 +10614,10 @@ static void register_again_during_conference(void) { linphone_core_set_network_reachable(pauline->lc, FALSE); linphone_core_set_network_reachable(laure->lc, FALSE); linphone_core_set_network_reachable(marie->lc, FALSE); - BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneSubscriptionTerminated, 1, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneSubscriptionTerminated, 1, 10000)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &pauline->stat.number_of_LinphoneSubscriptionTerminated, 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &laure->stat.number_of_LinphoneSubscriptionTerminated, 1, liblinphone_tester_sip_timeout)); proxyConfig = linphone_core_get_default_proxy_config(laure->lc); linphone_proxy_config_edit(proxyConfig); @@ -10460,18 +10626,24 @@ static void register_again_during_conference(void) { linphone_core_set_network_reachable(marie->lc, TRUE); BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneRegistrationOk, - initial_marie_stats.number_of_LinphoneRegistrationOk + 1, 10000)); + initial_marie_stats.number_of_LinphoneRegistrationOk + 1, + liblinphone_tester_sip_timeout)); linphone_core_set_network_reachable(pauline->lc, TRUE); BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneRegistrationOk, - initial_pauline_stats.number_of_LinphoneRegistrationOk + 1, 10000)); + initial_pauline_stats.number_of_LinphoneRegistrationOk + 1, + liblinphone_tester_sip_timeout)); linphone_core_set_network_reachable(laure->lc, TRUE); BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneRegistrationOk, - initial_laure_stats.number_of_LinphoneRegistrationOk + 1, 10000)); + initial_laure_stats.number_of_LinphoneRegistrationOk + 1, + liblinphone_tester_sip_timeout)); // Wait for subscriptins to be resent - BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneSubscriptionActive, 5, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneSubscriptionActive, 2, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneSubscriptionActive, 2, 10000)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &marie->stat.number_of_LinphoneSubscriptionActive, 5, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &pauline->stat.number_of_LinphoneSubscriptionActive, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &laure->stat.number_of_LinphoneSubscriptionActive, 2, liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(linphone_conference_is_in(marie_conference)); BC_ASSERT_EQUAL(linphone_conference_get_participant_count(marie_conference), 3, int, "%d"); @@ -10527,10 +10699,11 @@ simple_conference_base2(LinphoneCoreManager *local_conf, bctbx_list_t *participa for (bctbx_list_t *it = participants; it; it = bctbx_list_next(it)) { LinphoneCoreManager *m = (LinphoneCoreManager *)bctbx_list_get_data(it); /* Wait that all participants have joined the local conference, by checking the StreamsRunning states*/ - BC_ASSERT_TRUE(wait_for_list(lcs, &m->stat.number_of_LinphoneCallStreamsRunning, 2, 10000)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &m->stat.number_of_LinphoneCallStreamsRunning, 2, liblinphone_tester_sip_timeout)); } - BC_ASSERT_TRUE( - wait_for_list(lcs, &local_conf->stat.number_of_LinphoneCallStreamsRunning, 2 * no_participants, 10000)); + BC_ASSERT_TRUE(wait_for_list(lcs, &local_conf->stat.number_of_LinphoneCallStreamsRunning, 2 * no_participants, + liblinphone_tester_sip_timeout)); LinphoneConference *l_conference = linphone_core_get_conference(local_conf->lc); BC_ASSERT_PTR_NOT_NULL(l_conference); @@ -10578,7 +10751,8 @@ simple_conference_base2(LinphoneCoreManager *local_conf, bctbx_list_t *participa 5000)); BC_ASSERT_TRUE(wait_for_list(lcs, &local_conf->stat.number_of_LinphoneSubscriptionTerminated, - initial_conf_stats.number_of_LinphoneSubscriptionTerminated + idx + 1, 10000)); + initial_conf_stats.number_of_LinphoneSubscriptionTerminated + idx + 1, + liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &m->stat.number_of_LinphoneSubscriptionTerminated, initial_participants_stats[idx].number_of_LinphoneSubscriptionTerminated + 1, 3000)); @@ -10596,14 +10770,17 @@ simple_conference_base2(LinphoneCoreManager *local_conf, bctbx_list_t *participa } BC_ASSERT_TRUE(wait_for_list(lcs, &local_conf->stat.number_of_LinphoneSubscriptionTerminated, - initial_conf_stats.number_of_LinphoneSubscriptionActive, 10000)); + initial_conf_stats.number_of_LinphoneSubscriptionActive, + liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &local_conf->stat.number_of_LinphoneConferenceStateTerminationPending, (initial_conf_stats.number_of_LinphoneConferenceStateTerminationPending + 1), 5000)); BC_ASSERT_TRUE(wait_for_list(lcs, &local_conf->stat.number_of_LinphoneConferenceStateTerminated, - (initial_conf_stats.number_of_LinphoneConferenceStateTerminated + 1), 10000)); + (initial_conf_stats.number_of_LinphoneConferenceStateTerminated + 1), + liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &local_conf->stat.number_of_LinphoneConferenceStateDeleted, - (initial_conf_stats.number_of_LinphoneConferenceStateDeleted + 1), 10000)); + (initial_conf_stats.number_of_LinphoneConferenceStateDeleted + 1), + liblinphone_tester_sip_timeout)); } BC_ASSERT_PTR_NULL(linphone_core_get_conference(local_conf->lc)); @@ -11043,17 +11220,17 @@ static void conference_with_calls_queued(LinphoneCoreManager *local_conf, static void conference_with_calls_queued_without_ice(void) { LinphoneCoreManager *marie = create_mgr_for_conference("marie_rc", TRUE, NULL); linphone_core_enable_conference_server(marie->lc, TRUE); - linphone_core_set_inc_timeout(marie->lc, 10000); + linphone_core_set_inc_timeout(marie->lc, liblinphone_tester_sip_timeout); LinphoneCoreManager *pauline = create_mgr_for_conference("pauline_tcp_rc", TRUE, NULL); - linphone_core_set_inc_timeout(pauline->lc, 10000); + linphone_core_set_inc_timeout(pauline->lc, liblinphone_tester_sip_timeout); LinphoneCoreManager *laure = create_mgr_for_conference(liblinphone_tester_ipv6_available() ? "laure_tcp_rc" : "laure_rc_udp", TRUE, NULL); - linphone_core_set_inc_timeout(laure->lc, 10000); + linphone_core_set_inc_timeout(laure->lc, liblinphone_tester_sip_timeout); LinphoneCoreManager *michelle = create_mgr_for_conference("michelle_rc", TRUE, NULL); - linphone_core_set_inc_timeout(michelle->lc, 10000); + linphone_core_set_inc_timeout(michelle->lc, liblinphone_tester_sip_timeout); bctbx_list_t *participants = NULL; participants = bctbx_list_append(participants, michelle); @@ -11076,7 +11253,7 @@ static void conference_with_calls_queued_with_ice(void) { // ICE is enabled LinphoneCoreManager *marie = create_mgr_for_conference("marie_rc", TRUE, NULL); linphone_core_enable_conference_server(marie->lc, TRUE); - linphone_core_set_inc_timeout(marie->lc, 10000); + linphone_core_set_inc_timeout(marie->lc, liblinphone_tester_sip_timeout); if (linphone_core_media_encryption_supported(marie->lc, mode)) { linphone_core_set_firewall_policy(marie->lc, LinphonePolicyUseIce); linphone_core_set_media_encryption(marie->lc, mode); @@ -11084,7 +11261,7 @@ static void conference_with_calls_queued_with_ice(void) { // ICE is enabled LinphoneCoreManager *pauline = create_mgr_for_conference("pauline_tcp_rc", TRUE, NULL); - linphone_core_set_inc_timeout(pauline->lc, 10000); + linphone_core_set_inc_timeout(pauline->lc, liblinphone_tester_sip_timeout); if (linphone_core_media_encryption_supported(pauline->lc, mode)) { linphone_core_set_firewall_policy(pauline->lc, LinphonePolicyUseIce); linphone_core_set_media_encryption(pauline->lc, mode); @@ -11093,7 +11270,7 @@ static void conference_with_calls_queued_with_ice(void) { // ICE is enabled LinphoneCoreManager *laure = create_mgr_for_conference(liblinphone_tester_ipv6_available() ? "laure_tcp_rc" : "laure_rc_udp", TRUE, NULL); - linphone_core_set_inc_timeout(laure->lc, 10000); + linphone_core_set_inc_timeout(laure->lc, liblinphone_tester_sip_timeout); if (linphone_core_media_encryption_supported(laure->lc, mode)) { linphone_core_set_firewall_policy(laure->lc, LinphonePolicyUseIce); linphone_core_set_media_encryption(laure->lc, mode); @@ -11101,7 +11278,7 @@ static void conference_with_calls_queued_with_ice(void) { // ICE is enabled LinphoneCoreManager *chloe = create_mgr_for_conference("chloe_rc", TRUE, NULL); - linphone_core_set_inc_timeout(chloe->lc, 10000); + linphone_core_set_inc_timeout(chloe->lc, liblinphone_tester_sip_timeout); if (linphone_core_media_encryption_supported(chloe->lc, mode)) { linphone_core_set_firewall_policy(chloe->lc, LinphonePolicyUseIce); linphone_core_set_media_encryption(chloe->lc, mode); @@ -11125,17 +11302,17 @@ static void conference_with_calls_queued_with_ice(void) { static void conference_with_back_to_back_call_accept_without_ice(void) { LinphoneCoreManager *marie = create_mgr_for_conference("marie_rc", TRUE, NULL); linphone_core_enable_conference_server(marie->lc, TRUE); - linphone_core_set_inc_timeout(marie->lc, 10000); + linphone_core_set_inc_timeout(marie->lc, liblinphone_tester_sip_timeout); LinphoneCoreManager *pauline = create_mgr_for_conference("pauline_tcp_rc", TRUE, NULL); - linphone_core_set_inc_timeout(pauline->lc, 10000); + linphone_core_set_inc_timeout(pauline->lc, liblinphone_tester_sip_timeout); LinphoneCoreManager *laure = create_mgr_for_conference(liblinphone_tester_ipv6_available() ? "laure_tcp_rc" : "laure_rc_udp", TRUE, NULL); - linphone_core_set_inc_timeout(laure->lc, 10000); + linphone_core_set_inc_timeout(laure->lc, liblinphone_tester_sip_timeout); LinphoneCoreManager *michelle = create_mgr_for_conference("michelle_rc", TRUE, NULL); - linphone_core_set_inc_timeout(michelle->lc, 10000); + linphone_core_set_inc_timeout(michelle->lc, liblinphone_tester_sip_timeout); bctbx_list_t *participants = NULL; participants = bctbx_list_append(participants, michelle); @@ -11158,7 +11335,7 @@ static void conference_with_back_to_back_call_accept_with_ice(void) { // ICE is enabled LinphoneCoreManager *marie = create_mgr_for_conference("marie_rc", TRUE, NULL); linphone_core_enable_conference_server(marie->lc, TRUE); - linphone_core_set_inc_timeout(marie->lc, 10000); + linphone_core_set_inc_timeout(marie->lc, liblinphone_tester_sip_timeout); if (linphone_core_media_encryption_supported(marie->lc, mode)) { linphone_core_set_firewall_policy(marie->lc, LinphonePolicyUseIce); linphone_core_set_media_encryption(marie->lc, mode); @@ -11166,7 +11343,7 @@ static void conference_with_back_to_back_call_accept_with_ice(void) { // ICE is enabled LinphoneCoreManager *pauline = create_mgr_for_conference("pauline_tcp_rc", TRUE, NULL); - linphone_core_set_inc_timeout(pauline->lc, 10000); + linphone_core_set_inc_timeout(pauline->lc, liblinphone_tester_sip_timeout); if (linphone_core_media_encryption_supported(pauline->lc, mode)) { linphone_core_set_firewall_policy(pauline->lc, LinphonePolicyUseIce); linphone_core_set_media_encryption(pauline->lc, mode); @@ -11175,7 +11352,7 @@ static void conference_with_back_to_back_call_accept_with_ice(void) { // ICE is enabled LinphoneCoreManager *laure = create_mgr_for_conference(liblinphone_tester_ipv6_available() ? "laure_tcp_rc" : "laure_rc_udp", TRUE, NULL); - linphone_core_set_inc_timeout(laure->lc, 10000); + linphone_core_set_inc_timeout(laure->lc, liblinphone_tester_sip_timeout); if (linphone_core_media_encryption_supported(laure->lc, mode)) { linphone_core_set_firewall_policy(laure->lc, LinphonePolicyUseIce); linphone_core_set_media_encryption(laure->lc, mode); @@ -11183,7 +11360,7 @@ static void conference_with_back_to_back_call_accept_with_ice(void) { // ICE is enabled LinphoneCoreManager *chloe = create_mgr_for_conference("chloe_rc", TRUE, NULL); - linphone_core_set_inc_timeout(chloe->lc, 10000); + linphone_core_set_inc_timeout(chloe->lc, liblinphone_tester_sip_timeout); if (linphone_core_media_encryption_supported(chloe->lc, mode)) { linphone_core_set_firewall_policy(chloe->lc, LinphonePolicyUseIce); linphone_core_set_media_encryption(chloe->lc, mode); @@ -11207,17 +11384,17 @@ static void conference_with_back_to_back_call_accept_with_ice(void) { static void conference_with_back_to_back_call_invite_accept_without_ice(void) { LinphoneCoreManager *marie = create_mgr_for_conference("marie_rc", TRUE, NULL); linphone_core_enable_conference_server(marie->lc, TRUE); - linphone_core_set_inc_timeout(marie->lc, 10000); + linphone_core_set_inc_timeout(marie->lc, liblinphone_tester_sip_timeout); LinphoneCoreManager *pauline = create_mgr_for_conference("pauline_tcp_rc", TRUE, NULL); - linphone_core_set_inc_timeout(pauline->lc, 10000); + linphone_core_set_inc_timeout(pauline->lc, liblinphone_tester_sip_timeout); LinphoneCoreManager *laure = create_mgr_for_conference(liblinphone_tester_ipv6_available() ? "laure_tcp_rc" : "laure_rc_udp", TRUE, NULL); - linphone_core_set_inc_timeout(laure->lc, 10000); + linphone_core_set_inc_timeout(laure->lc, liblinphone_tester_sip_timeout); LinphoneCoreManager *michelle = create_mgr_for_conference("michelle_rc", TRUE, NULL); - linphone_core_set_inc_timeout(michelle->lc, 10000); + linphone_core_set_inc_timeout(michelle->lc, liblinphone_tester_sip_timeout); bctbx_list_t *participants = NULL; participants = bctbx_list_append(participants, michelle); @@ -11241,7 +11418,7 @@ static void conference_with_back_to_back_call_invite_accept_with_ice(void) { // ICE is enabled LinphoneCoreManager* marie = create_mgr_for_conference( "marie_rc", TRUE, NULL); linphone_core_enable_conference_server(marie->lc,TRUE); - linphone_core_set_inc_timeout(marie->lc, 10000); + linphone_core_set_inc_timeout(marie->lc, liblinphone_tester_sip_timeout); if (linphone_core_media_encryption_supported(marie->lc,mode)) { linphone_core_set_firewall_policy(marie->lc,LinphonePolicyUseIce); linphone_core_set_media_encryption(marie->lc,mode); @@ -11249,7 +11426,7 @@ static void conference_with_back_to_back_call_invite_accept_with_ice(void) { // ICE is enabled LinphoneCoreManager* pauline = create_mgr_for_conference( "pauline_tcp_rc", TRUE, NULL); - linphone_core_set_inc_timeout(pauline->lc, 10000); + linphone_core_set_inc_timeout(pauline->lc, liblinphone_tester_sip_timeout); if (linphone_core_media_encryption_supported(pauline->lc,mode)) { linphone_core_set_firewall_policy(pauline->lc,LinphonePolicyUseIce); linphone_core_set_media_encryption(pauline->lc,mode); @@ -11257,7 +11434,7 @@ static void conference_with_back_to_back_call_invite_accept_with_ice(void) { // ICE is enabled LinphoneCoreManager* laure = create_mgr_for_conference( liblinphone_tester_ipv6_available() ? "laure_tcp_rc" : "laure_rc_udp", TRUE, NULL); - linphone_core_set_inc_timeout(laure->lc, 10000); + linphone_core_set_inc_timeout(laure->lc, liblinphone_tester_sip_timeout); if (linphone_core_media_encryption_supported(laure->lc,mode)) { linphone_core_set_firewall_policy(laure->lc,LinphonePolicyUseIce); linphone_core_set_media_encryption(laure->lc,mode); @@ -11265,7 +11442,7 @@ static void conference_with_back_to_back_call_invite_accept_with_ice(void) { // ICE is enabled LinphoneCoreManager* chloe = create_mgr_for_conference( "chloe_rc", TRUE, NULL); - linphone_core_set_inc_timeout(chloe->lc, 10000); + linphone_core_set_inc_timeout(chloe->lc, liblinphone_tester_sip_timeout); if (linphone_core_media_encryption_supported(chloe->lc,mode)) { linphone_core_set_firewall_policy(chloe->lc,LinphonePolicyUseIce); linphone_core_set_media_encryption(chloe->lc,mode); @@ -11290,17 +11467,17 @@ static void conference_with_back_to_back_call_invite_accept_with_ice(void) { static void back_to_back_conferences_same_core(void) { LinphoneCoreManager *marie = create_mgr_for_conference("marie_rc", TRUE, NULL); linphone_core_enable_conference_server(marie->lc, TRUE); - linphone_core_set_inc_timeout(marie->lc, 10000); + linphone_core_set_inc_timeout(marie->lc, liblinphone_tester_sip_timeout); LinphoneCoreManager *pauline = create_mgr_for_conference("pauline_tcp_rc", TRUE, NULL); - linphone_core_set_inc_timeout(pauline->lc, 10000); + linphone_core_set_inc_timeout(pauline->lc, liblinphone_tester_sip_timeout); LinphoneCoreManager *laure = create_mgr_for_conference(liblinphone_tester_ipv6_available() ? "laure_tcp_rc" : "laure_rc_udp", TRUE, NULL); - linphone_core_set_inc_timeout(laure->lc, 10000); + linphone_core_set_inc_timeout(laure->lc, liblinphone_tester_sip_timeout); LinphoneCoreManager *michelle = create_mgr_for_conference("michelle_rc", TRUE, NULL); - linphone_core_set_inc_timeout(michelle->lc, 10000); + linphone_core_set_inc_timeout(michelle->lc, liblinphone_tester_sip_timeout); bctbx_list_t *participants = NULL; participants = bctbx_list_append(participants, michelle); @@ -11329,17 +11506,17 @@ static void back_to_back_conferences_same_core(void) { static void back_to_back_conferences(void) { LinphoneCoreManager *marie = create_mgr_for_conference("marie_rc", TRUE, NULL); linphone_core_enable_conference_server(marie->lc, TRUE); - linphone_core_set_inc_timeout(marie->lc, 10000); + linphone_core_set_inc_timeout(marie->lc, liblinphone_tester_sip_timeout); LinphoneCoreManager *pauline = create_mgr_for_conference("pauline_tcp_rc", TRUE, NULL); - linphone_core_set_inc_timeout(pauline->lc, 10000); + linphone_core_set_inc_timeout(pauline->lc, liblinphone_tester_sip_timeout); LinphoneCoreManager *laure = create_mgr_for_conference(liblinphone_tester_ipv6_available() ? "laure_tcp_rc" : "laure_rc_udp", TRUE, NULL); - linphone_core_set_inc_timeout(laure->lc, 10000); + linphone_core_set_inc_timeout(laure->lc, liblinphone_tester_sip_timeout); LinphoneCoreManager *michelle = create_mgr_for_conference("michelle_rc", TRUE, NULL); - linphone_core_set_inc_timeout(michelle->lc, 10000); + linphone_core_set_inc_timeout(michelle->lc, liblinphone_tester_sip_timeout); bctbx_list_t *participants = NULL; participants = bctbx_list_append(participants, michelle); @@ -11368,17 +11545,17 @@ static void back_to_back_conferences(void) { static void try_to_create_second_conference_with_local_participant(void) { LinphoneCoreManager *marie = create_mgr_for_conference("marie_rc", TRUE, NULL); linphone_core_enable_conference_server(marie->lc, TRUE); - linphone_core_set_inc_timeout(marie->lc, 10000); + linphone_core_set_inc_timeout(marie->lc, liblinphone_tester_sip_timeout); LinphoneCoreManager *pauline = create_mgr_for_conference("pauline_tcp_rc", TRUE, NULL); - linphone_core_set_inc_timeout(pauline->lc, 10000); + linphone_core_set_inc_timeout(pauline->lc, liblinphone_tester_sip_timeout); LinphoneCoreManager *laure = create_mgr_for_conference(liblinphone_tester_ipv6_available() ? "laure_tcp_rc" : "laure_rc_udp", TRUE, NULL); - linphone_core_set_inc_timeout(laure->lc, 10000); + linphone_core_set_inc_timeout(laure->lc, liblinphone_tester_sip_timeout); LinphoneCoreManager *chloe = create_mgr_for_conference("chloe_rc", TRUE, NULL); - linphone_core_set_inc_timeout(chloe->lc, 10000); + linphone_core_set_inc_timeout(chloe->lc, liblinphone_tester_sip_timeout); bctbx_list_t *participants = NULL; participants = bctbx_list_append(participants, laure); @@ -11470,9 +11647,11 @@ static void try_to_create_second_conference_with_local_participant(void) { // Wait for calls to be terminated BC_ASSERT_TRUE(wait_for_list(lcs, &m->stat.number_of_LinphoneCallEnd, - lcm_stats[idx].number_of_LinphoneCallEnd + no_calls, 10000)); + lcm_stats[idx].number_of_LinphoneCallEnd + no_calls, + liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &m->stat.number_of_LinphoneCallReleased, - lcm_stats[idx].number_of_LinphoneCallReleased + no_calls, 10000)); + lcm_stats[idx].number_of_LinphoneCallReleased + no_calls, + liblinphone_tester_sip_timeout)); // Wait for all conferences to be terminated BC_ASSERT_TRUE(wait_for_list(lcs, &m->stat.number_of_LinphoneConferenceStateTerminationPending, @@ -11488,7 +11667,7 @@ static void try_to_create_second_conference_with_local_participant(void) { if ((m != marie) && event_log_enabled) { BC_ASSERT_TRUE(wait_for_list(lcs, &m->stat.number_of_LinphoneSubscriptionTerminated, lcm_stats[idx].number_of_LinphoneSubscriptionTerminated + no_conference, - 10000)); + liblinphone_tester_sip_timeout)); } LinphoneConference *conference = linphone_core_get_conference(c); @@ -11526,20 +11705,20 @@ static void try_to_create_second_conference_with_local_participant(void) { static void interleaved_conferences_base(bool_t add_participants_immediately_after_creation) { LinphoneCoreManager *marie = create_mgr_for_conference("marie_rc", TRUE, NULL); linphone_core_enable_conference_server(marie->lc, TRUE); - linphone_core_set_inc_timeout(marie->lc, 10000); + linphone_core_set_inc_timeout(marie->lc, liblinphone_tester_sip_timeout); LinphoneCoreManager *pauline = create_mgr_for_conference("pauline_tcp_rc", TRUE, NULL); - linphone_core_set_inc_timeout(pauline->lc, 10000); + linphone_core_set_inc_timeout(pauline->lc, liblinphone_tester_sip_timeout); LinphoneCoreManager *laure = create_mgr_for_conference(liblinphone_tester_ipv6_available() ? "laure_tcp_rc" : "laure_rc_udp", TRUE, NULL); - linphone_core_set_inc_timeout(laure->lc, 10000); + linphone_core_set_inc_timeout(laure->lc, liblinphone_tester_sip_timeout); LinphoneCoreManager *michelle = create_mgr_for_conference("michelle_rc", TRUE, NULL); - linphone_core_set_inc_timeout(michelle->lc, 10000); + linphone_core_set_inc_timeout(michelle->lc, liblinphone_tester_sip_timeout); LinphoneCoreManager *chloe = create_mgr_for_conference("chloe_rc", TRUE, NULL); - linphone_core_set_inc_timeout(chloe->lc, 10000); + linphone_core_set_inc_timeout(chloe->lc, liblinphone_tester_sip_timeout); bctbx_list_t *participants = NULL; participants = bctbx_list_append(participants, michelle); @@ -11645,9 +11824,11 @@ static void interleaved_conferences_base(bool_t add_participants_immediately_aft // Wait for calls to be terminated BC_ASSERT_TRUE(wait_for_list(lcs, &m->stat.number_of_LinphoneCallEnd, - lcm_stats[idx].number_of_LinphoneCallEnd + no_calls, 10000)); + lcm_stats[idx].number_of_LinphoneCallEnd + no_calls, + liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(lcs, &m->stat.number_of_LinphoneCallReleased, - lcm_stats[idx].number_of_LinphoneCallReleased + no_calls, 10000)); + lcm_stats[idx].number_of_LinphoneCallReleased + no_calls, + liblinphone_tester_sip_timeout)); // Wait for all conferences to be terminated BC_ASSERT_TRUE(wait_for_list(lcs, &m->stat.number_of_LinphoneConferenceStateTerminationPending, @@ -11663,7 +11844,7 @@ static void interleaved_conferences_base(bool_t add_participants_immediately_aft if ((m != marie) && event_log_enabled) { BC_ASSERT_TRUE(wait_for_list(lcs, &m->stat.number_of_LinphoneSubscriptionTerminated, lcm_stats[idx].number_of_LinphoneSubscriptionTerminated + no_conference, - 10000)); + liblinphone_tester_sip_timeout)); } LinphoneConference *conference = linphone_core_get_conference(c); @@ -11717,20 +11898,20 @@ static void interleaved_conference_creation_with_quick_participant_addition(void static void multiple_conferences_in_server_mode(void) { LinphoneCoreManager *marie = create_mgr_for_conference("marie_rc", TRUE, NULL); linphone_core_enable_conference_server(marie->lc, TRUE); - linphone_core_set_inc_timeout(marie->lc, 10000); + linphone_core_set_inc_timeout(marie->lc, liblinphone_tester_sip_timeout); LinphoneCoreManager *pauline = create_mgr_for_conference("pauline_tcp_rc", TRUE, NULL); - linphone_core_set_inc_timeout(pauline->lc, 10000); + linphone_core_set_inc_timeout(pauline->lc, liblinphone_tester_sip_timeout); LinphoneCoreManager *laure = create_mgr_for_conference(liblinphone_tester_ipv6_available() ? "laure_tcp_rc" : "laure_rc_udp", TRUE, NULL); - linphone_core_set_inc_timeout(laure->lc, 10000); + linphone_core_set_inc_timeout(laure->lc, liblinphone_tester_sip_timeout); LinphoneCoreManager *michelle = create_mgr_for_conference("michelle_rc", TRUE, NULL); - linphone_core_set_inc_timeout(michelle->lc, 10000); + linphone_core_set_inc_timeout(michelle->lc, liblinphone_tester_sip_timeout); LinphoneCoreManager *chloe = create_mgr_for_conference("chloe_rc", TRUE, NULL); - linphone_core_set_inc_timeout(chloe->lc, 10000); + linphone_core_set_inc_timeout(chloe->lc, liblinphone_tester_sip_timeout); bctbx_list_t *participants = NULL; participants = bctbx_list_append(participants, michelle); @@ -12020,8 +12201,10 @@ static void conference_mix_created_by_merging_video_calls_base(LinphoneConferenc int marie_calls = (int)bctbx_list_size(linphone_core_get_calls(marie->lc)); linphone_core_terminate_all_calls(marie->lc); - BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallEnd, marie_calls, 10000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallReleased, marie_calls, 10000)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &marie->stat.number_of_LinphoneCallEnd, marie_calls, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(lcs, &marie->stat.number_of_LinphoneCallReleased, marie_calls, liblinphone_tester_sip_timeout)); if (conf) { linphone_conference_unref(conf); diff --git a/tester/group_chat_tester.c b/tester/group_chat_tester.c index c9119af383b1e4fd0eb701adbc00882cabb11d22..ba0a4de43e4975b0c668c9b47700e861f4a2d1ac 100644 --- a/tester/group_chat_tester.c +++ b/tester/group_chat_tester.c @@ -1368,13 +1368,16 @@ static void group_chat_room_add_participant(void) { coresList = bctbx_list_concat(coresList, tmpCoresList); linphone_core_manager_start(pauline, TRUE); paulineCr = linphone_core_search_chat_room(pauline->lc, NULL, NULL, paulineAddr, NULL); + BC_ASSERT_PTR_NOT_NULL(paulineCr); linphone_address_unref(paulineAddr); // Pauline adds Chloe to the chat room participantsAddresses = NULL; participantsAddresses = bctbx_list_append(participantsAddresses, linphone_address_new(linphone_core_get_identity(chloe->lc))); - linphone_chat_room_add_participants(paulineCr, participantsAddresses); + if (paulineCr) { + linphone_chat_room_add_participants(paulineCr, participantsAddresses); + } bctbx_list_free_with_data(participantsAddresses, (bctbx_list_free_func)linphone_address_unref); participantsAddresses = NULL; @@ -7945,7 +7948,7 @@ static void participant_removed_then_added(void) { coresList = bctbx_list_concat(coresList, tmpCoresList); linphone_core_manager_start(pauline1, TRUE); - // Check that the chat room has correctly created on Laure's side and that the participants are added + // Check that the chat room has correctly created on Pauline's side and that the participants are added pauline1Cr = check_has_chat_room_client_side(coresList, pauline1, &initialPauline1Stats, confAddr, initialSubject, 2, FALSE); diff --git a/tester/ics-tester.cpp b/tester/ics-tester.cpp index bc6310c3a0a96a5691aed87d28008a7559fd5c07..69aadc122fbe3dd3fd94b5f7690ffe431bce968e 100644 --- a/tester/ics-tester.cpp +++ b/tester/ics-tester.cpp @@ -25,14 +25,14 @@ #include "chat/ics/ics.h" #include "chat/ics/parser/ics-parser.h" #include "conference/conference-info.h" +#include "conference/participant-info.h" #include "content/content-type.h" #include "content/content.h" #include "core/core.h" +#include "liblinphone_tester.h" #include "linphone/api/c-api.h" // TODO: Remove me later. #include "private.h" - -#include "liblinphone_tester.h" #include "tester_utils.h" // ============================================================================= @@ -125,15 +125,13 @@ static void parse_folded_example() { const auto &participants = confInfo->getParticipants(); BC_ASSERT_EQUAL(participants.size(), 3, size_t, "%0zu"); for (const auto &participant : participants) { - const auto &address = participant.first; - const auto ¶ms = participant.second; + const auto &address = participant->getAddress(); + const auto ¶ms = participant->getAllParameters(); size_t no_params = 0; bool found = false; if (*address == *Address::create("sip:jdoe@sip.example.org")) { no_params = 2; - for (const auto ¶m : params) { - const auto &name = param.first; - const auto &value = param.second; + for (const auto &[name, value] : params) { BC_ASSERT_TRUE((name.compare("RSVP") == 0) || (name.compare("X-PARAM") == 0)); if (name.compare("RSVP") == 0) { BC_ASSERT_STRING_EQUAL(value.c_str(), "TRUE"); @@ -145,9 +143,7 @@ static void parse_folded_example() { } } else if (*address == *Address::create("sip:pwhite@sip.example.org")) { no_params = 1; - for (const auto ¶m : params) { - const auto &name = param.first; - const auto &value = param.second; + for (const auto &[name, value] : params) { BC_ASSERT_TRUE((name.compare("ROLE") == 0)); if (name.compare("ROLE") == 0) { BC_ASSERT_STRING_EQUAL(value.c_str(), "CHAIR"); @@ -157,9 +153,7 @@ static void parse_folded_example() { } else if (*address == *Address::create("sip:tmurphy@sip.example.org")) { // no_params = 2; no_params = 1; - for (const auto ¶m : params) { - const auto &name = param.first; - const auto &value = param.second; + for (const auto &[name, value] : params) { BC_ASSERT_TRUE((name.compare("MEMBER") == 0) || (name.compare("X-PARAM") == 0)); if (name.compare("MEMBER") == 0) { BC_ASSERT_STRING_EQUAL(value.c_str(), "mailto:devs@example.com"); @@ -369,13 +363,13 @@ static void send_conference_invitations(bool_t enable_encryption, marie->identity, linphone_conference_info_get_organizer(conf_info_from_original_content))); BC_ASSERT_TRUE(linphone_address_weak_equal( conf_uri, linphone_conference_info_get_uri(conf_info_from_original_content))); - bctbx_list_t *participants = linphone_conference_info_get_participants(conf_info_from_original_content); + const bctbx_list_t *participants = + linphone_conference_info_get_participants(conf_info_from_original_content); if (add_participant_in_error) { BC_ASSERT_EQUAL(bctbx_list_size(participants), 3, size_t, "%zu"); } else { BC_ASSERT_EQUAL(bctbx_list_size(participants), 2, size_t, "%zu"); } - bctbx_list_free(participants); BC_ASSERT_EQUAL(linphone_conference_info_get_duration(conf_info_from_original_content), 120, int, "%d"); BC_ASSERT_TRUE(linphone_conference_info_get_date_time(conf_info_from_original_content) == conf_time); linphone_conference_info_unref(conf_info_from_original_content); @@ -423,13 +417,12 @@ static void send_conference_invitations(bool_t enable_encryption, linphone_conference_info_get_organizer(conf_info_from_content))); BC_ASSERT_TRUE( linphone_address_weak_equal(conf_uri, linphone_conference_info_get_uri(conf_info_from_content))); - bctbx_list_t *participants = linphone_conference_info_get_participants(conf_info_from_content); + const bctbx_list_t *participants = linphone_conference_info_get_participants(conf_info_from_content); if (add_participant_in_error) { BC_ASSERT_EQUAL(bctbx_list_size(participants), 3, size_t, "%zu"); } else { BC_ASSERT_EQUAL(bctbx_list_size(participants), 2, size_t, "%zu"); } - bctbx_list_free(participants); BC_ASSERT_EQUAL(linphone_conference_info_get_duration(conf_info_from_content), 120, int, "%d"); BC_ASSERT_TRUE(linphone_conference_info_get_date_time(conf_info_from_content) == conf_time); linphone_conference_info_unref(conf_info_from_content); diff --git a/tester/local_conference_edition_tester.cpp b/tester/local_conference_edition_tester.cpp index 06d65eb17b47f11e545c43e90f18c9f8cb26b306..9ca0d071357f8239f5bdb596b3e9a5e8c22a037d 100644 --- a/tester/local_conference_edition_tester.cpp +++ b/tester/local_conference_edition_tester.cpp @@ -30,13 +30,16 @@ static void edit_simple_conference_base(bool_t from_organizer, bool_t join, bool_t enable_encryption, bool_t server_restart, - LinphoneConferenceSecurityLevel security_level) { + LinphoneConferenceSecurityLevel security_level, + bool_t role_changed) { + Focus focus("chloe_rc"); { // to make sure focus is destroyed after clients. ClientConference marie("marie_rc", focus.getIdentity()); ClientConference pauline("pauline_rc", focus.getIdentity()); ClientConference laure("laure_tcp_rc", focus.getIdentity()); ClientConference michelle("michelle_rc", focus.getIdentity()); + ClientConference lise("lise_rc", focus.getIdentity()); LinphoneCoreManager *manager_editing = (from_organizer) ? marie.getCMgr() : laure.getCMgr(); linphone_core_enable_rtp_bundle(manager_editing->lc, enable_bundle_mode); @@ -54,6 +57,7 @@ static void edit_simple_conference_base(bool_t from_organizer, focus.registerAsParticipantDevice(pauline); focus.registerAsParticipantDevice(laure); focus.registerAsParticipantDevice(michelle); + focus.registerAsParticipantDevice(lise); setup_conference_info_cbs(marie.getCMgr()); linphone_core_set_file_transfer_server(marie.getLc(), file_transfer_url); @@ -82,6 +86,7 @@ static void edit_simple_conference_base(bool_t from_organizer, coresList = bctbx_list_append(coresList, pauline.getLc()); coresList = bctbx_list_append(coresList, laure.getLc()); coresList = bctbx_list_append(coresList, michelle.getLc()); + coresList = bctbx_list_append(coresList, lise.getLc()); std::list<LinphoneCoreManager *> invitedParticipants{pauline.getCMgr(), laure.getCMgr()}; @@ -91,7 +96,14 @@ static void edit_simple_conference_base(bool_t from_organizer, const char *initialSubject = "Test characters: <S-F12><S-F11><S-F6> £$%§"; const char *description = "Testing characters"; - LinphoneAddress *confAddr = create_conference_on_server(focus, marie, invitedParticipants, start_time, end_time, + std::map<LinphoneCoreManager *, LinphoneParticipantRole> participantList; + LinphoneParticipantRole role = LinphoneParticipantRoleSpeaker; + for (auto &p : invitedParticipants) { + participantList.insert(std::make_pair(p, role)); + role = (role == LinphoneParticipantRoleSpeaker) ? LinphoneParticipantRoleListener + : LinphoneParticipantRoleSpeaker; + } + LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, initialSubject, description, TRUE, security_level); BC_ASSERT_PTR_NOT_NULL(confAddr); @@ -99,37 +111,227 @@ static void edit_simple_conference_base(bool_t from_organizer, BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateCreated, 2, liblinphone_tester_sip_timeout)); - const char *subject = "Test characters: <S-F12><S-F11><S-F6> £$%§ (+edits)"; - const char *description2 = "Testing characters (+edits)"; - - stats manager_editing_stat = manager_editing->stat; - LinphoneAccount *editing_account = NULL; - if (use_default_account) { - editing_account = linphone_core_get_default_account(manager_editing->lc); - } else { - editing_account = linphone_core_lookup_known_account(manager_editing->lc, alternative_address); - } - BC_ASSERT_PTR_NOT_NULL(editing_account); - if (editing_account) { - LinphoneAccountParams *account_params = - linphone_account_params_clone(linphone_account_get_params(editing_account)); - linphone_account_params_enable_rtp_bundle(account_params, enable_bundle_mode); - linphone_account_set_params(editing_account, account_params); - linphone_account_params_unref(account_params); - } + char *conference_address_str = (confAddr) ? linphone_address_as_string(confAddr) : ms_strdup("<unknown>"); + std::list<LinphoneCoreManager *> participants{pauline.getCMgr(), laure.getCMgr()}; char *uid = NULL; unsigned int sequence = 0; - LinphoneConferenceInfo *info = linphone_core_find_conference_information_from_uri(marie.getLc(), confAddr); - if (BC_ASSERT_PTR_NOT_NULL(info)) { - uid = ms_strdup(linphone_conference_info_get_ics_uid(info)); + LinphoneConferenceInfo *conf_info = linphone_core_find_conference_information_from_uri(marie.getLc(), confAddr); + BC_ASSERT_PTR_NOT_NULL(conf_info); + if (conf_info) { + uid = ms_strdup(linphone_conference_info_get_ics_uid(conf_info)); BC_ASSERT_PTR_NOT_NULL(uid); - sequence = linphone_conference_info_get_ics_sequence(info); - linphone_conference_info_unref(info); + sequence = linphone_conference_info_get_ics_sequence(conf_info); + + ms_message("%s is trying to update conference %s - adding %s", linphone_core_get_identity(marie.getLc()), + conference_address_str, linphone_core_get_identity(lise.getLc())); + + participants.push_back(lise.getCMgr()); + + stats focus_stat = focus.getStats(); + stats marie_stat = marie.getStats(); + linphone_conference_info_set_subject(conf_info, initialSubject); + linphone_conference_info_set_description(conf_info, description); + LinphoneParticipantInfo *lise_participant_info = linphone_participant_info_new(lise.getCMgr()->identity); + linphone_participant_info_set_role(lise_participant_info, LinphoneParticipantRoleListener); + linphone_conference_info_add_participant_2(conf_info, lise_participant_info); + linphone_participant_info_unref(lise_participant_info); + + if (role_changed) { + LinphoneParticipantRole current_pauline_role = participantList[pauline.getCMgr()]; + LinphoneParticipantRole new_pauline_role = (current_pauline_role == LinphoneParticipantRoleListener) + ? LinphoneParticipantRoleSpeaker + : LinphoneParticipantRoleListener; + ms_message("%s is trying to update conference %s - changing role of %s from %s to %s", + linphone_core_get_identity(marie.getLc()), conference_address_str, + linphone_core_get_identity(pauline.getLc()), + linphone_participant_role_to_string(current_pauline_role), + linphone_participant_role_to_string(new_pauline_role)); + const LinphoneParticipantInfo *old_pauline_partipant_info = + linphone_conference_info_find_participant(conf_info, pauline.getCMgr()->identity); + BC_ASSERT_PTR_NOT_NULL(old_pauline_partipant_info); + if (old_pauline_partipant_info) { + LinphoneParticipantInfo *pauline_participant_info = + linphone_participant_info_clone(old_pauline_partipant_info); + linphone_participant_info_set_role(pauline_participant_info, new_pauline_role); + linphone_conference_info_update_participant(conf_info, pauline_participant_info); + linphone_participant_info_unref(pauline_participant_info); + } + participantList[pauline.getCMgr()] = new_pauline_role; + } + + const auto ics_participant_number = 3; + const bctbx_list_t *ics_participants = linphone_conference_info_get_participants(conf_info); + BC_ASSERT_EQUAL(bctbx_list_size(ics_participants), ics_participant_number, size_t, "%zu"); + + std::list<stats> participant_stats; + for (auto mgr : {focus.getCMgr(), marie.getCMgr(), laure.getCMgr(), pauline.getCMgr(), lise.getCMgr()}) { + participant_stats.push_back(mgr->stat); + } + + LinphoneConferenceScheduler *conference_scheduler = + linphone_core_create_conference_scheduler(marie.getLc()); + LinphoneConferenceSchedulerCbs *cbs = + linphone_factory_create_conference_scheduler_cbs(linphone_factory_get()); + linphone_conference_scheduler_cbs_set_state_changed(cbs, conference_scheduler_state_changed); + linphone_conference_scheduler_cbs_set_invitations_sent(cbs, conference_scheduler_invitations_sent); + linphone_conference_scheduler_add_callbacks(conference_scheduler, cbs); + linphone_conference_scheduler_cbs_unref(cbs); + linphone_conference_scheduler_set_info(conference_scheduler, conf_info); + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_ConferenceSchedulerStateUpdating, + marie_stat.number_of_ConferenceSchedulerStateUpdating + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_ConferenceSchedulerStateReady, + marie_stat.number_of_ConferenceSchedulerStateReady + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallIncomingReceived, + focus_stat.number_of_LinphoneCallIncomingReceived + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallStreamsRunning, + focus_stat.number_of_LinphoneCallStreamsRunning + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallEnd, + focus_stat.number_of_LinphoneCallEnd + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallReleased, + focus_stat.number_of_LinphoneCallReleased + 1, + liblinphone_tester_sip_timeout)); + + LinphoneChatRoomParams *chat_room_params = linphone_core_create_default_chat_room_params(marie.getLc()); + linphone_chat_room_params_set_backend(chat_room_params, LinphoneChatRoomBackendBasic); + linphone_conference_scheduler_send_invitations(conference_scheduler, chat_room_params); + linphone_chat_room_params_unref(chat_room_params); + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_ConferenceSchedulerInvitationsSent, + marie_stat.number_of_ConferenceSchedulerInvitationsSent + 1, + liblinphone_tester_sip_timeout)); + + for (auto mgr : {focus.getCMgr(), marie.getCMgr(), laure.getCMgr(), pauline.getCMgr(), lise.getCMgr()}) { + auto old_stats = participant_stats.front(); + if ((mgr != focus.getCMgr()) && (mgr != marie.getCMgr())) { + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneMessageReceived, + old_stats.number_of_LinphoneMessageReceived + 1, + liblinphone_tester_sip_timeout)); + if (!linphone_core_conference_ics_in_message_body_enabled(marie.getLc())) { + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneMessageReceivedWithFile, + old_stats.number_of_LinphoneMessageReceivedWithFile + 1, + liblinphone_tester_sip_timeout)); + } + + BC_ASSERT_PTR_NOT_NULL(mgr->stat.last_received_chat_message); + if (mgr->stat.last_received_chat_message != NULL) { + const string expected = ContentType::Icalendar.getMediaType(); + BC_ASSERT_STRING_EQUAL( + linphone_chat_message_get_content_type(mgr->stat.last_received_chat_message), + expected.c_str()); + } + + bctbx_list_t *participant_chat_room_participants = + bctbx_list_append(NULL, marie.getCMgr()->identity); + LinphoneChatRoom *pcr = linphone_core_search_chat_room(mgr->lc, NULL, mgr->identity, NULL, + participant_chat_room_participants); + bctbx_list_free(participant_chat_room_participants); + BC_ASSERT_PTR_NOT_NULL(pcr); + bctbx_list_t *chat_room_participants = bctbx_list_append(NULL, mgr->identity); + + LinphoneChatRoom *cr = linphone_core_search_chat_room( + marie.getLc(), NULL, marie.getCMgr()->identity, NULL, chat_room_participants); + bctbx_list_free(chat_room_participants); + BC_ASSERT_PTR_NOT_NULL(cr); + + BC_ASSERT_EQUAL((int)bctbx_list_size(linphone_core_get_chat_rooms(mgr->lc)), 1, int, "%d"); + + if (cr) { + LinphoneChatMessage *msg = linphone_chat_room_get_last_message_in_history(cr); + BC_ASSERT_PTR_NOT_NULL(msg); + + const bctbx_list_t *original_contents = linphone_chat_message_get_contents(msg); + BC_ASSERT_EQUAL((int)bctbx_list_size(original_contents), 1, int, "%d"); + LinphoneContent *original_content = (LinphoneContent *)bctbx_list_get_data(original_contents); + if (BC_ASSERT_PTR_NOT_NULL(original_content)) { + LinphoneConferenceInfo *conf_info_from_original_content = + linphone_factory_create_conference_info_from_icalendar_content(linphone_factory_get(), + original_content); + if (BC_ASSERT_PTR_NOT_NULL(conf_info_from_original_content)) { + BC_ASSERT_TRUE(linphone_address_weak_equal( + marie.getCMgr()->identity, + linphone_conference_info_get_organizer(conf_info_from_original_content))); + BC_ASSERT_TRUE(linphone_address_weak_equal( + confAddr, linphone_conference_info_get_uri(conf_info_from_original_content))); + + const bctbx_list_t *ics_participants = + linphone_conference_info_get_participants(conf_info_from_original_content); + BC_ASSERT_EQUAL(bctbx_list_size(ics_participants), ics_participant_number, size_t, + "%zu"); + + if (start_time > 0) { + BC_ASSERT_EQUAL((long long)linphone_conference_info_get_date_time( + conf_info_from_original_content), + (long long)start_time, long long, "%lld"); + if (end_time > 0) { + const int duration_m = + linphone_conference_info_get_duration(conf_info_from_original_content); + const int duration_s = duration_m * 60; + BC_ASSERT_EQUAL(duration_s, (int)(end_time - start_time), int, "%d"); + BC_ASSERT_EQUAL(duration_m, duration, int, "%d"); + } + } + if (initialSubject) { + BC_ASSERT_STRING_EQUAL( + linphone_conference_info_get_subject(conf_info_from_original_content), + initialSubject); + } else { + BC_ASSERT_PTR_NULL( + linphone_conference_info_get_subject(conf_info_from_original_content)); + } + if (description) { + BC_ASSERT_STRING_EQUAL( + linphone_conference_info_get_description(conf_info_from_original_content), + description); + } else { + BC_ASSERT_PTR_NULL( + linphone_conference_info_get_description(conf_info_from_original_content)); + } + BC_ASSERT_STRING_EQUAL( + linphone_conference_info_get_ics_uid(conf_info_from_original_content), uid); + const unsigned int ics_sequence = + linphone_conference_info_get_ics_sequence(conf_info_from_original_content); + int exp_sequence = 0; + if (mgr == lise.getCMgr()) { + exp_sequence = 0; + } else { + exp_sequence = (sequence + 1); + } + + BC_ASSERT_EQUAL(ics_sequence, exp_sequence, int, "%d"); + + LinphoneConferenceInfoState exp_state = LinphoneConferenceInfoStateNew; + + if (mgr == lise.getCMgr()) { + exp_state = LinphoneConferenceInfoStateNew; + } else { + exp_state = LinphoneConferenceInfoStateUpdated; + } + BC_ASSERT_EQUAL( + (int)linphone_conference_info_get_state(conf_info_from_original_content), + (int)exp_state, int, "%d"); + + linphone_conference_info_unref(conf_info_from_original_content); + } + } + linphone_chat_message_unref(msg); + } + } + participant_stats.pop_front(); + } + linphone_conference_info_unref(conf_info); + linphone_conference_scheduler_unref(conference_scheduler); } + ms_free(uid); + uid = NULL; if (join) { - std::list<LinphoneCoreManager *> participants{pauline.getCMgr()}; std::list<LinphoneCoreManager *> conferenceMgrs{focus.getCMgr(), marie.getCMgr(), pauline.getCMgr()}; std::list<LinphoneCoreManager *> members{marie.getCMgr(), pauline.getCMgr()}; @@ -190,14 +392,28 @@ static void edit_simple_conference_base(bool_t from_organizer, focus_stat.number_of_participant_devices_joined + 2, liblinphone_tester_sip_timeout)); - wait_for_conference_streams({focus, marie, pauline, laure, michelle}, conferenceMgrs, focus.getCMgr(), - members, confAddr, TRUE); + std::map<LinphoneCoreManager *, LinphoneParticipantRole> memberList; + for (const auto &member : members) { + try { + const auto &participantInfo = participantList.at(member); + memberList.insert(std::make_pair(member, participantInfo)); + } catch (std::out_of_range &) { + if (member == marie.getCMgr()) { + memberList.insert(std::make_pair(marie.getCMgr(), LinphoneParticipantRoleSpeaker)); + } else { + ms_fatal("Unable to find active participant %s in the participant list", + linphone_core_get_identity(member->lc)); + } + } + } + wait_for_conference_streams({focus, marie, pauline, laure, michelle, lise}, conferenceMgrs, focus.getCMgr(), + memberList, confAddr, TRUE); LinphoneConference *fconference = linphone_core_search_conference_2(focus.getLc(), confAddr); BC_ASSERT_PTR_NOT_NULL(fconference); // wait bit more to detect side effect if any - CoreManagerAssert({focus, marie, pauline, laure, michelle}).waitUntil(chrono::seconds(2), [] { + CoreManagerAssert({focus, marie, pauline, laure, michelle, lise}).waitUntil(chrono::seconds(2), [] { return false; }); @@ -328,17 +544,42 @@ static void edit_simple_conference_base(bool_t from_organizer, coresList = bctbx_list_append(coresList, focus.getLc()); } + const char *subject = "Test characters: <S-F12><S-F11><S-F6> £$%§ (+edits)"; + const char *description2 = "Testing characters (+edits)"; + + stats manager_editing_stat = manager_editing->stat; + LinphoneAccount *editing_account = NULL; + if (use_default_account) { + editing_account = linphone_core_get_default_account(manager_editing->lc); + } else { + editing_account = linphone_core_lookup_known_account(manager_editing->lc, alternative_address); + } + BC_ASSERT_PTR_NOT_NULL(editing_account); + if (editing_account) { + LinphoneAccountParams *account_params = + linphone_account_params_clone(linphone_account_get_params(editing_account)); + linphone_account_params_enable_rtp_bundle(account_params, enable_bundle_mode); + linphone_account_set_params(editing_account, account_params); + linphone_account_params_unref(account_params); + } + + LinphoneConferenceInfo *info = linphone_core_find_conference_information_from_uri(marie.getLc(), confAddr); + if (BC_ASSERT_PTR_NOT_NULL(info)) { + uid = ms_strdup(linphone_conference_info_get_ics_uid(info)); + BC_ASSERT_PTR_NOT_NULL(uid); + sequence = linphone_conference_info_get_ics_sequence(info); + linphone_conference_info_unref(info); + } + bool_t add = TRUE; for (int attempt = 0; attempt < 3; attempt++) { - char *conference_address_str = (confAddr) ? linphone_address_as_string(confAddr) : ms_strdup("<unknown>"); ms_message("%s is trying to update conference %s - attempt %0d - %s %s", linphone_core_get_identity(manager_editing->lc), conference_address_str, attempt, (add) ? "adding" : "removing", linphone_core_get_identity(michelle.getLc())); - ms_free(conference_address_str); stats focus_stat = focus.getStats(); - std::list<LinphoneCoreManager *> participants{pauline.getCMgr(), laure.getCMgr()}; + std::list<LinphoneCoreManager *> participants{pauline.getCMgr(), laure.getCMgr(), lise.getCMgr()}; LinphoneConferenceInfo *conf_info = linphone_core_find_conference_information_from_uri(manager_editing->lc, confAddr); BC_ASSERT_PTR_NOT_NULL(conf_info); @@ -352,14 +593,13 @@ static void edit_simple_conference_base(bool_t from_organizer, linphone_conference_info_remove_participant(conf_info, michelle.getCMgr()->identity); } - const auto ics_participant_number = ((add) ? 3 : 2) + ((join) ? 1 : 0); - bctbx_list_t *ics_participants = linphone_conference_info_get_participants(conf_info); + const auto ics_participant_number = ((add) ? 4 : 3) + ((join) ? 1 : 0); + const bctbx_list_t *ics_participants = linphone_conference_info_get_participants(conf_info); BC_ASSERT_EQUAL(bctbx_list_size(ics_participants), ics_participant_number, size_t, "%zu"); - bctbx_list_free(ics_participants); std::list<stats> participant_stats; - for (auto mgr : - {focus.getCMgr(), marie.getCMgr(), laure.getCMgr(), pauline.getCMgr(), michelle.getCMgr()}) { + for (auto mgr : {focus.getCMgr(), marie.getCMgr(), laure.getCMgr(), pauline.getCMgr(), + michelle.getCMgr(), lise.getCMgr()}) { participant_stats.push_back(mgr->stat); } @@ -408,8 +648,8 @@ static void edit_simple_conference_base(bool_t from_organizer, manager_editing_stat.number_of_ConferenceSchedulerInvitationsSent + 1, liblinphone_tester_sip_timeout)); - for (auto mgr : - {focus.getCMgr(), marie.getCMgr(), laure.getCMgr(), pauline.getCMgr(), michelle.getCMgr()}) { + for (auto mgr : {focus.getCMgr(), marie.getCMgr(), laure.getCMgr(), pauline.getCMgr(), + michelle.getCMgr(), lise.getCMgr()}) { auto old_stats = participant_stats.front(); if ((mgr != focus.getCMgr()) && (mgr != manager_editing)) { BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneMessageReceived, @@ -443,12 +683,21 @@ static void edit_simple_conference_base(bool_t from_organizer, bctbx_list_free(chat_room_participants); BC_ASSERT_PTR_NOT_NULL(cr); + int number_chat_rooms = 0; + if (mgr == marie.getCMgr()) { + number_chat_rooms = 3; + } else if (from_organizer || (mgr == michelle.getCMgr())) { + number_chat_rooms = 1; + } else { + number_chat_rooms = 2; + } + BC_ASSERT_EQUAL((int)bctbx_list_size(linphone_core_get_chat_rooms(mgr->lc)), - (from_organizer || (mgr == michelle.getCMgr())) ? 1 : 2, int, "%d"); + number_chat_rooms, int, "%d"); if (cr) { LinphoneChatMessage *msg = linphone_chat_room_get_last_message_in_history(cr); - linphone_chat_room_unref(cr); + BC_ASSERT_PTR_NOT_NULL(msg); const bctbx_list_t *original_contents = linphone_chat_message_get_contents(msg); BC_ASSERT_EQUAL((int)bctbx_list_size(original_contents), 1, int, "%d"); @@ -466,11 +715,10 @@ static void edit_simple_conference_base(bool_t from_organizer, confAddr, linphone_conference_info_get_uri(conf_info_from_original_content))); - bctbx_list_t *ics_participants = + const bctbx_list_t *ics_participants = linphone_conference_info_get_participants(conf_info_from_original_content); BC_ASSERT_EQUAL(bctbx_list_size(ics_participants), ics_participant_number, size_t, "%zu"); - bctbx_list_free(ics_participants); if (start_time > 0) { BC_ASSERT_EQUAL((long long)linphone_conference_info_get_date_time( @@ -511,6 +759,8 @@ static void edit_simple_conference_base(bool_t from_organizer, } else { exp_sequence = 1; } + } else if (mgr == lise.getCMgr()) { + exp_sequence = (attempt + 1); } else { exp_sequence = (sequence + attempt + 1); } @@ -518,7 +768,6 @@ static void edit_simple_conference_base(bool_t from_organizer, BC_ASSERT_EQUAL(ics_sequence, exp_sequence, int, "%d"); LinphoneConferenceInfoState exp_state = LinphoneConferenceInfoStateNew; - if (mgr == michelle.getCMgr()) { if (add) { exp_state = LinphoneConferenceInfoStateNew; @@ -548,8 +797,8 @@ static void edit_simple_conference_base(bool_t from_organizer, } linphone_conference_scheduler_unref(conference_scheduler); - for (auto mgr : - {focus.getCMgr(), marie.getCMgr(), laure.getCMgr(), pauline.getCMgr(), michelle.getCMgr()}) { + for (auto mgr : {focus.getCMgr(), marie.getCMgr(), laure.getCMgr(), pauline.getCMgr(), + michelle.getCMgr(), lise.getCMgr()}) { LinphoneConferenceInfo *info = linphone_core_find_conference_information_from_uri(mgr->lc, confAddr); if (!use_default_account && (mgr == michelle.getCMgr())) { @@ -574,10 +823,9 @@ static void edit_simple_conference_base(bool_t from_organizer, unsigned int exp_sequence = 0; LinphoneConferenceInfoState exp_state = LinphoneConferenceInfoStateNew; - if ((mgr == focus.getCMgr()) || !use_default_account) { + if (mgr == focus.getCMgr()) { exp_sequence = 0; - exp_state = use_default_account ? LinphoneConferenceInfoStateUpdated - : LinphoneConferenceInfoStateNew; + exp_state = LinphoneConferenceInfoStateUpdated; } else { if (mgr == michelle.getCMgr()) { if (add) { @@ -585,6 +833,9 @@ static void edit_simple_conference_base(bool_t from_organizer, } else { exp_state = LinphoneConferenceInfoStateCancelled; } + } else if (mgr == lise.getCMgr()) { + exp_state = use_default_account ? LinphoneConferenceInfoStateUpdated + : LinphoneConferenceInfoStateNew; } else { exp_state = LinphoneConferenceInfoStateUpdated; } @@ -594,14 +845,17 @@ static void edit_simple_conference_base(bool_t from_organizer, } else { exp_sequence = 1; } + } else if (mgr == lise.getCMgr()) { + exp_sequence = use_default_account ? (attempt + 1) : 0; } else { - exp_sequence = (sequence + attempt + 1); + exp_sequence = use_default_account ? (sequence + attempt + 1) : 1; } } check_conference_info(mgr, confAddr, marie.getCMgr(), - ((use_default_account && add) ? 3 : 2) + ((join) ? 1 : 0), start_time, - duration, exp_subject, exp_description, exp_sequence, exp_state, - security_level); + ((use_default_account && add) ? 4 : 3) + + ((join || (mgr == focus.getCMgr())) ? 1 : 0), + start_time, duration, exp_subject, exp_description, exp_sequence, + exp_state, security_level); if (mgr != focus.getCMgr()) { for (auto &p : participants) { @@ -609,10 +863,14 @@ static void edit_simple_conference_base(bool_t from_organizer, // If not using the default account (which was used to create the conference), the // conference scheduler errors out and Michelle is not added if ((use_default_account) || (p != michelle.getCMgr())) { - if ((!use_default_account) || (p == michelle.getCMgr())) { + if (!use_default_account) { + exp_participant_sequence = (p == lise.getCMgr()) ? 0 : 1; + } else if (p == michelle.getCMgr()) { exp_participant_sequence = 0; - } else { + } else if (p == lise.getCMgr()) { exp_participant_sequence = attempt + 1; + } else { + exp_participant_sequence = attempt + 2; } linphone_conference_info_check_participant(info, p->identity, exp_participant_sequence); @@ -620,9 +878,9 @@ static void edit_simple_conference_base(bool_t from_organizer, } int exp_organizer_sequence = 0; if (use_default_account) { - exp_organizer_sequence = attempt + 1; + exp_organizer_sequence = attempt + 2; } else { - exp_organizer_sequence = 0; + exp_organizer_sequence = 1; } linphone_conference_info_check_organizer(info, exp_organizer_sequence); } @@ -636,6 +894,7 @@ static void edit_simple_conference_base(bool_t from_organizer, add = !add; } ms_free(uid); + ms_free(conference_address_str); linphone_address_unref(alternative_address); linphone_address_unref(confAddr); bctbx_list_free(coresList); @@ -643,26 +902,30 @@ static void edit_simple_conference_base(bool_t from_organizer, } static void organizer_edits_simple_conference(void) { - edit_simple_conference_base(TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, LinphoneConferenceSecurityLevelNone); + edit_simple_conference_base(TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, LinphoneConferenceSecurityLevelNone, FALSE); } static void organizer_edits_simple_conference_using_different_account(void) { - edit_simple_conference_base(TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, LinphoneConferenceSecurityLevelNone); + edit_simple_conference_base(TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, LinphoneConferenceSecurityLevelNone, FALSE); } static void organizer_edits_simple_conference_while_active(void) { - edit_simple_conference_base(TRUE, TRUE, FALSE, TRUE, TRUE, FALSE, LinphoneConferenceSecurityLevelNone); + edit_simple_conference_base(TRUE, TRUE, FALSE, TRUE, TRUE, FALSE, LinphoneConferenceSecurityLevelNone, FALSE); } static void participant_edits_simple_conference(void) { - edit_simple_conference_base(FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, LinphoneConferenceSecurityLevelNone); + edit_simple_conference_base(FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, LinphoneConferenceSecurityLevelNone, FALSE); } static void participant_edits_simple_conference_using_different_account(void) { - edit_simple_conference_base(FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, LinphoneConferenceSecurityLevelNone); + edit_simple_conference_base(FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, LinphoneConferenceSecurityLevelNone, FALSE); } static void organizer_edits_simple_conference_with_server_restart(void) { - edit_simple_conference_base(TRUE, TRUE, FALSE, FALSE, TRUE, TRUE, LinphoneConferenceSecurityLevelNone); + edit_simple_conference_base(TRUE, TRUE, FALSE, FALSE, TRUE, TRUE, LinphoneConferenceSecurityLevelNone, FALSE); +} + +static void conference_edition_with_participant_role_changed(void) { + edit_simple_conference_base(TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, LinphoneConferenceSecurityLevelNone, TRUE); } static void conference_edition_with_simultaneous_participant_add_remove_base(bool_t codec_mismatch) { @@ -722,7 +985,14 @@ static void conference_edition_with_simultaneous_participant_add_remove_base(boo const char *description = "Testing characters"; LinphoneConferenceSecurityLevel security_level = LinphoneConferenceSecurityLevelNone; - LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participants, start_time, end_time, + std::map<LinphoneCoreManager *, LinphoneParticipantRole> participantList; + LinphoneParticipantRole role = LinphoneParticipantRoleSpeaker; + for (auto &p : participants) { + participantList.insert(std::make_pair(p, role)); + role = (role == LinphoneParticipantRoleSpeaker) ? LinphoneParticipantRoleListener + : LinphoneParticipantRoleSpeaker; + } + LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, initialSubject, description, TRUE, security_level); BC_ASSERT_PTR_NOT_NULL(confAddr); @@ -761,9 +1031,8 @@ static void conference_edition_with_simultaneous_participant_add_remove_base(boo linphone_conference_info_add_participant(conf_info, michelle.getCMgr()->identity); linphone_conference_info_remove_participant(conf_info, laure.getCMgr()->identity); - bctbx_list_t *ics_participants = linphone_conference_info_get_participants(conf_info); + const bctbx_list_t *ics_participants = linphone_conference_info_get_participants(conf_info); BC_ASSERT_EQUAL(bctbx_list_size(ics_participants), participants.size(), size_t, "%zu"); - bctbx_list_free(ics_participants); std::list<stats> participant_stats; for (auto mgr : {focus.getCMgr(), marie.getCMgr(), laure.getCMgr(), pauline.getCMgr(), michelle.getCMgr()}) { @@ -843,7 +1112,7 @@ static void conference_edition_with_simultaneous_participant_add_remove_base(boo if (cr) { LinphoneChatMessage *msg = linphone_chat_room_get_last_message_in_history(cr); - linphone_chat_room_unref(cr); + BC_ASSERT_PTR_NOT_NULL(msg); const bctbx_list_t *original_contents = linphone_chat_message_get_contents(msg); BC_ASSERT_EQUAL((int)bctbx_list_size(original_contents), 1, int, "%d"); @@ -859,10 +1128,9 @@ static void conference_edition_with_simultaneous_participant_add_remove_base(boo BC_ASSERT_TRUE(linphone_address_weak_equal( confAddr, linphone_conference_info_get_uri(conf_info_from_original_content))); - bctbx_list_t *ics_participants = + const bctbx_list_t *ics_participants = linphone_conference_info_get_participants(conf_info_from_original_content); BC_ASSERT_EQUAL(bctbx_list_size(ics_participants), participants.size(), size_t, "%zu"); - bctbx_list_free(ics_participants); if (start_time > 0) { BC_ASSERT_EQUAL( @@ -998,7 +1266,14 @@ static void conference_cancelled_through_edit_base(bool_t server_restart) { const char *description = "Testing characters"; LinphoneConferenceSecurityLevel security_level = LinphoneConferenceSecurityLevelNone; - LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participants, start_time, end_time, + std::map<LinphoneCoreManager *, LinphoneParticipantRole> participantList; + LinphoneParticipantRole role = LinphoneParticipantRoleSpeaker; + for (auto &p : participants) { + participantList.insert(std::make_pair(p, role)); + role = (role == LinphoneParticipantRoleSpeaker) ? LinphoneParticipantRoleListener + : LinphoneParticipantRoleSpeaker; + } + LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, initialSubject, description, TRUE, security_level); BC_ASSERT_PTR_NOT_NULL(confAddr); @@ -1106,7 +1381,7 @@ static void conference_cancelled_through_edit_base(bool_t server_restart) { if (cr) { LinphoneChatMessage *msg = linphone_chat_room_get_last_message_in_history(cr); - linphone_chat_room_unref(cr); + BC_ASSERT_PTR_NOT_NULL(msg); const bctbx_list_t *original_contents = linphone_chat_message_get_contents(msg); BC_ASSERT_EQUAL((int)bctbx_list_size(original_contents), 1, int, "%d"); @@ -1122,10 +1397,9 @@ static void conference_cancelled_through_edit_base(bool_t server_restart) { BC_ASSERT_TRUE(linphone_address_weak_equal( confAddr, linphone_conference_info_get_uri(conf_info_from_original_content))); - bctbx_list_t *ics_participants = + const bctbx_list_t *ics_participants = linphone_conference_info_get_participants(conf_info_from_original_content); BC_ASSERT_EQUAL(bctbx_list_size(ics_participants), 3, size_t, "%zu"); - bctbx_list_free(ics_participants); if (start_time > 0) { BC_ASSERT_EQUAL( @@ -1219,9 +1493,8 @@ static void conference_cancelled_through_edit_base(bool_t server_restart) { linphone_conference_info_set_subject(conf_info, subject); linphone_conference_info_set_description(conf_info, description2); - bctbx_list_t *ics_participants = linphone_conference_info_get_participants(conf_info); + const bctbx_list_t *ics_participants = linphone_conference_info_get_participants(conf_info); BC_ASSERT_EQUAL(bctbx_list_size(ics_participants), 3, size_t, "%zu"); - bctbx_list_free(ics_participants); conference_scheduler = linphone_core_create_conference_scheduler(marie.getLc()); linphone_conference_scheduler_set_account(conference_scheduler, editing_account); @@ -1304,7 +1577,7 @@ static void conference_cancelled_through_edit_base(bool_t server_restart) { if (cr) { LinphoneChatMessage *msg = linphone_chat_room_get_last_message_in_history(cr); - linphone_chat_room_unref(cr); + BC_ASSERT_PTR_NOT_NULL(msg); const bctbx_list_t *original_contents = linphone_chat_message_get_contents(msg); BC_ASSERT_EQUAL((int)bctbx_list_size(original_contents), 1, int, "%d"); @@ -1320,10 +1593,9 @@ static void conference_cancelled_through_edit_base(bool_t server_restart) { BC_ASSERT_TRUE(linphone_address_weak_equal( confAddr, linphone_conference_info_get_uri(conf_info_from_original_content))); - bctbx_list_t *ics_participants = + const bctbx_list_t *ics_participants = linphone_conference_info_get_participants(conf_info_from_original_content); BC_ASSERT_EQUAL(bctbx_list_size(ics_participants), 0, size_t, "%zu"); - bctbx_list_free(ics_participants); if (start_time > 0) { BC_ASSERT_EQUAL( @@ -1432,6 +1704,8 @@ static test_t local_conference_conference_edition_tests[] = { TEST_NO_TAG("Conference cancelled through edit", LinphoneTest::conference_cancelled_through_edit), TEST_NO_TAG("Conference edition with simultanoues participant added removed", LinphoneTest::conference_edition_with_simultaneous_participant_add_remove), + TEST_NO_TAG("Conference edition with participant role changed", + LinphoneTest::conference_edition_with_participant_role_changed), TEST_NO_TAG("Conference edition with organizer codec mismatch", LinphoneTest::conference_edition_with_organizer_codec_mismatch), TEST_NO_TAG("Create conference with server restart (conference cancelled)", diff --git a/tester/local_conference_tester.cpp b/tester/local_conference_tester.cpp new file mode 100644 index 0000000000000000000000000000000000000000..10243dc51c2ab5b64ecee9d606839734b94664e8 --- /dev/null +++ b/tester/local_conference_tester.cpp @@ -0,0 +1,17485 @@ +/* + * copyright (c) 2010-2022 belledonne communications sarl. + * + * This file is part of Liblinphone + * (see https://gitlab.linphone.org/BC/public/liblinphone). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <map> + +#include "bctoolbox/crypto.h" +#include "bctoolbox/ownership.hh" +#include <bctoolbox/defs.h> + +#include "address/address.h" +#include "bctoolbox/crypto.h" +#include "c-wrapper/c-wrapper.h" +#include "chat/chat-room/chat-room.h" +#include "chat/chat-room/server-group-chat-room-p.h" +#include "conference/participant.h" +#include "core/core.h" +#include "liblinphone_tester++.h" +#include "liblinphone_tester.h" +#include "linphone/api/c-chat-room-params.h" +#include "linphone/api/c-chat-room.h" +#include "linphone/api/c-participant-info.h" +#include "linphone/api/c-types.h" +#include "linphone/core.h" +#include "linphone/wrapper_utils.h" +#include "shared_tester_functions.h" +#include "tester_utils.h" + +#if __clang__ || ((__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4) +#pragma GCC diagnostic push +#endif +#ifdef _MSC_VER +#pragma warning(disable : 4996) +#endif + +using namespace LinphonePrivate; +using namespace std; +using namespace Linphone::Tester; +using namespace ownership; + +void setup_conference_info_cbs(LinphoneCoreManager *mgr) { + // Needed to send the ICS + linphone_core_set_file_transfer_server(mgr->lc, file_transfer_url); +} + +namespace LinphoneTest { + +class BcAssert { +public: + void addCustomIterate(const std::function<void()> &iterate) { + mIterateFuncs.push_back(iterate); + } + bool waitUntil(std::chrono::duration<double> timeout, const std::function<bool()> &condition) { + auto start = std::chrono::steady_clock::now(); + + bool_t result; + while (!(result = condition()) && (std::chrono::steady_clock::now() - start < timeout)) { + for (const std::function<void()> &iterate : mIterateFuncs) { + iterate(); + } + ms_usleep(100); + } + return result; + } + bool wait(const std::function<bool()> &condition) { + return waitUntil(std::chrono::seconds(10), condition); + } + +private: + std::list<std::function<void()>> mIterateFuncs; +}; + +class CoreAssert : public BcAssert { +public: + CoreAssert(std::initializer_list<std::shared_ptr<Core>> cores) { + for (shared_ptr<Core> core : cores) { + addCustomIterate([core] { linphone_core_iterate(L_GET_C_BACK_PTR(core)); }); + } + } +}; + +class ClientConference; + +class ConfCoreManager : public CoreManager { +public: + ConfCoreManager(std::string rc) : CoreManager(rc.c_str()) { + mMgr->user_info = this; + } + + ConfCoreManager(std::string rc, const std::function<void()> &preStart) + : CoreManager(owned(linphone_core_manager_create(rc.c_str()))), mPreStart(preStart) { + mMgr->user_info = this; + mPreStart(); + start(true); + } + + void reStart(bool check_for_proxies = true) { + linphone_core_manager_reinit(mMgr.get()); + mPreStart(); + start(check_for_proxies); + } + + void configureCoreForConference(const Address &factoryUri) { + _configure_core_for_conference(mMgr.get(), factoryUri.toC()); + } + void setupMgrForConference(const char *conferenceVersion = nullptr) { + setup_mgr_for_conference(mMgr.get(), conferenceVersion); + } + BorrowedMut<LinphoneChatRoom> searchChatRoom(const LinphoneAddress *localAddr, + const LinphoneAddress *remoteAddr, + const bctbx_list_t *participants = nullptr, + const LinphoneChatRoomParams *params = nullptr) { + return borrowed_mut(linphone_core_search_chat_room(mMgr->lc, params, localAddr, remoteAddr, participants)); + } + LinphoneProxyConfig *getDefaultProxyConfig() const { + return linphone_core_get_default_proxy_config(mMgr->lc); + } + stats &getStats() const { + return mMgr->stat; + } + LinphoneCoreManager *getCMgr() { + return mMgr.get(); + }; + BorrowedMut<LinphoneCore> getLc() const { + return borrowed_mut(mMgr->lc); + }; + +private: + const std::function<void()> mPreStart = [] { return; }; +}; + +class CoreManagerAssert : public BcAssert { +public: + CoreManagerAssert(std::initializer_list<std::reference_wrapper<CoreManager>> coreMgrs) : BcAssert() { + for (CoreManager &coreMgr : coreMgrs) { + addCustomIterate([&coreMgr] { coreMgr.iterate(); }); + } + } + + CoreManagerAssert(std::list<std::reference_wrapper<CoreManager>> coreMgrs) : BcAssert() { + for (CoreManager &coreMgr : coreMgrs) { + addCustomIterate([&coreMgr] { coreMgr.iterate(); }); + } + } +}; +class Focus; + +/*Core manager acting as a client*/ +class ClientConference : public ConfCoreManager { +public: + ClientConference(std::string rc, Address factoryUri, bool encrypted = false) + : ConfCoreManager(rc, + [this, factoryUri, encrypted] { + configureCoreForConference(factoryUri); + _configure_core_for_audio_video_conference(mMgr.get(), factoryUri.toC()); + setupMgrForConference(); + LinphoneCoreCbs *cbs = linphone_factory_create_core_cbs(linphone_factory_get()); + linphone_core_cbs_set_chat_room_state_changed(cbs, core_chat_room_state_changed); + linphone_core_cbs_set_chat_room_subject_changed(cbs, core_chat_room_subject_changed); + linphone_core_add_callbacks(getLc(), cbs); + linphone_core_cbs_unref(cbs); + if (encrypted) { + set_lime_server_and_curve(25519, mMgr.get()); + } + }), + mFocus(nullptr) { + } + + void deleteChatRoomSync(AbstractChatRoom &chatroom) { + linphone_core_delete_chat_room(getLc(), L_GET_C_BACK_PTR(&chatroom)); + CoreManagerAssert({*mFocus, *this}).wait([&chatroom] { + return chatroom.getState() == ChatRoom::State::Deleted; + }); + } + + ~ClientConference() { + for (auto chatRoom : getCore().getChatRooms()) { + deleteChatRoomSync(*chatRoom); + } + if (mMgr->user_info) { + ms_free(mMgr->user_info); + } + } + + static LinphoneChatMessage *sendTextMsg(LinphoneChatRoom *cr, const std::string text) { + LinphoneChatMessage *msg = nullptr; + if (cr && !text.empty()) { + lInfo() << " Chat room " << L_GET_CPP_PTR_FROM_C_OBJECT(cr)->getConferenceId() + << " is sending message with text " << text; + msg = linphone_chat_room_create_message_from_utf8(cr, text.c_str()); + BC_ASSERT_PTR_NOT_NULL(msg); + LinphoneChatMessageCbs *cbs = linphone_chat_message_get_callbacks(msg); + linphone_chat_message_cbs_set_msg_state_changed(cbs, liblinphone_tester_chat_message_msg_state_changed); + linphone_chat_message_send(msg); + } + return msg; + } + + friend Focus; + +protected: + void setFocus(BorrowedMut<CoreManager> myFocus) { + mFocus = myFocus; + } + +private: + BorrowedMut<CoreManager> mFocus; +}; + +/* Core manager acting as a focus*/ +class Focus : public ConfCoreManager { +public: + Focus(std::string rc) : ConfCoreManager(rc, [this] { linphone_core_enable_conference_server(getLc(), TRUE); }) { + configureFocus(); + } + ~Focus() { + CoreManagerAssert({*this}).waitUntil(chrono::seconds(1), [] { return false; }); + } + + void registerAsParticipantDevice(ClientConference &otherMgr) { + const LinphoneAddress *cAddr = linphone_proxy_config_get_contact(otherMgr.getDefaultProxyConfig()); + Address participantDevice = Address::toCpp(const_cast<LinphoneAddress *>(cAddr))->getUri(); + Address participant = participantDevice.getUriWithoutGruu(); + mParticipantDevices.insert({participant, participantDevice}); + // to allow client conference to delete chatroom in its destructor + otherMgr.setFocus(borrowed_mut(this)); + } + + void subscribeParticipantDevice(const LinphoneAddress *conferenceAddress, + const LinphoneAddress *participantDevice) { + auto cr = searchChatRoom(conferenceAddress, conferenceAddress); + BC_ASSERT_PTR_NOT_NULL(cr); + // CALL_CHAT_ROOM_CBS(cr, ParticipantRegistrationSubscriptionRequested, + // participant_registration_subscription_requested, cr, participantDevice) + _linphone_chat_room_notify_participant_registration_subscription_requested(cr, participantDevice); + } + + void notifyParticipantDeviceRegistration(const LinphoneAddress *conferenceAddress, + const LinphoneAddress *participantDevice) { + auto cr = searchChatRoom(conferenceAddress, conferenceAddress); + BC_ASSERT_PTR_NOT_NULL(cr); + linphone_chat_room_notify_participant_device_registration(cr, participantDevice); + } + + void reStart(bool check_for_proxies = TRUE) { + ConfCoreManager::reStart(check_for_proxies); + configureFocus(); + } + +private: + static void server_core_chat_room_conference_address_generation(LinphoneChatRoom *cr) { + Focus *focus = + (Focus *)(((LinphoneCoreManager *)linphone_core_get_user_data(linphone_chat_room_get_core(cr)))->user_info); + char config_id[6]; + belle_sip_random_token(config_id, sizeof(config_id)); + LinphoneAddress *conference_address = + linphone_address_clone(linphone_proxy_config_get_contact(focus->getDefaultProxyConfig())); + linphone_address_set_uri_param(conference_address, "conf-id", config_id); + linphone_chat_room_set_conference_address(cr, conference_address); + linphone_address_unref(conference_address); + } + + static void + server_core_chat_room_state_changed(LinphoneCore *core, LinphoneChatRoom *cr, LinphoneChatRoomState state) { + Focus *focus = (Focus *)(((LinphoneCoreManager *)linphone_core_get_user_data(core))->user_info); + switch (state) { + case LinphoneChatRoomStateInstantiated: { + LinphoneChatRoomCbs *cbs = linphone_factory_create_chat_room_cbs(linphone_factory_get()); + linphone_chat_room_cbs_set_participant_registration_subscription_requested( + cbs, chat_room_participant_registration_subscription_requested); + linphone_chat_room_cbs_set_conference_address_generation( + cbs, server_core_chat_room_conference_address_generation); + setup_chat_room_callbacks(cbs); + linphone_chat_room_add_callbacks(cr, cbs); + linphone_chat_room_cbs_set_user_data(cbs, focus); + linphone_chat_room_cbs_unref(cbs); + break; + } + default: + break; + } + } + + static void chat_room_participant_registration_subscription_requested(LinphoneChatRoom *cr, + const LinphoneAddress *participantAddr) { + BC_ASSERT_PTR_NOT_NULL(participantAddr); + if (participantAddr) { + Address participant = Address::toCpp(const_cast<LinphoneAddress *>(participantAddr))->getUri(); + BC_ASSERT_TRUE(participant.isValid()); + if (participant.isValid()) { + Focus *focus = + (Focus *)(linphone_chat_room_cbs_get_user_data(linphone_chat_room_get_current_callbacks(cr))); + bctbx_list_t *devices = NULL; + auto participantRange = focus->mParticipantDevices.equal_range(participant); + for (auto participantIt = participantRange.first; participantIt != participantRange.second; + participantIt++) { + LinphoneAddress *deviceAddr = linphone_address_new(participantIt->second.toString().c_str()); + LinphoneParticipantDeviceIdentity *identity = + linphone_factory_create_participant_device_identity(linphone_factory_get(), deviceAddr, ""); + linphone_participant_device_identity_set_capability_descriptor( + identity, linphone_core_get_linphone_specs(linphone_chat_room_get_core(cr))); + devices = bctbx_list_append(devices, identity); + linphone_address_unref(deviceAddr); + } + linphone_chat_room_set_participant_devices(cr, participant.toC(), devices); + bctbx_list_free_with_data(devices, (bctbx_list_free_func)belle_sip_object_unref); + } + } + } + + void configureFocus() { + LinphoneCoreCbs *cbs = linphone_core_get_first_callbacks(getLc()); + linphone_config_set_int(linphone_core_get_config(getLc()), "misc", "hide_empty_chat_rooms", 0); + linphone_config_set_int(linphone_core_get_config(getLc()), "sip", "reject_duplicated_calls", 0); + linphone_config_set_int(linphone_core_get_config(getLc()), "misc", "hide_chat_rooms_from_removed_proxies", 0); + linphone_core_enable_rtp_bundle(getLc(), TRUE); + + LinphoneAccount *account = linphone_core_get_default_account(getLc()); + const LinphoneAccountParams *account_params = linphone_account_get_params(account); + LinphoneAccountParams *new_account_params = linphone_account_params_clone(account_params); + linphone_account_params_enable_rtp_bundle(new_account_params, TRUE); + linphone_account_params_set_conference_factory_uri(new_account_params, getIdentity().toString().c_str()); + linphone_account_set_params(account, new_account_params); + linphone_account_params_unref(new_account_params); + BC_ASSERT_TRUE(linphone_account_params_rtp_bundle_enabled(linphone_account_get_params(account))); + + linphone_core_cbs_set_subscription_state_changed(cbs, linphone_subscription_state_change); + linphone_core_cbs_set_chat_room_state_changed(cbs, server_core_chat_room_state_changed); + // linphone_core_cbs_set_refer_received(cbs, linphone_conference_server_refer_received); + } + + std::multimap<Address, Address> mParticipantDevices; +}; + +void sendEphemeralMessageInAdminMode(Focus &focus, + ClientConference &sender, + ClientConference &recipient, + LinphoneChatRoom *senderCr, + LinphoneChatRoom *recipientCr, + const std::string basicText, + const int noMsg) { + + bctbx_list_t *coresList = bctbx_list_append(NULL, focus.getLc()); + coresList = bctbx_list_append(coresList, sender.getLc()); + coresList = bctbx_list_append(coresList, recipient.getLc()); + + bctbx_list_t *senderHistory = linphone_chat_room_get_history(senderCr, 0); + auto initialSenderMessages = (int)bctbx_list_size(senderHistory); + bctbx_list_free_with_data(senderHistory, (bctbx_list_free_func)linphone_chat_message_unref); + + bctbx_list_t *recipientHistory = linphone_chat_room_get_history(recipientCr, 0); + auto initialRecipientMessages = (int)bctbx_list_size(recipientHistory); + bctbx_list_free_with_data(recipientHistory, (bctbx_list_free_func)linphone_chat_message_unref); + + int initialUnreadMessages = linphone_chat_room_get_unread_messages_count(recipientCr); + + auto sender_stat = sender.getStats(); + auto recipient_stat = recipient.getStats(); + + std::list<LinphoneChatMessage *> messages; + // Marie sends messages + for (int i = 0; i < noMsg; i++) { + const std::string text = basicText + std::to_string(i); + messages.push_back(_send_message_ephemeral(senderCr, text.c_str(), TRUE)); + } + + senderHistory = linphone_chat_room_get_history(senderCr, 0); + BC_ASSERT_EQUAL((int)bctbx_list_size(senderHistory), (noMsg + initialSenderMessages), int, "%i"); + set_ephemeral_cbs(senderHistory); + + BC_ASSERT_TRUE(wait_for_list(coresList, &recipient.getStats().number_of_LinphoneMessageReceived, + recipient_stat.number_of_LinphoneMessageReceived + noMsg, 11000)); + + // Check that the message has been delivered to Pauline + for (const auto &msg : messages) { + BC_ASSERT_TRUE(CoreManagerAssert({focus, sender, recipient}).wait([msg] { + return (linphone_chat_message_get_state(msg) == LinphoneChatMessageStateDelivered); + })); + } + + BC_ASSERT_TRUE(CoreManagerAssert({focus, sender, recipient}).wait([&, recipientCr] { + return linphone_chat_room_get_unread_messages_count(recipientCr) == (noMsg + initialUnreadMessages); + })); + + recipientHistory = linphone_chat_room_get_history(recipientCr, 0); + BC_ASSERT_EQUAL((int)bctbx_list_size(recipientHistory), (noMsg + initialRecipientMessages), int, "%i"); + set_ephemeral_cbs(recipientHistory); + + BC_ASSERT_TRUE(wait_for_list(coresList, &sender.getStats().number_of_LinphoneMessageDeliveredToUser, + sender_stat.number_of_LinphoneMessageDeliveredToUser + noMsg, + liblinphone_tester_sip_timeout)); + + // Pauline marks the message as read, check that the state is now displayed on Marie's side + linphone_chat_room_mark_as_read(recipientCr); + BC_ASSERT_TRUE(wait_for_list(coresList, &sender.getStats().number_of_LinphoneMessageDisplayed, + sender_stat.number_of_LinphoneMessageDisplayed + noMsg, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &sender.getStats().number_of_LinphoneChatRoomEphemeralTimerStarted, + sender_stat.number_of_LinphoneChatRoomEphemeralTimerStarted + noMsg, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &recipient.getStats().number_of_LinphoneChatRoomEphemeralTimerStarted, + recipient_stat.number_of_LinphoneChatRoomEphemeralTimerStarted + noMsg, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &sender.getStats().number_of_LinphoneMessageEphemeralTimerStarted, + sender_stat.number_of_LinphoneMessageEphemeralTimerStarted + noMsg, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &recipient.getStats().number_of_LinphoneMessageEphemeralTimerStarted, + recipient_stat.number_of_LinphoneMessageEphemeralTimerStarted + noMsg, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &sender.getStats().number_of_LinphoneChatRoomEphemeralDeleted, + sender_stat.number_of_LinphoneChatRoomEphemeralDeleted + noMsg, 15000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &recipient.getStats().number_of_LinphoneChatRoomEphemeralDeleted, + recipient_stat.number_of_LinphoneChatRoomEphemeralDeleted + noMsg, 15000)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &sender.getStats().number_of_LinphoneMessageEphemeralDeleted, + sender_stat.number_of_LinphoneMessageEphemeralDeleted + noMsg, 15000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &recipient.getStats().number_of_LinphoneMessageEphemeralDeleted, + recipient_stat.number_of_LinphoneMessageEphemeralDeleted + noMsg, 15000)); + + bctbx_list_free_with_data(recipientHistory, (bctbx_list_free_func)linphone_chat_message_unref); + bctbx_list_free_with_data(senderHistory, (bctbx_list_free_func)linphone_chat_message_unref); + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, sender, recipient}).waitUntil(chrono::seconds(2), [] { return false; }); + + recipientHistory = linphone_chat_room_get_history(recipientCr, 0); + BC_ASSERT_EQUAL((int)bctbx_list_size(recipientHistory), initialRecipientMessages, int, "%i"); + senderHistory = linphone_chat_room_get_history(senderCr, 0); + BC_ASSERT_EQUAL((int)bctbx_list_size(senderHistory), initialSenderMessages, int, "%i"); + + for (auto &msg : messages) { + linphone_chat_message_unref(msg); + } + + bctbx_list_free_with_data(recipientHistory, (bctbx_list_free_func)linphone_chat_message_unref); + bctbx_list_free_with_data(senderHistory, (bctbx_list_free_func)linphone_chat_message_unref); + bctbx_list_free(coresList); +} + +bool checkChatroom(Focus &focus, const ConfCoreManager &core, const time_t baseJoiningTime) { + const auto &chatRooms = core.getCore().getChatRooms(); + if (chatRooms.size() < 1) { + return false; + } + + for (const auto &chatRoom : chatRooms) { + auto participants = chatRoom->getParticipants(); + if (focus.getLc() != core.getLc()) { + participants.push_back(chatRoom->getMe()); + } + for (const auto &participant : participants) { + for (const auto &device : participant->getDevices()) { + if (device->getState() != ParticipantDevice::State::Present) { + return false; + } + if (linphone_participant_device_is_in_conference(device->toC()) == FALSE) { + return false; + } + if ((baseJoiningTime >= 0) && + (linphone_participant_device_get_time_of_joining(device->toC()) >= baseJoiningTime)) { + return false; + } + } + } + } + return true; +} + +static void group_chat_room_creation_server(void) { + Focus focus("chloe_rc"); + { // to make sure focus is destroyed after clients. + ClientConference marie("marie_rc", focus.getIdentity()); + ClientConference pauline("pauline_rc", focus.getIdentity()); + ClientConference laure("laure_tcp_rc", focus.getIdentity()); + + focus.registerAsParticipantDevice(marie); + focus.registerAsParticipantDevice(pauline); + focus.registerAsParticipantDevice(laure); + + bctbx_list_t *coresList = bctbx_list_append(NULL, focus.getLc()); + coresList = bctbx_list_append(coresList, marie.getLc()); + coresList = bctbx_list_append(coresList, pauline.getLc()); + coresList = bctbx_list_append(coresList, laure.getLc()); + + Address paulineAddr = pauline.getIdentity(); + Address laureAddr = laure.getIdentity(); + bctbx_list_t *participantsAddresses = bctbx_list_append(NULL, linphone_address_ref(paulineAddr.toC())); + participantsAddresses = bctbx_list_append(participantsAddresses, linphone_address_ref(laureAddr.toC())); + + stats initialMarieStats = marie.getStats(); + stats initialPaulineStats = pauline.getStats(); + stats initialLaureStats = laure.getStats(); + + // Marie creates a new group chat room + const char *initialSubject = "Colleagues @work"; + LinphoneChatRoom *marieCr = + create_chat_room_client_side(coresList, marie.getCMgr(), &initialMarieStats, participantsAddresses, + initialSubject, FALSE, LinphoneChatRoomEphemeralModeDeviceManaged); + const LinphoneAddress *confAddr = linphone_chat_room_get_conference_address(marieCr); + + // Check that the chat room is correctly created on Pauline's side and that the participants are added + LinphoneChatRoom *paulineCr = check_creation_chat_room_client_side( + coresList, pauline.getCMgr(), &initialPaulineStats, confAddr, initialSubject, 2, FALSE); + LinphoneChatRoom *laureCr = check_creation_chat_room_client_side(coresList, laure.getCMgr(), &initialLaureStats, + confAddr, initialSubject, 2, FALSE); + + // Marie now changes the subject + const char *newSubject = "Let's go drink a beer #party"; + linphone_chat_room_set_subject(marieCr, newSubject); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_subject_changed, + initialMarieStats.number_of_subject_changed + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_subject_changed, + initialPaulineStats.number_of_subject_changed + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_subject_changed, + initialLaureStats.number_of_subject_changed + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_STRING_EQUAL(linphone_chat_room_get_subject(marieCr), newSubject); + BC_ASSERT_STRING_EQUAL(linphone_chat_room_get_subject(paulineCr), newSubject); + BC_ASSERT_STRING_EQUAL(linphone_chat_room_get_subject(laureCr), newSubject); + + // Bring Laure offline and remove her from the chat room + // As Laure is offline, she is not notified of the removal + linphone_core_set_network_reachable(laure.getLc(), FALSE); + BC_ASSERT_FALSE(linphone_core_is_network_reachable(laure.getLc())); + CoreManagerAssert({focus, marie, pauline, laure}).waitUntil(std::chrono::seconds(1), [] { return false; }); + LinphoneParticipant *laureParticipant = linphone_chat_room_find_participant(marieCr, laureAddr.toC()); + BC_ASSERT_PTR_NOT_NULL(laureParticipant); + linphone_chat_room_remove_participant(marieCr, laureParticipant); + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_participants_removed, + initialMarieStats.number_of_participants_removed + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_participants_removed, + initialPaulineStats.number_of_participants_removed + 1, + liblinphone_tester_sip_timeout)); + + // Check that Laure's conference is not destroyed + BC_ASSERT_EQUAL(laure.getStats().number_of_LinphoneConferenceStateTerminated, + initialLaureStats.number_of_LinphoneConferenceStateTerminated, int, "%d"); + + coresList = bctbx_list_remove(coresList, focus.getLc()); + // Restart flexisip + focus.reStart(); + coresList = bctbx_list_append(coresList, focus.getLc()); + + for (auto chatRoom : focus.getCore().getChatRooms()) { + + BC_ASSERT_EQUAL(linphone_chat_room_get_nb_participants(L_GET_C_BACK_PTR(chatRoom)), 3, int, "%d"); + } + + BC_ASSERT_FALSE(wait_for_list(coresList, &laure.getStats().number_of_LinphoneConferenceStateTerminated, + initialLaureStats.number_of_LinphoneConferenceStateTerminated + 1, 3000)); + + // Laure comes back online and its chatroom is expected to be deleted + linphone_core_set_network_reachable(laure.getLc(), TRUE); + LinphoneAddress *laureDeviceAddress = linphone_address_clone( + linphone_proxy_config_get_contact(linphone_core_get_default_proxy_config(laure.getLc()))); + // Notify chat room that a participant has registered + focus.notifyParticipantDeviceRegistration(linphone_chat_room_get_conference_address(marieCr), + laureDeviceAddress); + linphone_address_unref(laureDeviceAddress); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_LinphoneConferenceStateTerminated, + initialLaureStats.number_of_LinphoneConferenceStateTerminated + 1, + liblinphone_tester_sip_timeout)); + + for (auto chatRoom : focus.getCore().getChatRooms()) { + BC_ASSERT_EQUAL(linphone_chat_room_get_nb_participants(L_GET_C_BACK_PTR(chatRoom)), 3, int, "%d"); + } + + linphone_chat_room_leave(paulineCr); + } +} + +static void group_chat_room_server_deletion(void) { + Focus focus("chloe_rc"); + { // to make sure focus is destroyed after clients. + ClientConference marie("marie_rc", focus.getIdentity()); + ClientConference pauline("pauline_rc", focus.getIdentity()); + + focus.registerAsParticipantDevice(marie); + focus.registerAsParticipantDevice(pauline); + + bctbx_list_t *coresList = bctbx_list_append(NULL, focus.getLc()); + coresList = bctbx_list_append(coresList, marie.getLc()); + coresList = bctbx_list_append(coresList, pauline.getLc()); + Address paulineAddr = pauline.getIdentity(); + bctbx_list_t *participantsAddresses = bctbx_list_append(NULL, linphone_address_ref(paulineAddr.toC())); + + stats initialMarieStats = marie.getStats(); + stats initialPaulineStats = pauline.getStats(); + + // Marie creates a new group chat room + const char *initialSubject = "Colleagues #together"; + LinphoneChatRoom *marieCr = + create_chat_room_client_side(coresList, marie.getCMgr(), &initialMarieStats, participantsAddresses, + initialSubject, FALSE, LinphoneChatRoomEphemeralModeDeviceManaged); + const LinphoneAddress *confAddr = linphone_chat_room_get_conference_address(marieCr); + + // Check that the chat room is correctly created on Pauline's side and that the participants are added + LinphoneChatRoom *paulineCr = check_creation_chat_room_client_side( + coresList, pauline.getCMgr(), &initialPaulineStats, confAddr, initialSubject, 1, FALSE); + + /*BC_ASSERT_TRUE(*/ CoreManagerAssert({focus, marie, pauline}).wait([&focus] { + for (auto chatRoom : focus.getCore().getChatRooms()) { + for (auto participant : chatRoom->getParticipants()) { + for (auto device : participant->getDevices()) + if (device->getState() != ParticipantDevice::State::Present) { + return false; + } + } + } + return true; + }) /*)*/; + + LinphoneChatMessage *msg = linphone_chat_room_create_message_from_utf8(marieCr, "message blabla"); + linphone_chat_message_send(msg); + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline}).wait([msg] { + return (linphone_chat_message_get_state(msg) == LinphoneChatMessageStateDelivered); + })); + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline}).wait([paulineCr] { + return linphone_chat_room_get_unread_messages_count(paulineCr) == 1; + })); + + for (auto chatRoom : focus.getCore().getChatRooms()) { + for (auto participant : chatRoom->getParticipants()) { + // force deletion by removing devices + std::shared_ptr<Address> participantAddress = participant->getAddress(); + linphone_chat_room_set_participant_devices(L_GET_C_BACK_PTR(chatRoom), participantAddress->toC(), NULL); + } + } + + // wait until chatroom is deleted server side + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline}).wait([&focus] { + return focus.getCore().getChatRooms().size() == 0; + })); + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline}).waitUntil(chrono::seconds(2), [] { return false; }); + + // Clean db from chat room + linphone_chat_message_unref(msg); + + bctbx_list_free(coresList); + } +} + +static void group_chat_room_server_deletion_with_rmt_lst_event_handler(void) { + Focus focus("chloe_rc"); + { // to make sure focus is destroyed after clients. + ClientConference marie("marie_rc", focus.getIdentity()); + ClientConference pauline("pauline_rc", focus.getIdentity()); + + focus.registerAsParticipantDevice(marie); + focus.registerAsParticipantDevice(pauline); + + bctbx_list_t *coresList = bctbx_list_append(NULL, focus.getLc()); + coresList = bctbx_list_append(coresList, marie.getLc()); + coresList = bctbx_list_append(coresList, pauline.getLc()); + Address paulineAddr = pauline.getIdentity(); + bctbx_list_t *participantsAddresses = bctbx_list_append(NULL, linphone_address_ref(paulineAddr.toC())); + + stats initialMarieStats = marie.getStats(); + stats initialPaulineStats = pauline.getStats(); + + // Marie creates a new group chat room + const char *initialSubject = "Colleagues (characters: $ £ çà )"; + LinphoneChatRoom *marieCr = + create_chat_room_client_side(coresList, marie.getCMgr(), &initialMarieStats, participantsAddresses, + initialSubject, FALSE, LinphoneChatRoomEphemeralModeDeviceManaged); + const LinphoneAddress *confAddr = linphone_chat_room_get_conference_address(marieCr); + + // Check that the chat room is correctly created on Pauline's side and that the participants are added + LinphoneChatRoom *paulineCr = check_creation_chat_room_client_side( + coresList, pauline.getCMgr(), &initialPaulineStats, confAddr, initialSubject, 1, FALSE); + + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline}).wait([&focus] { + for (auto chatRoom : focus.getCore().getChatRooms()) { + for (auto participant : chatRoom->getParticipants()) { + for (auto device : participant->getDevices()) + if (device->getState() != ParticipantDevice::State::Present) { + return false; + } + } + } + return true; + })); + + LinphoneChatMessage *msg = linphone_chat_room_create_message_from_utf8(marieCr, "message blabla"); + linphone_chat_message_send(msg); + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline}).wait([msg] { + return (linphone_chat_message_get_state(msg) == LinphoneChatMessageStateDelivered); + })); + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline}).wait([paulineCr] { + return linphone_chat_room_get_unread_messages_count(paulineCr) == 1; + })); + + // now with simulate foregound/backgroud switch to get a remote event handler list instead of a simple remote + // event handler + linphone_core_enter_background(pauline.getLc()); + linphone_config_set_bool(linphone_core_get_config(pauline.getLc()), "misc", + "conference_event_package_force_full_state", TRUE); + CoreManagerAssert({focus, marie, pauline}).waitUntil(std::chrono::seconds(1), [] { return false; }); + + unsigned int paulineCrNo = (unsigned int)bctbx_list_size(linphone_core_get_chat_rooms(pauline.getLc())); + coresList = bctbx_list_remove(coresList, pauline.getLc()); + pauline.reStart(); + coresList = bctbx_list_append(coresList, pauline.getLc()); + + // Wait for chat rooms to be recovered from the main DB + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneConferenceStateCreated, + paulineCrNo, liblinphone_tester_sip_timeout)); + char *paulineDeviceIdentity = linphone_core_get_device_identity(pauline.getLc()); + LinphoneAddress *paulineLocalAddr = linphone_address_new(paulineDeviceIdentity); + bctbx_free(paulineDeviceIdentity); + paulineCr = pauline.searchChatRoom(paulineLocalAddr, confAddr); + linphone_address_unref(paulineLocalAddr); + BC_ASSERT_PTR_NOT_NULL(paulineCr); + + CoreManagerAssert({focus, marie, pauline}).waitUntil(std::chrono::seconds(1), [] { return false; }); + + CoreManagerAssert({focus, marie}).waitUntil(std::chrono::seconds(2), [] { return false; }); + + for (auto chatRoom : focus.getCore().getChatRooms()) { + for (auto participant : chatRoom->getParticipants()) { + // force deletion by removing devices + std::shared_ptr<Address> participantAddress = participant->getAddress(); + linphone_chat_room_set_participant_devices(L_GET_C_BACK_PTR(chatRoom), participantAddress->toC(), NULL); + } + } + + // wait until chatroom is deleted server side + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline}).wait([&focus] { + return focus.getCore().getChatRooms().size() == 0; + })); + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline}).waitUntil(chrono::seconds(2), [] { return false; }); + + // to avoid creation attempt of a new chatroom + auto config = focus.getDefaultProxyConfig(); + linphone_proxy_config_edit(config); + linphone_proxy_config_set_conference_factory_uri(config, NULL); + linphone_proxy_config_done(config); + + linphone_chat_message_unref(msg); + + bctbx_list_free(coresList); + } +} + +static void group_chat_room_server_admin_managed_messages_base(bool_t encrypted) { + Focus focus("chloe_rc"); + { // to make sure focus is destroyed after clients. + ClientConference marie("marie_rc", focus.getIdentity(), encrypted); + ClientConference pauline("pauline_rc", focus.getIdentity(), encrypted); + + focus.registerAsParticipantDevice(marie); + focus.registerAsParticipantDevice(pauline); + + linphone_core_set_default_ephemeral_lifetime(marie.getLc(), 25); + + // Enable IMDN + linphone_im_notif_policy_enable_all(linphone_core_get_im_notif_policy(marie.getLc())); + linphone_im_notif_policy_enable_all(linphone_core_get_im_notif_policy(pauline.getLc())); + + bctbx_list_t *coresList = bctbx_list_append(NULL, focus.getLc()); + coresList = bctbx_list_append(coresList, marie.getLc()); + coresList = bctbx_list_append(coresList, pauline.getLc()); + Address paulineAddr = pauline.getIdentity(); + bctbx_list_t *participantsAddresses = bctbx_list_append(NULL, linphone_address_ref(paulineAddr.toC())); + + stats chloe_stat = focus.getStats(); + stats marie_stat = marie.getStats(); + stats pauline_stat = pauline.getStats(); + + // Marie creates a new group chat room + const char *initialSubject = "Colleagues (characters: $ £ çà )"; + const LinphoneChatRoomEphemeralMode adminMode = LinphoneChatRoomEphemeralModeAdminManaged; + LinphoneChatRoomParams *params = linphone_core_create_default_chat_room_params(marie.getLc()); + + linphone_chat_room_params_enable_group(params, FALSE); + linphone_chat_room_params_enable_encryption(params, FALSE); + linphone_chat_room_params_set_ephemeral_mode(params, adminMode); + linphone_chat_room_params_set_ephemeral_lifetime(params, 5); + linphone_chat_room_params_set_backend(params, LinphoneChatRoomBackendFlexisipChat); + + LinphoneChatRoom *marieCr = create_chat_room_client_side_with_params( + coresList, marie.getCMgr(), &marie_stat, participantsAddresses, initialSubject, params); + linphone_chat_room_params_unref(params); + BC_ASSERT_PTR_NOT_NULL(marieCr); + const LinphoneAddress *confAddr = linphone_chat_room_get_conference_address(marieCr); + + // Check that the chat room is correctly created on Pauline's side and that the participants are added + LinphoneChatRoom *paulineCr = check_creation_chat_room_client_side(coresList, pauline.getCMgr(), &pauline_stat, + confAddr, initialSubject, 1, FALSE); + + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_mode(marieCr), adminMode, int, "%d"); + BC_ASSERT_TRUE(linphone_chat_room_ephemeral_enabled(marieCr)); + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_lifetime(marieCr), 5, int, "%d"); + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_mode(paulineCr), adminMode, int, "%d"); + BC_ASSERT_TRUE(linphone_chat_room_ephemeral_enabled(paulineCr)); + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_lifetime(paulineCr), 5, int, "%d"); + + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline}).wait([&focus] { + for (auto chatRoom : focus.getCore().getChatRooms()) { + for (auto participant : chatRoom->getParticipants()) { + for (auto device : participant->getDevices()) + if (device->getState() != ParticipantDevice::State::Present) { + return false; + } + } + } + return true; + })); + + chloe_stat = focus.getStats(); + marie_stat = marie.getStats(); + pauline_stat = pauline.getStats(); + + constexpr int noMsg = 10; + sendEphemeralMessageInAdminMode(focus, marie, pauline, marieCr, paulineCr, "Hello ", noMsg); + + for (auto chatRoom : focus.getCore().getChatRooms()) { + for (auto participant : chatRoom->getParticipants()) { + // force deletion by removing devices + std::shared_ptr<Address> participantAddress = participant->getAddress(); + linphone_chat_room_set_participant_devices(L_GET_C_BACK_PTR(chatRoom), participantAddress->toC(), NULL); + } + } + + // wait until chatroom is deleted server side + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline}).wait([&focus] { + return focus.getCore().getChatRooms().size() == 0; + })); + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline}).waitUntil(chrono::seconds(2), [] { return false; }); + + // to avoid creation attempt of a new chatroom + auto config = focus.getDefaultProxyConfig(); + linphone_proxy_config_edit(config); + linphone_proxy_config_set_conference_factory_uri(config, NULL); + linphone_proxy_config_done(config); + + bctbx_list_free(coresList); + } +} + +static void group_chat_room_server_admin_managed_messages_unencrypted(void) { + group_chat_room_server_admin_managed_messages_base(FALSE); +} + +static void group_chat_room_server_admin_managed_messages_ephemeral_enabled_after_creation(void) { + Focus focus("chloe_rc"); + { // to make sure focus is destroyed after clients. + ClientConference marie("marie_rc", focus.getIdentity()); + ClientConference pauline("pauline_rc", focus.getIdentity()); + + focus.registerAsParticipantDevice(marie); + focus.registerAsParticipantDevice(pauline); + + // Enable IMDN + linphone_im_notif_policy_enable_all(linphone_core_get_im_notif_policy(marie.getLc())); + linphone_im_notif_policy_enable_all(linphone_core_get_im_notif_policy(pauline.getLc())); + + bctbx_list_t *coresList = bctbx_list_append(NULL, focus.getLc()); + coresList = bctbx_list_append(coresList, marie.getLc()); + coresList = bctbx_list_append(coresList, pauline.getLc()); + Address paulineAddr = pauline.getIdentity(); + bctbx_list_t *participantsAddresses = bctbx_list_append(NULL, linphone_address_ref(paulineAddr.toC())); + + stats chloe_stat = focus.getStats(); + stats marie_stat = marie.getStats(); + stats pauline_stat = pauline.getStats(); + + // Marie creates a new group chat room + const char *initialSubject = "Colleagues (characters: $ £ çà )"; + const LinphoneChatRoomEphemeralMode adminMode = LinphoneChatRoomEphemeralModeAdminManaged; + LinphoneChatRoomParams *params = linphone_core_create_default_chat_room_params(marie.getLc()); + + linphone_chat_room_params_enable_group(params, FALSE); + linphone_chat_room_params_enable_encryption(params, FALSE); + linphone_chat_room_params_set_ephemeral_mode(params, adminMode); + linphone_chat_room_params_set_ephemeral_lifetime(params, 0); + linphone_chat_room_params_set_backend(params, LinphoneChatRoomBackendFlexisipChat); + + LinphoneChatRoom *marieCr = create_chat_room_client_side_with_params( + coresList, marie.getCMgr(), &marie_stat, participantsAddresses, initialSubject, params); + linphone_chat_room_params_unref(params); + BC_ASSERT_PTR_NOT_NULL(marieCr); + LinphoneAddress *confAddr = linphone_address_clone(linphone_chat_room_get_conference_address(marieCr)); + + // Check that the chat room is correctly created on Pauline's side and that the participants are added + LinphoneChatRoom *paulineCr = check_creation_chat_room_client_side(coresList, pauline.getCMgr(), &pauline_stat, + confAddr, initialSubject, 1, FALSE); + + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_mode(marieCr), adminMode, int, "%d"); + BC_ASSERT_FALSE(linphone_chat_room_ephemeral_enabled(marieCr)); + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_mode(paulineCr), adminMode, int, "%d"); + BC_ASSERT_FALSE(linphone_chat_room_ephemeral_enabled(paulineCr)); + + pauline_stat = pauline.getStats(); + linphone_chat_room_set_ephemeral_lifetime(marieCr, 10); + + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_NotifyReceived, + pauline_stat.number_of_NotifyReceived + 1, liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline}).waitUntil(chrono::seconds(50), [&pauline] { + for (auto chatRoom : pauline.getCore().getChatRooms()) { + if (!chatRoom->ephemeralEnabled() || (chatRoom->getEphemeralLifetime() != 10)) { + return false; + } + } + return true; + })); + + BC_ASSERT_TRUE(linphone_chat_room_ephemeral_enabled(marieCr)); + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_mode(marieCr), adminMode, int, "%d"); + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_lifetime(marieCr), 10, int, "%d"); + BC_ASSERT_TRUE(linphone_chat_room_ephemeral_enabled(paulineCr)); + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_mode(paulineCr), adminMode, int, "%d"); + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_lifetime(paulineCr), 10, int, "%d"); + + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline}).wait([&focus] { + for (auto chatRoom : focus.getCore().getChatRooms()) { + for (auto participant : chatRoom->getParticipants()) { + for (auto device : participant->getDevices()) + if (device->getState() != ParticipantDevice::State::Present) { + return false; + } + } + } + return true; + })); + + chloe_stat = focus.getStats(); + marie_stat = marie.getStats(); + pauline_stat = pauline.getStats(); + + constexpr int noMsg = 10; + sendEphemeralMessageInAdminMode(focus, marie, pauline, marieCr, paulineCr, "Hello ", noMsg); + + coresList = bctbx_list_remove(coresList, marie.getLc()); + marie.reStart(); + marie.setupMgrForConference(); + coresList = bctbx_list_append(coresList, marie.getLc()); + linphone_im_notif_policy_enable_all(linphone_core_get_im_notif_policy(marie.getLc())); + + // Retrieve chat room + LinphoneAddress *marieDeviceAddr = + linphone_address_clone(linphone_proxy_config_get_contact(marie.getDefaultProxyConfig())); + marieCr = marie.searchChatRoom(marieDeviceAddr, confAddr); + BC_ASSERT_PTR_NOT_NULL(marieCr); + + // Wait for chat rooms to be recovered from the main DB + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateCreated, 1, + liblinphone_tester_sip_timeout)); + + if (marieCr) { + BC_ASSERT_TRUE(linphone_chat_room_ephemeral_enabled(marieCr)); + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_mode(marieCr), adminMode, int, "%d"); + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_lifetime(marieCr), 10, int, "%d"); + + marie_stat = marie.getStats(); + pauline_stat = pauline.getStats(); + + linphone_chat_room_set_ephemeral_lifetime(marieCr, 5); + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneSubscriptionActive, + marie_stat.number_of_LinphoneSubscriptionActive + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(linphone_chat_room_ephemeral_enabled(marieCr)); + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_mode(marieCr), adminMode, int, "%d"); + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_lifetime(marieCr), 5, int, "%d"); + + sendEphemeralMessageInAdminMode(focus, marie, pauline, marieCr, paulineCr, "Back online ", noMsg); + } + + for (auto chatRoom : focus.getCore().getChatRooms()) { + for (auto participant : chatRoom->getParticipants()) { + // force deletion by removing devices + std::shared_ptr<Address> participantAddress = participant->getAddress(); + linphone_chat_room_set_participant_devices(L_GET_C_BACK_PTR(chatRoom), participantAddress->toC(), NULL); + } + } + + // wait until chatroom is deleted server side + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline}).wait([&focus] { + return focus.getCore().getChatRooms().size() == 0; + })); + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline}).waitUntil(chrono::seconds(2), [] { return false; }); + + // to avoid creation attempt of a new chatroom + auto config = focus.getDefaultProxyConfig(); + linphone_proxy_config_edit(config); + linphone_proxy_config_set_conference_factory_uri(config, NULL); + linphone_proxy_config_done(config); + + linphone_address_unref(marieDeviceAddr); + linphone_address_unref(confAddr); + bctbx_list_free(coresList); + } +} + +static void group_chat_room_server_admin_managed_messages_ephemeral_disabled_after_creation(void) { + Focus focus("chloe_rc"); + { // to make sure focus is destroyed after clients. + ClientConference marie("marie_rc", focus.getIdentity()); + ClientConference pauline("pauline_rc", focus.getIdentity()); + + focus.registerAsParticipantDevice(marie); + focus.registerAsParticipantDevice(pauline); + + // Enable IMDN + linphone_im_notif_policy_enable_all(linphone_core_get_im_notif_policy(marie.getLc())); + linphone_im_notif_policy_enable_all(linphone_core_get_im_notif_policy(pauline.getLc())); + + linphone_core_set_default_ephemeral_lifetime(marie.getLc(), 1); + + bctbx_list_t *coresList = bctbx_list_append(NULL, focus.getLc()); + coresList = bctbx_list_append(coresList, marie.getLc()); + coresList = bctbx_list_append(coresList, pauline.getLc()); + Address paulineAddr = pauline.getIdentity(); + bctbx_list_t *participantsAddresses = bctbx_list_append(NULL, linphone_address_ref(paulineAddr.toC())); + + stats chloe_stat = focus.getStats(); + stats marie_stat = marie.getStats(); + stats pauline_stat = pauline.getStats(); + + // Marie creates a new group chat room + const char *initialSubject = "Colleagues (characters: $ £ çà )"; + const LinphoneChatRoomEphemeralMode adminMode = LinphoneChatRoomEphemeralModeAdminManaged; + LinphoneChatRoom *marieCr = create_chat_room_client_side( + coresList, marie.getCMgr(), &marie_stat, participantsAddresses, initialSubject, FALSE, adminMode); + BC_ASSERT_PTR_NOT_NULL(marieCr); + LinphoneAddress *confAddr = linphone_address_clone(linphone_chat_room_get_conference_address(marieCr)); + + // Check that the chat room is correctly created on Pauline's side and that the participants are added + LinphoneChatRoom *paulineCr = check_creation_chat_room_client_side(coresList, pauline.getCMgr(), &pauline_stat, + confAddr, initialSubject, 1, FALSE); + + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_mode(marieCr), adminMode, int, "%d"); + BC_ASSERT_TRUE(linphone_chat_room_ephemeral_enabled(marieCr)); + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_lifetime(marieCr), 1, int, "%d"); + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_mode(paulineCr), adminMode, int, "%d"); + BC_ASSERT_TRUE(linphone_chat_room_ephemeral_enabled(paulineCr)); + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_lifetime(paulineCr), 1, int, "%d"); + + pauline_stat = pauline.getStats(); + linphone_chat_room_set_ephemeral_lifetime(marieCr, 0); + + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneChatRoomEphemeralMessageDisabled, + pauline_stat.number_of_LinphoneChatRoomEphemeralMessageDisabled + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline}).waitUntil(chrono::seconds(50), [&pauline] { + for (auto chatRoom : pauline.getCore().getChatRooms()) { + if (chatRoom->ephemeralEnabled()) { + return false; + } + } + return true; + })); + + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_mode(marieCr), adminMode, int, "%d"); + BC_ASSERT_FALSE(linphone_chat_room_ephemeral_enabled(marieCr)); + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_mode(paulineCr), adminMode, int, "%d"); + BC_ASSERT_FALSE(linphone_chat_room_ephemeral_enabled(paulineCr)); + + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline}).wait([&focus] { + for (auto chatRoom : focus.getCore().getChatRooms()) { + for (auto participant : chatRoom->getParticipants()) { + for (auto device : participant->getDevices()) + if (device->getState() != ParticipantDevice::State::Present) { + return false; + } + } + } + return true; + })); + + chloe_stat = focus.getStats(); + marie_stat = marie.getStats(); + pauline_stat = pauline.getStats(); + + constexpr int noMsg = 10; + LinphoneChatMessage *message[noMsg]; + // Marie sends messages + for (int i = 0; i < noMsg; i++) { + const std::string text = std::string("Hello ") + std::to_string(i); + message[i] = _send_message_ephemeral(marieCr, text.c_str(), TRUE); + } + + bctbx_list_t *marieHistory = linphone_chat_room_get_history(marieCr, 0); + BC_ASSERT_EQUAL((int)bctbx_list_size(marieHistory), noMsg, int, "%i"); + + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneMessageReceived, + pauline_stat.number_of_LinphoneMessageReceived + noMsg, 11000)); + + // Check that the message has been delivered to Pauline + for (int i = 0; i < noMsg; i++) { + const auto msg = message[i]; + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline}).wait([msg] { + return (linphone_chat_message_get_state(msg) == LinphoneChatMessageStateDelivered); + })); + } + + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline}).wait([&, paulineCr] { + return linphone_chat_room_get_unread_messages_count(paulineCr) == noMsg; + })); + + bctbx_list_t *paulineHistory = linphone_chat_room_get_history(paulineCr, 0); + BC_ASSERT_EQUAL((int)bctbx_list_size(paulineHistory), noMsg, int, "%i"); + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneMessageDeliveredToUser, + marie_stat.number_of_LinphoneMessageDeliveredToUser + noMsg, + liblinphone_tester_sip_timeout)); + + // Pauline marks the message as read, check that the state is now displayed on Marie's side + linphone_chat_room_mark_as_read(paulineCr); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneMessageDisplayed, + marie_stat.number_of_LinphoneMessageDisplayed + noMsg, + liblinphone_tester_sip_timeout)); + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline}).waitUntil(chrono::seconds(2), [] { return false; }); + + bctbx_list_free_with_data(paulineHistory, (bctbx_list_free_func)linphone_chat_message_unref); + bctbx_list_free_with_data(marieHistory, (bctbx_list_free_func)linphone_chat_message_unref); + paulineHistory = linphone_chat_room_get_history(paulineCr, 0); + BC_ASSERT_EQUAL((int)bctbx_list_size(paulineHistory), noMsg, int, "%i"); + marieHistory = linphone_chat_room_get_history(marieCr, 0); + BC_ASSERT_EQUAL((int)bctbx_list_size(marieHistory), noMsg, int, "%i"); + + bctbx_list_free_with_data(paulineHistory, (bctbx_list_free_func)linphone_chat_message_unref); + bctbx_list_free_with_data(marieHistory, (bctbx_list_free_func)linphone_chat_message_unref); + + for (int i = 0; i < noMsg; i++) { + linphone_chat_message_unref(message[i]); + } + + coresList = bctbx_list_remove(coresList, marie.getLc()); + marie.reStart(); + marie.setupMgrForConference(); + coresList = bctbx_list_append(coresList, marie.getLc()); + linphone_im_notif_policy_enable_all(linphone_core_get_im_notif_policy(marie.getLc())); + + // Retrieve chat room + LinphoneAddress *marieDeviceAddr = + linphone_address_clone(linphone_proxy_config_get_contact(marie.getDefaultProxyConfig())); + marieCr = marie.searchChatRoom(marieDeviceAddr, confAddr); + BC_ASSERT_PTR_NOT_NULL(marieCr); + + // Wait for chat rooms to be recovered from the main DB + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateCreated, 1, + liblinphone_tester_sip_timeout)); + + if (marieCr) { + BC_ASSERT_FALSE(linphone_chat_room_ephemeral_enabled(marieCr)); + + marie_stat = marie.getStats(); + pauline_stat = pauline.getStats(); + + linphone_chat_room_set_ephemeral_lifetime(marieCr, 5); + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneSubscriptionActive, + marie_stat.number_of_LinphoneSubscriptionActive + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list( + coresList, &pauline.getStats().number_of_LinphoneChatRoomEphemeralMessageEnabled, + pauline_stat.number_of_LinphoneChatRoomEphemeralMessageEnabled + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list( + coresList, &pauline.getStats().number_of_LinphoneChatRoomEphemeralLifetimeChanged, + pauline_stat.number_of_LinphoneChatRoomEphemeralLifetimeChanged + 1, liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(linphone_chat_room_ephemeral_enabled(marieCr)); + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_mode(marieCr), adminMode, int, "%d"); + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_lifetime(marieCr), 5, int, "%d"); + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_mode(paulineCr), adminMode, int, "%d"); + BC_ASSERT_TRUE(linphone_chat_room_ephemeral_enabled(paulineCr)); + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_lifetime(paulineCr), 5, int, "%d"); + + sendEphemeralMessageInAdminMode(focus, marie, pauline, marieCr, paulineCr, "Back online ", noMsg); + } + + for (auto chatRoom : focus.getCore().getChatRooms()) { + for (auto participant : chatRoom->getParticipants()) { + // force deletion by removing devices + std::shared_ptr<Address> participantAddress = participant->getAddress(); + linphone_chat_room_set_participant_devices(L_GET_C_BACK_PTR(chatRoom), participantAddress->toC(), NULL); + } + } + + // wait until chatroom is deleted server side + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline}).wait([&focus] { + return focus.getCore().getChatRooms().size() == 0; + })); + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline}).waitUntil(chrono::seconds(2), [] { return false; }); + + // to avoid creation attempt of a new chatroom + auto config = focus.getDefaultProxyConfig(); + linphone_proxy_config_edit(config); + linphone_proxy_config_set_conference_factory_uri(config, NULL); + linphone_proxy_config_done(config); + + linphone_address_unref(marieDeviceAddr); + linphone_address_unref(confAddr); + bctbx_list_free(coresList); + } +} + +static void group_chat_room_server_admin_managed_messages_ephemeral_lifetime_update(void) { + Focus focus("chloe_rc"); + { // to make sure focus is destroyed after clients. + ClientConference marie("marie_rc", focus.getIdentity()); + ClientConference pauline("pauline_rc", focus.getIdentity()); + + linphone_core_set_default_ephemeral_lifetime(marie.getLc(), 5); + + focus.registerAsParticipantDevice(marie); + focus.registerAsParticipantDevice(pauline); + + // Enable IMDN + linphone_im_notif_policy_enable_all(linphone_core_get_im_notif_policy(marie.getLc())); + linphone_im_notif_policy_enable_all(linphone_core_get_im_notif_policy(pauline.getLc())); + + bctbx_list_t *coresList = bctbx_list_append(NULL, focus.getLc()); + coresList = bctbx_list_append(coresList, marie.getLc()); + coresList = bctbx_list_append(coresList, pauline.getLc()); + Address paulineAddr = pauline.getIdentity(); + bctbx_list_t *participantsAddresses = bctbx_list_append(NULL, linphone_address_ref(paulineAddr.toC())); + + stats marie_stat = marie.getStats(); + stats pauline_stat = pauline.getStats(); + + // Marie creates a new group chat room + const char *initialSubject = "Colleagues (characters: $ £ çà )"; + const LinphoneChatRoomEphemeralMode adminMode = LinphoneChatRoomEphemeralModeAdminManaged; + LinphoneChatRoom *marieCr = create_chat_room_client_side( + coresList, marie.getCMgr(), &marie_stat, participantsAddresses, initialSubject, FALSE, adminMode); + BC_ASSERT_PTR_NOT_NULL(marieCr); + LinphoneAddress *confAddr = linphone_address_clone(linphone_chat_room_get_conference_address(marieCr)); + + // Check that the chat room is correctly created on Pauline's side and that the participants are added + LinphoneChatRoom *paulineCr = check_creation_chat_room_client_side(coresList, pauline.getCMgr(), &pauline_stat, + confAddr, initialSubject, 1, FALSE); + + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline}).wait([&focus] { + for (auto chatRoom : focus.getCore().getChatRooms()) { + for (auto participant : chatRoom->getParticipants()) { + for (auto device : participant->getDevices()) + if (device->getState() != ParticipantDevice::State::Present) { + return false; + } + } + } + return true; + })); + + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_mode(marieCr), adminMode, int, "%d"); + BC_ASSERT_TRUE(linphone_chat_room_ephemeral_enabled(marieCr)); + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_lifetime(marieCr), 5, int, "%d"); + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_mode(paulineCr), adminMode, int, "%d"); + BC_ASSERT_TRUE(linphone_chat_room_ephemeral_enabled(paulineCr)); + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_lifetime(paulineCr), 5, int, "%d"); + + coresList = bctbx_list_remove(coresList, marie.getLc()); + marie.reStart(); + marie.setupMgrForConference(); + coresList = bctbx_list_append(coresList, marie.getLc()); + linphone_im_notif_policy_enable_all(linphone_core_get_im_notif_policy(marie.getLc())); + + // Retrieve chat room + LinphoneAddress *marieDeviceAddr = + linphone_address_clone(linphone_proxy_config_get_contact(marie.getDefaultProxyConfig())); + marieCr = marie.searchChatRoom(marieDeviceAddr, confAddr); + BC_ASSERT_PTR_NOT_NULL(marieCr); + + // Wait for chat rooms to be recovered from the main DB + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateCreated, 1, + liblinphone_tester_sip_timeout)); + + if (marieCr) { + BC_ASSERT_TRUE(linphone_chat_room_ephemeral_enabled(marieCr)); + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_mode(marieCr), adminMode, int, "%d"); + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_lifetime(marieCr), 5, int, "%d"); + + marie_stat = marie.getStats(); + pauline_stat = pauline.getStats(); + linphone_chat_room_set_ephemeral_lifetime(marieCr, 10); + + BC_ASSERT_TRUE(wait_for_list( + coresList, &pauline.getStats().number_of_LinphoneChatRoomEphemeralLifetimeChanged, + pauline_stat.number_of_LinphoneChatRoomEphemeralLifetimeChanged + 1, liblinphone_tester_sip_timeout)); + + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_mode(marieCr), adminMode, int, "%d"); + BC_ASSERT_TRUE(linphone_chat_room_ephemeral_enabled(marieCr)); + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_lifetime(marieCr), 10, int, "%d"); + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_mode(paulineCr), adminMode, int, "%d"); + BC_ASSERT_TRUE(linphone_chat_room_ephemeral_enabled(paulineCr)); + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_lifetime(paulineCr), 10, int, "%d"); + + constexpr int noMsg = 10; + sendEphemeralMessageInAdminMode(focus, marie, pauline, marieCr, paulineCr, "Hello ", noMsg); + } + + for (auto chatRoom : focus.getCore().getChatRooms()) { + for (auto participant : chatRoom->getParticipants()) { + // force deletion by removing devices + std::shared_ptr<Address> participantAddress = participant->getAddress(); + linphone_chat_room_set_participant_devices(L_GET_C_BACK_PTR(chatRoom), participantAddress->toC(), NULL); + } + } + + // wait until chatroom is deleted server side + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline}).wait([&focus] { + return focus.getCore().getChatRooms().size() == 0; + })); + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline}).waitUntil(chrono::seconds(2), [] { return false; }); + + // to avoid creation attempt of a new chatroom + auto config = focus.getDefaultProxyConfig(); + linphone_proxy_config_edit(config); + linphone_proxy_config_set_conference_factory_uri(config, NULL); + linphone_proxy_config_done(config); + + linphone_address_unref(marieDeviceAddr); + linphone_address_unref(confAddr); + bctbx_list_free(coresList); + } +} + +static void group_chat_room_server_admin_managed_messages_ephemeral_lifetime_toggle_using_different_methods(void) { + Focus focus("chloe_rc"); + { // to make sure focus is destroyed after clients. + ClientConference marie("marie_rc", focus.getIdentity()); + ClientConference pauline("pauline_rc", focus.getIdentity()); + + focus.registerAsParticipantDevice(marie); + focus.registerAsParticipantDevice(pauline); + + // Enable IMDN + linphone_im_notif_policy_enable_all(linphone_core_get_im_notif_policy(marie.getLc())); + linphone_im_notif_policy_enable_all(linphone_core_get_im_notif_policy(pauline.getLc())); + + linphone_core_set_default_ephemeral_lifetime(marie.getLc(), 5); + + bctbx_list_t *coresList = bctbx_list_append(NULL, focus.getLc()); + coresList = bctbx_list_append(coresList, marie.getLc()); + coresList = bctbx_list_append(coresList, pauline.getLc()); + Address paulineAddr = pauline.getIdentity(); + bctbx_list_t *participantsAddresses = bctbx_list_append(NULL, linphone_address_ref(paulineAddr.toC())); + + stats chloe_stat = focus.getStats(); + stats marie_stat = marie.getStats(); + stats pauline_stat = pauline.getStats(); + + // Marie creates a new group chat room + const char *initialSubject = "Colleagues"; + const LinphoneChatRoomEphemeralMode adminMode = LinphoneChatRoomEphemeralModeAdminManaged; + LinphoneChatRoom *marieCr = create_chat_room_client_side( + coresList, marie.getCMgr(), &marie_stat, participantsAddresses, initialSubject, FALSE, adminMode); + BC_ASSERT_PTR_NOT_NULL(marieCr); + const LinphoneAddress *confAddr = linphone_chat_room_get_conference_address(marieCr); + + // Check that the chat room is correctly created on Pauline's side and that the participants are added + LinphoneChatRoom *paulineCr = check_creation_chat_room_client_side(coresList, pauline.getCMgr(), &pauline_stat, + confAddr, initialSubject, 1, FALSE); + + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline}).wait([&focus] { + for (auto chatRoom : focus.getCore().getChatRooms()) { + for (auto participant : chatRoom->getParticipants()) { + for (auto device : participant->getDevices()) + if (device->getState() != ParticipantDevice::State::Present) { + return false; + } + } + } + return true; + })); + + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_mode(marieCr), adminMode, int, "%d"); + BC_ASSERT_TRUE(linphone_chat_room_ephemeral_enabled(marieCr)); + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_lifetime(marieCr), 5, int, "%d"); + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_mode(paulineCr), adminMode, int, "%d"); + BC_ASSERT_TRUE(linphone_chat_room_ephemeral_enabled(paulineCr)); + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_lifetime(paulineCr), 5, int, "%d"); + + pauline_stat = pauline.getStats(); + linphone_chat_room_set_ephemeral_lifetime(marieCr, 10); + + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneChatRoomEphemeralLifetimeChanged, + pauline_stat.number_of_LinphoneChatRoomEphemeralLifetimeChanged + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_mode(marieCr), adminMode, int, "%d"); + BC_ASSERT_TRUE(linphone_chat_room_ephemeral_enabled(marieCr)); + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_lifetime(marieCr), 10, int, "%d"); + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_mode(paulineCr), adminMode, int, "%d"); + BC_ASSERT_TRUE(linphone_chat_room_ephemeral_enabled(paulineCr)); + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_lifetime(paulineCr), 10, int, "%d"); + + chloe_stat = focus.getStats(); + marie_stat = marie.getStats(); + pauline_stat = pauline.getStats(); + + constexpr int noMsg = 10; + sendEphemeralMessageInAdminMode(focus, marie, pauline, marieCr, paulineCr, "Hello ", noMsg); + + pauline_stat = pauline.getStats(); + // Disable ephemeral + linphone_chat_room_set_ephemeral_lifetime(marieCr, 0); + + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneChatRoomEphemeralMessageDisabled, + pauline_stat.number_of_LinphoneChatRoomEphemeralMessageDisabled + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_mode(marieCr), adminMode, int, "%d"); + BC_ASSERT_FALSE(linphone_chat_room_ephemeral_enabled(marieCr)); + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_lifetime(marieCr), 0, int, "%d"); + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_mode(paulineCr), adminMode, int, "%d"); + BC_ASSERT_FALSE(linphone_chat_room_ephemeral_enabled(paulineCr)); + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_lifetime(paulineCr), 0, int, "%d"); + + chloe_stat = focus.getStats(); + marie_stat = marie.getStats(); + pauline_stat = pauline.getStats(); + + LinphoneChatMessage *non_ephemeral_message; + // Marie sends messages + const std::string non_ephemeral_text = std::string("Not an ephemeral message"); + non_ephemeral_message = _send_message_ephemeral(marieCr, non_ephemeral_text.c_str(), TRUE); + + auto marieHistory = linphone_chat_room_get_history(marieCr, 0); + BC_ASSERT_EQUAL((int)bctbx_list_size(marieHistory), 1, int, "%i"); + + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneMessageReceived, + pauline_stat.number_of_LinphoneMessageReceived + 1, 11000)); + + // Check that the message has been delivered to Pauline + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline}).wait([non_ephemeral_message] { + return (linphone_chat_message_get_state(non_ephemeral_message) == LinphoneChatMessageStateDelivered); + })); + + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline}).wait([&, paulineCr] { + return linphone_chat_room_get_unread_messages_count(paulineCr) == 1; + })); + + auto paulineHistory = linphone_chat_room_get_history(paulineCr, 0); + BC_ASSERT_EQUAL((int)bctbx_list_size(paulineHistory), 1, int, "%i"); + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneMessageDeliveredToUser, + marie_stat.number_of_LinphoneMessageDeliveredToUser + 1, + liblinphone_tester_sip_timeout)); + + // Pauline marks the message as read, check that the state is now displayed on Marie's side + linphone_chat_room_mark_as_read(paulineCr); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneMessageDisplayed, + marie_stat.number_of_LinphoneMessageDisplayed + 1, + liblinphone_tester_sip_timeout)); + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline}).waitUntil(chrono::seconds(2), [] { return false; }); + + bctbx_list_free_with_data(paulineHistory, (bctbx_list_free_func)linphone_chat_message_unref); + bctbx_list_free_with_data(marieHistory, (bctbx_list_free_func)linphone_chat_message_unref); + + pauline_stat = pauline.getStats(); + linphone_chat_room_enable_ephemeral(marieCr, TRUE); + + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneChatRoomEphemeralMessageEnabled, + pauline_stat.number_of_LinphoneChatRoomEphemeralMessageEnabled + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_mode(marieCr), adminMode, int, "%d"); + BC_ASSERT_TRUE(linphone_chat_room_ephemeral_enabled(marieCr)); + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_lifetime(marieCr), + linphone_core_get_default_ephemeral_lifetime(marie.getLc()), int, "%d"); + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_mode(paulineCr), adminMode, int, "%d"); + BC_ASSERT_TRUE(linphone_chat_room_ephemeral_enabled(paulineCr)); + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_lifetime(paulineCr), + linphone_core_get_default_ephemeral_lifetime(marie.getLc()), int, "%d"); + + chloe_stat = focus.getStats(); + marie_stat = marie.getStats(); + pauline_stat = pauline.getStats(); + + constexpr int noShortMsg = 10; + sendEphemeralMessageInAdminMode(focus, marie, pauline, marieCr, paulineCr, "Test ephemeral message #", + noShortMsg); + + linphone_chat_message_unref(non_ephemeral_message); + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline}).waitUntil(chrono::seconds(2), [] { return false; }); + + for (auto chatRoom : focus.getCore().getChatRooms()) { + for (auto participant : chatRoom->getParticipants()) { + // force deletion by removing devices + std::shared_ptr<Address> participantAddress = participant->getAddress(); + linphone_chat_room_set_participant_devices(L_GET_C_BACK_PTR(chatRoom), participantAddress->toC(), NULL); + } + } + + // wait until chatroom is deleted server side + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline}).wait([&focus] { + return focus.getCore().getChatRooms().size() == 0; + })); + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline}).waitUntil(chrono::seconds(2), [] { return false; }); + + // to avoid creation attempt of a new chatroom + auto config = focus.getDefaultProxyConfig(); + linphone_proxy_config_edit(config); + linphone_proxy_config_set_conference_factory_uri(config, NULL); + linphone_proxy_config_done(config); + + bctbx_list_free(coresList); + } +} + +static void group_chat_room_bulk_notify_to_participant(void) { + Focus focus("chloe_rc"); + { // to make sure focus is destroyed after clients. + ClientConference marie("marie_rc", focus.getIdentity()); + ClientConference pauline("pauline_rc", focus.getIdentity()); + ClientConference michelle("michelle_rc", focus.getIdentity()); + + focus.registerAsParticipantDevice(marie); + focus.registerAsParticipantDevice(pauline); + focus.registerAsParticipantDevice(michelle); + + bctbx_list_t *coresList = bctbx_list_append(NULL, focus.getLc()); + coresList = bctbx_list_append(coresList, marie.getLc()); + coresList = bctbx_list_append(coresList, pauline.getLc()); + coresList = bctbx_list_append(coresList, michelle.getLc()); + Address paulineAddr = pauline.getIdentity(); + bctbx_list_t *participantsAddresses = bctbx_list_append(NULL, linphone_address_ref(paulineAddr.toC())); + Address michelleAddr = michelle.getIdentity(); + participantsAddresses = bctbx_list_append(participantsAddresses, linphone_address_ref(michelleAddr.toC())); + + stats initialMarieStats = marie.getStats(); + stats initialPaulineStats = pauline.getStats(); + stats initialMichelleStats = michelle.getStats(); + + // Marie creates a new group chat room + const char *initialSubject = "Colleagues (characters: $ £ çà )"; + LinphoneChatRoom *marieCr = + create_chat_room_client_side(coresList, marie.getCMgr(), &initialMarieStats, participantsAddresses, + initialSubject, FALSE, LinphoneChatRoomEphemeralModeDeviceManaged); + const LinphoneAddress *confAddr = linphone_chat_room_get_conference_address(marieCr); + + // Check that the chat room is correctly created on Pauline's side and that the participants are added + LinphoneChatRoom *paulineCr = check_creation_chat_room_client_side( + coresList, pauline.getCMgr(), &initialPaulineStats, confAddr, initialSubject, 2, FALSE); + LinphoneChatRoom *michelleCr = check_creation_chat_room_client_side( + coresList, michelle.getCMgr(), &initialMichelleStats, confAddr, initialSubject, 2, FALSE); + + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline, michelle}).wait([&focus] { + for (auto chatRoom : focus.getCore().getChatRooms()) { + for (auto participant : chatRoom->getParticipants()) { + for (auto device : participant->getDevices()) + if (device->getState() != ParticipantDevice::State::Present) { + return false; + } + } + } + return true; + })); + + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneConferenceStateCreated, + initialPaulineStats.number_of_LinphoneConferenceStateCreated + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_LinphoneConferenceStateCreated, + initialMichelleStats.number_of_LinphoneConferenceStateCreated + 1, + liblinphone_tester_sip_timeout)); + + // Marie now changes the subject + const char *newSubject = "Let's go drink a beer"; + linphone_chat_room_set_subject(marieCr, newSubject); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_subject_changed, + initialMarieStats.number_of_subject_changed + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_subject_changed, + initialPaulineStats.number_of_subject_changed + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_subject_changed, + initialMichelleStats.number_of_subject_changed + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_STRING_EQUAL(linphone_chat_room_get_subject(marieCr), newSubject); + BC_ASSERT_STRING_EQUAL(linphone_chat_room_get_subject(paulineCr), newSubject); + BC_ASSERT_STRING_EQUAL(linphone_chat_room_get_subject(michelleCr), newSubject); + + // Pauline goes offline + linphone_core_set_network_reachable(pauline.getLc(), FALSE); + + // Adding Laure + ClientConference laure("laure_tcp_rc", focus.getIdentity()); + coresList = bctbx_list_append(coresList, laure.getLc()); + focus.registerAsParticipantDevice(laure); + + initialMarieStats = marie.getStats(); + initialPaulineStats = pauline.getStats(); + initialMichelleStats = michelle.getStats(); + stats initialLaureStats = laure.getStats(); + + Address laureAddr = laure.getIdentity(); + participantsAddresses = bctbx_list_append(NULL, linphone_address_ref(laureAddr.toC())); + linphone_chat_room_add_participants(marieCr, participantsAddresses); + + LinphoneChatRoom *laureCr = check_creation_chat_room_client_side(coresList, laure.getCMgr(), &initialLaureStats, + confAddr, newSubject, 3, FALSE); + BC_ASSERT_PTR_NOT_NULL(laureCr); + + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_LinphoneConferenceStateCreationPending, + initialLaureStats.number_of_LinphoneConferenceStateCreationPending + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_LinphoneConferenceStateCreated, + initialLaureStats.number_of_LinphoneConferenceStateCreated + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_LinphoneChatRoomConferenceJoined, + initialLaureStats.number_of_LinphoneChatRoomConferenceJoined + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_participants_added, + initialMarieStats.number_of_participants_added + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_FALSE(wait_for_list(coresList, &pauline.getStats().number_of_participants_added, + initialPaulineStats.number_of_participants_added + 1, 3000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_participants_added, + initialMichelleStats.number_of_participants_added + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_participant_devices_added, + initialMarieStats.number_of_participant_devices_added + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_FALSE(wait_for_list(coresList, &pauline.getStats().number_of_participant_devices_added, + initialPaulineStats.number_of_participant_devices_added + 1, 3000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_participant_devices_added, + initialMichelleStats.number_of_participant_devices_added + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_EQUAL(linphone_chat_room_get_nb_participants(marieCr), 3, int, "%d"); + BC_ASSERT_EQUAL(linphone_chat_room_get_nb_participants(paulineCr), 2, int, "%d"); + BC_ASSERT_EQUAL(linphone_chat_room_get_nb_participants(michelleCr), 3, int, "%d"); + + // Wait a little bit to detect side effects + CoreManagerAssert({focus, marie, laure, pauline, michelle}).waitUntil(std::chrono::seconds(2), [] { + return false; + }); + + initialMarieStats = marie.getStats(); + initialPaulineStats = pauline.getStats(); + initialMichelleStats = michelle.getStats(); + initialLaureStats = laure.getStats(); + + // Marie now changes the subject again + const char *newSubject2 = "Seriously, ladies... Tonight we go out"; + linphone_chat_room_set_subject(marieCr, newSubject2); + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_subject_changed, + initialMarieStats.number_of_subject_changed + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_FALSE(wait_for_list(coresList, &pauline.getStats().number_of_subject_changed, + initialPaulineStats.number_of_subject_changed + 1, 3000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_subject_changed, + initialLaureStats.number_of_subject_changed + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_subject_changed, + initialMichelleStats.number_of_subject_changed + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_STRING_EQUAL(linphone_chat_room_get_subject(marieCr), newSubject2); + BC_ASSERT_STRING_EQUAL(linphone_chat_room_get_subject(paulineCr), newSubject); + BC_ASSERT_STRING_EQUAL(linphone_chat_room_get_subject(michelleCr), newSubject2); + BC_ASSERT_STRING_EQUAL(linphone_chat_room_get_subject(laureCr), newSubject2); + + initialMarieStats = marie.getStats(); + initialPaulineStats = pauline.getStats(); + initialMichelleStats = michelle.getStats(); + initialLaureStats = laure.getStats(); + + char *laureDeviceIdentity = linphone_core_get_device_identity(laure.getLc()); + LinphoneAddress *laureLocalAddr = linphone_address_new(laureDeviceIdentity); + BC_ASSERT_PTR_NOT_NULL(laureLocalAddr); + bctbx_free(laureDeviceIdentity); + + // Focus deletes Laure's device + for (auto chatRoom : focus.getCore().getChatRooms()) { + std::shared_ptr<Participant> participant = + chatRoom->findParticipant(Address::toCpp(laureLocalAddr)->getSharedFromThis()); + BC_ASSERT_PTR_NOT_NULL(participant); + if (participant) { + // force deletion by removing devices + std::shared_ptr<Address> participantAddress = participant->getAddress(); + // Do not use laureLocalAddr because it has a GRUU + linphone_chat_room_set_participant_devices(L_GET_C_BACK_PTR(chatRoom), participantAddress->toC(), NULL); + } + } + + // Marie removes Laure from the chat room + LinphoneParticipant *laureParticipant = linphone_chat_room_find_participant(marieCr, laureLocalAddr); + BC_ASSERT_PTR_NOT_NULL(laureParticipant); + linphone_chat_room_remove_participant(marieCr, laureParticipant); + + linphone_address_unref(laureLocalAddr); + + BC_ASSERT_FALSE(wait_for_list(coresList, &laure.getStats().number_of_LinphoneConferenceStateTerminated, + initialLaureStats.number_of_LinphoneConferenceStateTerminated + 1, 3000)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_participant_devices_removed, + initialMarieStats.number_of_participant_devices_removed + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_FALSE(wait_for_list(coresList, &pauline.getStats().number_of_participant_devices_removed, + initialPaulineStats.number_of_participant_devices_removed + 1, 3000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_participant_devices_removed, + initialMichelleStats.number_of_participant_devices_removed + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_participants_removed, + initialMarieStats.number_of_participants_removed + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_FALSE(wait_for_list(coresList, &pauline.getStats().number_of_participants_removed, + initialPaulineStats.number_of_participants_removed + 1, 3000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_participants_removed, + initialMichelleStats.number_of_participants_removed + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_EQUAL(linphone_chat_room_get_nb_participants(marieCr), 2, int, "%d"); + BC_ASSERT_EQUAL(linphone_chat_room_get_nb_participants(paulineCr), 2, int, "%d"); + + // Wait a little bit to detect side effects + CoreManagerAssert({focus, marie, pauline, michelle}).waitUntil(std::chrono::seconds(2), [] { return false; }); + + initialPaulineStats = pauline.getStats(); + // Pauline comes up online + linphone_core_set_network_reachable(pauline.getLc(), TRUE); + + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneRegistrationOk, + initialPaulineStats.number_of_LinphoneRegistrationOk + 1, + liblinphone_tester_sip_timeout)); + + // Check that Pauline receives the backlog of events occurred while she was offline + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_participants_added, + initialPaulineStats.number_of_participants_added + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_participant_devices_added, + initialPaulineStats.number_of_participant_devices_added + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_subject_changed, + initialPaulineStats.number_of_subject_changed + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_participant_devices_removed, + initialPaulineStats.number_of_participant_devices_removed + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_participants_removed, + initialPaulineStats.number_of_participants_removed + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_EQUAL(linphone_chat_room_get_nb_participants(paulineCr), 2, int, "%d"); + BC_ASSERT_STRING_EQUAL(linphone_chat_room_get_subject(paulineCr), newSubject2); + + CoreManagerAssert({focus, marie, pauline, michelle}).waitUntil(std::chrono::seconds(1), [] { return false; }); + + CoreManagerAssert({focus, marie}).waitUntil(std::chrono::seconds(2), [] { return false; }); + + for (auto chatRoom : focus.getCore().getChatRooms()) { + for (auto participant : chatRoom->getParticipants()) { + // force deletion by removing devices + std::shared_ptr<Address> participantAddress = participant->getAddress(); + linphone_chat_room_set_participant_devices(L_GET_C_BACK_PTR(chatRoom), participantAddress->toC(), NULL); + } + } + + // wait until chatroom is deleted server side + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline}).wait([&focus] { + return focus.getCore().getChatRooms().size() == 0; + })); + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline}).waitUntil(chrono::seconds(2), [] { return false; }); + + // to avoid creation attempt of a new chatroom + auto config = focus.getDefaultProxyConfig(); + linphone_proxy_config_edit(config); + linphone_proxy_config_set_conference_factory_uri(config, NULL); + linphone_proxy_config_done(config); + + bctbx_list_free(coresList); + } +} + +static void group_chat_room_with_client_restart_base(bool encrypted) { + Focus focus("chloe_rc"); + { // to make sure focus is destroyed after clients. + ClientConference marie("marie_rc", focus.getIdentity(), encrypted); + ClientConference michelle("michelle_rc", focus.getIdentity(), encrypted); + ClientConference laure("laure_tcp_rc", focus.getIdentity(), encrypted); + ClientConference berthe("berthe_rc", focus.getIdentity(), encrypted); + + focus.registerAsParticipantDevice(marie); + focus.registerAsParticipantDevice(michelle); + focus.registerAsParticipantDevice(laure); + focus.registerAsParticipantDevice(berthe); + + bctbx_list_t *coresList = bctbx_list_append(NULL, focus.getLc()); + coresList = bctbx_list_append(coresList, marie.getLc()); + coresList = bctbx_list_append(coresList, michelle.getLc()); + coresList = bctbx_list_append(coresList, laure.getLc()); + coresList = bctbx_list_append(coresList, berthe.getLc()); + bctbx_list_t *participantsAddresses = NULL; + Address michelleAddr = michelle.getIdentity(); + participantsAddresses = bctbx_list_append(participantsAddresses, linphone_address_ref(michelleAddr.toC())); + Address bertheAddr = berthe.getIdentity(); + participantsAddresses = bctbx_list_append(participantsAddresses, linphone_address_ref(bertheAddr.toC())); + + stats initialMarieStats = marie.getStats(); + stats initialMichelleStats = michelle.getStats(); + stats initialBertheStats = berthe.getStats(); + stats initialLaureStats = laure.getStats(); + + if (encrypted) { + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_X3dhUserCreationSuccess, + initialMarieStats.number_of_X3dhUserCreationSuccess + 1, + x3dhServer_creationTimeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_X3dhUserCreationSuccess, + initialMichelleStats.number_of_X3dhUserCreationSuccess + 1, + x3dhServer_creationTimeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &berthe.getStats().number_of_X3dhUserCreationSuccess, + initialBertheStats.number_of_X3dhUserCreationSuccess + 1, + x3dhServer_creationTimeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_X3dhUserCreationSuccess, + initialLaureStats.number_of_X3dhUserCreationSuccess + 1, + x3dhServer_creationTimeout)); + BC_ASSERT_TRUE(linphone_core_lime_x3dh_enabled(marie.getLc())); + BC_ASSERT_TRUE(linphone_core_lime_x3dh_enabled(michelle.getLc())); + BC_ASSERT_TRUE(linphone_core_lime_x3dh_enabled(berthe.getLc())); + BC_ASSERT_TRUE(linphone_core_lime_x3dh_enabled(laure.getLc())); + } + + // Marie creates a new group chat room + const char *initialSubject = "Colleagues (characters: $ £ çà )"; + LinphoneChatRoom *marieCr = create_chat_room_client_side_with_expected_number_of_participants( + coresList, marie.getCMgr(), &initialMarieStats, participantsAddresses, initialSubject, 2, encrypted, + LinphoneChatRoomEphemeralModeDeviceManaged); + const LinphoneAddress *confAddr = linphone_chat_room_get_conference_address(marieCr); + + // Check that the chat room is correctly created on Michelle's side and that the participants are added + LinphoneChatRoom *michelleCr = check_creation_chat_room_client_side( + coresList, michelle.getCMgr(), &initialMichelleStats, confAddr, initialSubject, 2, FALSE); + + LinphoneChatRoom *bertheCr = check_creation_chat_room_client_side( + coresList, berthe.getCMgr(), &initialBertheStats, confAddr, initialSubject, 2, FALSE); + + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, michelle, laure, berthe}).wait([&focus] { + for (const auto &chatRoom : focus.getCore().getChatRooms()) { + for (const auto &participant : chatRoom->getParticipants()) { + for (const auto &device : participant->getDevices()) { + if (device->getState() != ParticipantDevice::State::Present) { + return false; + } + } + } + } + return true; + })); + + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_LinphoneConferenceStateCreated, + initialMichelleStats.number_of_LinphoneConferenceStateCreated + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &berthe.getStats().number_of_LinphoneConferenceStateCreated, + initialBertheStats.number_of_LinphoneConferenceStateCreated + 1, + liblinphone_tester_sip_timeout)); + + // Marie now changes the subject + const char *newSubject = "Let's go drink a beer"; + linphone_chat_room_set_subject(marieCr, newSubject); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_subject_changed, + initialMarieStats.number_of_subject_changed + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_subject_changed, + initialMichelleStats.number_of_subject_changed + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &berthe.getStats().number_of_subject_changed, + initialBertheStats.number_of_subject_changed + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_STRING_EQUAL(linphone_chat_room_get_subject(marieCr), newSubject); + BC_ASSERT_STRING_EQUAL(linphone_chat_room_get_subject(michelleCr), newSubject); + BC_ASSERT_STRING_EQUAL(linphone_chat_room_get_subject(bertheCr), newSubject); + + BC_ASSERT_EQUAL(linphone_chat_room_get_nb_participants(marieCr), 2, int, "%d"); + BC_ASSERT_EQUAL(linphone_chat_room_get_nb_participants(michelleCr), 2, int, "%d"); + BC_ASSERT_EQUAL(linphone_chat_room_get_nb_participants(bertheCr), 2, int, "%d"); + + const std::initializer_list<std::reference_wrapper<ConfCoreManager>> cores2{focus, marie, michelle, berthe}; + for (const ConfCoreManager &core : cores2) { + BC_ASSERT_TRUE( + CoreManagerAssert({focus, marie, michelle, berthe, laure}) + .waitUntil(chrono::seconds(10), [&focus, &core] { return checkChatroom(focus, core, -1); })); + }; + + ClientConference michelle2("michelle_rc", focus.getIdentity(), encrypted); + stats initialMichelle2Stats = michelle2.getStats(); + coresList = bctbx_list_append(coresList, michelle2.getLc()); + if (encrypted) { + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle2.getStats().number_of_X3dhUserCreationSuccess, + initialMichelle2Stats.number_of_X3dhUserCreationSuccess + 1, + x3dhServer_creationTimeout)); + BC_ASSERT_TRUE(linphone_core_lime_x3dh_enabled(michelle2.getLc())); + } + + focus.registerAsParticipantDevice(michelle2); + + LinphoneAddress *michelle2Contact = linphone_address_clone( + linphone_proxy_config_get_contact(linphone_core_get_default_proxy_config(michelle2.getLc()))); + ms_message("%s is adding device %s", linphone_core_get_identity(focus.getLc()), + linphone_address_as_string(michelle2Contact)); + focus.registerAsParticipantDevice(michelle2); + + // Notify chat room that a participant has registered + bctbx_list_t *devices = NULL; + const LinphoneAddress *deviceAddr = linphone_proxy_config_get_contact(michelle.getDefaultProxyConfig()); + LinphoneParticipantDeviceIdentity *identity = + linphone_factory_create_participant_device_identity(linphone_factory_get(), deviceAddr, ""); + linphone_participant_device_identity_set_capability_descriptor( + identity, linphone_core_get_linphone_specs(michelle.getLc())); + devices = bctbx_list_append(devices, identity); + + deviceAddr = linphone_proxy_config_get_contact(michelle2.getDefaultProxyConfig()); + identity = linphone_factory_create_participant_device_identity(linphone_factory_get(), deviceAddr, ""); + linphone_participant_device_identity_set_capability_descriptor( + identity, linphone_core_get_linphone_specs(michelle2.getLc())); + devices = bctbx_list_append(devices, identity); + + for (auto chatRoom : focus.getCore().getChatRooms()) { + linphone_chat_room_set_participant_devices(L_GET_C_BACK_PTR(chatRoom), michelle.getCMgr()->identity, + devices); + } + bctbx_list_free_with_data(devices, (bctbx_list_free_func)belle_sip_object_unref); + + LinphoneChatRoom *michelle2Cr = check_creation_chat_room_client_side( + coresList, michelle2.getCMgr(), &initialMichelle2Stats, confAddr, newSubject, 2, FALSE); + BC_ASSERT_PTR_NOT_NULL(michelle2Cr); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle2.getStats().number_of_LinphoneConferenceStateCreated, + initialMichelle2Stats.number_of_LinphoneConferenceStateCreated + 1, + liblinphone_tester_sip_timeout)); + + const std::initializer_list<std::reference_wrapper<ConfCoreManager>> cores3{focus, marie, michelle, michelle2, + berthe}; + for (const ConfCoreManager &core : cores3) { + BC_ASSERT_TRUE( + CoreManagerAssert({focus, marie, michelle, michelle2, berthe, laure}) + .waitUntil(chrono::seconds(10), [&focus, &core] { return checkChatroom(focus, core, -1); })); + }; + + initialMarieStats = marie.getStats(); + initialMichelleStats = michelle.getStats(); + initialBertheStats = berthe.getStats(); + initialLaureStats = laure.getStats(); + + Address laureAddr = laure.getIdentity(); + participantsAddresses = bctbx_list_append(NULL, linphone_address_ref(laureAddr.toC())); + ms_message("%s is adding participant %s", linphone_core_get_identity(focus.getLc()), + linphone_core_get_identity(laure.getLc())); + linphone_chat_room_add_participants(marieCr, participantsAddresses); + + LinphoneChatRoom *laureCr = check_creation_chat_room_client_side(coresList, laure.getCMgr(), &initialLaureStats, + confAddr, newSubject, 3, FALSE); + BC_ASSERT_PTR_NOT_NULL(laureCr); + + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_LinphoneConferenceStateCreationPending, + initialLaureStats.number_of_LinphoneConferenceStateCreationPending + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_LinphoneConferenceStateCreated, + initialLaureStats.number_of_LinphoneConferenceStateCreated + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_LinphoneChatRoomConferenceJoined, + initialLaureStats.number_of_LinphoneChatRoomConferenceJoined + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_participants_added, + initialMarieStats.number_of_participants_added + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &berthe.getStats().number_of_participants_added, + initialBertheStats.number_of_participants_added + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_participants_added, + initialMichelleStats.number_of_participants_added + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle2.getStats().number_of_participants_added, + initialMichelle2Stats.number_of_participants_added + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_participant_devices_added, + initialMarieStats.number_of_participant_devices_added + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &berthe.getStats().number_of_participant_devices_added, + initialBertheStats.number_of_participant_devices_added + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_participant_devices_added, + initialMichelleStats.number_of_participant_devices_added + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle2.getStats().number_of_participant_devices_added, + initialMichelle2Stats.number_of_participant_devices_added + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_EQUAL(linphone_chat_room_get_nb_participants(marieCr), 3, int, "%d"); + BC_ASSERT_EQUAL(linphone_chat_room_get_nb_participants(bertheCr), 3, int, "%d"); + BC_ASSERT_EQUAL(linphone_chat_room_get_nb_participants(michelleCr), 3, int, "%d"); + BC_ASSERT_EQUAL(linphone_chat_room_get_nb_participants(michelle2Cr), 3, int, "%d"); + BC_ASSERT_EQUAL(linphone_chat_room_get_nb_participants(laureCr), 3, int, "%d"); + + const std::initializer_list<std::reference_wrapper<ConfCoreManager>> cores{focus, marie, michelle, + michelle2, laure, berthe}; + for (const ConfCoreManager &core : cores) { + BC_ASSERT_TRUE( + CoreManagerAssert({focus, marie, michelle, michelle2, laure, berthe}) + .waitUntil(chrono::seconds(10), [&focus, &core] { return checkChatroom(focus, core, -1); })); + for (auto chatRoom : core.getCore().getChatRooms()) { + BC_ASSERT_EQUAL(chatRoom->getParticipants().size(), ((focus.getLc() == core.getLc())) ? 4 : 3, size_t, + "%zu"); + BC_ASSERT_STRING_EQUAL(chatRoom->getSubject().c_str(), newSubject); + } + }; + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, michelle, michelle2}).waitUntil(chrono::seconds(1), [] { return false; }); + + time_t participantAddedTime = ms_time(nullptr); + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, michelle, michelle2}).waitUntil(chrono::seconds(10), [] { return false; }); + + ms_message("%s is restarting its core", linphone_core_get_identity(focus.getLc())); + coresList = bctbx_list_remove(coresList, focus.getLc()); + // Restart flexisip + focus.reStart(); + coresList = bctbx_list_append(coresList, focus.getLc()); + for (auto chatRoom : focus.getCore().getChatRooms()) { + BC_ASSERT_EQUAL(linphone_chat_room_get_nb_participants(L_GET_C_BACK_PTR(chatRoom)), 4, int, "%d"); + } + + ms_message("%s is restarting its core", linphone_address_as_string(michelle2Contact)); + coresList = bctbx_list_remove(coresList, michelle2.getLc()); + // Restart michelle + michelle2.reStart(); + michelle2.setupMgrForConference(); + coresList = bctbx_list_append(coresList, michelle2.getLc()); + + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle2.getStats().number_of_LinphoneConferenceStateCreated, 1, + liblinphone_tester_sip_timeout)); + LinphoneAddress *michelleDeviceAddr = + linphone_address_clone(linphone_proxy_config_get_contact(michelle2.getDefaultProxyConfig())); + michelle2Cr = michelle2.searchChatRoom(michelleDeviceAddr, confAddr); + BC_ASSERT_PTR_NOT_NULL(michelle2Cr); + for (const ConfCoreManager &core : cores) { + BC_ASSERT_TRUE(checkChatroom(focus, core, participantAddedTime)); + for (auto chatRoom : core.getCore().getChatRooms()) { + BC_ASSERT_EQUAL(chatRoom->getParticipants().size(), ((focus.getLc() == core.getLc())) ? 4 : 3, size_t, + "%zu"); + BC_ASSERT_STRING_EQUAL(chatRoom->getSubject().c_str(), newSubject); + } + } + + LinphoneChatMessage *msg = linphone_chat_room_create_message_from_utf8(michelle2Cr, "back with you"); + linphone_chat_message_send(msg); + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, michelle, michelle2}).wait([msg] { + return (linphone_chat_message_get_state(msg) == LinphoneChatMessageStateDelivered); + })); + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, michelle, michelle2}).wait([marieCr] { + return linphone_chat_room_get_unread_messages_count(marieCr) == 1; + })); + linphone_chat_message_unref(msg); + msg = NULL; + + msg = linphone_chat_room_create_message_from_utf8(marieCr, "welcome back"); + linphone_chat_message_send(msg); + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, michelle, michelle2}).wait([msg] { + return (linphone_chat_message_get_state(msg) == LinphoneChatMessageStateDelivered); + })); + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, michelle, michelle2}).wait([michelleCr] { + return linphone_chat_room_get_unread_messages_count(michelleCr) == 1; + })); + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, michelle, michelle2}).wait([michelle2Cr] { + return linphone_chat_room_get_unread_messages_count(michelle2Cr) == 1; + })); + linphone_chat_message_unref(msg); + msg = NULL; + + msg = linphone_chat_room_create_message_from_utf8(michelleCr, "message blabla"); + linphone_chat_message_send(msg); + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, michelle, michelle2}).wait([msg] { + return (linphone_chat_message_get_state(msg) == LinphoneChatMessageStateDelivered); + })); + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, michelle, michelle2}).wait([marieCr] { + return linphone_chat_room_get_unread_messages_count(marieCr) == 2; + })); + linphone_chat_message_unref(msg); + + CoreManagerAssert({focus, marie}).waitUntil(std::chrono::seconds(1), [] { return false; }); + + CoreManagerAssert({focus, marie}).waitUntil(std::chrono::seconds(2), [] { return false; }); + + for (auto chatRoom : focus.getCore().getChatRooms()) { + for (auto participant : chatRoom->getParticipants()) { + // force deletion by removing devices + std::shared_ptr<Address> participantAddress = participant->getAddress(); + linphone_chat_room_set_participant_devices(L_GET_C_BACK_PTR(chatRoom), participantAddress->toC(), NULL); + } + } + + // wait until chatroom is deleted server side + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, michelle, michelle2}).wait([&focus] { + return focus.getCore().getChatRooms().size() == 0; + })); + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, michelle, michelle2}).waitUntil(chrono::seconds(2), [] { return false; }); + + // to avoid creation attempt of a new chatroom + auto config = focus.getDefaultProxyConfig(); + linphone_proxy_config_edit(config); + linphone_proxy_config_set_conference_factory_uri(config, NULL); + linphone_proxy_config_done(config); + + linphone_address_unref(michelle2Contact); + linphone_address_unref(michelleDeviceAddr); + bctbx_list_free(coresList); + } +} + +static void group_chat_room_with_client_restart(void) { + group_chat_room_with_client_restart_base(false); +} + +static void secure_group_chat_room_with_client_restart(void) { + group_chat_room_with_client_restart_base(true); +} + +static void group_chat_room_with_client_removed_added(void) { + Focus focus("chloe_rc"); + { // to make sure focus is destroyed after clients. + ClientConference marie("marie_rc", focus.getIdentity()); + ClientConference michelle("michelle_rc", focus.getIdentity()); + ClientConference pauline("pauline_rc", focus.getIdentity()); + + focus.registerAsParticipantDevice(marie); + focus.registerAsParticipantDevice(michelle); + focus.registerAsParticipantDevice(pauline); + + bctbx_list_t *coresList = bctbx_list_append(NULL, focus.getLc()); + coresList = bctbx_list_append(coresList, marie.getLc()); + coresList = bctbx_list_append(coresList, michelle.getLc()); + coresList = bctbx_list_append(coresList, pauline.getLc()); + bctbx_list_t *participantsAddresses = NULL; + Address michelleAddr = michelle.getIdentity(); + participantsAddresses = bctbx_list_append(participantsAddresses, linphone_address_ref(michelleAddr.toC())); + Address paulineAddr = pauline.getIdentity(); + participantsAddresses = bctbx_list_append(participantsAddresses, linphone_address_ref(paulineAddr.toC())); + + stats initialMarieStats = marie.getStats(); + stats initialMichelleStats = michelle.getStats(); + stats initialPaulineStats = pauline.getStats(); + + // Marie creates a new group chat room + const char *initialSubject = "Colleagues (characters: $ £ çà )"; + LinphoneChatRoom *marieCr = create_chat_room_client_side_with_expected_number_of_participants( + coresList, marie.getCMgr(), &initialMarieStats, participantsAddresses, initialSubject, 2, FALSE, + LinphoneChatRoomEphemeralModeDeviceManaged); + const LinphoneAddress *confAddr = linphone_chat_room_get_conference_address(marieCr); + + // Check that the chat room is correctly created on Michelle's side and that the participants are added + LinphoneChatRoom *michelleCr = check_creation_chat_room_client_side( + coresList, michelle.getCMgr(), &initialMichelleStats, confAddr, initialSubject, 2, FALSE); + + // Check that the chat room is correctly created on Pauline's side and that the participants are added + LinphoneChatRoom *paulineCr = check_creation_chat_room_client_side( + coresList, pauline.getCMgr(), &initialPaulineStats, confAddr, initialSubject, 2, FALSE); + + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline, michelle}).wait([&focus] { + for (auto chatRoom : focus.getCore().getChatRooms()) { + for (auto participant : chatRoom->getParticipants()) { + for (auto device : participant->getDevices()) + if (device->getState() != ParticipantDevice::State::Present) { + return false; + } + } + } + return true; + })); + + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_LinphoneConferenceStateCreated, + initialMichelleStats.number_of_LinphoneConferenceStateCreated + 1, + liblinphone_tester_sip_timeout)); + + // Marie now changes the subject + const char *newSubject = "Let's go drink a beer"; + linphone_chat_room_set_subject(marieCr, newSubject); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_subject_changed, + initialMarieStats.number_of_subject_changed + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_subject_changed, + initialMichelleStats.number_of_subject_changed + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_subject_changed, + initialPaulineStats.number_of_subject_changed + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_STRING_EQUAL(linphone_chat_room_get_subject(marieCr), newSubject); + BC_ASSERT_STRING_EQUAL(linphone_chat_room_get_subject(paulineCr), newSubject); + BC_ASSERT_STRING_EQUAL(linphone_chat_room_get_subject(michelleCr), newSubject); + + BC_ASSERT_EQUAL(linphone_chat_room_get_nb_participants(marieCr), 2, int, "%d"); + BC_ASSERT_EQUAL(linphone_chat_room_get_nb_participants(paulineCr), 2, int, "%d"); + BC_ASSERT_EQUAL(linphone_chat_room_get_nb_participants(michelleCr), 2, int, "%d"); + + initialMarieStats = marie.getStats(); + initialMichelleStats = michelle.getStats(); + initialPaulineStats = pauline.getStats(); + + ClientConference michelle2("michelle_rc", focus.getIdentity()); + stats initialMichelle2Stats = michelle2.getStats(); + coresList = bctbx_list_append(coresList, michelle2.getLc()); + focus.registerAsParticipantDevice(michelle2); + // Notify chat room that a participant has registered + + bctbx_list_t *devices = NULL; + const LinphoneAddress *deviceAddr = linphone_proxy_config_get_contact(michelle.getDefaultProxyConfig()); + LinphoneParticipantDeviceIdentity *identity = + linphone_factory_create_participant_device_identity(linphone_factory_get(), deviceAddr, ""); + linphone_participant_device_identity_set_capability_descriptor( + identity, linphone_core_get_linphone_specs(michelle.getLc())); + devices = bctbx_list_append(devices, identity); + + deviceAddr = linphone_proxy_config_get_contact(michelle2.getDefaultProxyConfig()); + identity = linphone_factory_create_participant_device_identity(linphone_factory_get(), deviceAddr, ""); + linphone_participant_device_identity_set_capability_descriptor( + identity, linphone_core_get_linphone_specs(michelle2.getLc())); + devices = bctbx_list_append(devices, identity); + + for (auto chatRoom : focus.getCore().getChatRooms()) { + linphone_chat_room_set_participant_devices(L_GET_C_BACK_PTR(chatRoom), michelle.getCMgr()->identity, + devices); + } + bctbx_list_free_with_data(devices, (bctbx_list_free_func)belle_sip_object_unref); + + LinphoneChatRoom *michelle2Cr = check_creation_chat_room_client_side( + coresList, michelle2.getCMgr(), &initialMichelle2Stats, confAddr, newSubject, 2, FALSE); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle2.getStats().number_of_LinphoneConferenceStateCreated, + initialMichelle2Stats.number_of_LinphoneConferenceStateCreated + 1, + liblinphone_tester_sip_timeout)); + + for (auto chatRoom : focus.getCore().getChatRooms()) { + BC_ASSERT_EQUAL(linphone_chat_room_get_nb_participants(L_GET_C_BACK_PTR(chatRoom)), 3, int, "%d"); + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_participant_devices_added, + initialMarieStats.number_of_participant_devices_added + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_participant_devices_added, + initialPaulineStats.number_of_participant_devices_added + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_participant_devices_added, + initialMichelleStats.number_of_participant_devices_added + 1, + liblinphone_tester_sip_timeout)); + + LinphoneAddress *michelle2Contact = + linphone_address_clone(linphone_proxy_config_get_contact(michelle2.getDefaultProxyConfig())); + ms_message("%s is restarting its core", linphone_address_as_string(michelle2Contact)); + linphone_address_unref(michelle2Contact); + coresList = bctbx_list_remove(coresList, michelle2.getLc()); + // Restart flexisip + michelle2.reStart(); + michelle2.setupMgrForConference(); + coresList = bctbx_list_append(coresList, michelle2.getLc()); + + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle2.getStats().number_of_LinphoneConferenceStateCreated, 1, + liblinphone_tester_sip_timeout)); + LinphoneAddress *michelleDeviceAddr = + linphone_address_clone(linphone_proxy_config_get_contact(michelle2.getDefaultProxyConfig())); + michelle2Cr = michelle2.searchChatRoom(michelleDeviceAddr, confAddr); + BC_ASSERT_PTR_NOT_NULL(michelle2Cr); + + if (michelle2Cr) { + BC_ASSERT_EQUAL(linphone_chat_room_get_nb_participants(michelle2Cr), 2, int, "%d"); + BC_ASSERT_STRING_EQUAL(linphone_chat_room_get_subject(michelle2Cr), newSubject); + } + + BC_ASSERT_EQUAL(linphone_chat_room_get_nb_participants(marieCr), 2, int, "%d"); + BC_ASSERT_STRING_EQUAL(linphone_chat_room_get_subject(marieCr), newSubject); + + BC_ASSERT_EQUAL(linphone_chat_room_get_nb_participants(michelleCr), 2, int, "%d"); + BC_ASSERT_STRING_EQUAL(linphone_chat_room_get_subject(michelleCr), newSubject); + + BC_ASSERT_EQUAL(linphone_chat_room_get_nb_participants(paulineCr), 2, int, "%d"); + BC_ASSERT_STRING_EQUAL(linphone_chat_room_get_subject(paulineCr), newSubject); + + linphone_core_delete_chat_room(michelle2.getLc(), michelle2Cr); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle2.getStats().number_of_LinphoneConferenceStateDeleted, + initialMichelle2Stats.number_of_LinphoneConferenceStateDeleted + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_participants_removed, + initialMarieStats.number_of_participant_devices_removed + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_participants_removed, + initialPaulineStats.number_of_participant_devices_removed + 1, + liblinphone_tester_sip_timeout)); + + initialMarieStats = marie.getStats(); + initialMichelleStats = michelle.getStats(); + initialPaulineStats = pauline.getStats(); + + Address michelle2Addr = michelle2.getIdentity(); + linphone_chat_room_add_participant(marieCr, linphone_address_ref(michelle2Addr.toC())); + + michelle2Cr = check_creation_chat_room_client_side(coresList, michelle2.getCMgr(), &initialMichelle2Stats, + confAddr, newSubject, 2, FALSE); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle2.getStats().number_of_LinphoneConferenceStateCreated, + initialMichelle2Stats.number_of_LinphoneConferenceStateCreated + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_participant_devices_added, + initialMarieStats.number_of_participant_devices_added + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_participant_devices_added, + initialPaulineStats.number_of_participant_devices_added + 1, + liblinphone_tester_sip_timeout)); + + LinphoneChatMessage *msg = linphone_chat_room_create_message_from_utf8(marieCr, "message blabla"); + linphone_chat_message_send(msg); + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline, michelle, michelle2}).wait([msg] { + return (linphone_chat_message_get_state(msg) == LinphoneChatMessageStateDelivered); + })); + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline, michelle, michelle2}).wait([michelleCr] { + return linphone_chat_room_get_unread_messages_count(michelleCr) == 1; + })); + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline, michelle, michelle2}).wait([michelle2Cr] { + return linphone_chat_room_get_unread_messages_count(michelle2Cr) == 1; + })); + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline, michelle, michelle2}).wait([paulineCr] { + return linphone_chat_room_get_unread_messages_count(paulineCr) == 1; + })); + linphone_chat_message_unref(msg); + + CoreManagerAssert({focus, marie, pauline, michelle, michelle2}).waitUntil(std::chrono::seconds(3), [] { + return false; + }); + + for (auto chatRoom : focus.getCore().getChatRooms()) { + for (auto participant : chatRoom->getParticipants()) { + // force deletion by removing devices + std::shared_ptr<Address> participantAddress = participant->getAddress(); + linphone_chat_room_set_participant_devices(L_GET_C_BACK_PTR(chatRoom), participantAddress->toC(), NULL); + } + } + + // wait until chatroom is deleted server side + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline, michelle, michelle2}).wait([&focus] { + return focus.getCore().getChatRooms().size() == 0; + })); + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, michelle, michelle2}).waitUntil(chrono::seconds(2), [] { + return false; + }); + + // to avoid creation attempt of a new chatroom + auto config = focus.getDefaultProxyConfig(); + linphone_proxy_config_edit(config); + linphone_proxy_config_set_conference_factory_uri(config, NULL); + linphone_proxy_config_done(config); + + linphone_address_unref(michelleDeviceAddr); + bctbx_list_free(coresList); + } +} + +static void +group_chat_room_with_client_idmn_after_restart_base(bool_t encrypted, bool_t add_participant, bool_t stop_core) { + Focus focus("chloe_rc"); + { // to make sure focus is destroyed after clients. + ClientConference marie("marie_rc", focus.getIdentity(), encrypted); + ClientConference michelle("michelle_rc", focus.getIdentity(), encrypted); + ClientConference michelle2("michelle_rc", focus.getIdentity(), encrypted); + ClientConference pauline("pauline_rc", focus.getIdentity(), encrypted); + ClientConference laure("laure_tcp_rc", focus.getIdentity(), encrypted); + + focus.registerAsParticipantDevice(marie); + focus.registerAsParticipantDevice(michelle); + focus.registerAsParticipantDevice(michelle2); + focus.registerAsParticipantDevice(laure); + focus.registerAsParticipantDevice(pauline); + + linphone_im_notif_policy_enable_all(linphone_core_get_im_notif_policy(marie.getLc())); + linphone_im_notif_policy_enable_all(linphone_core_get_im_notif_policy(pauline.getLc())); + linphone_im_notif_policy_enable_all(linphone_core_get_im_notif_policy(laure.getLc())); + linphone_im_notif_policy_enable_all(linphone_core_get_im_notif_policy(michelle.getLc())); + linphone_im_notif_policy_enable_all(linphone_core_get_im_notif_policy(michelle2.getLc())); + + stats marie_stat = marie.getStats(); + stats pauline_stat = pauline.getStats(); + stats laure_stat = laure.getStats(); + stats michelle_stat = michelle.getStats(); + stats michelle2_stat = michelle2.getStats(); + bctbx_list_t *coresList = bctbx_list_append(NULL, focus.getLc()); + coresList = bctbx_list_append(coresList, marie.getLc()); + coresList = bctbx_list_append(coresList, pauline.getLc()); + coresList = bctbx_list_append(coresList, laure.getLc()); + coresList = bctbx_list_append(coresList, michelle.getLc()); + coresList = bctbx_list_append(coresList, michelle2.getLc()); + + if (encrypted) { + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_X3dhUserCreationSuccess, + marie_stat.number_of_X3dhUserCreationSuccess + 1, x3dhServer_creationTimeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_X3dhUserCreationSuccess, + laure_stat.number_of_X3dhUserCreationSuccess + 1, x3dhServer_creationTimeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_X3dhUserCreationSuccess, + pauline_stat.number_of_X3dhUserCreationSuccess + 1, + x3dhServer_creationTimeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_X3dhUserCreationSuccess, + michelle_stat.number_of_X3dhUserCreationSuccess + 1, + x3dhServer_creationTimeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle2.getStats().number_of_X3dhUserCreationSuccess, + michelle2_stat.number_of_X3dhUserCreationSuccess + 1, + x3dhServer_creationTimeout)); + + BC_ASSERT_TRUE(linphone_core_lime_x3dh_enabled(marie.getLc())); + BC_ASSERT_TRUE(linphone_core_lime_x3dh_enabled(pauline.getLc())); + BC_ASSERT_TRUE(linphone_core_lime_x3dh_enabled(laure.getLc())); + BC_ASSERT_TRUE(linphone_core_lime_x3dh_enabled(michelle.getLc())); + BC_ASSERT_TRUE(linphone_core_lime_x3dh_enabled(michelle2.getLc())); + } + + bctbx_list_t *participantsAddresses = NULL; + Address michelleAddr = michelle.getIdentity(); + participantsAddresses = bctbx_list_append(participantsAddresses, linphone_address_ref(michelleAddr.toC())); + Address michelle2Addr = michelle2.getIdentity(); + participantsAddresses = bctbx_list_append(participantsAddresses, linphone_address_ref(michelle2Addr.toC())); + Address laureAddr = laure.getIdentity(); + participantsAddresses = bctbx_list_append(participantsAddresses, linphone_address_ref(laureAddr.toC())); + Address paulineAddr = pauline.getIdentity(); + participantsAddresses = bctbx_list_append(participantsAddresses, linphone_address_ref(paulineAddr.toC())); + + // Marie creates a new group chat room + const char *initialSubject = "Colleagues (characters: $ £ çà )"; + LinphoneChatRoom *marieCr = create_chat_room_client_side_with_expected_number_of_participants( + coresList, marie.getCMgr(), &marie_stat, participantsAddresses, initialSubject, 3, encrypted, + LinphoneChatRoomEphemeralModeDeviceManaged); + BC_ASSERT_PTR_NOT_NULL(marieCr); + const LinphoneAddress *confAddr = linphone_chat_room_get_conference_address(marieCr); + char *conference_address = linphone_address_as_string(confAddr); + + // Check that the chat room is correctly created on Michelle's side and that the participants are added + LinphoneChatRoom *michelleCr = check_creation_chat_room_client_side( + coresList, michelle.getCMgr(), &michelle_stat, confAddr, initialSubject, 3, FALSE); + BC_ASSERT_PTR_NOT_NULL(michelleCr); + LinphoneChatRoom *michelle2Cr = check_creation_chat_room_client_side( + coresList, michelle2.getCMgr(), &michelle2_stat, confAddr, initialSubject, 3, FALSE); + BC_ASSERT_PTR_NOT_NULL(michelle2Cr); + + // Check that the chat room is correctly created on Pauline's side and that the participants are added + LinphoneChatRoom *paulineCr = check_creation_chat_room_client_side(coresList, pauline.getCMgr(), &pauline_stat, + confAddr, initialSubject, 3, FALSE); + BC_ASSERT_PTR_NOT_NULL(paulineCr); + + // Check that the chat room is correctly created on Laure's side and that the participants are added + LinphoneChatRoom *laureCr = check_creation_chat_room_client_side(coresList, laure.getCMgr(), &laure_stat, + confAddr, initialSubject, 3, FALSE); + BC_ASSERT_PTR_NOT_NULL(laureCr); + + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline, michelle, michelle2, laure}).wait([&focus] { + for (auto chatRoom : focus.getCore().getChatRooms()) { + for (auto participant : chatRoom->getParticipants()) { + for (auto device : participant->getDevices()) + if (device->getState() != ParticipantDevice::State::Present) { + return false; + } + } + } + return true; + })); + + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_LinphoneConferenceStateCreated, + michelle_stat.number_of_LinphoneConferenceStateCreated + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle2.getStats().number_of_LinphoneConferenceStateCreated, + michelle2_stat.number_of_LinphoneConferenceStateCreated + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_LinphoneConferenceStateCreated, + laure_stat.number_of_LinphoneConferenceStateCreated + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneConferenceStateCreated, + pauline_stat.number_of_LinphoneConferenceStateCreated + 1, + liblinphone_tester_sip_timeout)); + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, michelle, michelle2, laure}).waitUntil(chrono::seconds(5), [] { + return false; + }); + + ms_message("%s goes offline", linphone_core_get_identity(laure.getLc())); + linphone_core_set_network_reachable(laure.getLc(), FALSE); + + ClientConference berthe("berthe_rc", focus.getIdentity(), encrypted); + focus.registerAsParticipantDevice(berthe); + linphone_im_notif_policy_enable_all(linphone_core_get_im_notif_policy(berthe.getLc())); + stats berthe_stat = berthe.getStats(); + coresList = bctbx_list_append(coresList, berthe.getLc()); + + if (encrypted) { + BC_ASSERT_TRUE(wait_for_list(coresList, &berthe.getStats().number_of_X3dhUserCreationSuccess, + berthe_stat.number_of_X3dhUserCreationSuccess + 1, + x3dhServer_creationTimeout)); + BC_ASSERT_TRUE(linphone_core_lime_x3dh_enabled(berthe.getLc())); + } + + marie_stat = marie.getStats(); + pauline_stat = pauline.getStats(); + laure_stat = laure.getStats(); + michelle_stat = michelle.getStats(); + michelle2_stat = michelle2.getStats(); + LinphoneChatRoom *bertheCr = NULL; + if (add_participant) { + Address bertheAddr = berthe.getIdentity(); + ms_message("%s adds %s to chatroom %s", linphone_core_get_identity(marie.getLc()), + linphone_core_get_identity(berthe.getLc()), conference_address); + linphone_chat_room_add_participant(marieCr, bertheAddr.toC()); + bertheCr = check_creation_chat_room_client_side(coresList, berthe.getCMgr(), &berthe_stat, confAddr, + initialSubject, 4, FALSE); + BC_ASSERT_PTR_NOT_NULL(bertheCr); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_participants_added, + pauline_stat.number_of_participants_added + 1, 5000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_participant_devices_added, + pauline_stat.number_of_participant_devices_added + 1, 5000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_participants_added, + michelle_stat.number_of_participants_added + 1, 5000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_participant_devices_added, + michelle_stat.number_of_participant_devices_added + 1, 5000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle2.getStats().number_of_participants_added, + michelle2_stat.number_of_participants_added + 1, 5000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle2.getStats().number_of_participant_devices_added, + michelle2_stat.number_of_participant_devices_added + 1, 5000)); + } + + std::string msg_text = "message pauline blabla"; + LinphoneChatMessage *msg = ClientConference::sendTextMsg(paulineCr, msg_text); + + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, michelle, michelle2, berthe, laure, pauline}).wait([msg] { + return (linphone_chat_message_get_state(msg) == LinphoneChatMessageStateDelivered); + })); + + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, michelle, michelle2, berthe, laure, pauline}).wait([marieCr] { + return linphone_chat_room_get_unread_messages_count(marieCr) == 1; + })); + LinphoneChatMessage *marieLastMsg = marie.getStats().last_received_chat_message; + BC_ASSERT_PTR_NOT_NULL(marieLastMsg); + if (marieLastMsg) { + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_utf8_text(marieLastMsg), msg_text.c_str()); + } + + BC_ASSERT_TRUE( + CoreManagerAssert({focus, marie, michelle, michelle2, berthe, laure, pauline}).wait([michelleCr] { + return linphone_chat_room_get_unread_messages_count(michelleCr) == 1; + })); + LinphoneChatMessage *michelleLastMsg = michelle.getStats().last_received_chat_message; + BC_ASSERT_PTR_NOT_NULL(michelleLastMsg); + if (michelleLastMsg) { + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_utf8_text(michelleLastMsg), msg_text.c_str()); + } + + BC_ASSERT_TRUE( + CoreManagerAssert({focus, marie, michelle, michelle2, berthe, laure, pauline}).wait([michelle2Cr] { + return linphone_chat_room_get_unread_messages_count(michelle2Cr) == 1; + })); + LinphoneChatMessage *michelle2LastMsg = michelle2.getStats().last_received_chat_message; + BC_ASSERT_PTR_NOT_NULL(michelle2LastMsg); + if (michelle2LastMsg) { + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_utf8_text(michelle2LastMsg), msg_text.c_str()); + } + + if (bertheCr) { + BC_ASSERT_TRUE( + CoreManagerAssert({focus, marie, michelle, michelle2, berthe, laure, pauline}).wait([bertheCr] { + return linphone_chat_room_get_unread_messages_count(bertheCr) == 1; + })); + LinphoneChatMessage *bertheLastMsg = berthe.getStats().last_received_chat_message; + BC_ASSERT_PTR_NOT_NULL(bertheLastMsg); + if (bertheLastMsg) { + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_utf8_text(bertheLastMsg), msg_text.c_str()); + } + } + + linphone_chat_room_mark_as_read(michelleCr); + linphone_chat_room_mark_as_read(michelle2Cr); + if (bertheCr) { + linphone_chat_room_mark_as_read(bertheCr); + } + linphone_chat_room_mark_as_read(marieCr); + linphone_chat_room_mark_as_read(paulineCr); + + for (const auto client : {marie.getCMgr(), michelle.getCMgr(), michelle2.getCMgr(), berthe.getCMgr(), + laure.getCMgr(), pauline.getCMgr()}) { + BC_ASSERT_TRUE( + CoreManagerAssert({focus, marie, michelle, michelle2, berthe, laure, pauline}) + .wait([client, &berthe, &laure, &pauline, &add_participant, &msg] { + bool ret = false; + LinphoneChatMessage *lastMsg = + (client->lc == pauline.getLc()) ? msg : client->stat.last_received_chat_message; + if ((client->lc == laure.getLc()) || (!add_participant && (client->lc == berthe.getLc()))) { + ret = (lastMsg == nullptr); + } else { + ret = (lastMsg != nullptr); + if (lastMsg) { + bctbx_list_t *displayed_list = linphone_chat_message_get_participants_by_imdn_state( + lastMsg, LinphoneChatMessageStateDisplayed); + const size_t expected_displayed_number = + ((client->lc == pauline.getLc()) ? 2 : 1) + (add_participant ? 1 : 0); + ret &= (bctbx_list_size(displayed_list) == expected_displayed_number); + bctbx_list_free_with_data(displayed_list, + (bctbx_list_free_func)linphone_participant_imdn_state_unref); + } + } + return ret; + })); + } + + ms_message("%s comes back online", linphone_core_get_identity(laure.getLc())); + linphone_core_set_network_reachable(laure.getLc(), TRUE); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_LinphoneMessageReceived, + laure_stat.number_of_LinphoneMessageReceived + 1, liblinphone_tester_sip_timeout)); + LinphoneAddress *laureDeviceAddr = linphone_address_clone( + linphone_proxy_config_get_contact(linphone_core_get_default_proxy_config(laure.getLc()))); + laureCr = linphone_core_search_chat_room(laure.getLc(), NULL, laureDeviceAddr, confAddr, NULL); + BC_ASSERT_PTR_NOT_NULL(laureCr); + char *uuid = NULL; + if (linphone_config_get_string(linphone_core_get_config(laure.getLc()), "misc", "uuid", NULL)) { + uuid = + bctbx_strdup(linphone_config_get_string(linphone_core_get_config(laure.getLc()), "misc", "uuid", NULL)); + } + if (laureCr) { + BC_ASSERT_TRUE( + CoreManagerAssert({focus, marie, michelle, michelle2, berthe, laure, pauline}).wait([laureCr] { + return linphone_chat_room_get_unread_messages_count(laureCr) == 1; + })); + linphone_chat_room_mark_as_read(laureCr); + if (stop_core) { + ms_message("%s stops its core", linphone_core_get_identity(laure.getLc())); + coresList = bctbx_list_remove(coresList, laure.getLc()); + linphone_core_manager_stop(laure.getCMgr()); + } + } + linphone_address_unref(laureDeviceAddr); + + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneMessageDisplayed, + pauline_stat.number_of_LinphoneMessageDisplayed + 1, + liblinphone_tester_sip_timeout)); + + if (stop_core) { + linphone_core_manager_configure(laure.getCMgr()); + // Make sure gruu is preserved + linphone_config_set_string(linphone_core_get_config(laure.getLc()), "misc", "uuid", uuid); + ms_message("%s starts its core", linphone_core_get_identity(laure.getLc())); + linphone_im_notif_policy_enable_all(linphone_core_get_im_notif_policy(laure.getLc())); + linphone_core_manager_start(laure.getCMgr(), TRUE); + coresList = bctbx_list_append(coresList, laure.getLc()); + } + + if (uuid) { + bctbx_free(uuid); + } + + for (const auto client : {marie.getCMgr(), michelle.getCMgr(), michelle2.getCMgr(), berthe.getCMgr(), + laure.getCMgr(), pauline.getCMgr()}) { + BC_ASSERT_TRUE( + CoreManagerAssert({focus, marie, michelle, michelle2, berthe, laure, pauline}) + .wait([client, &berthe, &pauline, &add_participant, &confAddr, &stop_core] { + const LinphoneAddress *deviceAddr = + linphone_proxy_config_get_contact(linphone_core_get_default_proxy_config(client->lc)); + LinphoneChatRoom *cr = + linphone_core_search_chat_room(client->lc, NULL, deviceAddr, confAddr, NULL); + LinphoneChatMessage *lastMsg = cr ? linphone_chat_room_get_last_message_in_history(cr) : NULL; + bool ret = false; + if (!add_participant && (client->lc == berthe.getLc())) { + ret = (lastMsg == nullptr); + } else { + ret = (lastMsg != nullptr); + if (lastMsg) { + bctbx_list_t *displayed_list = linphone_chat_message_get_participants_by_imdn_state( + lastMsg, LinphoneChatMessageStateDisplayed); + size_t expected_displayed_number = 0; + if (client->lc == berthe.getLc()) { + expected_displayed_number = 2 + (stop_core ? 0 : 1); + } else { + expected_displayed_number = + ((client->lc == pauline.getLc()) ? 3 : 2) + (add_participant ? 1 : 0); + } + ret &= (bctbx_list_size(displayed_list) == expected_displayed_number); + bctbx_list_free_with_data(displayed_list, + (bctbx_list_free_func)linphone_participant_imdn_state_unref); + } + } + return ret; + })); + } + + linphone_chat_message_unref(msg); + msg = nullptr; + + for (auto chatRoom : focus.getCore().getChatRooms()) { + for (auto participant : chatRoom->getParticipants()) { + // force deletion by removing devices + std::shared_ptr<Address> participantAddress = participant->getAddress(); + linphone_chat_room_set_participant_devices(L_GET_C_BACK_PTR(chatRoom), participantAddress->toC(), NULL); + } + } + + // wait until chatroom is deleted server side + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline, michelle, michelle2, berthe, laure}).wait([&focus] { + return focus.getCore().getChatRooms().size() == 0; + })); + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, michelle, michelle2, berthe, laure}) + .waitUntil(chrono::seconds(2), [] { return false; }); + + // to avoid creation attempt of a new chatroom + auto config = focus.getDefaultProxyConfig(); + linphone_proxy_config_edit(config); + linphone_proxy_config_set_conference_factory_uri(config, NULL); + linphone_proxy_config_done(config); + + ms_free(conference_address); + bctbx_list_free(coresList); + } +} + +static void group_chat_room_with_client_idmn_after_restart(void) { + group_chat_room_with_client_idmn_after_restart_base(FALSE, TRUE, FALSE); +} + +static void secure_group_chat_room_with_client_idmn_sent_after_restart(void) { + group_chat_room_with_client_idmn_after_restart_base(TRUE, FALSE, FALSE); +} + +static void secure_group_chat_room_with_client_idmn_sent_after_restart_and_participant_added(void) { + group_chat_room_with_client_idmn_after_restart_base(TRUE, TRUE, FALSE); +} + +static void secure_group_chat_room_with_client_idmn_sent_after_restart_and_participant_added_and_core_stopped(void) { + group_chat_room_with_client_idmn_after_restart_base(TRUE, TRUE, TRUE); +} + +static void group_chat_room_with_client_deletes_chatroom_after_restart(void) { + Focus focus("chloe_rc"); + { // to make sure focus is destroyed after clients. + bool encrypted = false; + ClientConference marie("marie_rc", focus.getIdentity(), encrypted); + ClientConference michelle("michelle_rc", focus.getIdentity(), encrypted); + ClientConference pauline("pauline_rc", focus.getIdentity(), encrypted); + ClientConference laure("laure_tcp_rc", focus.getIdentity(), encrypted); + + focus.registerAsParticipantDevice(marie); + focus.registerAsParticipantDevice(michelle); + focus.registerAsParticipantDevice(laure); + focus.registerAsParticipantDevice(pauline); + + stats marie_stat = marie.getStats(); + stats pauline_stat = pauline.getStats(); + stats laure_stat = laure.getStats(); + stats michelle_stat = michelle.getStats(); + bctbx_list_t *coresList = bctbx_list_append(NULL, focus.getLc()); + coresList = bctbx_list_append(coresList, marie.getLc()); + coresList = bctbx_list_append(coresList, pauline.getLc()); + coresList = bctbx_list_append(coresList, laure.getLc()); + coresList = bctbx_list_append(coresList, michelle.getLc()); + + bctbx_list_t *participantsAddresses = NULL; + Address michelleAddr = michelle.getIdentity(); + participantsAddresses = bctbx_list_append(participantsAddresses, linphone_address_ref(michelleAddr.toC())); + Address laureAddr = laure.getIdentity(); + participantsAddresses = bctbx_list_append(participantsAddresses, linphone_address_ref(laureAddr.toC())); + Address paulineAddr = pauline.getIdentity(); + participantsAddresses = bctbx_list_append(participantsAddresses, linphone_address_ref(paulineAddr.toC())); + + // Marie creates a new group chat room + const char *initialSubject = "Colleagues (characters: $ £ çà )"; + LinphoneChatRoom *marieCr = create_chat_room_client_side_with_expected_number_of_participants( + coresList, marie.getCMgr(), &marie_stat, participantsAddresses, initialSubject, 3, encrypted, + LinphoneChatRoomEphemeralModeDeviceManaged); + BC_ASSERT_PTR_NOT_NULL(marieCr); + const LinphoneAddress *confAddr = linphone_chat_room_get_conference_address(marieCr); + char *conference_address = linphone_address_as_string(confAddr); + + // Check that the chat room is correctly created on Michelle's side and that the participants are added + LinphoneChatRoom *michelleCr = check_creation_chat_room_client_side( + coresList, michelle.getCMgr(), &michelle_stat, confAddr, initialSubject, 3, FALSE); + BC_ASSERT_PTR_NOT_NULL(michelleCr); + + // Check that the chat room is correctly created on Pauline's side and that the participants are added + LinphoneChatRoom *paulineCr = check_creation_chat_room_client_side(coresList, pauline.getCMgr(), &pauline_stat, + confAddr, initialSubject, 3, FALSE); + BC_ASSERT_PTR_NOT_NULL(paulineCr); + + // Check that the chat room is correctly created on Laure's side and that the participants are added + LinphoneChatRoom *laureCr = check_creation_chat_room_client_side(coresList, laure.getCMgr(), &laure_stat, + confAddr, initialSubject, 3, FALSE); + BC_ASSERT_PTR_NOT_NULL(laureCr); + + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline, michelle, laure}).wait([&focus] { + for (auto chatRoom : focus.getCore().getChatRooms()) { + for (auto participant : chatRoom->getParticipants()) { + for (auto device : participant->getDevices()) + if (device->getState() != ParticipantDevice::State::Present) { + return false; + } + } + } + return true; + })); + + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_LinphoneConferenceStateCreated, + michelle_stat.number_of_LinphoneConferenceStateCreated + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_LinphoneConferenceStateCreated, + laure_stat.number_of_LinphoneConferenceStateCreated + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneConferenceStateCreated, + pauline_stat.number_of_LinphoneConferenceStateCreated + 1, + liblinphone_tester_sip_timeout)); + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, michelle, laure}).waitUntil(chrono::seconds(5), [] { return false; }); + + ms_message("%s reinitializes its core", linphone_core_get_identity(laure.getLc())); + coresList = bctbx_list_remove(coresList, laure.getLc()); + linphone_core_manager_reinit(laure.getCMgr()); + + stats focus_stat = focus.getStats(); + marie_stat = marie.getStats(); + pauline_stat = pauline.getStats(); + laure_stat = laure.getStats(); + michelle_stat = michelle.getStats(); + + ms_message("%s starts again its core", linphone_core_get_identity(laure.getLc())); + linphone_core_manager_start(laure.getCMgr(), TRUE); + setup_mgr_for_conference(laure.getCMgr(), NULL); + coresList = bctbx_list_append(coresList, laure.getLc()); + + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_LinphoneRegistrationOk, + laure_stat.number_of_LinphoneRegistrationOk + 1, liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_LinphoneSubscriptionActive, + laure_stat.number_of_LinphoneSubscriptionActive + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline, michelle, laure}).wait([&laure] { + if (laure.getCore().getChatRooms().size() < 1) { + return false; + } + for (auto chatRoom : laure.getCore().getChatRooms()) { + if (chatRoom->getState() != ChatRoom::State::Created) { + return false; + } + } + return true; + })); + + const LinphoneAddress *laureDeviceAddr = + linphone_proxy_config_get_contact(linphone_core_get_default_proxy_config(laure.getLc())); + laureCr = linphone_core_search_chat_room(laure.getLc(), NULL, laureDeviceAddr, confAddr, NULL); + BC_ASSERT_PTR_NOT_NULL(laureCr); + + if (laureCr) { + linphone_core_delete_chat_room(laure.getLc(), laureCr); + } + + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline, michelle, laure}).wait([&laure] { + return (laure.getCore().getChatRooms().size() == 0); + })); + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participants_removed, + focus_stat.number_of_participants_removed + 1, liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_participants_removed, + marie_stat.number_of_participants_removed + 1, liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_participants_removed, + pauline_stat.number_of_participants_removed + 1, liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_participants_removed, + michelle_stat.number_of_participants_removed + 1, liblinphone_tester_sip_timeout)); + + for (auto chatRoom : focus.getCore().getChatRooms()) { + for (auto participant : chatRoom->getParticipants()) { + // force deletion by removing devices + std::shared_ptr<Address> participantAddress = participant->getAddress(); + linphone_chat_room_set_participant_devices(L_GET_C_BACK_PTR(chatRoom), participantAddress->toC(), NULL); + } + } + + // wait until chatroom is deleted server side + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline, michelle, laure}).wait([&focus] { + return focus.getCore().getChatRooms().size() == 0; + })); + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, michelle, laure}).waitUntil(chrono::seconds(2), [] { return false; }); + + // to avoid creation attempt of a new chatroom + auto config = focus.getDefaultProxyConfig(); + linphone_proxy_config_edit(config); + linphone_proxy_config_set_conference_factory_uri(config, NULL); + linphone_proxy_config_done(config); + + ms_free(conference_address); + bctbx_list_free(coresList); + } +} +static void chat_room_participant_added_sip_error(LinphoneChatRoom *cr, + BCTBX_UNUSED(const LinphoneEventLog *event_log)) { + if (bctbx_list_size(linphone_chat_room_get_participants(cr)) == 2) { + LinphoneCoreManager *initiator = (LinphoneCoreManager *)linphone_chat_room_get_user_data(cr); + ms_message("Turning off network for core %s", linphone_core_get_identity(initiator->lc)); + linphone_core_set_network_reachable(initiator->lc, FALSE); + } +} + +static void +server_core_chat_room_state_changed_sip_error(LinphoneCore *core, LinphoneChatRoom *cr, LinphoneChatRoomState state) { + Focus *focus = (Focus *)(((LinphoneCoreManager *)linphone_core_get_user_data(core))->user_info); + switch (state) { + case LinphoneChatRoomStateInstantiated: { + LinphoneChatRoomCbs *cbs = linphone_factory_create_chat_room_cbs(linphone_factory_get()); + linphone_chat_room_cbs_set_participant_added(cbs, chat_room_participant_added_sip_error); + linphone_chat_room_add_callbacks(cr, cbs); + linphone_chat_room_cbs_set_user_data(cbs, focus); + linphone_chat_room_cbs_unref(cbs); + break; + } + default: + break; + } +} + +static void group_chat_room_with_sip_errors_base(bool invite_error, bool subscribe_error, bool encrypted) { + Focus focus("chloe_rc"); + { // to make sure focus is destroyed after clients. + ClientConference marie("marie_rc", focus.getIdentity(), encrypted); + ClientConference pauline("pauline_rc", focus.getIdentity(), encrypted); + ClientConference laure("laure_tcp_rc", focus.getIdentity(), encrypted); + ClientConference berthe("berthe_rc", focus.getIdentity(), encrypted); + ClientConference michelle("michelle_rc", focus.getIdentity(), encrypted); + ClientConference michelle2("michelle_rc", focus.getIdentity(), encrypted); + + stats initialFocusStats = focus.getStats(); + stats initialMarieStats = marie.getStats(); + stats initialMichelleStats = michelle.getStats(); + stats initialMichelle2Stats = michelle2.getStats(); + stats initialLaureStats = laure.getStats(); + stats initialBertheStats = berthe.getStats(); + stats initialPaulineStats = pauline.getStats(); + + bctbx_list_t *coresList = bctbx_list_append(NULL, focus.getLc()); + coresList = bctbx_list_append(coresList, marie.getLc()); + coresList = bctbx_list_append(coresList, michelle.getLc()); + coresList = bctbx_list_append(coresList, michelle2.getLc()); + coresList = bctbx_list_append(coresList, pauline.getLc()); + coresList = bctbx_list_append(coresList, laure.getLc()); + coresList = bctbx_list_append(coresList, berthe.getLc()); + + if (encrypted) { + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_X3dhUserCreationSuccess, + initialMarieStats.number_of_X3dhUserCreationSuccess + 1, + x3dhServer_creationTimeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_X3dhUserCreationSuccess, + initialPaulineStats.number_of_X3dhUserCreationSuccess + 1, + x3dhServer_creationTimeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_X3dhUserCreationSuccess, + initialMichelleStats.number_of_X3dhUserCreationSuccess + 1, + x3dhServer_creationTimeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle2.getStats().number_of_X3dhUserCreationSuccess, + initialMichelle2Stats.number_of_X3dhUserCreationSuccess + 1, + x3dhServer_creationTimeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_X3dhUserCreationSuccess, + initialLaureStats.number_of_X3dhUserCreationSuccess + 1, + x3dhServer_creationTimeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &berthe.getStats().number_of_X3dhUserCreationSuccess, + initialBertheStats.number_of_X3dhUserCreationSuccess + 1, + x3dhServer_creationTimeout)); + + BC_ASSERT_TRUE(linphone_core_lime_x3dh_enabled(marie.getLc())); + BC_ASSERT_TRUE(linphone_core_lime_x3dh_enabled(pauline.getLc())); + BC_ASSERT_TRUE(linphone_core_lime_x3dh_enabled(laure.getLc())); + BC_ASSERT_TRUE(linphone_core_lime_x3dh_enabled(michelle.getLc())); + BC_ASSERT_TRUE(linphone_core_lime_x3dh_enabled(michelle2.getLc())); + BC_ASSERT_TRUE(linphone_core_lime_x3dh_enabled(berthe.getLc())); + } + + linphone_core_set_network_reachable(marie.getLc(), FALSE); + linphone_core_set_network_reachable(berthe.getLc(), FALSE); + + char *spec = bctbx_strdup_printf("groupchat/1.1"); + linphone_core_remove_linphone_spec(marie.getLc(), "groupchat"); + linphone_core_add_linphone_spec(marie.getLc(), spec); + linphone_core_remove_linphone_spec(berthe.getLc(), "groupchat"); + linphone_core_add_linphone_spec(berthe.getLc(), spec); + bctbx_free(spec); + + linphone_core_set_network_reachable(marie.getLc(), TRUE); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneRegistrationOk, + initialMarieStats.number_of_LinphoneRegistrationOk + 1, + liblinphone_tester_sip_timeout)); + linphone_core_set_network_reachable(berthe.getLc(), TRUE); + BC_ASSERT_TRUE(wait_for_list(coresList, &berthe.getStats().number_of_LinphoneRegistrationOk, + initialBertheStats.number_of_LinphoneRegistrationOk + 1, + liblinphone_tester_sip_timeout)); + + initialMarieStats = marie.getStats(); + initialBertheStats = berthe.getStats(); + + focus.registerAsParticipantDevice(marie); + focus.registerAsParticipantDevice(michelle); + focus.registerAsParticipantDevice(michelle2); + focus.registerAsParticipantDevice(pauline); + focus.registerAsParticipantDevice(berthe); + + if (invite_error) { + LinphoneCoreCbs *cbs = linphone_factory_create_core_cbs(linphone_factory_get()); + linphone_core_cbs_set_chat_room_state_changed(cbs, server_core_chat_room_state_changed_sip_error); + linphone_core_add_callbacks(focus.getLc(), cbs); + } + + linphone_im_notif_policy_enable_all(linphone_core_get_im_notif_policy(marie.getLc())); + linphone_im_notif_policy_enable_all(linphone_core_get_im_notif_policy(michelle.getLc())); + linphone_im_notif_policy_enable_all(linphone_core_get_im_notif_policy(michelle2.getLc())); + linphone_im_notif_policy_enable_all(linphone_core_get_im_notif_policy(pauline.getLc())); + linphone_im_notif_policy_enable_all(linphone_core_get_im_notif_policy(laure.getLc())); + linphone_im_notif_policy_enable_all(linphone_core_get_im_notif_policy(berthe.getLc())); + + bctbx_list_t *participantsAddresses = NULL; + Address michelleAddr = michelle.getIdentity(); + participantsAddresses = bctbx_list_append(participantsAddresses, linphone_address_ref(michelleAddr.toC())); + Address michelle2Addr = michelle2.getIdentity(); + participantsAddresses = bctbx_list_append(participantsAddresses, linphone_address_ref(michelle2Addr.toC())); + Address bertheAddr = berthe.getIdentity(); + participantsAddresses = bctbx_list_append(participantsAddresses, linphone_address_ref(bertheAddr.toC())); + Address paulineAddr = pauline.getIdentity(); + participantsAddresses = bctbx_list_append(participantsAddresses, linphone_address_ref(paulineAddr.toC())); + + const char *initialSubject = "Colleagues (characters: $ £ çà )"; + int participantsAddressesSize = (int)bctbx_list_size(participantsAddresses); + LinphoneChatRoomParams *params = linphone_core_create_default_chat_room_params(marie.getLc()); + + linphone_chat_room_params_enable_encryption(params, encrypted); + linphone_chat_room_params_set_ephemeral_mode(params, LinphoneChatRoomEphemeralModeDeviceManaged); + linphone_chat_room_params_set_backend(params, LinphoneChatRoomBackendFlexisipChat); + linphone_chat_room_params_enable_group(params, participantsAddressesSize > 1 ? TRUE : FALSE); + // Marie creates a new group chat room + LinphoneChatRoom *marieCr = + linphone_core_create_chat_room_2(marie.getLc(), params, initialSubject, participantsAddresses); + linphone_chat_room_params_unref(params); + + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline, michelle, michelle2, laure, berthe}).wait([&focus] { + return focus.getCore().getChatRooms().size() == 1; + })); + + for (auto chatRoom : focus.getCore().getChatRooms()) { + linphone_chat_room_set_user_data(L_GET_C_BACK_PTR(chatRoom), marie.getCMgr()); + } + + if (invite_error) { + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle2.getStats().number_of_LinphoneConferenceStateCreated, + initialMichelle2Stats.number_of_LinphoneConferenceStateCreated + 1, + liblinphone_tester_sip_timeout)); + ms_message("Disabling network of core %s (contact %s)", linphone_core_get_identity(michelle2.getLc()), + linphone_address_as_string(linphone_proxy_config_get_contact( + linphone_core_get_default_proxy_config(michelle2.getLc())))); + linphone_core_set_network_reachable(michelle2.getLc(), FALSE); + BC_ASSERT_TRUE(wait_for_list(coresList, &berthe.getStats().number_of_LinphoneConferenceStateCreated, + initialBertheStats.number_of_LinphoneConferenceStateCreated + 1, + liblinphone_tester_sip_timeout)); + ms_message("Disabling network of core %s (contact %s)", linphone_core_get_identity(berthe.getLc()), + linphone_address_as_string( + linphone_proxy_config_get_contact(linphone_core_get_default_proxy_config(berthe.getLc())))); + linphone_core_set_network_reachable(berthe.getLc(), FALSE); + } else if (subscribe_error) { + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneSubscriptionActive, + initialMarieStats.number_of_LinphoneSubscriptionActive + 1, + liblinphone_tester_sip_timeout)); + ms_message("Disabling network of core %s (contact %s)", linphone_core_get_identity(marie.getLc()), + linphone_address_as_string( + linphone_proxy_config_get_contact(linphone_core_get_default_proxy_config(marie.getLc())))); + linphone_core_set_network_reachable(marie.getLc(), FALSE); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle2.getStats().number_of_LinphoneSubscriptionActive, + initialMichelle2Stats.number_of_LinphoneSubscriptionActive + 1, + liblinphone_tester_sip_timeout)); + ms_message("Disabling network of core %s (contact %s)", linphone_core_get_identity(michelle2.getLc()), + linphone_address_as_string(linphone_proxy_config_get_contact( + linphone_core_get_default_proxy_config(michelle2.getLc())))); + linphone_core_set_network_reachable(michelle2.getLc(), FALSE); + BC_ASSERT_TRUE(wait_for_list(coresList, &berthe.getStats().number_of_LinphoneSubscriptionActive, + initialBertheStats.number_of_LinphoneSubscriptionActive + 1, + liblinphone_tester_sip_timeout)); + ms_message("Disabling network of core %s (contact %s)", linphone_core_get_identity(berthe.getLc()), + linphone_address_as_string( + linphone_proxy_config_get_contact(linphone_core_get_default_proxy_config(berthe.getLc())))); + linphone_core_set_network_reachable(berthe.getLc(), FALSE); + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participants_added, + initialFocusStats.number_of_participants_added + 4, 5000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_added, + initialFocusStats.number_of_participant_devices_added + 5, 5000)); + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, michelle, michelle2, laure, berthe}) + .waitUntil(chrono::seconds(60), [] { return false; }); + + check_create_chat_room_client_side(coresList, marie.getCMgr(), marieCr, &initialMarieStats, + participantsAddresses, initialSubject, 2); + const LinphoneAddress *confAddr = linphone_chat_room_get_conference_address(marieCr); + // Check that the chat room is correctly created on Pauline's and Michelle's side and that the participants are + // added + LinphoneChatRoom *michelleCr = check_creation_chat_room_client_side( + coresList, michelle.getCMgr(), &initialMichelleStats, confAddr, initialSubject, 3, FALSE); + LinphoneChatRoom *michelle2Cr = check_creation_chat_room_client_side( + coresList, michelle2.getCMgr(), &initialMichelle2Stats, confAddr, initialSubject, 3, FALSE); + LinphoneChatRoom *bertheCr = check_creation_chat_room_client_side( + coresList, berthe.getCMgr(), &initialBertheStats, confAddr, initialSubject, 3, FALSE); + LinphoneChatRoom *paulineCr = check_creation_chat_room_client_side( + coresList, pauline.getCMgr(), &initialPaulineStats, confAddr, initialSubject, 3, FALSE); + + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_LinphoneConferenceStateCreated, + initialMichelleStats.number_of_LinphoneConferenceStateCreated + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle2.getStats().number_of_LinphoneConferenceStateCreated, + initialMichelle2Stats.number_of_LinphoneConferenceStateCreated + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &berthe.getStats().number_of_LinphoneConferenceStateCreated, + initialBertheStats.number_of_LinphoneConferenceStateCreated + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneConferenceStateCreated, + initialPaulineStats.number_of_LinphoneConferenceStateCreated + 1, + liblinphone_tester_sip_timeout)); + + std::string msg_text = "message michelle blabla"; + LinphoneChatMessage *msg = ClientConference::sendTextMsg(michelleCr, msg_text); + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline, michelle, michelle2, laure, berthe}).wait([msg] { + return (msg && (linphone_chat_message_get_state(msg) == LinphoneChatMessageStateDelivered)); + })); + + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline, michelle, michelle2, laure, berthe}).wait([paulineCr] { + return linphone_chat_room_get_unread_messages_count(paulineCr) == 1; + })); + LinphoneChatMessage *paulineLastMsg = pauline.getStats().last_received_chat_message; + BC_ASSERT_PTR_NOT_NULL(paulineLastMsg); + if (paulineLastMsg) { + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_utf8_text(paulineLastMsg), msg_text.c_str()); + } + linphone_chat_room_mark_as_read(paulineCr); + + BC_ASSERT_FALSE(wait_for_list(coresList, &michelle.getStats().number_of_LinphoneMessageDisplayed, + initialMichelleStats.number_of_LinphoneMessageDisplayed + 1, 3000)); + + if (invite_error || subscribe_error) { + ms_message("Enabling network of core %s (contact %s)", linphone_core_get_identity(marie.getLc()), + linphone_address_as_string( + linphone_proxy_config_get_contact(linphone_core_get_default_proxy_config(marie.getLc())))); + linphone_core_set_network_reachable(marie.getLc(), TRUE); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneRegistrationOk, + initialMarieStats.number_of_LinphoneRegistrationOk + 1, + liblinphone_tester_sip_timeout)); + marieCr = check_creation_chat_room_client_side(coresList, marie.getCMgr(), &initialMarieStats, confAddr, + initialSubject, 3, TRUE); + } + + focus.registerAsParticipantDevice(laure); + Address laureAddr = laure.getIdentity(); + linphone_chat_room_add_participant(marieCr, linphone_address_ref(laureAddr.toC())); + LinphoneChatRoom *laureCr = check_creation_chat_room_client_side( + coresList, laure.getCMgr(), &initialPaulineStats, confAddr, initialSubject, 4, FALSE); + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participants_added, + initialFocusStats.number_of_participants_added + 4, 5000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_added, + initialFocusStats.number_of_participant_devices_added + 5, 5000)); + + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline, michelle, michelle2, laure, berthe}).wait([marieCr] { + return linphone_chat_room_get_unread_messages_count(marieCr) == 1; + })); + LinphoneChatMessage *marieLastMsg = marie.getStats().last_received_chat_message; + BC_ASSERT_PTR_NOT_NULL(marieLastMsg); + if (marieLastMsg) { + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_utf8_text(marieLastMsg), msg_text.c_str()); + } + + msg_text = "message laure blabla"; + LinphoneChatMessage *msg2 = ClientConference::sendTextMsg(laureCr, msg_text); + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline, michelle, michelle2, laure, berthe}).wait([msg2] { + return (linphone_chat_message_get_state(msg2) == LinphoneChatMessageStateDelivered); + })); + + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline, michelle, michelle2, laure, berthe}).wait([paulineCr] { + return linphone_chat_room_get_unread_messages_count(paulineCr) == 1; + })); + paulineLastMsg = pauline.getStats().last_received_chat_message; + BC_ASSERT_PTR_NOT_NULL(paulineLastMsg); + if (paulineLastMsg) { + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_utf8_text(paulineLastMsg), msg_text.c_str()); + } + + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline, michelle, michelle2, laure, berthe}).wait([marieCr] { + return linphone_chat_room_get_unread_messages_count(marieCr) == 2; + })); + marieLastMsg = marie.getStats().last_received_chat_message; + BC_ASSERT_PTR_NOT_NULL(marieLastMsg); + if (marieLastMsg) { + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_utf8_text(marieLastMsg), msg_text.c_str()); + } + + BC_ASSERT_TRUE( + CoreManagerAssert({focus, marie, pauline, michelle, michelle2, laure, berthe}).wait([michelleCr] { + return linphone_chat_room_get_unread_messages_count(michelleCr) == 1; + })); + LinphoneChatMessage *michelleLastMsg = michelle.getStats().last_received_chat_message; + if (michelleLastMsg) { + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_utf8_text(michelleLastMsg), msg_text.c_str()); + } + + linphone_chat_room_mark_as_read(paulineCr); + linphone_chat_room_mark_as_read(marieCr); + linphone_chat_room_mark_as_read(michelleCr); + + BC_ASSERT_FALSE(wait_for_list(coresList, &laure.getStats().number_of_LinphoneMessageDisplayed, + initialLaureStats.number_of_LinphoneMessageDisplayed + 1, 3000)); + + if (invite_error || subscribe_error) { + ms_message("Enabling network of core %s (contact %s)", linphone_core_get_identity(michelle2.getLc()), + linphone_address_as_string(linphone_proxy_config_get_contact( + linphone_core_get_default_proxy_config(michelle2.getLc())))); + + linphone_core_set_network_reachable(michelle2.getLc(), TRUE); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle2.getStats().number_of_LinphoneRegistrationOk, + initialMichelle2Stats.number_of_LinphoneRegistrationOk + 1, + liblinphone_tester_sip_timeout)); + michelle2Cr = check_creation_chat_room_client_side(coresList, michelle2.getCMgr(), &initialMichelle2Stats, + confAddr, initialSubject, 4, FALSE); + + ms_message("Enabling network of core %s (contact %s)", linphone_core_get_identity(berthe.getLc()), + linphone_address_as_string( + linphone_proxy_config_get_contact(linphone_core_get_default_proxy_config(berthe.getLc())))); + linphone_core_set_network_reachable(berthe.getLc(), TRUE); + BC_ASSERT_TRUE(wait_for_list(coresList, &berthe.getStats().number_of_LinphoneRegistrationOk, + initialBertheStats.number_of_LinphoneRegistrationOk + 1, + liblinphone_tester_sip_timeout)); + bertheCr = check_creation_chat_room_client_side(coresList, berthe.getCMgr(), &initialBertheStats, confAddr, + initialSubject, 4, FALSE); + } + + LinphoneChatMessage *michelle2LastMsg = NULL; + if (!invite_error) { + + BC_ASSERT_TRUE( + CoreManagerAssert({focus, marie, pauline, michelle, michelle2, laure, berthe}).wait([michelle2Cr] { + return linphone_chat_room_get_history_size(michelle2Cr) == 2; + })); + michelle2LastMsg = michelle2.getStats().last_received_chat_message; + BC_ASSERT_PTR_NOT_NULL(michelle2LastMsg); + if (michelle2LastMsg) { + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_utf8_text(michelle2LastMsg), msg_text.c_str()); + } + linphone_chat_room_mark_as_read(michelle2Cr); + } + + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline, michelle, michelle2, laure, berthe}).wait([bertheCr] { + return linphone_chat_room_get_history_size(bertheCr) == 2; + })); + + LinphoneChatMessage *bertheLastMsg = berthe.getStats().last_received_chat_message; + BC_ASSERT_PTR_NOT_NULL(bertheLastMsg); + if (bertheLastMsg) { + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_utf8_text(bertheLastMsg), msg_text.c_str()); + } + linphone_chat_room_mark_as_read(bertheCr); + + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_LinphoneMessageDisplayed, + initialMichelleStats.number_of_LinphoneMessageDisplayed + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_LinphoneMessageDisplayed, + initialLaureStats.number_of_LinphoneMessageDisplayed + 1, + liblinphone_tester_sip_timeout)); + + linphone_chat_message_unref(msg); + msg = nullptr; + linphone_chat_message_unref(msg2); + msg2 = nullptr; + + // Marie now changes the subject + const char *newSubject = "Let's go drink a beer"; + linphone_chat_room_set_subject(marieCr, newSubject); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_subject_changed, + initialMarieStats.number_of_subject_changed + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_subject_changed, + initialMichelleStats.number_of_subject_changed + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle2.getStats().number_of_subject_changed, + initialMichelle2Stats.number_of_subject_changed + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_subject_changed, + initialPaulineStats.number_of_subject_changed + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_subject_changed, + initialLaureStats.number_of_subject_changed + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &berthe.getStats().number_of_subject_changed, + initialBertheStats.number_of_subject_changed + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_STRING_EQUAL(linphone_chat_room_get_subject(marieCr), newSubject); + BC_ASSERT_STRING_EQUAL(linphone_chat_room_get_subject(michelleCr), newSubject); + BC_ASSERT_STRING_EQUAL(linphone_chat_room_get_subject(michelle2Cr), newSubject); + BC_ASSERT_STRING_EQUAL(linphone_chat_room_get_subject(paulineCr), newSubject); + BC_ASSERT_STRING_EQUAL(linphone_chat_room_get_subject(laureCr), newSubject); + BC_ASSERT_STRING_EQUAL(linphone_chat_room_get_subject(bertheCr), newSubject); + + BC_ASSERT_EQUAL(linphone_chat_room_get_nb_participants(marieCr), 4, int, "%d"); + BC_ASSERT_EQUAL(linphone_chat_room_get_nb_participants(michelleCr), 4, int, "%d"); + BC_ASSERT_EQUAL(linphone_chat_room_get_nb_participants(michelle2Cr), 4, int, "%d"); + BC_ASSERT_EQUAL(linphone_chat_room_get_nb_participants(paulineCr), 4, int, "%d"); + BC_ASSERT_EQUAL(linphone_chat_room_get_nb_participants(laureCr), 4, int, "%d"); + BC_ASSERT_EQUAL(linphone_chat_room_get_nb_participants(bertheCr), 4, int, "%d"); + + /* + LinphoneAddress *michelle2Contact = + linphone_address_clone(linphone_proxy_config_get_contact(linphone_core_get_default_proxy_config(michelle2.getLc()))); + ms_message("%s is restarting its core", linphone_address_as_string(michelle2Contact)); + linphone_address_unref(michelle2Contact); + initialFocusStats = focus.getStats(); + coresList = bctbx_list_remove(coresList, michelle2.getLc()); + //Restart michelle + michelle2.reStart(); + setup_mgr_for_conference(michelle2.getCMgr(), NULL); + coresList = bctbx_list_append(coresList, michelle2.getLc()); + + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle2.getStats().number_of_LinphoneRegistrationOk, 1, + liblinphone_tester_sip_timeout)); if (encrypted) { + BC_ASSERT_TRUE(linphone_core_lime_x3dh_enabled(michelle2.getLc())); + } + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle2.getStats().number_of_LinphoneConferenceStateCreated, + 1, liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(coresList, + &michelle2.getStats().number_of_LinphoneSubscriptionActive, 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneSubscriptionActive, + initialFocusStats.number_of_LinphoneSubscriptionActive + 1, liblinphone_tester_sip_timeout)); LinphoneAddress + *michelle2DeviceAddr = + linphone_address_clone(linphone_proxy_config_get_contact(linphone_core_get_default_proxy_config(michelle2.getLc()))); + michelle2Cr = linphone_core_search_chat_room(michelle2.getLc(), NULL, michelle2DeviceAddr, confAddr, + NULL); linphone_address_unref(michelle2DeviceAddr); BC_ASSERT_PTR_NOT_NULL(michelle2Cr); + */ + + msg_text = "message marie blabla"; + msg = ClientConference::sendTextMsg(marieCr, msg_text); + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline, michelle, michelle2, laure, berthe}).wait([msg] { + return (linphone_chat_message_get_state(msg) == LinphoneChatMessageStateDelivered); + })); + + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline, michelle, michelle2, laure, berthe}).wait([paulineCr] { + return linphone_chat_room_get_unread_messages_count(paulineCr) == 1; + })); + paulineLastMsg = pauline.getStats().last_received_chat_message; + BC_ASSERT_PTR_NOT_NULL(paulineLastMsg); + if (paulineLastMsg) { + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_utf8_text(paulineLastMsg), msg_text.c_str()); + } + + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline, michelle, michelle2, laure, berthe}).wait([laureCr] { + return linphone_chat_room_get_unread_messages_count(laureCr) == 1; + })); + LinphoneChatMessage *laureLastMsg = laure.getStats().last_received_chat_message; + BC_ASSERT_PTR_NOT_NULL(laureLastMsg); + if (laureLastMsg) { + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_utf8_text(laureLastMsg), msg_text.c_str()); + } + + BC_ASSERT_TRUE( + CoreManagerAssert({focus, marie, pauline, michelle, michelle2, laure, berthe}).wait([michelleCr] { + return linphone_chat_room_get_unread_messages_count(michelleCr) == 1; + })); + michelleLastMsg = michelle.getStats().last_received_chat_message; + if (michelleLastMsg) { + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_utf8_text(michelleLastMsg), msg_text.c_str()); + } + + BC_ASSERT_TRUE( + CoreManagerAssert({focus, marie, pauline, michelle, michelle2, laure, berthe}).wait([michelle2Cr] { + return linphone_chat_room_get_unread_messages_count(michelle2Cr) == 1; + })); + michelle2LastMsg = michelle2.getStats().last_received_chat_message; + BC_ASSERT_PTR_NOT_NULL(michelle2LastMsg); + if (michelle2LastMsg) { + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_utf8_text(michelle2LastMsg), msg_text.c_str()); + } + + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline, michelle, michelle2, laure, berthe}).wait([bertheCr] { + return linphone_chat_room_get_unread_messages_count(bertheCr) == 1; + })); + bertheLastMsg = berthe.getStats().last_received_chat_message; + BC_ASSERT_PTR_NOT_NULL(bertheLastMsg); + if (bertheLastMsg) { + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_utf8_text(bertheLastMsg), msg_text.c_str()); + } + + linphone_chat_room_mark_as_read(michelleCr); + linphone_chat_room_mark_as_read(michelle2Cr); + linphone_chat_room_mark_as_read(paulineCr); + linphone_chat_room_mark_as_read(laureCr); + linphone_chat_room_mark_as_read(bertheCr); + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneMessageDisplayed, + initialMarieStats.number_of_LinphoneMessageDisplayed + 1, + liblinphone_tester_sip_timeout)); + linphone_chat_message_unref(msg); + msg = nullptr; + + msg_text = "message michelle2 blabla"; + msg = ClientConference::sendTextMsg(michelle2Cr, msg_text); + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline, michelle, michelle2, laure, berthe}).wait([msg] { + return (linphone_chat_message_get_state(msg) == LinphoneChatMessageStateDelivered); + })); + + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline, michelle, michelle2, laure, berthe}).wait([paulineCr] { + return linphone_chat_room_get_unread_messages_count(paulineCr) == 1; + })); + paulineLastMsg = pauline.getStats().last_received_chat_message; + BC_ASSERT_PTR_NOT_NULL(paulineLastMsg); + if (paulineLastMsg) { + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_utf8_text(paulineLastMsg), msg_text.c_str()); + } + + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline, michelle, michelle2, laure, berthe}).wait([bertheCr] { + return linphone_chat_room_get_unread_messages_count(bertheCr) == 1; + })); + bertheLastMsg = berthe.getStats().last_received_chat_message; + BC_ASSERT_PTR_NOT_NULL(bertheLastMsg); + if (bertheLastMsg) { + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_utf8_text(bertheLastMsg), msg_text.c_str()); + } + + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline, michelle, michelle2, laure, berthe}).wait([laureCr] { + return linphone_chat_room_get_unread_messages_count(laureCr) == 1; + })); + laureLastMsg = laure.getStats().last_received_chat_message; + BC_ASSERT_PTR_NOT_NULL(laureLastMsg); + if (laureLastMsg) { + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_utf8_text(laureLastMsg), msg_text.c_str()); + } + + michelleLastMsg = michelle.getStats().last_received_chat_message; + if (michelleLastMsg) { + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_utf8_text(michelleLastMsg), msg_text.c_str()); + } + + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline, michelle, michelle2, laure, berthe}).wait([marieCr] { + return linphone_chat_room_get_unread_messages_count(marieCr) == 1; + })); + marieLastMsg = marie.getStats().last_received_chat_message; + BC_ASSERT_PTR_NOT_NULL(marieLastMsg); + if (marieLastMsg) { + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_utf8_text(marieLastMsg), msg_text.c_str()); + } + + linphone_chat_room_mark_as_read(michelleCr); + linphone_chat_room_mark_as_read(marieCr); + linphone_chat_room_mark_as_read(paulineCr); + linphone_chat_room_mark_as_read(laureCr); + linphone_chat_room_mark_as_read(bertheCr); + + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle2.getStats().number_of_LinphoneMessageDisplayed, + initialMichelle2Stats.number_of_LinphoneMessageDisplayed + 1, + liblinphone_tester_sip_timeout)); + linphone_chat_message_unref(msg); + msg = nullptr; + + msg_text = "message pauline blabla"; + msg = ClientConference::sendTextMsg(paulineCr, msg_text); + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline, michelle, michelle2, laure, berthe}).wait([msg] { + return (linphone_chat_message_get_state(msg) == LinphoneChatMessageStateDelivered); + })); + + BC_ASSERT_TRUE( + CoreManagerAssert({focus, marie, pauline, michelle, michelle2, laure, berthe}).wait([michelleCr] { + return linphone_chat_room_get_unread_messages_count(michelleCr) == 1; + })); + michelleLastMsg = michelle.getStats().last_received_chat_message; + if (michelleLastMsg) { + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_utf8_text(michelleLastMsg), msg_text.c_str()); + } + + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline, michelle, michelle2, laure, berthe}).wait([bertheCr] { + return linphone_chat_room_get_unread_messages_count(bertheCr) == 1; + })); + bertheLastMsg = berthe.getStats().last_received_chat_message; + BC_ASSERT_PTR_NOT_NULL(bertheLastMsg); + if (bertheLastMsg) { + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_utf8_text(bertheLastMsg), msg_text.c_str()); + } + + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline, michelle, michelle2, laure, berthe}).wait([marieCr] { + return linphone_chat_room_get_unread_messages_count(marieCr) == 1; + })); + marieLastMsg = marie.getStats().last_received_chat_message; + BC_ASSERT_PTR_NOT_NULL(marieLastMsg); + if (marieLastMsg) { + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_utf8_text(marieLastMsg), msg_text.c_str()); + } + + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline, michelle, michelle2, laure, berthe}).wait([laureCr] { + return linphone_chat_room_get_unread_messages_count(laureCr) == 1; + })); + laureLastMsg = laure.getStats().last_received_chat_message; + BC_ASSERT_PTR_NOT_NULL(laureLastMsg); + if (laureLastMsg) { + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_utf8_text(laureLastMsg), msg_text.c_str()); + } + + BC_ASSERT_TRUE( + CoreManagerAssert({focus, marie, pauline, michelle, michelle2, laure, berthe}).wait([michelle2Cr] { + return linphone_chat_room_get_unread_messages_count(michelle2Cr) == 1; + })); + michelle2LastMsg = michelle2.getStats().last_received_chat_message; + BC_ASSERT_PTR_NOT_NULL(michelle2LastMsg); + if (michelle2LastMsg) { + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_utf8_text(michelle2LastMsg), msg_text.c_str()); + } + + linphone_chat_room_mark_as_read(michelleCr); + linphone_chat_room_mark_as_read(michelle2Cr); + linphone_chat_room_mark_as_read(marieCr); + linphone_chat_room_mark_as_read(laureCr); + linphone_chat_room_mark_as_read(bertheCr); + + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneMessageDisplayed, + initialPaulineStats.number_of_LinphoneMessageDisplayed + 1, + liblinphone_tester_sip_timeout)); + linphone_chat_message_unref(msg); + msg = nullptr; + + CoreManagerAssert({focus, marie}).waitUntil(std::chrono::seconds(1), [] { return false; }); + + CoreManagerAssert({focus, marie}).waitUntil(std::chrono::seconds(2), [] { return false; }); + + for (auto chatRoom : focus.getCore().getChatRooms()) { + for (auto participant : chatRoom->getParticipants()) { + // force deletion by removing devices + std::shared_ptr<Address> participantAddress = participant->getAddress(); + linphone_chat_room_set_participant_devices(L_GET_C_BACK_PTR(chatRoom), participantAddress->toC(), NULL); + } + } + + // wait until chatroom is deleted server side + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline, michelle, michelle2, laure, berthe}).wait([&focus] { + return focus.getCore().getChatRooms().size() == 0; + })); + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, michelle, michelle2, laure, berthe}) + .waitUntil(chrono::seconds(2), [] { return false; }); + + // to avoid creation attempt of a new chatroom + LinphoneProxyConfig *config = linphone_core_get_default_proxy_config(focus.getLc()); + linphone_proxy_config_edit(config); + linphone_proxy_config_set_conference_factory_uri(config, NULL); + linphone_proxy_config_done(config); + + bctbx_list_free(coresList); + } +} + +static void group_chat_room_with_invite_error(void) { + group_chat_room_with_sip_errors_base(true, false, false); +} + +static void group_chat_room_with_subscribe_error(void) { + group_chat_room_with_sip_errors_base(false, true, false); +} + +static void secure_group_chat_room_with_invite_error(void) { + group_chat_room_with_sip_errors_base(true, false, true); +} + +static void secure_group_chat_room_with_subscribe_error(void) { + group_chat_room_with_sip_errors_base(false, true, true); +} + +static void secure_group_chat_room_with_chat_room_deleted_before_server_restart(void) { + Focus focus("chloe_rc"); + { // to make sure focus is destroyed after clients. + ClientConference marie("marie_rc", focus.getIdentity(), true); + ClientConference marie2("marie_rc", focus.getIdentity(), true); + ClientConference michelle("michelle_rc", focus.getIdentity(), true); + ClientConference michelle2("michelle_rc", focus.getIdentity(), true); + + stats initialFocusStats = focus.getStats(); + stats initialMarieStats = marie.getStats(); + stats initialMarie2Stats = marie2.getStats(); + stats initialMichelleStats = michelle.getStats(); + stats initialMichelle2Stats = michelle2.getStats(); + + bctbx_list_t *coresList = bctbx_list_append(NULL, focus.getLc()); + coresList = bctbx_list_append(coresList, marie.getLc()); + coresList = bctbx_list_append(coresList, marie2.getLc()); + coresList = bctbx_list_append(coresList, michelle.getLc()); + coresList = bctbx_list_append(coresList, michelle2.getLc()); + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_X3dhUserCreationSuccess, + initialMarieStats.number_of_X3dhUserCreationSuccess + 1, + x3dhServer_creationTimeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie2.getStats().number_of_X3dhUserCreationSuccess, + initialMarie2Stats.number_of_X3dhUserCreationSuccess + 1, + x3dhServer_creationTimeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_X3dhUserCreationSuccess, + initialMichelleStats.number_of_X3dhUserCreationSuccess + 1, + x3dhServer_creationTimeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle2.getStats().number_of_X3dhUserCreationSuccess, + initialMichelle2Stats.number_of_X3dhUserCreationSuccess + 1, + x3dhServer_creationTimeout)); + + BC_ASSERT_TRUE(linphone_core_lime_x3dh_enabled(marie.getLc())); + BC_ASSERT_TRUE(linphone_core_lime_x3dh_enabled(marie2.getLc())); + BC_ASSERT_TRUE(linphone_core_lime_x3dh_enabled(michelle.getLc())); + BC_ASSERT_TRUE(linphone_core_lime_x3dh_enabled(michelle2.getLc())); + + focus.registerAsParticipantDevice(marie); + focus.registerAsParticipantDevice(marie2); + focus.registerAsParticipantDevice(michelle); + focus.registerAsParticipantDevice(michelle2); + + linphone_im_notif_policy_enable_all(linphone_core_get_im_notif_policy(marie.getLc())); + linphone_im_notif_policy_enable_all(linphone_core_get_im_notif_policy(marie2.getLc())); + linphone_im_notif_policy_enable_all(linphone_core_get_im_notif_policy(michelle.getLc())); + linphone_im_notif_policy_enable_all(linphone_core_get_im_notif_policy(michelle2.getLc())); + + bctbx_list_t *participantsAddresses = NULL; + Address michelleAddr = michelle.getIdentity(); + participantsAddresses = bctbx_list_append(participantsAddresses, linphone_address_ref(michelleAddr.toC())); + Address michelle2Addr = michelle2.getIdentity(); + participantsAddresses = bctbx_list_append(participantsAddresses, linphone_address_ref(michelle2Addr.toC())); + + const char *initialSubject = "Colleagues (characters: $ £ çà )"; + + LinphoneChatRoomParams *params = linphone_core_create_default_chat_room_params(marie.getLc()); + linphone_chat_room_params_enable_encryption(params, TRUE); + linphone_chat_room_params_set_ephemeral_mode(params, LinphoneChatRoomEphemeralModeDeviceManaged); + linphone_chat_room_params_set_backend(params, LinphoneChatRoomBackendFlexisipChat); + linphone_chat_room_params_enable_group(params, FALSE); + LinphoneChatRoom *marieCr = + linphone_core_create_chat_room_2(marie.getLc(), params, initialSubject, participantsAddresses); + linphone_chat_room_params_unref(params); + + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, marie2, michelle, michelle2}).wait([&focus] { + return focus.getCore().getChatRooms().size() == 1; + })); + + for (auto chatRoom : focus.getCore().getChatRooms()) { + linphone_chat_room_set_user_data(L_GET_C_BACK_PTR(chatRoom), marie.getCMgr()); + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participants_added, + initialFocusStats.number_of_participants_added + 1, 5000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_added, + initialFocusStats.number_of_participant_devices_added + 2, 5000)); + + check_create_chat_room_client_side(coresList, marie.getCMgr(), marieCr, &initialMarieStats, + participantsAddresses, initialSubject, 1); + + const LinphoneAddress *confAddr = linphone_chat_room_get_conference_address(marieCr); + // Check that the chat room is correctly created on Pauline's and Michelle's side and that the participants are + // added + LinphoneChatRoom *marie2Cr = check_creation_chat_room_client_side( + coresList, marie2.getCMgr(), &initialMarie2Stats, confAddr, initialSubject, 1, FALSE); + LinphoneChatRoom *michelleCr = check_creation_chat_room_client_side( + coresList, michelle.getCMgr(), &initialMichelleStats, confAddr, initialSubject, 1, FALSE); + LinphoneChatRoom *michelle2Cr = check_creation_chat_room_client_side( + coresList, michelle2.getCMgr(), &initialMichelle2Stats, confAddr, initialSubject, 1, FALSE); + + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_LinphoneConferenceStateCreated, + initialMichelleStats.number_of_LinphoneConferenceStateCreated + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle2.getStats().number_of_LinphoneConferenceStateCreated, + initialMichelle2Stats.number_of_LinphoneConferenceStateCreated + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateCreated, + initialMarieStats.number_of_LinphoneConferenceStateCreated + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie2.getStats().number_of_LinphoneConferenceStateCreated, + initialMarie2Stats.number_of_LinphoneConferenceStateCreated + 1, + liblinphone_tester_sip_timeout)); + + // Send a few messages + std::string msg_text = "message marie2 blabla"; + LinphoneChatMessage *msg = ClientConference::sendTextMsg(marie2Cr, msg_text); + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, marie2, michelle, michelle2}).wait([msg] { + return (linphone_chat_message_get_state(msg) == LinphoneChatMessageStateDelivered); + })); + + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, marie2, michelle, michelle2}).wait([michelleCr] { + return linphone_chat_room_get_unread_messages_count(michelleCr) == 1; + })); + LinphoneChatMessage *michelleLastMsg = michelle.getStats().last_received_chat_message; + BC_ASSERT_PTR_NOT_NULL(michelleLastMsg); + if (michelleLastMsg) { + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_utf8_text(michelleLastMsg), msg_text.c_str()); + } + linphone_chat_room_mark_as_read(michelleCr); + + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, marie2, michelle, michelle2}).wait([michelle2Cr] { + return linphone_chat_room_get_unread_messages_count(michelle2Cr) == 1; + })); + LinphoneChatMessage *michelle2LastMsg = michelle2.getStats().last_received_chat_message; + BC_ASSERT_PTR_NOT_NULL(michelle2LastMsg); + if (michelle2LastMsg) { + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_utf8_text(michelle2LastMsg), msg_text.c_str()); + } + linphone_chat_room_mark_as_read(michelle2Cr); + linphone_chat_room_mark_as_read(marieCr); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie2.getStats().number_of_LinphoneMessageDisplayed, + initialMarie2Stats.number_of_LinphoneMessageDisplayed + 1, + liblinphone_tester_sip_timeout)); + + linphone_chat_message_unref(msg); + msg = nullptr; + + msg_text = "message marie blabla"; + msg = ClientConference::sendTextMsg(marieCr, msg_text); + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, marie2, michelle, michelle2}).wait([msg] { + return (linphone_chat_message_get_state(msg) == LinphoneChatMessageStateDelivered); + })); + + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, marie2, michelle, michelle2}).wait([michelleCr] { + return linphone_chat_room_get_unread_messages_count(michelleCr) == 1; + })); + michelleLastMsg = michelle.getStats().last_received_chat_message; + if (michelleLastMsg) { + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_utf8_text(michelleLastMsg), msg_text.c_str()); + } + + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, marie2, michelle, michelle2}).wait([michelle2Cr] { + return linphone_chat_room_get_unread_messages_count(michelle2Cr) == 1; + })); + michelle2LastMsg = michelle2.getStats().last_received_chat_message; + BC_ASSERT_PTR_NOT_NULL(michelle2LastMsg); + if (michelle2LastMsg) { + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_utf8_text(michelle2LastMsg), msg_text.c_str()); + } + + linphone_chat_room_mark_as_read(marie2Cr); + linphone_chat_room_mark_as_read(michelleCr); + linphone_chat_room_mark_as_read(michelle2Cr); + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneMessageDisplayed, + initialMarieStats.number_of_LinphoneMessageDisplayed + 1, + liblinphone_tester_sip_timeout)); + linphone_chat_message_unref(msg); + msg = nullptr; + + msg_text = "message michelle2 blabla"; + msg = ClientConference::sendTextMsg(michelle2Cr, msg_text); + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, marie2, michelle, michelle2}).wait([msg] { + return (linphone_chat_message_get_state(msg) == LinphoneChatMessageStateDelivered); + })); + + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, marie2, michelle, michelle2}).wait([marieCr] { + return linphone_chat_room_get_unread_messages_count(marieCr) == 1; + })); + LinphoneChatMessage *marieLastMsg = marie.getStats().last_received_chat_message; + BC_ASSERT_PTR_NOT_NULL(marieLastMsg); + if (marieLastMsg) { + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_utf8_text(marieLastMsg), msg_text.c_str()); + } + + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, marie2, michelle, michelle2}).wait([marie2Cr] { + return linphone_chat_room_get_unread_messages_count(marie2Cr) == 1; + })); + LinphoneChatMessage *marie2LastMsg = marie2.getStats().last_received_chat_message; + BC_ASSERT_PTR_NOT_NULL(marie2LastMsg); + if (marie2LastMsg) { + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_utf8_text(marie2LastMsg), msg_text.c_str()); + } + + linphone_chat_room_mark_as_read(michelleCr); + linphone_chat_room_mark_as_read(marieCr); + linphone_chat_room_mark_as_read(marie2Cr); + + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle2.getStats().number_of_LinphoneMessageDisplayed, + initialMichelle2Stats.number_of_LinphoneMessageDisplayed + 1, + liblinphone_tester_sip_timeout)); + linphone_chat_message_unref(msg); + msg = nullptr; + + // Marie deletes the chat room + char *confAddrStr = (confAddr) ? linphone_address_as_string(confAddr) : ms_strdup("<unknown>"); + ms_message("%s deletes chat room %s", linphone_core_get_identity(marie.getLc()), confAddrStr); + ms_free(confAddrStr); + + linphone_core_manager_delete_chat_room(marie.getCMgr(), marieCr, coresList); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateTerminated, + initialMarieStats.number_of_LinphoneConferenceStateTerminated + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_LinphoneConferenceStateTerminated, + initialMichelleStats.number_of_LinphoneConferenceStateTerminated + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle2.getStats().number_of_LinphoneConferenceStateTerminated, + initialMichelle2Stats.number_of_LinphoneConferenceStateTerminated + 1, + liblinphone_tester_sip_timeout)); + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, marie2, michelle, michelle2}).waitUntil(chrono::seconds(5), [] { + return false; + }); + + ms_message("%s is restarting its core", linphone_core_get_identity(focus.getLc())); + coresList = bctbx_list_remove(coresList, focus.getLc()); + // Restart flexisip + focus.reStart(); + coresList = bctbx_list_append(coresList, focus.getLc()); + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, marie2, michelle, michelle2}).waitUntil(chrono::seconds(5), [] { + return false; + }); + + msg_text = "Cou cou Marieeee....."; + msg = ClientConference::sendTextMsg(michelle2Cr, msg_text); + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateCreated, + initialMarieStats.number_of_LinphoneConferenceStateCreated + 2, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_LinphoneConferenceStateCreated, + initialMichelleStats.number_of_LinphoneConferenceStateCreated + 2, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle2.getStats().number_of_LinphoneConferenceStateCreated, + initialMichelle2Stats.number_of_LinphoneConferenceStateCreated + 2, + liblinphone_tester_sip_timeout)); + + confAddr = linphone_chat_room_get_conference_address(michelle2Cr); + marieCr = check_creation_chat_room_client_side(coresList, marie.getCMgr(), &initialMarieStats, confAddr, + initialSubject, 1, FALSE); + + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, marie2, michelle, michelle2}).wait([msg] { + return (linphone_chat_message_get_state(msg) == LinphoneChatMessageStateDelivered); + })); + + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, marie2, michelle, michelle2}).wait([marieCr] { + return linphone_chat_room_get_unread_messages_count(marieCr) == 1; + })); + marieLastMsg = marie.getStats().last_received_chat_message; + BC_ASSERT_PTR_NOT_NULL(marieLastMsg); + if (marieLastMsg) { + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_utf8_text(marieLastMsg), msg_text.c_str()); + } + + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, marie2, michelle, michelle2}).wait([marie2Cr] { + return linphone_chat_room_get_unread_messages_count(marie2Cr) == 1; + })); + marie2LastMsg = marie2.getStats().last_received_chat_message; + BC_ASSERT_PTR_NOT_NULL(marie2LastMsg); + if (marie2LastMsg) { + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_utf8_text(marie2LastMsg), msg_text.c_str()); + } + + linphone_chat_room_mark_as_read(michelleCr); + linphone_chat_room_mark_as_read(marieCr); + linphone_chat_room_mark_as_read(marie2Cr); + + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle2.getStats().number_of_LinphoneMessageDisplayed, + initialMichelle2Stats.number_of_LinphoneMessageDisplayed + 1, + liblinphone_tester_sip_timeout)); + + for (auto chatRoom : focus.getCore().getChatRooms()) { + for (auto participant : chatRoom->getParticipants()) { + // force deletion by removing devices + std::shared_ptr<Address> participantAddress = participant->getAddress(); + linphone_chat_room_set_participant_devices(L_GET_C_BACK_PTR(chatRoom), participantAddress->toC(), NULL); + } + } + + // wait until chatroom is deleted server side + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, marie2, michelle, michelle2}).wait([&focus] { + return focus.getCore().getChatRooms().size() == 0; + })); + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, marie2, michelle, michelle2}).waitUntil(chrono::seconds(2), [] { + return false; + }); + + // to avoid creation attempt of a new chatroom + LinphoneProxyConfig *config = linphone_core_get_default_proxy_config(focus.getLc()); + linphone_proxy_config_edit(config); + linphone_proxy_config_set_conference_factory_uri(config, NULL); + linphone_proxy_config_done(config); + + bctbx_list_free(coresList); + } +} + +static void group_chat_room_add_participant_with_invalid_address(void) { + Focus focus("chloe_rc"); + { // to make sure focus is destroyed after clients. + ClientConference marie("marie_rc", focus.getIdentity()); + ClientConference pauline("pauline_rc", focus.getIdentity()); + ClientConference michelle("michelle_rc", focus.getIdentity()); + + focus.registerAsParticipantDevice(marie); + focus.registerAsParticipantDevice(pauline); + focus.registerAsParticipantDevice(michelle); + + bctbx_list_t *coresList = bctbx_list_append(NULL, focus.getLc()); + coresList = bctbx_list_append(coresList, marie.getLc()); + coresList = bctbx_list_append(coresList, pauline.getLc()); + coresList = bctbx_list_append(coresList, michelle.getLc()); + Address paulineAddr = pauline.getIdentity(); + bctbx_list_t *participantsAddresses = bctbx_list_append(NULL, linphone_address_ref(paulineAddr.toC())); + Address invalidAddr = Address(); + participantsAddresses = bctbx_list_append(participantsAddresses, linphone_address_ref(invalidAddr.toC())); + Address michelleAddr = michelle.getIdentity(); + participantsAddresses = bctbx_list_append(participantsAddresses, linphone_address_ref(michelleAddr.toC())); + + stats initialMarieStats = marie.getStats(); + stats initialPaulineStats = pauline.getStats(); + stats initialMichelleStats = michelle.getStats(); + + // Marie creates a new group chat room + const char *initialSubject = "Colleagues"; + LinphoneChatRoom *marieCr = create_chat_room_client_side_with_expected_number_of_participants( + coresList, marie.getCMgr(), &initialMarieStats, participantsAddresses, initialSubject, 2, FALSE, + LinphoneChatRoomEphemeralModeDeviceManaged); + const LinphoneAddress *confAddr = linphone_chat_room_get_conference_address(marieCr); + + // Check that the chat room is correctly created on Pauline's side and that the participants are added + LinphoneChatRoom *paulineCr = check_creation_chat_room_client_side( + coresList, pauline.getCMgr(), &initialPaulineStats, confAddr, initialSubject, 2, FALSE); + LinphoneChatRoom *michelleCr = check_creation_chat_room_client_side( + coresList, michelle.getCMgr(), &initialMichelleStats, confAddr, initialSubject, 2, FALSE); + + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline, michelle}).wait([&focus] { + for (auto chatRoom : focus.getCore().getChatRooms()) { + for (auto participant : chatRoom->getParticipants()) { + for (auto device : participant->getDevices()) + if (device->getState() != ParticipantDevice::State::Present) { + return false; + } + } + } + return true; + })); + + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneConferenceStateCreated, + initialPaulineStats.number_of_LinphoneConferenceStateCreated + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_LinphoneConferenceStateCreated, + initialMichelleStats.number_of_LinphoneConferenceStateCreated + 1, + liblinphone_tester_sip_timeout)); + + // Marie now changes the subject + const char *newSubject = "Let's go drink a beer"; + linphone_chat_room_set_subject(marieCr, newSubject); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_subject_changed, + initialMarieStats.number_of_subject_changed + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_subject_changed, + initialPaulineStats.number_of_subject_changed + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_subject_changed, + initialMichelleStats.number_of_subject_changed + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_STRING_EQUAL(linphone_chat_room_get_subject(marieCr), newSubject); + BC_ASSERT_STRING_EQUAL(linphone_chat_room_get_subject(paulineCr), newSubject); + BC_ASSERT_STRING_EQUAL(linphone_chat_room_get_subject(michelleCr), newSubject); + + BC_ASSERT_EQUAL(linphone_chat_room_get_nb_participants(marieCr), 2, int, "%d"); + BC_ASSERT_EQUAL(linphone_chat_room_get_nb_participants(paulineCr), 2, int, "%d"); + BC_ASSERT_EQUAL(linphone_chat_room_get_nb_participants(michelleCr), 2, int, "%d"); + + initialMarieStats = marie.getStats(); + initialPaulineStats = pauline.getStats(); + initialMichelleStats = michelle.getStats(); + + linphone_chat_room_add_participant(marieCr, linphone_address_ref(invalidAddr.toC())); + + BC_ASSERT_FALSE(wait_for_list(coresList, &marie.getStats().number_of_participants_added, + initialMarieStats.number_of_participants_added + 1, 5000)); + BC_ASSERT_FALSE(wait_for_list(coresList, &marie.getStats().number_of_participant_devices_added, + initialMarieStats.number_of_participant_devices_added + 1, 1000)); + BC_ASSERT_FALSE(wait_for_list(coresList, &marie.getStats().number_of_participant_devices_joined, + initialMarieStats.number_of_participant_devices_joined + 1, 1000)); + BC_ASSERT_FALSE(wait_for_list(coresList, &pauline.getStats().number_of_participants_added, + initialPaulineStats.number_of_participants_added + 1, 1000)); + BC_ASSERT_FALSE(wait_for_list(coresList, &pauline.getStats().number_of_participant_devices_added, + initialPaulineStats.number_of_participant_devices_added + 1, 1000)); + BC_ASSERT_FALSE(wait_for_list(coresList, &pauline.getStats().number_of_participant_devices_joined, + initialPaulineStats.number_of_participant_devices_joined + 1, 1000)); + BC_ASSERT_FALSE(wait_for_list(coresList, &michelle.getStats().number_of_participants_added, + initialMichelleStats.number_of_participants_added + 1, 1000)); + BC_ASSERT_FALSE(wait_for_list(coresList, &michelle.getStats().number_of_participant_devices_added, + initialMichelleStats.number_of_participant_devices_added + 1, 1000)); + BC_ASSERT_FALSE(wait_for_list(coresList, &michelle.getStats().number_of_participant_devices_joined, + initialMichelleStats.number_of_participant_devices_joined + 1, 1000)); + + BC_ASSERT_EQUAL(linphone_chat_room_get_nb_participants(marieCr), 2, int, "%d"); + BC_ASSERT_EQUAL(linphone_chat_room_get_nb_participants(paulineCr), 2, int, "%d"); + BC_ASSERT_EQUAL(linphone_chat_room_get_nb_participants(michelleCr), 2, int, "%d"); + + CoreManagerAssert({focus, marie, pauline, michelle}).waitUntil(std::chrono::seconds(1), [] { return false; }); + + CoreManagerAssert({focus, marie}).waitUntil(std::chrono::seconds(2), [] { return false; }); + + for (auto chatRoom : focus.getCore().getChatRooms()) { + for (auto participant : chatRoom->getParticipants()) { + // force deletion by removing devices + std::shared_ptr<Address> participantAddress = participant->getAddress(); + linphone_chat_room_set_participant_devices(L_GET_C_BACK_PTR(chatRoom), participantAddress->toC(), NULL); + } + } + + // wait until chatroom is deleted server side + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline, michelle}).wait([&focus] { + return focus.getCore().getChatRooms().size() == 0; + })); + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, michelle}).waitUntil(chrono::seconds(2), [] { return false; }); + + // to avoid creation attempt of a new chatroom + auto config = focus.getDefaultProxyConfig(); + linphone_proxy_config_edit(config); + linphone_proxy_config_set_conference_factory_uri(config, NULL); + linphone_proxy_config_done(config); + + linphone_address_unref(invalidAddr.toC()); + bctbx_list_free(coresList); + } +} + +static void group_chat_room_with_only_participant_with_invalid_address(void) { + Focus focus("chloe_rc"); + { // to make sure focus is destroyed after clients. + ClientConference marie("marie_rc", focus.getIdentity()); + + focus.registerAsParticipantDevice(marie); + + bctbx_list_t *coresList = bctbx_list_append(NULL, focus.getLc()); + coresList = bctbx_list_append(coresList, marie.getLc()); + Address invalidAddr = Address(); + bctbx_list_t *participantsAddresses = bctbx_list_append(NULL, linphone_address_ref(invalidAddr.toC())); + + stats initialMarieStats = marie.getStats(); + + // Marie creates a new group chat room + const char *initialSubject = "Colleagues"; + + LinphoneChatRoomParams *chatRoomParams = linphone_core_create_default_chat_room_params(marie.getLc()); + linphone_chat_room_params_enable_encryption(chatRoomParams, FALSE); + linphone_chat_room_params_set_backend(chatRoomParams, LinphoneChatRoomBackendFlexisipChat); + linphone_chat_room_params_enable_group(chatRoomParams, TRUE); + LinphoneChatRoom *marieCr = create_chat_room_client_side_with_expected_number_of_participants( + coresList, marie.getCMgr(), &initialMarieStats, participantsAddresses, initialSubject, 0, FALSE, + LinphoneChatRoomEphemeralModeDeviceManaged); + linphone_chat_room_params_unref(chatRoomParams); + BC_ASSERT_PTR_NOT_NULL(marieCr); + + // Check that the chat room has not been created + BC_ASSERT_FALSE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateCreated, + initialMarieStats.number_of_LinphoneConferenceStateCreated + 1, 3000)); + BC_ASSERT_FALSE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneChatRoomConferenceJoined, + initialMarieStats.number_of_LinphoneChatRoomConferenceJoined + 1, 1000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateCreationFailed, + initialMarieStats.number_of_LinphoneConferenceStateCreationFailed + 1, + liblinphone_tester_sip_timeout)); + + // to avoid creation attempt of a new chatroom + auto config = focus.getDefaultProxyConfig(); + linphone_proxy_config_edit(config); + linphone_proxy_config_set_conference_factory_uri(config, NULL); + linphone_proxy_config_done(config); + + bctbx_list_free(coresList); + } +} + +static void one_to_one_chatroom_exhumed_while_offline(void) { + Focus focus("chloe_rc"); + { // to make sure focus is destroyed after clients. + ClientConference marie("marie_rc", focus.getIdentity()); + ClientConference pauline("pauline_rc", focus.getIdentity()); + + focus.registerAsParticipantDevice(marie); + focus.registerAsParticipantDevice(pauline); + + bctbx_list_t *coresList = bctbx_list_append(NULL, focus.getLc()); + coresList = bctbx_list_append(coresList, marie.getLc()); + coresList = bctbx_list_append(coresList, pauline.getLc()); + Address paulineAddr = pauline.getIdentity(); + bctbx_list_t *participantsAddresses = bctbx_list_append(NULL, linphone_address_ref(paulineAddr.toC())); + + stats initialMarieStats = marie.getStats(); + stats initialPaulineStats = pauline.getStats(); + + // Marie creates a new one to one chat room + const char *initialSubject = "one to one with Pauline"; + LinphoneChatRoom *marieCr = + create_chat_room_client_side(coresList, marie.getCMgr(), &initialMarieStats, participantsAddresses, + initialSubject, FALSE, LinphoneChatRoomEphemeralModeDeviceManaged); + BC_ASSERT_PTR_NOT_NULL(marieCr); + LinphoneAddress *confAddr = linphone_address_clone(linphone_chat_room_get_conference_address(marieCr)); + BC_ASSERT_PTR_NOT_NULL(confAddr); + + // Check that the chat room is correctly created on Pauline's side and that the participants are added + LinphoneChatRoom *paulineCr = check_creation_chat_room_client_side( + coresList, pauline.getCMgr(), &initialPaulineStats, confAddr, initialSubject, 1, FALSE); + + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline}).wait([&focus] { + for (auto chatRoom : focus.getCore().getChatRooms()) { + for (auto participant : chatRoom->getParticipants()) { + for (auto device : participant->getDevices()) + if (device->getState() != ParticipantDevice::State::Present) { + return false; + } + } + } + return true; + })); + + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneConferenceStateCreated, + initialPaulineStats.number_of_LinphoneConferenceStateCreated + 1, + liblinphone_tester_sip_timeout)); + + // Pauline goes offline + linphone_core_set_network_reachable(pauline.getLc(), FALSE); + + LinphoneChatMessage *marieMsg1 = linphone_chat_room_create_message_from_utf8(marieCr, "Long live the C++ !"); + linphone_chat_message_send(marieMsg1); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneMessageSent, + initialMarieStats.number_of_LinphoneMessageSent + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_FALSE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneMessageReceived, + initialPaulineStats.number_of_LinphoneMessageReceived + 1, 3000)); + linphone_chat_message_unref(marieMsg1); + + int marieMsgs = linphone_chat_room_get_history_size(marieCr); + BC_ASSERT_EQUAL(marieMsgs, 1, int, "%d"); + // Pauline didn't received the message as she was offline + int paulineMsgs = linphone_chat_room_get_history_size(paulineCr); + BC_ASSERT_EQUAL(paulineMsgs, 0, int, "%d"); + + // Wait a little bit to detect side effects + CoreManagerAssert({focus, marie, pauline}).waitUntil(std::chrono::seconds(2), [] { return false; }); + + // Marie deletes the chat room + // Pauline cannot now this because she is offline + linphone_core_manager_delete_chat_room(marie.getCMgr(), marieCr, coresList); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateTerminated, + initialMarieStats.number_of_LinphoneConferenceStateTerminated + 1, + liblinphone_tester_sip_timeout)); + + initialMarieStats = marie.getStats(); + initialPaulineStats = pauline.getStats(); + + paulineAddr = pauline.getIdentity(); + participantsAddresses = bctbx_list_append(NULL, linphone_address_ref(paulineAddr.toC())); + + marieCr = create_chat_room_client_side(coresList, marie.getCMgr(), &initialMarieStats, participantsAddresses, + initialSubject, FALSE, LinphoneChatRoomEphemeralModeDeviceManaged); + BC_ASSERT_PTR_NOT_NULL(marieCr); + LinphoneAddress *exhumedConfAddrPtr = (LinphoneAddress *)linphone_chat_room_get_conference_address(marieCr); + BC_ASSERT_PTR_NOT_NULL(exhumedConfAddrPtr); + LinphoneAddress *exhumedConfAddr = NULL; + if (exhumedConfAddrPtr) { + exhumedConfAddr = + linphone_address_clone((LinphoneAddress *)linphone_chat_room_get_conference_address(marieCr)); + BC_ASSERT_PTR_NOT_NULL(exhumedConfAddr); + if (exhumedConfAddr) { + BC_ASSERT_FALSE(linphone_address_equal(confAddr, exhumedConfAddr)); + } + } + + BC_ASSERT_EQUAL((int)marie.getCore().getChatRooms().size(), 1, int, "%d"); + BC_ASSERT_EQUAL(linphone_chat_room_get_nb_participants(marieCr), 1, int, "%d"); + + BC_ASSERT_EQUAL(linphone_chat_room_get_nb_participants(paulineCr), 1, int, "%d"); + + // Wait a little bit to detect side effects + CoreManagerAssert({focus, marie, pauline}).waitUntil(std::chrono::seconds(2), [] { return false; }); + + initialPaulineStats = pauline.getStats(); + // Pauline comes up online + linphone_core_set_network_reachable(pauline.getLc(), TRUE); + + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneRegistrationOk, + initialPaulineStats.number_of_LinphoneRegistrationOk + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneChatRoomConferenceJoined, + initialPaulineStats.number_of_LinphoneChatRoomConferenceJoined + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_EQUAL((int)pauline.getCore().getChatRooms().size(), 1, int, "%d"); + + char *paulineDeviceIdentity = linphone_core_get_device_identity(pauline.getLc()); + LinphoneAddress *paulineDeviceAddr = linphone_address_new(paulineDeviceIdentity); + bctbx_free(paulineDeviceIdentity); + auto newPaulineCr = pauline.searchChatRoom(paulineDeviceAddr, confAddr); + linphone_address_unref(paulineDeviceAddr); + BC_ASSERT_PTR_NOT_NULL(newPaulineCr); + BC_ASSERT_PTR_EQUAL(newPaulineCr, paulineCr); + + if (newPaulineCr) { + LinphoneAddress *paulineNewConfAddr = + linphone_address_ref((LinphoneAddress *)linphone_chat_room_get_conference_address(newPaulineCr)); + BC_ASSERT_PTR_NOT_NULL(paulineNewConfAddr); + if (paulineNewConfAddr) { + BC_ASSERT_FALSE(linphone_address_equal(confAddr, paulineNewConfAddr)); + if (exhumedConfAddr) { + BC_ASSERT_TRUE(linphone_address_equal(exhumedConfAddr, paulineNewConfAddr)); + } + } + linphone_address_unref(paulineNewConfAddr); + + BC_ASSERT_EQUAL(linphone_chat_room_get_nb_participants(newPaulineCr), 1, int, "%d"); + BC_ASSERT_STRING_EQUAL(linphone_chat_room_get_subject(newPaulineCr), initialSubject); + + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneMessageReceived, + initialPaulineStats.number_of_LinphoneMessageReceived + 1, + liblinphone_tester_sip_timeout)); + paulineMsgs = linphone_chat_room_get_history_size(newPaulineCr); + BC_ASSERT_EQUAL(paulineMsgs, 1, int, "%d"); + + LinphoneChatMessage *paulineMsg = + linphone_chat_room_create_message_from_utf8(newPaulineCr, "Sorry I was offline :("); + linphone_chat_message_send(paulineMsg); + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline}).wait([paulineMsg] { + return (linphone_chat_message_get_state(paulineMsg) == LinphoneChatMessageStateDelivered); + })); + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline}).wait([marieCr] { + return linphone_chat_room_get_unread_messages_count(marieCr) == 1; + })); + + // Since Marie has deleted the chat room, she lost all messages she sent before deleting it + marieMsgs = linphone_chat_room_get_history_size(marieCr); + BC_ASSERT_EQUAL(marieMsgs, 1, int, "%d"); + paulineMsgs = linphone_chat_room_get_history_size(newPaulineCr); + BC_ASSERT_EQUAL(paulineMsgs, 2, int, "%d"); + + LinphoneChatMessage *marieMsg = linphone_chat_room_create_message_from_utf8(marieCr, "exhumed!!"); + linphone_chat_message_send(marieMsg); + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline}).wait([marieMsg] { + return (linphone_chat_message_get_state(marieMsg) == LinphoneChatMessageStateDelivered); + })); + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline}).wait([newPaulineCr]() mutable { + return linphone_chat_room_get_unread_messages_count(newPaulineCr) == 2; + })); + linphone_chat_message_unref(marieMsg); + + marieMsgs = linphone_chat_room_get_history_size(marieCr); + BC_ASSERT_EQUAL(marieMsgs, 2, int, "%d"); + paulineMsgs = linphone_chat_room_get_history_size(newPaulineCr); + BC_ASSERT_EQUAL(paulineMsgs, 3, int, "%d"); + } + + linphone_address_unref(exhumedConfAddr); + + CoreManagerAssert({focus, marie, pauline}).waitUntil(std::chrono::seconds(1), [] { return false; }); + + CoreManagerAssert({focus, marie}).waitUntil(std::chrono::seconds(2), [] { return false; }); + + initialMarieStats = marie.getStats(); + initialPaulineStats = pauline.getStats(); + + linphone_core_manager_delete_chat_room(marie.getCMgr(), marieCr, coresList); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateTerminated, + initialMarieStats.number_of_LinphoneConferenceStateTerminated + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneConferenceStateTerminated, + initialPaulineStats.number_of_LinphoneConferenceStateTerminated + 1, + liblinphone_tester_sip_timeout)); + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline}).waitUntil(chrono::seconds(2), [] { return false; }); + + // to avoid creation attempt of a new chatroom + auto config = focus.getDefaultProxyConfig(); + linphone_proxy_config_edit(config); + linphone_proxy_config_set_conference_factory_uri(config, NULL); + linphone_proxy_config_done(config); + + bctbx_list_free(coresList); + } +} + +static void multidomain_group_chat_room(void) { + Focus focusExampleDotOrg("chloe_rc"); + Focus focusAuth1DotExampleDotOrg("arthur_rc"); + { // to make sure focus is destroyed after clients. + ClientConference marie("marie_rc", focusExampleDotOrg.getIdentity()); + ClientConference pauline("pauline_rc", focusExampleDotOrg.getIdentity()); + ClientConference michelle("michelle_rc", focusExampleDotOrg.getIdentity()); + + focusExampleDotOrg.registerAsParticipantDevice(marie); + focusExampleDotOrg.registerAsParticipantDevice(pauline); + focusExampleDotOrg.registerAsParticipantDevice(michelle); + + bctbx_list_t *coresList = bctbx_list_append(NULL, focusExampleDotOrg.getLc()); + coresList = bctbx_list_append(coresList, marie.getLc()); + coresList = bctbx_list_append(coresList, pauline.getLc()); + coresList = bctbx_list_append(coresList, michelle.getLc()); + Address paulineAddr = pauline.getIdentity(); + bctbx_list_t *participantsAddresses = bctbx_list_append(NULL, linphone_address_ref(paulineAddr.toC())); + Address michelleAddr = michelle.getIdentity(); + participantsAddresses = bctbx_list_append(participantsAddresses, linphone_address_ref(michelleAddr.toC())); + + stats initialMarieStats = marie.getStats(); + stats initialPaulineStats = pauline.getStats(); + stats initialMichelleStats = michelle.getStats(); + + // Marie creates a new group chat room + const char *initialSubject = "Colleagues"; + LinphoneChatRoom *marieCr = + create_chat_room_client_side(coresList, marie.getCMgr(), &initialMarieStats, participantsAddresses, + initialSubject, FALSE, LinphoneChatRoomEphemeralModeDeviceManaged); + LinphoneAddress *confAddr = linphone_address_clone(linphone_chat_room_get_conference_address(marieCr)); + + // Check that the chat room is correctly created on Pauline's side and that the participants are added + LinphoneChatRoom *paulineCr = check_creation_chat_room_client_side( + coresList, pauline.getCMgr(), &initialPaulineStats, confAddr, initialSubject, 2, FALSE); + + LinphoneChatRoom *michelleCr = check_creation_chat_room_client_side( + coresList, michelle.getCMgr(), &initialMichelleStats, confAddr, initialSubject, 2, FALSE); + + if (paulineCr || michelleCr) { + // throw BCTBX_EXCEPTION << "Cannot create chatroom giving up"; + // goto end; + } + BC_ASSERT_TRUE(CoreManagerAssert({focusExampleDotOrg, marie, pauline, michelle}).wait([&focusExampleDotOrg] { + for (auto chatRoom : focusExampleDotOrg.getCore().getChatRooms()) { + for (auto participant : chatRoom->getParticipants()) { + for (auto device : participant->getDevices()) + if (device->getState() != ParticipantDevice::State::Present) { + return false; + } + } + } + return true; + })); + + LinphoneChatMessage *msg = linphone_chat_room_create_message_from_utf8(marieCr, "message blabla"); + linphone_chat_message_send(msg); + BC_ASSERT_TRUE(CoreManagerAssert({focusExampleDotOrg, marie, pauline, michelle}).wait([msg] { + return (linphone_chat_message_get_state(msg) == LinphoneChatMessageStateDelivered); + })); + BC_ASSERT_TRUE(CoreManagerAssert({focusExampleDotOrg, marie, pauline, michelle}).wait([paulineCr] { + return linphone_chat_room_get_unread_messages_count(paulineCr) == 1; + })); + BC_ASSERT_TRUE(CoreManagerAssert({focusExampleDotOrg, marie, pauline, michelle}).wait([michelleCr] { + return linphone_chat_room_get_unread_messages_count(michelleCr) == 1; + })); + linphone_chat_message_unref(msg); + + // now change focus in order to get conference with multiple domain. + focusAuth1DotExampleDotOrg.registerAsParticipantDevice(marie); + focusAuth1DotExampleDotOrg.registerAsParticipantDevice(pauline); + focusAuth1DotExampleDotOrg.registerAsParticipantDevice(michelle); + + // change conference factory uri + Address focusAuth1DotExampleDotOrgFactoryAddress = focusAuth1DotExampleDotOrg.getIdentity(); + marie.configureCoreForConference(focusAuth1DotExampleDotOrgFactoryAddress); + pauline.configureCoreForConference(focusAuth1DotExampleDotOrgFactoryAddress); + michelle.configureCoreForConference(focusAuth1DotExampleDotOrgFactoryAddress); + + coresList = bctbx_list_append(coresList, focusAuth1DotExampleDotOrg.getLc()); + initialMarieStats = marie.getStats(); + initialPaulineStats = pauline.getStats(); + initialMichelleStats = michelle.getStats(); + participantsAddresses = bctbx_list_append(NULL, linphone_address_ref(paulineAddr.toC())); + participantsAddresses = bctbx_list_append(participantsAddresses, linphone_address_ref(michelleAddr.toC())); + LinphoneChatRoom *marieCrfocusAuth1DotExampleDotOrg = + create_chat_room_client_side(coresList, marie.getCMgr(), &initialMarieStats, participantsAddresses, + initialSubject, FALSE, LinphoneChatRoomEphemeralModeDeviceManaged); + LinphoneAddress *confAddrfocusAuth1DotExampleDotOrg = + linphone_address_clone(linphone_chat_room_get_conference_address(marieCrfocusAuth1DotExampleDotOrg)); + + // Check that the chat room is correctly created on Pauline's side and that the participants are added + LinphoneChatRoom *paulineCrfocusAuth1DotExampleDotOrg = + check_creation_chat_room_client_side(coresList, pauline.getCMgr(), &initialPaulineStats, + confAddrfocusAuth1DotExampleDotOrg, initialSubject, 2, FALSE); + BC_ASSERT_PTR_NOT_NULL(paulineCrfocusAuth1DotExampleDotOrg); + LinphoneChatRoom *michelleCrfocusAuth1DotExampleDotOrg = + check_creation_chat_room_client_side(coresList, michelle.getCMgr(), &initialMichelleStats, + confAddrfocusAuth1DotExampleDotOrg, initialSubject, 2, FALSE); + BC_ASSERT_PTR_NOT_NULL(michelleCrfocusAuth1DotExampleDotOrg); + + BC_ASSERT_TRUE(CoreManagerAssert({focusAuth1DotExampleDotOrg, marie, pauline, michelle}) + .wait([&focusAuth1DotExampleDotOrg] { + for (auto chatRoom : focusAuth1DotExampleDotOrg.getCore().getChatRooms()) { + for (auto participant : chatRoom->getParticipants()) { + for (auto device : participant->getDevices()) + if (device->getState() != ParticipantDevice::State::Present) { + return false; + } + } + } + return true; + })); + + msg = linphone_chat_room_create_message_from_utf8(marieCrfocusAuth1DotExampleDotOrg, "message blabla"); + linphone_chat_message_send(msg); + BC_ASSERT_TRUE(CoreManagerAssert({focusAuth1DotExampleDotOrg, marie, pauline, michelle}).wait([msg] { + return (linphone_chat_message_get_state(msg) == LinphoneChatMessageStateDelivered); + })); + + // great, now I want to see what happened if marie restart. + coresList = bctbx_list_remove(coresList, marie.getLc()); + marie.reStart(); + marie.setupMgrForConference(); + coresList = bctbx_list_append(coresList, marie.getLc()); + + // Retrieve chat room + LinphoneAddress *marieDeviceAddr = + linphone_address_clone(linphone_proxy_config_get_contact(marie.getDefaultProxyConfig())); + marieCr = marie.searchChatRoom(marieDeviceAddr, confAddr); + BC_ASSERT_PTR_NOT_NULL(marieCr); + marieCrfocusAuth1DotExampleDotOrg = marie.searchChatRoom(marieDeviceAddr, confAddrfocusAuth1DotExampleDotOrg); + BC_ASSERT_PTR_NOT_NULL(marieCrfocusAuth1DotExampleDotOrg); + + CoreManagerAssert({focusExampleDotOrg, marie, pauline, michelle}).waitUntil(chrono::seconds(2), [] { + return false; + }); + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneSubscriptionActive, 2, + liblinphone_tester_sip_timeout)); + + ClientConference laure("laure_tcp_rc", focusExampleDotOrg.getIdentity()); + coresList = bctbx_list_append(coresList, laure.getLc()); + Address laureAddr = laure.getIdentity(); + focusExampleDotOrg.registerAsParticipantDevice(laure); + + initialMarieStats = marie.getStats(); + initialPaulineStats = pauline.getStats(); + initialMichelleStats = michelle.getStats(); + stats initialLaureStats = laure.getStats(); + + participantsAddresses = bctbx_list_append(NULL, linphone_address_ref(laureAddr.toC())); + linphone_chat_room_add_participants(marieCr, participantsAddresses); + + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_LinphoneConferenceStateCreationPending, + initialLaureStats.number_of_LinphoneConferenceStateCreationPending + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_LinphoneConferenceStateCreated, + initialLaureStats.number_of_LinphoneConferenceStateCreated + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_LinphoneChatRoomConferenceJoined, + initialLaureStats.number_of_LinphoneChatRoomConferenceJoined + 1, + liblinphone_tester_sip_timeout)); + + LinphoneChatRoom *laureCr = check_creation_chat_room_client_side(coresList, laure.getCMgr(), &initialLaureStats, + confAddr, initialSubject, 3, FALSE); + BC_ASSERT_PTR_NOT_NULL(laureCr); + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_participants_added, + initialMarieStats.number_of_participants_added + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_participants_added, + initialPaulineStats.number_of_participants_added + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_participants_added, + initialMichelleStats.number_of_participants_added + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_EQUAL(linphone_chat_room_get_nb_participants(marieCr), 3, int, "%d"); + BC_ASSERT_EQUAL(linphone_chat_room_get_nb_participants(paulineCr), 3, int, "%d"); + BC_ASSERT_EQUAL(linphone_chat_room_get_nb_participants(michelleCr), 3, int, "%d"); + + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_NotifyReceived, + initialPaulineStats.number_of_NotifyReceived + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_NotifyReceived, + initialMichelleStats.number_of_NotifyReceived + 1, + liblinphone_tester_sip_timeout)); + + focusAuth1DotExampleDotOrg.registerAsParticipantDevice(laure); + laure.configureCoreForConference(focusAuth1DotExampleDotOrgFactoryAddress); + + participantsAddresses = bctbx_list_append(NULL, linphone_address_ref(laureAddr.toC())); + linphone_chat_room_add_participants(marieCrfocusAuth1DotExampleDotOrg, participantsAddresses); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_LinphoneConferenceStateCreationPending, + initialLaureStats.number_of_LinphoneConferenceStateCreationPending + 2, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_LinphoneConferenceStateCreated, + initialLaureStats.number_of_LinphoneConferenceStateCreated + 2, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_LinphoneChatRoomConferenceJoined, + initialLaureStats.number_of_LinphoneChatRoomConferenceJoined + 2, + liblinphone_tester_sip_timeout)); + + LinphoneChatRoom *laureCrfocusAuth1DotExampleDotOrg = + check_creation_chat_room_client_side(coresList, laure.getCMgr(), &initialLaureStats, + confAddrfocusAuth1DotExampleDotOrg, initialSubject, 3, FALSE); + BC_ASSERT_PTR_NOT_NULL(laureCrfocusAuth1DotExampleDotOrg); + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_participants_added, + initialMarieStats.number_of_participants_added + 2, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_participants_added, + initialPaulineStats.number_of_participants_added + 2, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_participants_added, + initialMichelleStats.number_of_participants_added + 2, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_EQUAL(linphone_chat_room_get_nb_participants(marieCrfocusAuth1DotExampleDotOrg), 3, int, "%d"); + BC_ASSERT_EQUAL(linphone_chat_room_get_nb_participants(paulineCrfocusAuth1DotExampleDotOrg), 3, int, "%d"); + BC_ASSERT_EQUAL(linphone_chat_room_get_nb_participants(michelleCrfocusAuth1DotExampleDotOrg), 3, int, "%d"); + + linphone_chat_message_unref(msg); + + linphone_address_unref(marieDeviceAddr); + bctbx_list_free(coresList); + } +} + +static void group_chat_room_server_ephemeral_mode_changed(void) { + Focus focus("chloe_rc"); + { // to make sure focus is destroyed after clients. + ClientConference marie("marie_rc", focus.getIdentity()); + ClientConference pauline("pauline_rc", focus.getIdentity()); + + focus.registerAsParticipantDevice(marie); + focus.registerAsParticipantDevice(pauline); + + // Enable IMDN + linphone_im_notif_policy_enable_all(linphone_core_get_im_notif_policy(marie.getLc())); + linphone_im_notif_policy_enable_all(linphone_core_get_im_notif_policy(pauline.getLc())); + + bctbx_list_t *coresList = bctbx_list_append(NULL, focus.getLc()); + coresList = bctbx_list_append(coresList, marie.getLc()); + coresList = bctbx_list_append(coresList, pauline.getLc()); + Address paulineAddr = pauline.getIdentity(); + bctbx_list_t *participantsAddresses = bctbx_list_append(NULL, linphone_address_ref(paulineAddr.toC())); + + stats chloe_stat = focus.getStats(); + stats marie_stat = marie.getStats(); + stats pauline_stat = pauline.getStats(); + + // Marie creates a new group chat room + const char *initialSubject = "Colleagues"; + const LinphoneChatRoomEphemeralMode adminMode = LinphoneChatRoomEphemeralModeAdminManaged; + LinphoneChatRoomParams *params = linphone_core_create_default_chat_room_params(marie.getLc()); + + linphone_chat_room_params_enable_group(params, FALSE); + linphone_chat_room_params_enable_encryption(params, FALSE); + linphone_chat_room_params_set_ephemeral_mode(params, adminMode); + linphone_chat_room_params_set_ephemeral_lifetime(params, 0); + linphone_chat_room_params_set_backend(params, LinphoneChatRoomBackendFlexisipChat); + + LinphoneChatRoom *marieCr = create_chat_room_client_side_with_params( + coresList, marie.getCMgr(), &marie_stat, participantsAddresses, initialSubject, params); + linphone_chat_room_params_unref(params); + BC_ASSERT_PTR_NOT_NULL(marieCr); + const LinphoneAddress *confAddr = linphone_chat_room_get_conference_address(marieCr); + + // Check that the chat room is correctly created on Pauline's side and that the participants are added + LinphoneChatRoom *paulineCr = check_creation_chat_room_client_side(coresList, pauline.getCMgr(), &pauline_stat, + confAddr, initialSubject, 1, FALSE); + + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_mode(marieCr), adminMode, int, "%d"); + BC_ASSERT_FALSE(linphone_chat_room_ephemeral_enabled(marieCr)); + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_mode(paulineCr), adminMode, int, "%d"); + BC_ASSERT_FALSE(linphone_chat_room_ephemeral_enabled(paulineCr)); + + pauline_stat = pauline.getStats(); + linphone_chat_room_set_ephemeral_lifetime(marieCr, 10); + + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_NotifyReceived, + pauline_stat.number_of_NotifyReceived + 1, liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline}).waitUntil(chrono::seconds(50), [&pauline] { + for (auto chatRoom : pauline.getCore().getChatRooms()) { + if (!chatRoom->ephemeralEnabled() || (chatRoom->getEphemeralLifetime() != 10)) { + return false; + } + } + return true; + })); + + BC_ASSERT_TRUE(linphone_chat_room_ephemeral_enabled(marieCr)); + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_mode(marieCr), adminMode, int, "%d"); + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_lifetime(marieCr), 10, int, "%d"); + BC_ASSERT_TRUE(linphone_chat_room_ephemeral_enabled(paulineCr)); + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_mode(paulineCr), adminMode, int, "%d"); + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_lifetime(paulineCr), 10, int, "%d"); + + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline}).wait([&focus] { + for (auto chatRoom : focus.getCore().getChatRooms()) { + for (auto participant : chatRoom->getParticipants()) { + for (auto device : participant->getDevices()) + if (device->getState() != ParticipantDevice::State::Present) { + return false; + } + } + } + return true; + })); + + chloe_stat = focus.getStats(); + marie_stat = marie.getStats(); + pauline_stat = pauline.getStats(); + + constexpr int noMsg = 10; + sendEphemeralMessageInAdminMode(focus, marie, pauline, marieCr, paulineCr, "Hello ", noMsg); + + pauline_stat = pauline.getStats(); + linphone_chat_room_set_ephemeral_lifetime(marieCr, 0); + + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_NotifyReceived, + pauline_stat.number_of_NotifyReceived + 1, liblinphone_tester_sip_timeout)); + + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_mode(marieCr), adminMode, int, "%d"); + BC_ASSERT_FALSE(linphone_chat_room_ephemeral_enabled(marieCr)); + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_mode(paulineCr), adminMode, int, "%d"); + BC_ASSERT_FALSE(linphone_chat_room_ephemeral_enabled(paulineCr)); + + pauline_stat = pauline.getStats(); + const LinphoneChatRoomEphemeralMode deviceMode = LinphoneChatRoomEphemeralModeDeviceManaged; + linphone_chat_room_set_ephemeral_mode(marieCr, deviceMode); + + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_NotifyReceived, + pauline_stat.number_of_NotifyReceived + 1, liblinphone_tester_sip_timeout)); + + pauline_stat = pauline.getStats(); + marie_stat = marie.getStats(); + + linphone_chat_room_enable_ephemeral(paulineCr, TRUE); + linphone_chat_room_set_ephemeral_lifetime(paulineCr, 5); + + wait_for_list(coresList, NULL, 1, 2000); + + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_mode(marieCr), deviceMode, int, "%d"); + BC_ASSERT_FALSE(linphone_chat_room_ephemeral_enabled(marieCr)); + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_mode(paulineCr), deviceMode, int, "%d"); + BC_ASSERT_TRUE(linphone_chat_room_ephemeral_enabled(paulineCr)); + BC_ASSERT_EQUAL(linphone_chat_room_get_ephemeral_lifetime(paulineCr), 5, int, "%d"); + + LinphoneChatMessage *nonEphemeralMessage = _send_message(marieCr, "I have disabled ephemeral messages"); + + auto marieHistory = linphone_chat_room_get_history(marieCr, 0); + BC_ASSERT_EQUAL((int)bctbx_list_size(marieHistory), 1, int, "%i"); + bctbx_list_free_with_data(marieHistory, (bctbx_list_free_func)linphone_chat_message_unref); + + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneMessageReceived, + pauline_stat.number_of_LinphoneMessageReceived + 1, 11000)); + + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline}).wait([nonEphemeralMessage] { + return (linphone_chat_message_get_state(nonEphemeralMessage) == LinphoneChatMessageStateDelivered); + })); + + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline}).wait([&, paulineCr] { + return linphone_chat_room_get_unread_messages_count(paulineCr) == 1; + })); + + auto paulineHistory = linphone_chat_room_get_history(paulineCr, 0); + BC_ASSERT_EQUAL((int)bctbx_list_size(paulineHistory), 1, int, "%i"); + bctbx_list_free_with_data(paulineHistory, (bctbx_list_free_func)linphone_chat_message_unref); + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneMessageDeliveredToUser, + marie_stat.number_of_LinphoneMessageDeliveredToUser + 1, + liblinphone_tester_sip_timeout)); + + // Pauline marks the message as read, check that the state is now displayed on Marie's side + linphone_chat_room_mark_as_read(paulineCr); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneMessageDisplayed, + marie_stat.number_of_LinphoneMessageDisplayed + 1, + liblinphone_tester_sip_timeout)); + + sendEphemeralMessageInAdminMode(focus, pauline, marie, paulineCr, marieCr, "Test ephemeral message ", noMsg); + + if (nonEphemeralMessage) { + linphone_chat_message_unref(nonEphemeralMessage); + } + + for (auto chatRoom : focus.getCore().getChatRooms()) { + for (auto participant : chatRoom->getParticipants()) { + // force deletion by removing devices + std::shared_ptr<Address> participantAddress = participant->getAddress(); + linphone_chat_room_set_participant_devices(L_GET_C_BACK_PTR(chatRoom), participantAddress->toC(), NULL); + } + } + + // wait until chatroom is deleted server side + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline}).wait([&focus] { + return focus.getCore().getChatRooms().size() == 0; + })); + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline}).waitUntil(chrono::seconds(2), [] { return false; }); + + // to avoid creation attempt of a new chatroom + auto config = focus.getDefaultProxyConfig(); + linphone_proxy_config_edit(config); + linphone_proxy_config_set_conference_factory_uri(config, NULL); + linphone_proxy_config_done(config); + + bctbx_list_free(coresList); + } +} + +static void group_chat_room_lime_server_message(bool encrypted) { + Focus focus("chloe_rc"); + LinphoneChatMessage *msg; + { // to make sure focus is destroyed after clients. + linphone_core_enable_lime_x3dh(focus.getLc(), true); + + ClientConference marie("marie_rc", focus.getIdentity(), encrypted); + ClientConference pauline("pauline_rc", focus.getIdentity(), encrypted); + ClientConference laure("laure_tcp_rc", focus.getIdentity(), encrypted); + + focus.registerAsParticipantDevice(marie); + focus.registerAsParticipantDevice(pauline); + focus.registerAsParticipantDevice(laure); + + stats marie_stat = marie.getStats(); + stats pauline_stat = pauline.getStats(); + stats laure_stat = laure.getStats(); + bctbx_list_t *coresList = bctbx_list_append(NULL, focus.getLc()); + coresList = bctbx_list_append(coresList, marie.getLc()); + coresList = bctbx_list_append(coresList, pauline.getLc()); + coresList = bctbx_list_append(coresList, laure.getLc()); + + if (encrypted) { + auto rawEncryptionSuccess = 0; + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_X3dhUserCreationSuccess, + marie_stat.number_of_X3dhUserCreationSuccess + 1, x3dhServer_creationTimeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_X3dhUserCreationSuccess, + laure_stat.number_of_X3dhUserCreationSuccess + 1, x3dhServer_creationTimeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_X3dhUserCreationSuccess, + pauline_stat.number_of_X3dhUserCreationSuccess + 1, + x3dhServer_creationTimeout)); + + BC_ASSERT_TRUE(linphone_core_lime_x3dh_enabled(marie.getLc())); + BC_ASSERT_TRUE(linphone_core_lime_x3dh_enabled(pauline.getLc())); + BC_ASSERT_TRUE(linphone_core_lime_x3dh_enabled(laure.getLc())); + + // Test the raw encryption/decryption + auto marieEncryptionEngine = L_GET_CPP_PTR_FROM_C_OBJECT(marie.getCMgr()->lc)->getEncryptionEngine(); + char *deviceId = linphone_address_as_string_uri_only( + linphone_proxy_config_get_contact(linphone_core_get_default_proxy_config(marie.getLc()))); + std::string marieAddressString{deviceId}; + bctbx_free(deviceId); + auto paulineEncryptionEngine = L_GET_CPP_PTR_FROM_C_OBJECT(pauline.getCMgr()->lc)->getEncryptionEngine(); + deviceId = linphone_address_as_string_uri_only( + linphone_proxy_config_get_contact(linphone_core_get_default_proxy_config(pauline.getLc()))); + std::string paulineAddressString{deviceId}; + bctbx_free(deviceId); + + std::string messageString = "This is my message to you Rudy"; + std::string ADString = "These are my AD to you Rudy"; + auto message = std::make_shared<std::vector<uint8_t>>(messageString.cbegin(), messageString.cend()); + auto AD = std::make_shared<std::vector<uint8_t>>(ADString.cbegin(), ADString.cend()); + std::vector<uint8_t> cipherText{}; + + marieEncryptionEngine->rawEncrypt( + marieAddressString, std::list<std::string>{paulineAddressString}, message, AD, + [&rawEncryptionSuccess, &cipherText, paulineAddressString]( + const bool status, std::unordered_map<std::string, std::vector<uint8_t>> cipherTexts) { + auto search = cipherTexts.find(paulineAddressString); + if (status && search != cipherTexts.end()) { + rawEncryptionSuccess++; + cipherText = cipherTexts[paulineAddressString]; + } + }); + + BC_ASSERT_TRUE(wait_for_list(coresList, &rawEncryptionSuccess, 1, x3dhServer_creationTimeout)); + if (rawEncryptionSuccess == 1) { + // try to decrypt only if encryption was a success + std::vector<uint8_t> plainText{}; + BC_ASSERT_TRUE(paulineEncryptionEngine->rawDecrypt(paulineAddressString, marieAddressString, *AD, + cipherText, plainText)); + std::string plainTextString{plainText.cbegin(), plainText.cend()}; + BC_ASSERT_TRUE(plainTextString == messageString); + } + } + + Address paulineAddr = pauline.getIdentity(); + Address laureAddr = laure.getIdentity(); + bctbx_list_t *participantsAddresses = bctbx_list_append(NULL, linphone_address_ref(paulineAddr.toC())); + participantsAddresses = bctbx_list_append(participantsAddresses, linphone_address_ref(laureAddr.toC())); + + // Marie creates a new group chat room + const char *initialSubject = "Colleagues"; + LinphoneChatRoom *marieCr = + create_chat_room_client_side(coresList, marie.getCMgr(), &marie_stat, participantsAddresses, initialSubject, + encrypted, LinphoneChatRoomEphemeralModeDeviceManaged); + BC_ASSERT_PTR_NOT_NULL(marieCr); + const LinphoneAddress *confAddr = linphone_chat_room_get_conference_address(marieCr); + + // Check that the chat room is correctly created on Pauline's side and that the participants are added + LinphoneChatRoom *paulineCr = check_creation_chat_room_client_side(coresList, pauline.getCMgr(), &pauline_stat, + confAddr, initialSubject, 2, FALSE); + BC_ASSERT_PTR_NOT_NULL(paulineCr); + LinphoneChatRoom *laureCr = check_creation_chat_room_client_side(coresList, laure.getCMgr(), &laure_stat, + confAddr, initialSubject, 2, FALSE); + BC_ASSERT_PTR_NOT_NULL(laureCr); + if (paulineCr && laureCr) { + // Marie sends the message + const char *marieMessage = "Hey ! What's up ?"; + msg = _send_message_ephemeral(marieCr, marieMessage, FALSE); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneMessageReceived, + pauline_stat.number_of_LinphoneMessageReceived + 1, + liblinphone_tester_sip_timeout)); + LinphoneChatMessage *paulineLastMsg = pauline.getStats().last_received_chat_message; + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_LinphoneMessageReceived, + laure_stat.number_of_LinphoneMessageReceived + 1, + liblinphone_tester_sip_timeout)); + LinphoneChatMessage *laureLastMsg = laure.getStats().last_received_chat_message; + linphone_chat_message_unref(msg); + if (paulineLastMsg && laureLastMsg) { + // Check that the message was correctly decrypted if encrypted + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_utf8_text(paulineLastMsg), marieMessage); + LinphoneAddress *marieAddr = linphone_address_new(linphone_core_get_identity(marie.getLc())); + BC_ASSERT_TRUE( + linphone_address_weak_equal(marieAddr, linphone_chat_message_get_from_address(paulineLastMsg))); + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_utf8_text(laureLastMsg), marieMessage); + BC_ASSERT_TRUE( + linphone_address_weak_equal(marieAddr, linphone_chat_message_get_from_address(laureLastMsg))); + linphone_address_unref(marieAddr); + } + } + + for (auto chatRoom : focus.getCore().getChatRooms()) { + for (auto participant : chatRoom->getParticipants()) { + // force deletion by removing devices + std::shared_ptr<Address> participantAddress = participant->getAddress(); + linphone_chat_room_set_participant_devices(L_GET_C_BACK_PTR(chatRoom), participantAddress->toC(), NULL); + } + } + + // wait until chatroom is deleted server side + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline, laure}).wait([&focus] { + return focus.getCore().getChatRooms().size() == 0; + })); + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, laure}).waitUntil(chrono::seconds(2), [] { return false; }); + + // to avoid creation attempt of a new chatroom + auto config = focus.getDefaultProxyConfig(); + linphone_proxy_config_edit(config); + linphone_proxy_config_set_conference_factory_uri(config, NULL); + linphone_proxy_config_done(config); + + bctbx_list_free(coresList); + } +} + +static void group_chat_room_lime_server_encrypted_message(void) { + group_chat_room_lime_server_message(TRUE); +} + +static void group_chat_room_lime_server_clear_message(void) { + group_chat_room_lime_server_message(FALSE); +} + +static void group_chat_room_lime_session_corrupted(void) { + Focus focus("chloe_rc"); + LinphoneChatMessage *msg; + { // to make sure focus is destroyed after clients. + linphone_core_enable_lime_x3dh(focus.getLc(), true); + + ClientConference marie("marie_rc", focus.getIdentity(), true); + ClientConference pauline("pauline_rc", focus.getIdentity(), true); + ClientConference laure("laure_tcp_rc", focus.getIdentity(), true); + + focus.registerAsParticipantDevice(marie); + focus.registerAsParticipantDevice(pauline); + focus.registerAsParticipantDevice(laure); + + linphone_im_notif_policy_enable_all(linphone_core_get_im_notif_policy(marie.getLc())); + linphone_im_notif_policy_enable_all(linphone_core_get_im_notif_policy(pauline.getLc())); + linphone_im_notif_policy_enable_all(linphone_core_get_im_notif_policy(laure.getLc())); + + stats marie_stat = marie.getStats(); + stats pauline_stat = pauline.getStats(); + stats laure_stat = laure.getStats(); + bctbx_list_t *coresList = bctbx_list_append(NULL, focus.getLc()); + coresList = bctbx_list_append(coresList, marie.getLc()); + coresList = bctbx_list_append(coresList, pauline.getLc()); + coresList = bctbx_list_append(coresList, laure.getLc()); + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_X3dhUserCreationSuccess, + marie_stat.number_of_X3dhUserCreationSuccess + 1, x3dhServer_creationTimeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_X3dhUserCreationSuccess, + laure_stat.number_of_X3dhUserCreationSuccess + 1, x3dhServer_creationTimeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_X3dhUserCreationSuccess, + pauline_stat.number_of_X3dhUserCreationSuccess + 1, x3dhServer_creationTimeout)); + + BC_ASSERT_TRUE(linphone_core_lime_x3dh_enabled(marie.getLc())); + BC_ASSERT_TRUE(linphone_core_lime_x3dh_enabled(pauline.getLc())); + BC_ASSERT_TRUE(linphone_core_lime_x3dh_enabled(laure.getLc())); + + Address paulineAddr = pauline.getIdentity(); + Address laureAddr = laure.getIdentity(); + bctbx_list_t *participantsAddresses = bctbx_list_append(NULL, linphone_address_ref(paulineAddr.toC())); + participantsAddresses = bctbx_list_append(participantsAddresses, linphone_address_ref(laureAddr.toC())); + + // Marie creates a new group chat room + const char *initialSubject = "Colleagues"; + LinphoneChatRoom *marieCr = + create_chat_room_client_side(coresList, marie.getCMgr(), &marie_stat, participantsAddresses, initialSubject, + TRUE, LinphoneChatRoomEphemeralModeDeviceManaged); + BC_ASSERT_PTR_NOT_NULL(marieCr); + const LinphoneAddress *confAddr = linphone_chat_room_get_conference_address(marieCr); + + // Check that the chat room is correctly created on Pauline's side and that the participants are added + LinphoneChatRoom *paulineCr = check_creation_chat_room_client_side(coresList, pauline.getCMgr(), &pauline_stat, + confAddr, initialSubject, 2, FALSE); + BC_ASSERT_PTR_NOT_NULL(paulineCr); + LinphoneChatRoom *laureCr = check_creation_chat_room_client_side(coresList, laure.getCMgr(), &laure_stat, + confAddr, initialSubject, 2, FALSE); + BC_ASSERT_PTR_NOT_NULL(laureCr); + if (paulineCr && laureCr) { + // Marie sends the message + const char *marieMessage = "Hey ! What's up ?"; + msg = _send_message(marieCr, marieMessage); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneMessageReceived, + pauline_stat.number_of_LinphoneMessageReceived + 1, + liblinphone_tester_sip_timeout)); + LinphoneChatMessage *paulineLastMsg = pauline.getStats().last_received_chat_message; + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_LinphoneMessageReceived, + laure_stat.number_of_LinphoneMessageReceived + 1, + liblinphone_tester_sip_timeout)); + LinphoneChatMessage *laureLastMsg = laure.getStats().last_received_chat_message; + linphone_chat_message_unref(msg); + if (!BC_ASSERT_PTR_NOT_NULL(paulineLastMsg)) goto end; + if (!BC_ASSERT_PTR_NOT_NULL(laureLastMsg)) goto end; + + // Check that the message was correctly decrypted if encrypted + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_utf8_text(paulineLastMsg), marieMessage); + LinphoneAddress *marieAddr = linphone_address_new(linphone_core_get_identity(marie.getLc())); + BC_ASSERT_TRUE( + linphone_address_weak_equal(marieAddr, linphone_chat_message_get_from_address(paulineLastMsg))); + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_utf8_text(laureLastMsg), marieMessage); + BC_ASSERT_TRUE( + linphone_address_weak_equal(marieAddr, linphone_chat_message_get_from_address(laureLastMsg))); + linphone_address_unref(marieAddr); + + // Corrupt Pauline sessions in lime database: WARNING: if SOCI is not found, this call does nothing and the + // test fails + lime_delete_DRSessions(pauline.getCMgr()->lime_database_path); + // Trick to force the reloading of the lime engine so the session in cache is cleared + linphone_core_enable_lime_x3dh(pauline.getLc(), FALSE); + linphone_core_enable_lime_x3dh(pauline.getLc(), TRUE); + + // Marie send a new message, it shall fail and get a 488 response + const char *marieTextMessage2 = "Do you copy?"; + msg = _send_message(marieCr, marieTextMessage2); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneMessageDelivered, + marie_stat.number_of_LinphoneMessageDelivered + 2, + liblinphone_tester_sip_timeout)); // Delivered to the server + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_LinphoneMessageReceived, + laure_stat.number_of_LinphoneMessageReceived + 2, + liblinphone_tester_sip_timeout)); // the message is correctly received by Laure + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneMessageNotDelivered, + marie_stat.number_of_LinphoneMessageNotDelivered + 1, + liblinphone_tester_sip_timeout)); // Not delivered to pauline + BC_ASSERT_EQUAL(pauline.getStats().number_of_LinphoneMessageReceived, 1, int, "%d"); + linphone_chat_message_unref(msg); + laureLastMsg = laure.getStats().last_received_chat_message; + if (!BC_ASSERT_PTR_NOT_NULL(laureLastMsg)) goto end; + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_utf8_text(laureLastMsg), marieTextMessage2); + marieAddr = linphone_address_new(linphone_core_get_identity(marie.getLc())); + BC_ASSERT_TRUE( + linphone_address_weak_equal(marieAddr, linphone_chat_message_get_from_address(laureLastMsg))); + linphone_address_unref(marieAddr); + + // Try again, it shall work this time + const char *marieTextMessage3 = "Hello again"; + marie_stat = marie.getStats(); + msg = _send_message(marieCr, marieTextMessage3); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneMessageSent, 1, 5000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneMessageDelivered, + marie_stat.number_of_LinphoneMessageDelivered + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneMessageReceived, + pauline_stat.number_of_LinphoneMessageReceived + 2, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_LinphoneMessageReceived, + laure_stat.number_of_LinphoneMessageReceived + 3, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneMessageDeliveredToUser, + marie_stat.number_of_LinphoneMessageDeliveredToUser + 1, + liblinphone_tester_sip_timeout)); + paulineLastMsg = pauline.getStats().last_received_chat_message; + if (!BC_ASSERT_PTR_NOT_NULL(paulineLastMsg)) goto end; + laureLastMsg = laure.getStats().last_received_chat_message; + if (!BC_ASSERT_PTR_NOT_NULL(laureLastMsg)) goto end; + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_utf8_text(paulineLastMsg), marieTextMessage3); + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_utf8_text(laureLastMsg), marieTextMessage3); + marieAddr = linphone_address_new(linphone_core_get_identity(marie.getLc())); + BC_ASSERT_TRUE( + linphone_address_weak_equal(marieAddr, linphone_chat_message_get_from_address(paulineLastMsg))); + BC_ASSERT_TRUE( + linphone_address_weak_equal(marieAddr, linphone_chat_message_get_from_address(laureLastMsg))); + linphone_address_unref(marieAddr); + linphone_chat_message_unref(msg); + } + + end: + for (auto chatRoom : focus.getCore().getChatRooms()) { + for (auto participant : chatRoom->getParticipants()) { + // force deletion by removing devices + std::shared_ptr<Address> participantAddress = participant->getAddress(); + linphone_chat_room_set_participant_devices(L_GET_C_BACK_PTR(chatRoom), participantAddress->toC(), NULL); + } + } + + // wait until chatroom is deleted server side + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline, laure}).wait([&focus] { + return focus.getCore().getChatRooms().size() == 0; + })); + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, laure}).waitUntil(chrono::seconds(2), [] { return false; }); + + // to avoid creation attempt of a new chatroom + auto config = focus.getDefaultProxyConfig(); + linphone_proxy_config_edit(config); + linphone_proxy_config_set_conference_factory_uri(config, NULL); + linphone_proxy_config_done(config); + + bctbx_list_free(coresList); + } +} + +static void one_to_one_group_chat_room_deletion_by_server_client_base(bool encrypted) { + Focus focus("chloe_rc"); + LinphoneChatMessage *msg; + { // to make sure focus is destroyed after clients. + linphone_core_enable_lime_x3dh(focus.getLc(), true); + + ClientConference marie("marie_rc", focus.getIdentity(), encrypted); + ClientConference pauline("pauline_rc", focus.getIdentity(), encrypted); + + focus.registerAsParticipantDevice(marie); + focus.registerAsParticipantDevice(pauline); + + stats marie_stat = marie.getStats(); + stats pauline_stat = pauline.getStats(); + bctbx_list_t *coresList = bctbx_list_append(NULL, focus.getLc()); + coresList = bctbx_list_append(coresList, marie.getLc()); + coresList = bctbx_list_append(coresList, pauline.getLc()); + + if (encrypted) { + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_X3dhUserCreationSuccess, + marie_stat.number_of_X3dhUserCreationSuccess + 1, x3dhServer_creationTimeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_X3dhUserCreationSuccess, + pauline_stat.number_of_X3dhUserCreationSuccess + 1, + x3dhServer_creationTimeout)); + + BC_ASSERT_TRUE(linphone_core_lime_x3dh_enabled(marie.getLc())); + BC_ASSERT_TRUE(linphone_core_lime_x3dh_enabled(pauline.getLc())); + } + + Address paulineAddr = pauline.getIdentity(); + bctbx_list_t *participantsAddresses = bctbx_list_append(NULL, linphone_address_ref(paulineAddr.toC())); + + // Marie creates a new group chat room + const char *initialSubject = "Colleagues"; + LinphoneChatRoom *marieCr = + create_chat_room_client_side(coresList, marie.getCMgr(), &marie_stat, participantsAddresses, initialSubject, + encrypted, LinphoneChatRoomEphemeralModeDeviceManaged); + BC_ASSERT_PTR_NOT_NULL(marieCr); + const LinphoneAddress *confAddr = linphone_chat_room_get_conference_address(marieCr); + + // Check that the chat room is correctly created on Pauline's side and that the participants are added + LinphoneChatRoom *paulineCr = check_creation_chat_room_client_side(coresList, pauline.getCMgr(), &pauline_stat, + confAddr, initialSubject, 1, FALSE); + BC_ASSERT_PTR_NOT_NULL(paulineCr); + + if (paulineCr && marieCr) { + // Marie sends the message + const char *marieMessage = "Hey ! What's up ?"; + msg = _send_message(marieCr, marieMessage); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneMessageReceived, + pauline_stat.number_of_LinphoneMessageReceived + 1, + liblinphone_tester_sip_timeout)); + LinphoneChatMessage *paulineLastMsg = pauline.getStats().last_received_chat_message; + linphone_chat_message_unref(msg); + BC_ASSERT_PTR_NOT_NULL(paulineLastMsg); + + // Check that the message was correctly decrypted if encrypted + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_utf8_text(paulineLastMsg), marieMessage); + LinphoneAddress *marieAddr = linphone_address_new(linphone_core_get_identity(marie.getLc())); + BC_ASSERT_TRUE( + linphone_address_weak_equal(marieAddr, linphone_chat_message_get_from_address(paulineLastMsg))); + linphone_address_unref(marieAddr); + + LinphoneAddress *localAddr = linphone_address_clone(linphone_chat_room_get_local_address(paulineCr)); + LinphoneAddress *peerAddr = linphone_address_clone(linphone_chat_room_get_peer_address(paulineCr)); + + // Restart pauline so that she has to send an INVITE and BYE it to exit the chatroom + coresList = bctbx_list_remove(coresList, pauline.getLc()); + pauline.reStart(); + setup_mgr_for_conference(pauline.getCMgr(), NULL); + coresList = bctbx_list_append(coresList, pauline.getLc()); + paulineCr = linphone_core_search_chat_room(pauline.getLc(), NULL, localAddr, peerAddr, NULL); + BC_ASSERT_PTR_NOT_NULL(paulineCr); + + LinphoneChatRoom *focusCr = linphone_core_search_chat_room(focus.getLc(), NULL, NULL, peerAddr, NULL); + BC_ASSERT_PTR_NOT_NULL(focusCr); + + LinphoneParticipant *paulineParticipant = NULL; + LinphoneParticipantDevice *paulineDevice = NULL; + + if (focusCr) { + paulineParticipant = linphone_chat_room_find_participant(focusCr, localAddr); + BC_ASSERT_PTR_NOT_NULL(paulineParticipant); + if (paulineParticipant) { + paulineDevice = linphone_participant_find_device(paulineParticipant, localAddr); + BC_ASSERT_PTR_NOT_NULL(paulineDevice); + } + } + + linphone_address_unref(localAddr); + linphone_address_unref(peerAddr); + + OrtpNetworkSimulatorParams simparams = {0}; + simparams.mode = OrtpNetworkSimulatorOutbound; + simparams.enabled = TRUE; + simparams.max_bandwidth = 430000; /*we first limit to 430 kbit/s*/ + simparams.max_buffer_size = (int)simparams.max_bandwidth; + simparams.latency = 60; + simparams.loss_rate = 90; + linphone_core_set_network_simulator_params(pauline.getLc(), &simparams); + linphone_core_set_network_simulator_params(focus.getLc(), &simparams); + + linphone_core_delete_chat_room(marie.getLc(), marieCr); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateDeleted, + marie_stat.number_of_LinphoneConferenceStateDeleted + 1, + liblinphone_tester_sip_timeout)); + + // wait until chatroom is deleted server side + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline}).wait([&paulineDevice] { + return (paulineDevice) ? (linphone_participant_device_get_state(paulineDevice) == + LinphoneParticipantDeviceStateLeaving) + : false; + })); + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneChatRoomSessionReleased, 1, + liblinphone_tester_sip_timeout)); + + linphone_core_delete_chat_room(pauline.getLc(), paulineCr); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneConferenceStateDeleted, + pauline_stat.number_of_LinphoneConferenceStateDeleted + 1, + liblinphone_tester_sip_timeout)); + } + } +} + +static void secure_one_to_one_group_chat_room_deletion_by_server_client(void) { + one_to_one_group_chat_room_deletion_by_server_client_base(TRUE); +} + +static void one_to_one_group_chat_room_deletion_by_server_client(void) { + one_to_one_group_chat_room_deletion_by_server_client_base(FALSE); +} + +static void conference_scheduler_state_changed(LinphoneConferenceScheduler *scheduler, + LinphoneConferenceSchedulerState state) { + stats *stat = get_stats(linphone_conference_scheduler_get_core(scheduler)); + switch (state) { + case LinphoneConferenceSchedulerStateIdle: + stat->number_of_ConferenceSchedulerStateIdle++; + break; + case LinphoneConferenceSchedulerStateAllocationPending: + stat->number_of_ConferenceSchedulerStateAllocationPending++; + break; + case LinphoneConferenceSchedulerStateReady: + stat->number_of_ConferenceSchedulerStateReady++; + break; + case LinphoneConferenceSchedulerStateError: + stat->number_of_ConferenceSchedulerStateError++; + break; + case LinphoneConferenceSchedulerStateUpdating: + stat->number_of_ConferenceSchedulerStateUpdating++; + break; + } +} + +static void conference_scheduler_invitations_sent(LinphoneConferenceScheduler *scheduler, + BCTBX_UNUSED(const bctbx_list_t *failed_addresses)) { + stats *stat = get_stats(linphone_conference_scheduler_get_core(scheduler)); + stat->number_of_ConferenceSchedulerInvitationsSent++; +} + +static void check_conference_info(LinphoneCoreManager *mgr, + LinphoneAddress *confAddr, + LinphoneCoreManager *organizer, + size_t no_members, + long long start_time, + int duration, + const char *subject, + const char *description, + unsigned int sequence, + LinphoneConferenceInfoState state) { + LinphoneConferenceInfo *info = linphone_core_find_conference_information_from_uri(mgr->lc, confAddr); + if (BC_ASSERT_PTR_NOT_NULL(info)) { + BC_ASSERT_TRUE(linphone_address_weak_equal(organizer->identity, linphone_conference_info_get_organizer(info))); + BC_ASSERT_TRUE(linphone_address_weak_equal(confAddr, linphone_conference_info_get_uri(info))); + + const bctbx_list_t *info_participants = linphone_conference_info_get_participants(info); + BC_ASSERT_EQUAL(bctbx_list_size(info_participants), no_members, size_t, "%zu"); + + if (start_time > 0) { + BC_ASSERT_EQUAL((long long)linphone_conference_info_get_date_time(info), start_time, long long, "%lld"); + } else { + BC_ASSERT_GREATER_STRICT((long long)linphone_conference_info_get_date_time(info), 0, long long, "%lld"); + } + const int duration_m = linphone_conference_info_get_duration(info); + BC_ASSERT_EQUAL(duration_m, ((duration < 0) ? 0 : duration), int, "%d"); + if (subject) { + BC_ASSERT_STRING_EQUAL(linphone_conference_info_get_subject(info), subject); + } else { + BC_ASSERT_PTR_NULL(linphone_conference_info_get_subject(info)); + } + if (description) { + BC_ASSERT_STRING_EQUAL(linphone_conference_info_get_description(info), description); + } else { + BC_ASSERT_PTR_NULL(linphone_conference_info_get_description(info)); + } + const unsigned int ics_sequence = linphone_conference_info_get_ics_sequence(info); + BC_ASSERT_EQUAL(ics_sequence, sequence, int, "%d"); + BC_ASSERT_EQUAL((int)linphone_conference_info_get_state(info), (int)state, int, "%d"); + linphone_conference_info_unref(info); + } +} + +static bool have_common_audio_payload(LinphoneCoreManager *mgr1, LinphoneCoreManager *mgr2) { + bool found = false; + const bctbx_list_t *elem = linphone_core_get_audio_codecs(mgr1->lc); + for (; elem != NULL; elem = elem->next) { + PayloadType *pt1 = (PayloadType *)elem->data; + if (linphone_core_payload_type_enabled(mgr1->lc, pt1) == TRUE) { + LinphonePayloadType *pt2 = + linphone_core_get_payload_type(mgr2->lc, pt1->mime_type, pt1->clock_rate, pt1->channels); + if (pt2 && linphone_payload_type_enabled(pt2)) { + found = true; + } + linphone_payload_type_unref(pt2); + } + } + return found; +} + +static LinphoneAddress * +create_conference_on_server(Focus &focus, + ClientConference &organizer, + std::map<LinphoneCoreManager *, LinphoneParticipantRole> requested_participants, + time_t start_time, + time_t end_time, + const char *subject, + const char *description, + bool_t send_ics) { + bctbx_list_t *coresList = bctbx_list_append(NULL, focus.getLc()); + coresList = bctbx_list_append(coresList, organizer.getLc()); + std::vector<stats> participant_stats; + std::map<LinphoneCoreManager *, LinphoneCall *> previous_calls; + bctbx_list_t *participant_infos = NULL; + std::list<LinphoneCoreManager *> participants; + const LinphoneConferenceInfo *updated_conf_info = NULL; + bool focus_organizer_common_payload = have_common_audio_payload(organizer.getCMgr(), focus.getCMgr()); + bool dialout = ((end_time <= 0) && (start_time <= 0)); + bool found_me = false; + char *conference_address_str = NULL; + size_t organizer_expected_participant_number = 0; + size_t participant_expected_participant_number = 0; + char *uid = NULL; + LinphoneConferenceInfo *info = NULL; + for (auto &p : requested_participants) { + LinphoneParticipantInfo *participant_info = linphone_participant_info_new(p.first->identity); + linphone_participant_info_set_role(participant_info, p.second); + participant_infos = bctbx_list_append(participant_infos, participant_info); + LinphoneCoreManager *mgr = p.first; + if (mgr == organizer.getCMgr()) { + found_me = true; + } else { + coresList = bctbx_list_append(coresList, mgr->lc); + participant_stats.push_back(mgr->stat); + participants.push_back(mgr); + } + } + + stats organizer_stat = organizer.getStats(); + stats focus_stat = focus.getStats(); + + for (auto &mgr : participants) { + previous_calls[mgr] = linphone_core_get_call_by_remote_address2(mgr->lc, focus.getCMgr()->identity); + } + + // Marie creates a new group chat room + LinphoneConferenceScheduler *conference_scheduler = linphone_core_create_conference_scheduler(organizer.getLc()); + LinphoneConferenceSchedulerCbs *cbs = linphone_factory_create_conference_scheduler_cbs(linphone_factory_get()); + linphone_conference_scheduler_cbs_set_state_changed(cbs, conference_scheduler_state_changed); + linphone_conference_scheduler_cbs_set_invitations_sent(cbs, conference_scheduler_invitations_sent); + linphone_conference_scheduler_add_callbacks(conference_scheduler, cbs); + linphone_conference_scheduler_cbs_unref(cbs); + + LinphoneConferenceInfo *conf_info = linphone_conference_info_new(); + + LinphoneAccount *default_account = linphone_core_get_default_account(organizer.getLc()); + LinphoneAddress *organizer_address = default_account + ? linphone_address_clone(linphone_account_params_get_identity_address( + linphone_account_get_params(default_account))) + : linphone_address_new(linphone_core_get_identity(organizer.getLc())); + linphone_conference_info_set_organizer(conf_info, organizer_address); + linphone_conference_info_set_participants_2(conf_info, participant_infos); + if ((end_time >= 0) && (start_time >= 0) && (end_time > start_time)) { + linphone_conference_info_set_duration( + conf_info, (int)((end_time - start_time) / 60)); // duration is expected to be set in minutes + } + linphone_conference_info_set_date_time(conf_info, start_time); + linphone_conference_info_set_subject(conf_info, subject); + linphone_conference_info_set_description(conf_info, description); + + linphone_conference_scheduler_set_info(conference_scheduler, conf_info); + linphone_conference_info_unref(conf_info); + + BC_ASSERT_TRUE(wait_for_list(coresList, &organizer.getStats().number_of_ConferenceSchedulerStateAllocationPending, + organizer_stat.number_of_ConferenceSchedulerStateAllocationPending + 1, + liblinphone_tester_sip_timeout)); + + int idx = 0; + std::list<LinphoneCoreManager *> actual_participants; + int call_errors_cnt = 0; + LinphoneAddress *conference_address = NULL; + if (dialout) { + + if (focus_organizer_common_payload) { + BC_ASSERT_TRUE(wait_for_list(coresList, &organizer.getStats().number_of_LinphoneCallOutgoingInit, + organizer_stat.number_of_LinphoneCallOutgoingInit + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallIncomingReceived, + focus_stat.number_of_LinphoneCallIncomingReceived + 1, + liblinphone_tester_sip_timeout)); + } else { + BC_ASSERT_TRUE(wait_for_list(coresList, &organizer.getStats().number_of_ConferenceSchedulerStateError, + organizer_stat.number_of_ConferenceSchedulerStateError + 1, + liblinphone_tester_sip_timeout)); + + updated_conf_info = linphone_conference_scheduler_get_info(conference_scheduler); + goto end; + } + BC_ASSERT_TRUE( + wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallOutgoingInit, + focus_stat.number_of_LinphoneCallOutgoingInit + static_cast<int>(participants.size()), + liblinphone_tester_sip_timeout)); + + int call_ok_cnt = 0; + // Conference server dials out participants + for (auto &mgr : participants) { + auto old_stats = participant_stats[idx]; + if (have_common_audio_payload(mgr, focus.getCMgr()) && !previous_calls[mgr]) { + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallIncomingReceived, + old_stats.number_of_LinphoneCallIncomingReceived + 1, + liblinphone_tester_sip_timeout)); + call_ok_cnt++; + actual_participants.push_back(mgr); + } else { + call_errors_cnt++; + } + idx++; + } + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallOutgoingProgress, + focus_stat.number_of_LinphoneCallOutgoingProgress + call_ok_cnt, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallOutgoingRinging, + focus_stat.number_of_LinphoneCallOutgoingRinging + call_ok_cnt, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_alerting, + focus_stat.number_of_participant_devices_alerting + call_ok_cnt, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallError, + focus_stat.number_of_LinphoneCallError + call_errors_cnt, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_removed, + focus_stat.number_of_participant_devices_removed + call_errors_cnt, + liblinphone_tester_sip_timeout)); + + if (focus_organizer_common_payload) { + BC_ASSERT_TRUE(wait_for_list(coresList, &organizer.getStats().number_of_participant_devices_removed, + organizer_stat.number_of_participant_devices_removed + call_errors_cnt, + liblinphone_tester_sip_timeout)); + } + + } else { + actual_participants = participants; + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &organizer.getStats().number_of_ConferenceSchedulerStateReady, + organizer_stat.number_of_ConferenceSchedulerStateReady + 1, + liblinphone_tester_sip_timeout)); + + updated_conf_info = linphone_conference_scheduler_get_info(conference_scheduler); + conference_address = linphone_address_clone(linphone_conference_info_get_uri(updated_conf_info)); + + if (!!send_ics) { + LinphoneChatRoomParams *chat_room_params = linphone_core_create_default_chat_room_params(organizer.getLc()); + linphone_chat_room_params_set_backend(chat_room_params, LinphoneChatRoomBackendBasic); + linphone_conference_scheduler_send_invitations(conference_scheduler, chat_room_params); + linphone_chat_room_params_unref(chat_room_params); + BC_ASSERT_TRUE(wait_for_list(coresList, &organizer.getStats().number_of_ConferenceSchedulerInvitationsSent, + organizer_stat.number_of_ConferenceSchedulerInvitationsSent + 1, + liblinphone_tester_sip_timeout)); + } + + info = linphone_core_find_conference_information_from_uri(organizer.getLc(), conference_address); + if (BC_ASSERT_PTR_NOT_NULL(info)) { + uid = ms_strdup(linphone_conference_info_get_ics_uid(info)); + if (!!send_ics) { + BC_ASSERT_PTR_NOT_NULL(uid); + for (auto &p : participants) { + linphone_conference_info_check_participant(info, p->identity, 0); + } + } else { + BC_ASSERT_PTR_NULL(uid); + } + linphone_conference_info_unref(info); + } + + organizer_expected_participant_number = + requested_participants.size() + ((!found_me && dialout && focus_organizer_common_payload) ? 1 : 0); + participant_expected_participant_number = + requested_participants.size() + ((!found_me && dialout && focus_organizer_common_payload) ? 1 : 0); + check_conference_info(organizer.getCMgr(), conference_address, organizer.getCMgr(), + organizer_expected_participant_number, start_time, + ((start_time > 0) && (end_time > 0)) ? (int)(end_time - start_time) / 60 : 0, subject, + description, 0, LinphoneConferenceInfoStateNew); + + idx = 0; + for (auto &mgr : participants) { + if (!dialout || !previous_calls[mgr]) { + auto old_stats = participant_stats[idx]; + if (!!send_ics) { + // chat room in created state + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateCreated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneMessageReceived, + old_stats.number_of_LinphoneMessageReceived + 1, + liblinphone_tester_sip_timeout)); + if (!linphone_core_conference_ics_in_message_body_enabled(organizer.getLc())) { + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneMessageReceivedWithFile, + old_stats.number_of_LinphoneMessageReceivedWithFile + 1, + liblinphone_tester_sip_timeout)); + } + + BC_ASSERT_PTR_NOT_NULL(mgr->stat.last_received_chat_message); + if (mgr->stat.last_received_chat_message != NULL) { + const string expected = ContentType::Icalendar.getMediaType(); + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_content_type(mgr->stat.last_received_chat_message), + expected.c_str()); + } + + bctbx_list_t *chat_room_participants = bctbx_list_append(NULL, mgr->identity); + LinphoneChatRoom *cr = linphone_core_search_chat_room(organizer.getLc(), NULL, organizer_address, NULL, + chat_room_participants); + bctbx_list_free(chat_room_participants); + BC_ASSERT_PTR_NOT_NULL(cr); + if (cr) { + LinphoneChatMessage *msg = linphone_chat_room_get_last_message_in_history(cr); + BC_ASSERT_PTR_NOT_NULL(msg); + + if (msg) { + const bctbx_list_t *original_contents = linphone_chat_message_get_contents(msg); + BC_ASSERT_EQUAL((int)bctbx_list_size(original_contents), 1, int, "%d"); + LinphoneContent *original_content = (LinphoneContent *)bctbx_list_get_data(original_contents); + BC_ASSERT_PTR_NOT_NULL(original_content); + + LinphoneConferenceInfo *conf_info_in_db = + linphone_core_find_conference_information_from_uri(mgr->lc, conference_address); + if (!BC_ASSERT_PTR_NOT_NULL(conf_info_in_db)) { + goto end; + } + + LinphoneConferenceInfo *conf_info_from_original_content = + linphone_factory_create_conference_info_from_icalendar_content(linphone_factory_get(), + original_content); + if (BC_ASSERT_PTR_NOT_NULL(conf_info_from_original_content)) { + BC_ASSERT_TRUE(linphone_address_weak_equal( + organizer_address, + linphone_conference_info_get_organizer(conf_info_from_original_content))); + BC_ASSERT_TRUE(linphone_address_equal( + linphone_conference_info_get_organizer(conf_info_in_db), + linphone_conference_info_get_organizer(conf_info_from_original_content))); + BC_ASSERT_TRUE(linphone_address_weak_equal( + conference_address, linphone_conference_info_get_uri(conf_info_from_original_content))); + BC_ASSERT_TRUE(linphone_address_equal( + linphone_conference_info_get_uri(conf_info_in_db), + linphone_conference_info_get_uri(conf_info_from_original_content))); + + const bctbx_list_t *ics_participants = + linphone_conference_info_get_participants(conf_info_from_original_content); + BC_ASSERT_EQUAL(bctbx_list_size(ics_participants), participant_expected_participant_number, + size_t, "%zu"); + + const bctbx_list_t *ics_participants_db = + linphone_conference_info_get_participants(conf_info_in_db); + BC_ASSERT_EQUAL(bctbx_list_size(ics_participants_db), + participant_expected_participant_number, size_t, "%zu"); + + if (start_time > 0) { + BC_ASSERT_EQUAL( + (long long)linphone_conference_info_get_date_time(conf_info_from_original_content), + (long long)start_time, long long, "%lld"); + BC_ASSERT_EQUAL( + (long long)linphone_conference_info_get_date_time(conf_info_in_db), + (long long)linphone_conference_info_get_date_time(conf_info_from_original_content), + long long, "%lld"); + if (end_time > 0) { + const int duration_s = + linphone_conference_info_get_duration(conf_info_from_original_content) * 60; + BC_ASSERT_EQUAL(duration_s, (int)(end_time - start_time), int, "%d"); + BC_ASSERT_EQUAL( + (int)linphone_conference_info_get_duration(conf_info_in_db), + (int)linphone_conference_info_get_duration(conf_info_from_original_content), + int, "%0d"); + } + } + if (subject) { + BC_ASSERT_STRING_EQUAL( + linphone_conference_info_get_subject(conf_info_from_original_content), subject); + BC_ASSERT_STRING_EQUAL( + linphone_conference_info_get_subject(conf_info_in_db), + linphone_conference_info_get_subject(conf_info_from_original_content)); + } else { + BC_ASSERT_PTR_NULL( + linphone_conference_info_get_subject(conf_info_from_original_content)); + BC_ASSERT_PTR_NULL(linphone_conference_info_get_subject(conf_info_in_db)); + } + if (description) { + BC_ASSERT_STRING_EQUAL( + linphone_conference_info_get_description(conf_info_from_original_content), + description); + BC_ASSERT_STRING_EQUAL( + linphone_conference_info_get_description(conf_info_from_original_content), + linphone_conference_info_get_description(conf_info_in_db)); + } else { + BC_ASSERT_PTR_NULL( + linphone_conference_info_get_description(conf_info_from_original_content)); + BC_ASSERT_PTR_NULL(linphone_conference_info_get_description(conf_info_in_db)); + } + BC_ASSERT_STRING_EQUAL( + linphone_conference_info_get_ics_uid(conf_info_from_original_content), uid); + BC_ASSERT_STRING_EQUAL( + linphone_conference_info_get_ics_uid(conf_info_in_db), + linphone_conference_info_get_ics_uid(conf_info_from_original_content)); + + const unsigned int ics_sequence = + linphone_conference_info_get_ics_sequence(conf_info_in_db); + BC_ASSERT_EQUAL(ics_sequence, 0, int, "%d"); + BC_ASSERT_EQUAL(linphone_conference_info_get_ics_sequence(conf_info_in_db), + linphone_conference_info_get_ics_sequence(conf_info_from_original_content), + unsigned int, "%0u"); + linphone_conference_info_unref(conf_info_from_original_content); + linphone_conference_info_unref(conf_info_in_db); + } + linphone_chat_message_unref(msg); + } + } + } + + if (!!send_ics || ((end_time <= 0) && (start_time <= 0))) { + auto itConferenceMgrs = std::find(actual_participants.begin(), actual_participants.end(), mgr); + if (itConferenceMgrs != actual_participants.end()) { + LinphoneConferenceInfo *conf_info_in_db = + linphone_core_find_conference_information_from_uri(mgr->lc, conference_address); + if (!BC_ASSERT_PTR_NOT_NULL(conf_info_in_db)) { + goto end; + } + check_conference_info( + mgr, conference_address, organizer.getCMgr(), participant_expected_participant_number, + start_time, ((start_time > 0) && (end_time > 0)) ? (int)(end_time - start_time) / 60 : 0, + subject, (!!send_ics) ? description : NULL, 0, LinphoneConferenceInfoStateNew); + if (!!send_ics) { + for (auto &p : participants) { + linphone_conference_info_check_participant(conf_info_in_db, p->identity, 0); + } + linphone_conference_info_check_organizer(conf_info_in_db, 0); + } + if (conf_info_in_db) { + linphone_conference_info_unref(conf_info_in_db); + } + } + } + } + + idx++; + } + + if (uid) { + ms_free(uid); + } + + if (!!send_ics || ((end_time <= 0) && (start_time <= 0))) { + if (conference_address) { + check_conference_info(organizer.getCMgr(), conference_address, organizer.getCMgr(), + organizer_expected_participant_number, start_time, + (((start_time > 0) && (end_time > 0)) ? (int)(end_time - start_time) / 60 : 0), + subject, description, 0, LinphoneConferenceInfoStateNew); + } + + BC_ASSERT_EQUAL(organizer.getStats().number_of_LinphoneConferenceStateTerminationPending, + organizer_stat.number_of_LinphoneConferenceStateTerminationPending, int, "%d"); + BC_ASSERT_EQUAL(organizer.getStats().number_of_LinphoneConferenceStateTerminated, + organizer_stat.number_of_LinphoneConferenceStateTerminated, int, "%d"); + BC_ASSERT_EQUAL(organizer.getStats().number_of_LinphoneConferenceStateDeleted, + organizer_stat.number_of_LinphoneConferenceStateDeleted, int, "%d"); + } + + conference_address_str = + (conference_address) ? linphone_address_as_string(conference_address) : ms_strdup("<unknown>"); + ms_message("%s is creating conference %s on server %s", linphone_core_get_identity(organizer.getLc()), + conference_address_str, linphone_core_get_identity(focus.getLc())); + ms_free(conference_address_str); + +end: + if (organizer_address) linphone_address_unref(organizer_address); + linphone_conference_scheduler_unref(conference_scheduler); + bctbx_list_free(coresList); + bctbx_list_free_with_data(participant_infos, (bctbx_list_free_func)linphone_participant_info_unref); + + return conference_address; +} + +static size_t compute_no_video_streams(bool_t enable_video, LinphoneCall *call, LinphoneConference *conference) { + size_t nb_video_streams = 0; + + const LinphoneCallParams *call_params = + linphone_call_is_in_conference(call) ? linphone_call_get_remote_params(call) : linphone_call_get_params(call); + + bool_t call_video_enabled = linphone_call_params_video_enabled(call_params); + LinphoneMediaDirection call_video_dir = linphone_call_params_get_video_direction(call_params); + LinphoneConferenceLayout call_video_layout = linphone_call_params_get_conference_video_layout(call_params); + if (enable_video && call_video_enabled && (call_video_dir != LinphoneMediaDirectionInactive)) { + bctbx_list_t *devices = linphone_conference_get_participant_device_list(conference); + for (bctbx_list_t *itd = devices; itd; itd = bctbx_list_next(itd)) { + LinphoneParticipantDevice *d = (LinphoneParticipantDevice *)bctbx_list_get_data(itd); + LinphoneMediaDirection dir = linphone_participant_device_get_stream_capability(d, LinphoneStreamTypeVideo); + if ((dir == LinphoneMediaDirectionSendRecv) || (dir == LinphoneMediaDirectionSendOnly)) { + nb_video_streams++; + } + } + bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); + + if (((call_video_layout == LinphoneConferenceLayoutActiveSpeaker) && + (call_video_dir == LinphoneMediaDirectionRecvOnly)) || + (call_video_dir == LinphoneMediaDirectionSendRecv) || (call_video_dir == LinphoneMediaDirectionSendOnly)) { + nb_video_streams += 1; + } + } else { + nb_video_streams = 0; + } + + return nb_video_streams; +} + +static void wait_for_conference_streams(std::initializer_list<std::reference_wrapper<CoreManager>> coreMgrs, + std::list<LinphoneCoreManager *> conferenceMgrs, + LinphoneCoreManager *focus, + std::map<LinphoneCoreManager *, LinphoneParticipantRole> members, + const LinphoneAddress *confAddr, + bool_t enable_video) { + for (auto mgr : conferenceMgrs) { + // wait bit more to detect side effect if any + BC_ASSERT_TRUE(CoreManagerAssert(coreMgrs).waitUntil(chrono::seconds(50), [mgr, &focus, &members, confAddr, + enable_video] { + size_t nb_audio_streams = 1; + size_t nb_text_streams = 0; + std::list<LinphoneCall *> calls; + + bool video_check = false; + bool participant_check = false; + bool device_present = false; + bool call_ok = true; + bool audio_direction_ok = true; + + if (mgr == focus) { + for (auto m : members) { + LinphoneCoreManager *mMgr = m.first; + LinphoneCall *pcall = linphone_core_get_call_by_remote_address2(mMgr->lc, confAddr); + call_ok &= (pcall != nullptr); + LinphoneCall *call = linphone_core_get_call_by_remote_address2(mgr->lc, mMgr->identity); + call_ok &= (call != nullptr); + if (call) { + calls.push_back(call); + } else { + calls.push_back(nullptr); + } + } + } else { + LinphoneCall *call = linphone_core_get_call_by_remote_address2(mgr->lc, confAddr); + call_ok &= (call != nullptr); + if (call) { + calls.push_back(call); + } else { + calls.push_back(nullptr); + } + } + + call_ok &= (calls.size() > 0); + LinphoneConference *conference = linphone_core_search_conference_2(mgr->lc, confAddr); + for (auto call : calls) { + if (call) { + size_t nb_video_streams = compute_no_video_streams(enable_video, call, conference); + const SalMediaDescription *call_result_desc = _linphone_call_get_result_desc(call); + call_ok &= ((call_result_desc->nbActiveStreamsOfType(SalAudio) == nb_audio_streams) && + (call_result_desc->nbActiveStreamsOfType(SalVideo) == nb_video_streams) && + (call_result_desc->nbActiveStreamsOfType(SalText) == nb_text_streams) && + (linphone_call_get_state(call) == LinphoneCallStateStreamsRunning)); + } + } + + if (conference) { + bctbx_list_t *devices = linphone_conference_get_participant_device_list(conference); + video_check = (bctbx_list_size(devices) > 0); + participant_check = (bctbx_list_size(devices) > 0); + device_present = (bctbx_list_size(devices) > 0); + LinphoneCall *call = NULL; + for (bctbx_list_t *itd = devices; itd; itd = bctbx_list_next(itd)) { + LinphoneParticipantDevice *d = (LinphoneParticipantDevice *)bctbx_list_get_data(itd); + const LinphoneAddress *device_address = linphone_participant_device_get_address(d); + bool_t found = FALSE; + for (const auto &p : members) { + LinphoneCoreManager *mgr = p.first; + found |= linphone_address_weak_equal(mgr->identity, device_address); + } + if (mgr == focus) { + call = linphone_core_get_call_by_remote_address2(mgr->lc, device_address); + } else { + if (calls.front()) { + call = calls.front(); + } + } + + bool_t is_me = linphone_conference_is_me(conference, device_address); + LinphoneParticipant *p = is_me ? linphone_conference_get_me(conference) + : linphone_conference_find_participant(conference, device_address); + participant_check &= (p != nullptr); + LinphoneMediaDirection expected_audio_direction = LinphoneMediaDirectionInactive; + if (p) { + LinphoneParticipantRole role = linphone_participant_get_role(p); + expected_audio_direction = + ((role == LinphoneParticipantRoleSpeaker) ? LinphoneMediaDirectionSendRecv + : LinphoneMediaDirectionRecvOnly); + LinphoneMediaDirection audio_dir = + linphone_participant_device_get_stream_capability(d, LinphoneStreamTypeAudio); + audio_direction_ok &= (audio_dir == expected_audio_direction); + } else { + audio_direction_ok = false; + } + + LinphoneParticipantDeviceState expected_state = LinphoneParticipantDeviceStateJoining; + if (found) { + expected_state = LinphoneParticipantDeviceStatePresent; + } else { + expected_state = LinphoneParticipantDeviceStateLeft; + } + device_present &= (linphone_participant_device_get_state(d) == expected_state); + if (call) { + if (is_me) { + const LinphoneCallParams *call_current_params = linphone_call_get_current_params(call); + LinphoneMediaDirection call_audio_direction = + linphone_call_params_get_audio_direction(call_current_params); + audio_direction_ok &= (call_audio_direction == expected_audio_direction); + } + + const LinphoneCallParams *call_params = linphone_call_get_params(call); + bool_t call_video_enabled = linphone_call_params_video_enabled(call_params); + if (enable_video && ((mgr == focus) || call_video_enabled)) { + // Video is available if the direction is sendrecv or sendonly + LinphoneMediaDirection video_dir = + linphone_participant_device_get_stream_capability(d, LinphoneStreamTypeVideo); + video_check &= + (linphone_participant_device_get_stream_availability(d, LinphoneStreamTypeVideo) == + ((video_dir == LinphoneMediaDirectionSendRecv) || + (video_dir == LinphoneMediaDirectionSendOnly))); + } else { + video_check &= + !linphone_participant_device_get_stream_availability(d, LinphoneStreamTypeVideo); + } + } + } + if (devices) { + bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); + } + + for (auto m : members) { + LinphoneCoreManager *mMgr = m.first; + LinphoneParticipantRole role = m.second; + LinphoneParticipant *p = linphone_conference_is_me(conference, mMgr->identity) + ? linphone_conference_get_me(conference) + : linphone_conference_find_participant(conference, mMgr->identity); + participant_check &= (p != nullptr); + if (p) { + participant_check &= (linphone_participant_get_role(p) == role); + } + } + } + return audio_direction_ok && video_check && device_present && call_ok && participant_check; + })); + } +} + +static void set_video_settings_in_conference(LinphoneCoreManager *focus, + LinphoneCoreManager *participant, + std::list<LinphoneCoreManager *> members, + const LinphoneAddress *confAddr, + bool_t enable_video, + LinphoneMediaDirection video_direction, + bool_t answer_enable_video, + LinphoneMediaDirection answer_video_direction) { + std::list<LinphoneCoreManager *> active_cores = members; + active_cores.push_back(focus); + + bctbx_list_t *coresList = bctbx_list_append(NULL, focus->lc); + stats *participants_initial_stats = NULL; + stats focus_stat = focus->stat; + int counter = 1; + bool_t sendonly_video = TRUE; + bool_t recvonly_video = TRUE; + + LinphoneConferenceLayout call_conference_layout = LinphoneConferenceLayoutGrid; + + for (auto mgr : members) { + coresList = bctbx_list_append(coresList, mgr->lc); + // Allocate memory + participants_initial_stats = (stats *)realloc(participants_initial_stats, counter * sizeof(stats)); + // Append element + participants_initial_stats[counter - 1] = mgr->stat; + + LinphoneCall *call = linphone_core_get_call_by_remote_address2(mgr->lc, confAddr); + BC_ASSERT_PTR_NOT_NULL(call); + if (call) { + if (participant != mgr) { + const LinphoneCallParams *call_params = linphone_call_get_current_params(call); + bool_t call_enable_video = linphone_call_params_video_enabled(call_params); + bool_t call_video_direction = linphone_call_params_get_video_direction(call_params); + if (call_enable_video) { + sendonly_video &= (call_video_direction == LinphoneMediaDirectionSendOnly); + recvonly_video &= (call_video_direction == LinphoneMediaDirectionRecvOnly); + } + } else { + const LinphoneCallParams *call_params = linphone_call_get_params(call); + call_conference_layout = linphone_call_params_get_conference_video_layout(call_params); + } + } + + // Increment counter + counter++; + } + + bool_t inactive_video = ((recvonly_video && (video_direction == LinphoneMediaDirectionRecvOnly)) || + (sendonly_video && (video_direction == LinphoneMediaDirectionSendOnly))) && + (call_conference_layout == LinphoneConferenceLayoutGrid); + bool_t previous_enable_video = FALSE; + LinphoneMediaDirection previous_video_direction = LinphoneMediaDirectionInvalid; + + LinphoneCall *participant_call = linphone_core_get_call_by_remote_address2(participant->lc, confAddr); + BC_ASSERT_PTR_NOT_NULL(participant_call); + if (participant_call) { + + const LinphoneCallParams *call_params = linphone_call_get_current_params(participant_call); + previous_enable_video = linphone_call_params_video_enabled(call_params); + previous_video_direction = linphone_call_params_get_video_direction(call_params); + + ms_message("%s %s video with direction %s", linphone_core_get_identity(participant->lc), + ((enable_video) ? "enables" : "disables"), linphone_media_direction_to_string(video_direction)); + + LinphoneCallParams *new_params = linphone_core_create_call_params(participant->lc, participant_call); + linphone_call_params_enable_video(new_params, enable_video); + linphone_call_params_set_video_direction(new_params, video_direction); + linphone_call_update(participant_call, new_params); + linphone_call_params_unref(new_params); + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus->stat.number_of_LinphoneCallUpdatedByRemote, + focus_stat.number_of_LinphoneCallUpdatedByRemote + 1, liblinphone_tester_sip_timeout)); + + int focus_defer_update = + !!linphone_config_get_int(linphone_core_get_config(focus->lc), "sip", "defer_update_default", FALSE); + bool_t enable = enable_video && ((focus_defer_update == TRUE) ? answer_enable_video : TRUE); + if (focus_defer_update == TRUE) { + LinphoneAddress *uri = linphone_address_new(linphone_core_get_identity(participant->lc)); + LinphoneCall *focus_call = linphone_core_get_call_by_remote_address2(focus->lc, uri); + linphone_address_unref(uri); + BC_ASSERT_PTR_NOT_NULL(focus_call); + if (focus_call) { + ms_message("%s %s video with direction %s", linphone_core_get_identity(focus->lc), + ((answer_enable_video) ? "accepts" : "refuses"), + linphone_media_direction_to_string(answer_video_direction)); + LinphoneCallParams *focus_params = linphone_core_create_call_params(focus->lc, focus_call); + linphone_call_params_enable_video(focus_params, answer_enable_video); + linphone_call_params_set_video_direction(focus_params, answer_video_direction); + linphone_call_accept_update(focus_call, focus_params); + linphone_call_params_unref(focus_params); + } + } + + counter = 0; + int no_updates = 0; + for (auto mgr : members) { + if (((previous_enable_video != enable) && (previous_video_direction == LinphoneMediaDirectionSendRecv)) || + (enable && previous_enable_video && (previous_video_direction != video_direction)) || + (mgr == participant)) { + LinphoneCall *call = linphone_core_get_call_by_remote_address2(mgr->lc, confAddr); + BC_ASSERT_PTR_NOT_NULL(call); + if (call) { + const LinphoneCallParams *call_lparams = linphone_call_get_params(call); + if (linphone_call_params_video_enabled(call_lparams)) { + + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallUpdating, + participants_initial_stats[counter].number_of_LinphoneCallUpdating + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallStreamsRunning, + participants_initial_stats[counter].number_of_LinphoneCallStreamsRunning + 1, + liblinphone_tester_sip_timeout)); + + no_updates++; + } + } + + if ((previous_enable_video == enable) && (!enable)) { + BC_ASSERT_FALSE(wait_for_list( + coresList, &mgr->stat.number_of_participant_devices_media_capability_changed, + participants_initial_stats[counter].number_of_participant_devices_media_capability_changed + 1, + 5000)); + } else { + BC_ASSERT_TRUE(wait_for_list( + coresList, &mgr->stat.number_of_participant_devices_media_capability_changed, + participants_initial_stats[counter].number_of_participant_devices_media_capability_changed + 1, + liblinphone_tester_sip_timeout)); + } + BC_ASSERT_EQUAL(mgr->stat.number_of_participants_added, + participants_initial_stats[counter].number_of_participants_added, int, "%0d"); + BC_ASSERT_EQUAL(mgr->stat.number_of_participant_devices_added, + participants_initial_stats[counter].number_of_participant_devices_added, int, "%0d"); + BC_ASSERT_EQUAL(mgr->stat.number_of_participant_devices_joined, + participants_initial_stats[counter].number_of_participant_devices_joined, int, "%0d"); + } + counter++; + } + BC_ASSERT_TRUE(wait_for_list(coresList, &focus->stat.number_of_LinphoneCallUpdatedByRemote, + focus_stat.number_of_LinphoneCallUpdatedByRemote + no_updates, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus->stat.number_of_LinphoneCallStreamsRunning, + focus_stat.number_of_LinphoneCallStreamsRunning + no_updates, + liblinphone_tester_sip_timeout)); + if ((previous_enable_video == enable) && (!enable)) { + BC_ASSERT_FALSE(wait_for_list(coresList, &focus->stat.number_of_participant_devices_media_capability_changed, + focus_stat.number_of_participant_devices_media_capability_changed + 1, 3000)); + } else { + BC_ASSERT_TRUE(wait_for_list(coresList, &focus->stat.number_of_participant_devices_media_capability_changed, + focus_stat.number_of_participant_devices_media_capability_changed + 1, + liblinphone_tester_sip_timeout)); + } + BC_ASSERT_EQUAL(focus->stat.number_of_participants_added, focus_stat.number_of_participants_added, int, "%0d"); + BC_ASSERT_EQUAL(focus->stat.number_of_participant_devices_added, focus_stat.number_of_participant_devices_added, + int, "%0d"); + BC_ASSERT_EQUAL(focus->stat.number_of_participant_devices_joined, focus_stat.number_of_participant_devices_joined, + int, "%0d"); + + for (auto mgr : active_cores) { + LinphoneAddress *uri2 = linphone_address_new(linphone_core_get_identity(mgr->lc)); + LinphoneConference *pconference = linphone_core_search_conference(mgr->lc, NULL, uri2, confAddr, NULL); + linphone_address_unref(uri2); + BC_ASSERT_PTR_NOT_NULL(pconference); + if (pconference) { + LinphoneParticipant *p = (mgr == participant) + ? linphone_conference_get_me(pconference) + : linphone_conference_find_participant(pconference, participant->identity); + BC_ASSERT_PTR_NOT_NULL(p); + if (p) { + bctbx_list_t *devices = linphone_participant_get_devices(p); + for (bctbx_list_t *itd = devices; itd; itd = bctbx_list_next(itd)) { + LinphoneParticipantDevice *d = (LinphoneParticipantDevice *)bctbx_list_get_data(itd); + LinphoneMediaDirection expected_video_direction = video_direction; + if (enable == TRUE) { + if (recvonly_video && (video_direction == LinphoneMediaDirectionRecvOnly) && + (call_conference_layout == LinphoneConferenceLayoutGrid)) { + expected_video_direction = LinphoneMediaDirectionInactive; + } else { + expected_video_direction = video_direction; + } + } else { + expected_video_direction = LinphoneMediaDirectionInactive; + } + BC_ASSERT_EQUAL(linphone_participant_device_get_stream_capability(d, LinphoneStreamTypeVideo), + expected_video_direction, int, "%0d"); + } + if (devices) { + bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); + } + } + } + } + + LinphoneCall *pcall = linphone_core_get_call_by_remote_address2(participant->lc, confAddr); + BC_ASSERT_PTR_NOT_NULL(pcall); + if (pcall) { + const LinphoneCallParams *call_lparams = linphone_call_get_params(pcall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_lparams), enable_video, int, "%0d"); + const LinphoneCallParams *call_rparams = linphone_call_get_remote_params(pcall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_rparams), + ((inactive_video) ? FALSE : ((focus_defer_update == TRUE) ? enable : enable_video)), int, + "%0d"); + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(pcall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_cparams), ((inactive_video) ? FALSE : enable), int, + "%0d"); + } + + LinphoneAddress *uri = linphone_address_new(linphone_core_get_identity(participant->lc)); + LinphoneCall *ccall = linphone_core_get_call_by_remote_address2(focus->lc, uri); + linphone_address_unref(uri); + BC_ASSERT_PTR_NOT_NULL(ccall); + if (ccall) { + const LinphoneCallParams *call_lparams = linphone_call_get_params(ccall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_lparams), + (focus_defer_update == TRUE) ? answer_enable_video : enable_video, int, "%0d"); + const LinphoneCallParams *call_rparams = linphone_call_get_remote_params(ccall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_rparams), enable_video, int, "%0d"); + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(ccall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_cparams), ((inactive_video) ? FALSE : enable), int, + "%0d"); + } + + bctbx_list_free(coresList); + ms_free(participants_initial_stats); +} + +static void call_to_inexisting_conference_address(void) { + Focus focus("chloe_rc"); + { // to make sure focus is destroyed after clients. + ClientConference marie("marie_rc", focus.getIdentity()); + + bctbx_list_t *coresList = NULL; + coresList = bctbx_list_append(coresList, focus.getLc()); + coresList = bctbx_list_append(coresList, marie.getLc()); + + focus.registerAsParticipantDevice(marie); + + LinphoneAddress *confAddr = linphone_address_new(L_STRING_TO_C(focus.getIdentity().toString())); + linphone_address_set_uri_param(confAddr, "conf-id", "xxxxx"); + + stats marie_stat = marie.getStats(); + + LinphoneCallParams *new_params = linphone_core_create_call_params(marie.getLc(), nullptr); + linphone_core_invite_address_with_params_2(marie.getLc(), confAddr, new_params, NULL, nullptr); + linphone_call_params_unref(new_params); + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallOutgoingInit, + marie_stat.number_of_LinphoneCallOutgoingInit + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallError, + marie_stat.number_of_LinphoneCallError + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallReleased, + marie_stat.number_of_LinphoneCallReleased + 1, liblinphone_tester_sip_timeout)); + + linphone_address_unref(confAddr); + bctbx_list_free(coresList); + } +} + +static void create_conference_base(time_t start_time, + int duration, + bool_t uninvited_participant_dials, + LinphoneConferenceParticipantListType participant_list_type, + bool_t remove_participant, + const LinphoneMediaEncryption encryption, + bool_t enable_video, + LinphoneConferenceLayout layout, + bool_t enable_ice, + bool_t enable_stun, + bool_t audio_only_participant, + bool_t server_restart, + bool_t client_restart, + bool_t do_not_use_proxy, + LinphoneMediaDirection video_direction, + bool_t network_restart) { + Focus focus("chloe_rc"); + { // to make sure focus is destroyed after clients. + ClientConference marie("marie_rc", focus.getIdentity()); + ClientConference pauline("pauline_rc", focus.getIdentity()); + ClientConference laure("laure_tcp_rc", focus.getIdentity()); + ClientConference michelle("michelle_rc", focus.getIdentity()); + ClientConference berthe("berthe_rc", focus.getIdentity()); + + focus.registerAsParticipantDevice(marie); + focus.registerAsParticipantDevice(pauline); + focus.registerAsParticipantDevice(laure); + focus.registerAsParticipantDevice(michelle); + focus.registerAsParticipantDevice(berthe); + + setup_conference_info_cbs(marie.getCMgr()); + + bctbx_list_t *coresList = NULL; + + for (auto mgr : {focus.getCMgr(), marie.getCMgr(), pauline.getCMgr(), laure.getCMgr(), michelle.getCMgr(), + berthe.getCMgr()}) { + if (enable_video) { + if ((audio_only_participant == FALSE) || (mgr != pauline.getCMgr())) { + LinphoneVideoActivationPolicy *pol = + linphone_factory_create_video_activation_policy(linphone_factory_get()); + linphone_video_activation_policy_set_automatically_accept(pol, TRUE); + linphone_video_activation_policy_set_automatically_initiate(pol, TRUE); + linphone_core_set_video_activation_policy(mgr->lc, pol); + linphone_video_activation_policy_unref(pol); + } + + linphone_core_set_video_device(mgr->lc, liblinphone_tester_mire_id); + linphone_core_enable_video_capture(mgr->lc, TRUE); + linphone_core_enable_video_display(mgr->lc, TRUE); + + if (layout == LinphoneConferenceLayoutGrid) { + linphone_core_set_preferred_video_definition_by_name(mgr->lc, "720p"); + linphone_config_set_string(linphone_core_get_config(mgr->lc), "video", "max_conference_size", + "vga"); + } + } + + if (mgr != focus.getCMgr()) { + linphone_core_set_default_conference_layout(mgr->lc, layout); + linphone_core_set_media_encryption(mgr->lc, encryption); + } + + if (do_not_use_proxy) { + linphone_core_set_default_proxy_config(mgr->lc, NULL); + } + + enable_stun_in_core(mgr, enable_stun, enable_ice); + linphone_core_manager_wait_for_stun_resolution(mgr); + + linphone_config_set_int(linphone_core_get_config(mgr->lc), "sip", "update_call_when_ice_completed", TRUE); + linphone_config_set_int(linphone_core_get_config(mgr->lc), "sip", + "update_call_when_ice_completed_with_dtls", FALSE); + + coresList = bctbx_list_append(coresList, mgr->lc); + } + + linphone_core_set_file_transfer_server(marie.getLc(), file_transfer_url); + linphone_core_set_conference_participant_list_type(focus.getLc(), participant_list_type); + + stats focus_stat = focus.getStats(); + + std::list<LinphoneCoreManager *> participants{pauline.getCMgr(), laure.getCMgr()}; + std::list<LinphoneCoreManager *> conferenceMgrs{focus.getCMgr(), marie.getCMgr(), pauline.getCMgr(), + laure.getCMgr()}; + std::list<LinphoneCoreManager *> members{marie.getCMgr(), pauline.getCMgr(), laure.getCMgr()}; + + time_t end_time = (duration <= 0) ? -1 : (start_time + duration * 60); + const char *initialSubject = "Test characters: ^ :) ¤ çà @"; + const char *description = "Paris Baker"; + + std::map<LinphoneCoreManager *, LinphoneParticipantRole> participantList; + LinphoneParticipantRole role = LinphoneParticipantRoleListener; + for (auto &p : participants) { + participantList.insert(std::make_pair(p, role)); + role = (role == LinphoneParticipantRoleSpeaker) ? LinphoneParticipantRoleListener + : LinphoneParticipantRoleSpeaker; + } + LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, + initialSubject, description, TRUE); + BC_ASSERT_PTR_NOT_NULL(confAddr); + char *conference_address_str = (confAddr) ? linphone_address_as_string(confAddr) : ms_strdup("<unknown>"); + + // Chat room creation to send ICS + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateCreated, 2, + liblinphone_tester_sip_timeout)); + + if (server_restart) { + coresList = bctbx_list_remove(coresList, focus.getLc()); + + ms_message("%s is restarting its core", linphone_core_get_identity(focus.getLc())); + // Restart flexisip + focus.reStart(); + + if (enable_video) { + LinphoneVideoActivationPolicy *pol = + linphone_factory_create_video_activation_policy(linphone_factory_get()); + linphone_video_activation_policy_set_automatically_accept(pol, TRUE); + linphone_video_activation_policy_set_automatically_initiate(pol, TRUE); + linphone_core_set_video_activation_policy(focus.getLc(), pol); + linphone_video_activation_policy_unref(pol); + + linphone_core_enable_video_capture(focus.getLc(), TRUE); + linphone_core_enable_video_display(focus.getLc(), TRUE); + } + + linphone_core_set_conference_participant_list_type(focus.getLc(), participant_list_type); + linphone_core_set_default_conference_layout(focus.getLc(), layout); + coresList = bctbx_list_append(coresList, focus.getLc()); + } + + for (auto mgr : members) { + LinphoneCallParams *new_params = linphone_core_create_call_params(mgr->lc, nullptr); + linphone_call_params_set_media_encryption(new_params, encryption); + linphone_call_params_set_video_direction(new_params, video_direction); + if (mgr == laure.getCMgr()) { + linphone_call_params_enable_mic(new_params, FALSE); + } + linphone_core_invite_address_with_params_2(mgr->lc, confAddr, new_params, NULL, nullptr); + linphone_call_params_unref(new_params); + } + + int idx = 1; + for (auto mgr : members) { + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallOutgoingProgress, 1, + liblinphone_tester_sip_timeout)); + int no_streams_running = + ((enable_ice && ((audio_only_participant == FALSE) || (mgr != pauline.getCMgr()))) ? 3 : 2); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallUpdating, (no_streams_running - 1), + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallStreamsRunning, no_streams_running, + liblinphone_tester_sip_timeout)); + // Update to add to conference. + // If ICE is enabled, the addition to a conference may go through a resume of the call + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateCreated, + ((mgr == marie.getCMgr()) ? 3 : 2), liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionOutgoingProgress, 1, 5000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionActive, 1, 5000)); + BC_ASSERT_TRUE( + wait_for_list(coresList, &mgr->stat.number_of_NotifyReceived, 1, liblinphone_tester_sip_timeout)); + + if ((encryption == LinphoneMediaEncryptionDTLS) || (encryption == LinphoneMediaEncryptionZRTP)) { + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallEncryptedOn, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallEncryptedOn, idx, + liblinphone_tester_sip_timeout)); + } + + LinphoneCall *pcall = linphone_core_get_call_by_remote_address2(mgr->lc, confAddr); + BC_ASSERT_PTR_NOT_NULL(pcall); + if (pcall) { + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(pcall); + const LinphoneMediaEncryption pcall_enc = linphone_call_params_get_media_encryption(call_cparams); + BC_ASSERT_EQUAL(pcall_enc, encryption, int, "%d"); + } + LinphoneCall *ccall = linphone_core_get_call_by_remote_address2(focus.getLc(), mgr->identity); + BC_ASSERT_PTR_NOT_NULL(ccall); + if (ccall) { + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(ccall); + const LinphoneMediaEncryption ccall_enc = linphone_call_params_get_media_encryption(call_cparams); + BC_ASSERT_EQUAL(ccall_enc, encryption, int, "%d"); + } + + idx++; + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallIncomingReceived, + focus_stat.number_of_LinphoneCallIncomingReceived + 3, + liblinphone_tester_sip_timeout)); + int focus_no_streams_running = ((enable_ice) ? 9 : 6); + // Update to end ICE negotiations + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallUpdatedByRemote, + focus_stat.number_of_LinphoneCallUpdatedByRemote + (focus_no_streams_running - 3), + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallStreamsRunning, + focus_stat.number_of_LinphoneCallStreamsRunning + focus_no_streams_running, + liblinphone_tester_sip_timeout)); + // Update to add to conference. + // If ICE is enabled, the addition to a conference may go through a resume of the call + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateCreated, + focus_stat.number_of_LinphoneConferenceStateCreated + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneSubscriptionIncomingReceived, + focus_stat.number_of_LinphoneSubscriptionIncomingReceived + 3, 5000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneSubscriptionActive, + focus_stat.number_of_LinphoneSubscriptionActive + 3, 5000)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participants_added, + focus_stat.number_of_participants_added + 3, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_added, + focus_stat.number_of_participant_devices_added + 3, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_joined, + focus_stat.number_of_participant_devices_joined + 3, + liblinphone_tester_sip_timeout)); + + std::map<LinphoneCoreManager *, LinphoneParticipantRole> memberList; + for (const auto &member : members) { + try { + const auto &participantInfo = participantList.at(member); + memberList.insert(std::make_pair(member, participantInfo)); + } catch (std::out_of_range &) { + if (member == marie.getCMgr()) { + memberList.insert(std::make_pair(marie.getCMgr(), LinphoneParticipantRoleSpeaker)); + } else { + ms_fatal("Unable to find active participant %s in the participant list", + linphone_core_get_identity(member->lc)); + } + } + } + wait_for_conference_streams({focus, marie, pauline, laure, michelle, berthe}, conferenceMgrs, focus.getCMgr(), + memberList, confAddr, enable_video); + + LinphoneConference *fconference = linphone_core_search_conference_2(focus.getLc(), confAddr); + BC_ASSERT_PTR_NOT_NULL(fconference); + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, laure, michelle, berthe}).waitUntil(chrono::seconds(2), [] { + return false; + }); + + for (auto mgr : conferenceMgrs) { + LinphoneConference *pconference = linphone_core_search_conference_2(mgr->lc, confAddr); + BC_ASSERT_PTR_NOT_NULL(pconference); + if (pconference) { + const LinphoneConferenceParams *conference_params = linphone_conference_get_current_params(pconference); + int no_participants = 0; + if (start_time >= 0) { + BC_ASSERT_EQUAL((long long)linphone_conference_params_get_start_time(conference_params), + (long long)start_time, long long, "%lld"); + } + BC_ASSERT_EQUAL((long long)linphone_conference_params_get_end_time(conference_params), + (long long)end_time, long long, "%lld"); + + bctbx_list_t *participant_device_list = linphone_conference_get_participant_device_list(pconference); + BC_ASSERT_EQUAL(bctbx_list_size(participant_device_list), members.size(), size_t, "%zu"); + for (bctbx_list_t *d_it = participant_device_list; d_it; d_it = bctbx_list_next(d_it)) { + LinphoneParticipantDevice *d = (LinphoneParticipantDevice *)bctbx_list_get_data(d_it); + BC_ASSERT_PTR_NOT_NULL(d); + if (d) { + BC_ASSERT_TRUE((!!linphone_participant_device_get_is_muted(d)) == + (linphone_address_weak_equal(linphone_participant_device_get_address(d), + laure.getCMgr()->identity))); + linphone_participant_device_set_user_data(d, mgr->lc); + LinphoneParticipantDeviceCbs *cbs = + linphone_factory_create_participant_device_cbs(linphone_factory_get()); + linphone_participant_device_cbs_set_is_muted(cbs, on_muted_notified); + linphone_participant_device_add_callbacks(d, cbs); + linphone_participant_device_cbs_unref(cbs); + } + } + bctbx_list_free_with_data(participant_device_list, (void (*)(void *))linphone_participant_device_unref); + + if (mgr == focus.getCMgr()) { + no_participants = static_cast<int>(members.size()); + BC_ASSERT_FALSE(linphone_conference_is_in(pconference)); + } else { + no_participants = static_cast<int>(members.size() - 1); + BC_ASSERT_TRUE(linphone_conference_is_in(pconference)); + LinphoneCall *current_call = linphone_core_get_current_call(mgr->lc); + BC_ASSERT_PTR_NOT_NULL(current_call); + if (current_call) { + BC_ASSERT_EQUAL((int)linphone_call_get_state(current_call), + (int)LinphoneCallStateStreamsRunning, int, "%0d"); + } + if (enable_ice) { + if (video_direction == LinphoneMediaDirectionSendRecv) { + BC_ASSERT_TRUE(check_ice(mgr, focus.getCMgr(), LinphoneIceStateHostConnection)); + } else { + LinphoneCall *c1, *c2; + MSTimeSpec ts; + + c1 = linphone_core_get_call_by_remote_address2(mgr->lc, focus.getCMgr()->identity); + c2 = linphone_core_get_call_by_remote_address2(focus.getLc(), mgr->identity); + BC_ASSERT_TRUE(linphone_call_get_microphone_muted(c1) == (mgr == laure.getCMgr())); + + BC_ASSERT_PTR_NOT_NULL(c1); + BC_ASSERT_PTR_NOT_NULL(c2); + if (!c1 || !c2) { + BC_ASSERT_EQUAL( + linphone_call_params_video_enabled(linphone_call_get_current_params(c1)), + linphone_call_params_video_enabled(linphone_call_get_current_params(c2)), int, + "%d"); + BC_ASSERT_EQUAL( + linphone_call_params_realtime_text_enabled(linphone_call_get_current_params(c1)), + linphone_call_params_realtime_text_enabled(linphone_call_get_current_params(c2)), + int, "%d"); + bool_t audio_enabled = + linphone_call_params_audio_enabled(linphone_call_get_current_params(c1)); + if (audio_enabled) { + liblinphone_tester_clock_start(&ts); + LinphoneCallStats *stats1 = NULL; + LinphoneCallStats *stats2 = NULL; + do { + if ((c1 != NULL) && (c2 != NULL)) { + stats1 = linphone_call_get_audio_stats(c1); + stats2 = linphone_call_get_audio_stats(c2); + if (linphone_call_stats_get_ice_state(stats1) == + LinphoneIceStateHostConnection && + linphone_call_stats_get_ice_state(stats2) == + LinphoneIceStateHostConnection) { + break; + } + linphone_core_iterate(mgr->lc); + linphone_core_iterate(focus.getLc()); + linphone_call_stats_unref(stats1); + linphone_call_stats_unref(stats2); + stats1 = stats2 = NULL; + } + ms_usleep(20000); + } while (!liblinphone_tester_clock_elapsed(&ts, liblinphone_tester_sip_timeout)); + if (stats1) linphone_call_stats_unref(stats1); + if (stats2) linphone_call_stats_unref(stats2); + } + } + } + } + + LinphoneVideoActivationPolicy *pol = linphone_core_get_video_activation_policy(mgr->lc); + bool_t enabled = !!linphone_video_activation_policy_get_automatically_initiate(pol); + linphone_video_activation_policy_unref(pol); + + int no_streams_audio = 1; + int no_streams_video = (enabled) ? 4 : 0; + int no_active_streams_video = 0; + int no_streams_text = 0; + + LinphoneCall *pcall = linphone_core_get_call_by_remote_address2(mgr->lc, confAddr); + BC_ASSERT_PTR_NOT_NULL(pcall); + if (pcall) { + no_active_streams_video = + static_cast<int>(compute_no_video_streams(enabled, pcall, pconference)); + if (!enable_ice) { + _linphone_call_check_max_nb_streams(pcall, no_streams_audio, no_streams_video, + no_streams_text); + } + _linphone_call_check_nb_active_streams(pcall, no_streams_audio, no_active_streams_video, + no_streams_text); + const LinphoneCallParams *call_lparams = linphone_call_get_params(pcall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_lparams), enabled, int, "%0d"); + const LinphoneCallParams *call_rparams = linphone_call_get_remote_params(pcall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_rparams), + enabled && (no_active_streams_video > 0), int, "%0d"); + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(pcall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_cparams), + enabled && (no_active_streams_video > 0), int, "%0d"); + + if (enabled && layout == LinphoneConferenceLayoutGrid && + video_direction == LinphoneMediaDirectionSendRecv) { + MSVideoSize vsize = linphone_call_params_get_sent_video_size(call_cparams); + BC_ASSERT_EQUAL(vsize.width, 640, int, "%d"); + BC_ASSERT_EQUAL(vsize.height, 480, int, "%d"); + } + } + LinphoneCall *ccall = linphone_core_get_call_by_remote_address2(focus.getLc(), mgr->identity); + BC_ASSERT_PTR_NOT_NULL(ccall); + if (ccall) { + _linphone_call_check_nb_active_streams(ccall, no_streams_audio, no_active_streams_video, + no_streams_text); + if (!enable_ice) { + _linphone_call_check_max_nb_streams(ccall, no_streams_audio, no_streams_video, + no_streams_text); + const LinphoneCallParams *call_lparams = linphone_call_get_params(ccall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_lparams), enabled, int, "%0d"); + const LinphoneCallParams *call_rparams = linphone_call_get_remote_params(ccall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_rparams), enabled, int, "%0d"); + } + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(ccall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_cparams), + enabled && (no_active_streams_video > 0), int, "%0d"); + } + } + BC_ASSERT_EQUAL(linphone_conference_get_participant_count(pconference), no_participants, int, "%0d"); + BC_ASSERT_STRING_EQUAL(linphone_conference_get_subject(pconference), initialSubject); + LinphoneParticipant *me = linphone_conference_get_me(pconference); + BC_ASSERT_TRUE(linphone_participant_is_admin(me) == + ((mgr == marie.getCMgr()) || (mgr == focus.getCMgr()))); + BC_ASSERT_TRUE(linphone_address_weak_equal(linphone_participant_get_address(me), mgr->identity)); + bctbx_list_t *participants = linphone_conference_get_participant_list(pconference); + for (bctbx_list_t *itp = participants; itp; itp = bctbx_list_next(itp)) { + LinphoneParticipant *p = (LinphoneParticipant *)bctbx_list_get_data(itp); + BC_ASSERT_TRUE( + linphone_participant_is_admin(p) == + linphone_address_weak_equal(linphone_participant_get_address(p), marie.getCMgr()->identity)); + } + bctbx_list_free_with_data(participants, (void (*)(void *))linphone_participant_unref); + + if (mgr != focus.getCMgr()) { + check_conference_ssrc(fconference, pconference); + } + } + } + + ms_message("Marie mutes its microphone"); + LinphoneConference *marie_conference = linphone_core_search_conference_2(marie.getLc(), confAddr); + BC_ASSERT_PTR_NOT_NULL(marie_conference); + if (marie_conference) { + linphone_conference_mute_microphone(marie_conference, TRUE); + } + + for (auto mgr : conferenceMgrs) { + if (mgr != marie.getCMgr()) { + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneParticipantDeviceMuted, 1, 5000)); + } + if (mgr != focus.getCMgr()) { + LinphoneCall *c1 = linphone_core_get_call_by_remote_address2(mgr->lc, focus.getCMgr()->identity); + BC_ASSERT_TRUE(linphone_call_get_microphone_muted(c1) == + ((mgr == laure.getCMgr()) || (mgr == marie.getCMgr()))); + } + + LinphoneConference *pconference = linphone_core_search_conference_2(mgr->lc, confAddr); + + bctbx_list_t *participant_device_list = linphone_conference_get_participant_device_list(pconference); + for (bctbx_list_t *d_it = participant_device_list; d_it; d_it = bctbx_list_next(d_it)) { + LinphoneParticipantDevice *d = (LinphoneParticipantDevice *)bctbx_list_get_data(d_it); + BC_ASSERT_PTR_NOT_NULL(d); + if (d) { + BC_ASSERT_TRUE((!!linphone_participant_device_get_is_muted(d)) == + ((linphone_address_weak_equal(linphone_participant_device_get_address(d), + laure.getCMgr()->identity)) || + (linphone_address_weak_equal(linphone_participant_device_get_address(d), + marie.getCMgr()->identity)))); + } + } + bctbx_list_free_with_data(participant_device_list, (void (*)(void *))linphone_participant_device_unref); + } + + if (client_restart) { + ms_message("Marie restarts its core"); + coresList = bctbx_list_remove(coresList, marie.getLc()); + // Restart flexisip + marie.reStart(); + + if (enable_video) { + LinphoneVideoActivationPolicy *pol = + linphone_factory_create_video_activation_policy(linphone_factory_get()); + linphone_video_activation_policy_set_automatically_accept(pol, TRUE); + linphone_video_activation_policy_set_automatically_initiate(pol, TRUE); + linphone_core_set_video_activation_policy(marie.getLc(), pol); + linphone_video_activation_policy_unref(pol); + + linphone_core_enable_video_capture(marie.getLc(), TRUE); + linphone_core_enable_video_display(marie.getLc(), TRUE); + } + + linphone_core_set_default_conference_layout(marie.getLc(), layout); + coresList = bctbx_list_append(coresList, marie.getLc()); + + stats focus_stat2 = focus.getStats(); + stats marie_stat2 = marie.getStats(); + + LinphoneCallParams *new_params = linphone_core_create_call_params(marie.getLc(), nullptr); + linphone_call_params_set_media_encryption(new_params, encryption); + linphone_core_invite_address_with_params_2(marie.getLc(), confAddr, new_params, NULL, nullptr); + linphone_call_params_unref(new_params); + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallOutgoingProgress, + marie_stat2.number_of_LinphoneCallOutgoingProgress + 1, + liblinphone_tester_sip_timeout)); + int no_streams_running = ((enable_ice) ? 3 : 2); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallUpdating, + marie_stat2.number_of_LinphoneCallUpdating + (no_streams_running - 1), + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallStreamsRunning, + marie_stat2.number_of_LinphoneCallStreamsRunning + no_streams_running, + liblinphone_tester_sip_timeout)); + // Update to add to conference. + // If ICE is enabled, the addition to a conference may go through a resume of the call + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateCreated, + marie_stat2.number_of_LinphoneConferenceStateCreated + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneSubscriptionOutgoingProgress, + marie_stat2.number_of_LinphoneSubscriptionOutgoingProgress + 1, 5000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneSubscriptionActive, + marie_stat2.number_of_LinphoneSubscriptionActive + 1, 5000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_NotifyReceived, + marie_stat2.number_of_NotifyReceived + 1, liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallIncomingReceived, + focus_stat2.number_of_LinphoneCallIncomingReceived + 1, + liblinphone_tester_sip_timeout)); + int focus_no_streams_running2 = ((enable_ice) ? 2 : 1); + // Update to end ICE negotiations + BC_ASSERT_TRUE( + wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallUpdatedByRemote, + focus_stat2.number_of_LinphoneCallUpdatedByRemote + (focus_no_streams_running2 - 1), + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallStreamsRunning, + focus_stat2.number_of_LinphoneCallStreamsRunning + focus_no_streams_running2, + liblinphone_tester_sip_timeout)); + + if ((encryption == LinphoneMediaEncryptionDTLS) || (encryption == LinphoneMediaEncryptionZRTP)) { + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallEncryptedOn, + marie_stat2.number_of_LinphoneCallEncryptedOn + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallEncryptedOn, + focus_stat2.number_of_LinphoneCallEncryptedOn + 1, + liblinphone_tester_sip_timeout)); + } + + LinphoneCall *pcall = linphone_core_get_call_by_remote_address2(marie.getLc(), confAddr); + BC_ASSERT_PTR_NOT_NULL(pcall); + if (pcall) { + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(pcall); + const LinphoneMediaEncryption pcall_enc = linphone_call_params_get_media_encryption(call_cparams); + BC_ASSERT_EQUAL(pcall_enc, encryption, int, "%d"); + } + LinphoneCall *ccall = linphone_core_get_call_by_remote_address2(focus.getLc(), marie.getCMgr()->identity); + BC_ASSERT_PTR_NOT_NULL(ccall); + if (ccall) { + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(ccall); + const LinphoneMediaEncryption ccall_enc = linphone_call_params_get_media_encryption(call_cparams); + BC_ASSERT_EQUAL(ccall_enc, encryption, int, "%d"); + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneSubscriptionIncomingReceived, + focus_stat2.number_of_LinphoneSubscriptionIncomingReceived + 1, 5000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneSubscriptionActive, + focus_stat2.number_of_LinphoneSubscriptionActive + 1, 5000)); + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, laure, michelle, berthe}).waitUntil(chrono::seconds(2), [] { + return false; + }); + + for (auto mgr : conferenceMgrs) { + LinphoneAddress *uri = linphone_address_new(linphone_core_get_identity(mgr->lc)); + LinphoneConference *pconference = linphone_core_search_conference(mgr->lc, NULL, uri, confAddr, NULL); + linphone_address_unref(uri); + BC_ASSERT_PTR_NOT_NULL(pconference); + if (pconference) { + const LinphoneConferenceParams *conference_params = + linphone_conference_get_current_params(pconference); + int no_participants = 0; + if (start_time >= 0) { + BC_ASSERT_EQUAL((long long)linphone_conference_params_get_start_time(conference_params), + (long long)start_time, long long, "%lld"); + } + BC_ASSERT_EQUAL((long long)linphone_conference_params_get_end_time(conference_params), + (long long)end_time, long long, "%lld"); + + bctbx_list_t *participant_device_list = + linphone_conference_get_participant_device_list(pconference); + for (bctbx_list_t *d_it = participant_device_list; d_it; d_it = bctbx_list_next(d_it)) { + LinphoneParticipantDevice *d = (LinphoneParticipantDevice *)bctbx_list_get_data(d_it); + BC_ASSERT_PTR_NOT_NULL(d); + if (d) { + BC_ASSERT_TRUE((!!linphone_participant_device_get_is_muted(d)) == + (linphone_address_weak_equal(linphone_participant_device_get_address(d), + laure.getCMgr()->identity))); + } + } + bctbx_list_free_with_data(participant_device_list, + (void (*)(void *))linphone_participant_device_unref); + + if (mgr == focus.getCMgr()) { + no_participants = static_cast<int>(members.size()); + BC_ASSERT_FALSE(linphone_conference_is_in(pconference)); + } else { + + no_participants = static_cast<int>(members.size() - 1); + BC_ASSERT_TRUE(linphone_conference_is_in(pconference)); + LinphoneCall *current_call = linphone_core_get_current_call(mgr->lc); + BC_ASSERT_PTR_NOT_NULL(current_call); + if (current_call) { + BC_ASSERT_EQUAL((int)linphone_call_get_state(current_call), + (int)LinphoneCallStateStreamsRunning, int, "%0d"); + } + if (enable_ice) { + BC_ASSERT_TRUE(check_ice(mgr, focus.getCMgr(), LinphoneIceStateHostConnection)); + } + + LinphoneVideoActivationPolicy *pol = linphone_core_get_video_activation_policy(mgr->lc); + bool_t enabled = !!linphone_video_activation_policy_get_automatically_initiate(pol); + linphone_video_activation_policy_unref(pol); + + int no_streams_audio = 1; + int no_streams_video = (enabled) ? 4 : 0; + int no_active_streams_video = 0; + int no_streams_text = 0; + + LinphoneCall *pcall = linphone_core_get_call_by_remote_address2(mgr->lc, confAddr); + BC_ASSERT_PTR_NOT_NULL(pcall); + if (pcall) { + no_active_streams_video = + static_cast<int>(compute_no_video_streams(enabled, pcall, pconference)); + _linphone_call_check_max_nb_streams(pcall, no_streams_audio, no_streams_video, + no_streams_text); + _linphone_call_check_nb_active_streams(pcall, no_streams_audio, no_active_streams_video, + no_streams_text); + const LinphoneCallParams *call_lparams = linphone_call_get_params(pcall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_lparams), enabled, int, "%0d"); + const LinphoneCallParams *call_rparams = linphone_call_get_remote_params(pcall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_rparams), enabled, int, "%0d"); + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(pcall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_cparams), enabled, int, "%0d"); + } + LinphoneCall *ccall = linphone_core_get_call_by_remote_address2(focus.getLc(), mgr->identity); + BC_ASSERT_PTR_NOT_NULL(ccall); + if (ccall) { + _linphone_call_check_max_nb_streams(ccall, no_streams_audio, no_streams_video, + no_streams_text); + _linphone_call_check_nb_active_streams(ccall, no_streams_audio, no_active_streams_video, + no_streams_text); + const LinphoneCallParams *call_lparams = linphone_call_get_params(ccall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_lparams), enabled, int, "%0d"); + const LinphoneCallParams *call_rparams = linphone_call_get_remote_params(ccall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_rparams), enabled, int, "%0d"); + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(ccall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_cparams), enabled, int, "%0d"); + } + } + BC_ASSERT_EQUAL(linphone_conference_get_participant_count(pconference), no_participants, int, + "%0d"); + bctbx_list_t *devices = linphone_conference_get_participant_device_list(pconference); + BC_ASSERT_EQUAL(bctbx_list_size(devices), 3, size_t, "%zu"); + if (devices) { + bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); + } + BC_ASSERT_STRING_EQUAL(linphone_conference_get_subject(pconference), initialSubject); + LinphoneParticipant *me = linphone_conference_get_me(pconference); + BC_ASSERT_TRUE(linphone_participant_is_admin(me) == + ((mgr == marie.getCMgr()) || (mgr == focus.getCMgr()))); + BC_ASSERT_TRUE(linphone_address_weak_equal(linphone_participant_get_address(me), mgr->identity)); + bctbx_list_t *participants = linphone_conference_get_participant_list(pconference); + for (bctbx_list_t *itp = participants; itp; itp = bctbx_list_next(itp)) { + LinphoneParticipant *p = (LinphoneParticipant *)bctbx_list_get_data(itp); + BC_ASSERT_TRUE(linphone_participant_is_admin(p) == + linphone_address_weak_equal(linphone_participant_get_address(p), + marie.getCMgr()->identity)); + } + bctbx_list_free_with_data(participants, (void (*)(void *))linphone_participant_unref); + + if (mgr != focus.getCMgr()) { + check_conference_ssrc(fconference, pconference); + } + } + } + } + + // Wait a little bit + wait_for_list(coresList, NULL, 0, 3000); + + if (network_restart) { + ms_message("Marie toggles its network"); + stats focus_stat2 = focus.getStats(); + stats marie_stat = marie.getStats(); + linphone_core_set_network_reachable(marie.getLc(), FALSE); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneSubscriptionTerminated, + marie_stat.number_of_LinphoneSubscriptionTerminated + 1, + liblinphone_tester_sip_timeout)); + + // Wait a little bit + wait_for_list(coresList, NULL, 0, 1000); + + linphone_core_set_network_reachable(marie.getLc(), TRUE); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneSubscriptionOutgoingProgress, + marie_stat.number_of_LinphoneSubscriptionOutgoingProgress + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneSubscriptionIncomingReceived, + focus_stat2.number_of_LinphoneSubscriptionIncomingReceived + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneSubscriptionActive, + marie_stat.number_of_LinphoneSubscriptionActive + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneSubscriptionActive, + focus_stat2.number_of_LinphoneSubscriptionActive + 1, + liblinphone_tester_sip_timeout)); + } + + if (enable_video) { + LinphoneCall *pauline_call = linphone_core_get_call_by_remote_address2(pauline.getLc(), confAddr); + BC_ASSERT_PTR_NOT_NULL(pauline_call); + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(pauline_call); + + Address paulineAddr = pauline.getIdentity(); + LinphoneCall *focus_call = linphone_core_get_call_by_remote_address2(focus.getLc(), paulineAddr.toC()); + BC_ASSERT_PTR_NOT_NULL(focus_call); + + LinphoneVideoActivationPolicy *pol = linphone_core_get_video_activation_policy(pauline.getLc()); + bool_t enable = !(!!linphone_video_activation_policy_get_automatically_initiate(pol) && + linphone_call_params_video_enabled(call_cparams)); + linphone_video_activation_policy_unref(pol); + + LinphoneAddress *paulineUri = linphone_address_new(linphone_core_get_identity(pauline.getLc())); + LinphoneConference *paulineConference = + linphone_core_search_conference(pauline.getLc(), NULL, paulineUri, confAddr, NULL); + linphone_address_unref(paulineUri); + BC_ASSERT_PTR_NOT_NULL(paulineConference); + + for (int i = 0; i < 4; i++) { + stats focus_stat2 = focus.getStats(); + stats marie_stat2 = marie.getStats(); + stats pauline_stat2 = pauline.getStats(); + stats laure_stat2 = laure.getStats(); + + LinphoneMediaDirection new_video_direction = video_direction; + if ((video_direction == LinphoneMediaDirectionRecvOnly) && (layout == LinphoneConferenceLayoutGrid)) { + new_video_direction = LinphoneMediaDirectionSendRecv; + } + + ms_message("%s %s video with direction %s", linphone_core_get_identity(pauline.getLc()), + (enable ? "enables" : "disables"), linphone_media_direction_to_string(new_video_direction)); + + if (pauline_call) { + LinphoneCallParams *new_params = linphone_core_create_call_params(pauline.getLc(), pauline_call); + linphone_call_params_enable_video(new_params, enable); + linphone_call_params_set_video_direction(new_params, new_video_direction); + linphone_call_update(pauline_call, new_params); + linphone_call_params_unref(new_params); + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneCallUpdating, + pauline_stat2.number_of_LinphoneCallUpdating + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneCallStreamsRunning, + pauline_stat2.number_of_LinphoneCallStreamsRunning + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallUpdatedByRemote, + focus_stat2.number_of_LinphoneCallUpdatedByRemote + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallStreamsRunning, + focus_stat2.number_of_LinphoneCallStreamsRunning + 1, + liblinphone_tester_sip_timeout)); + + if (new_video_direction == LinphoneMediaDirectionSendRecv) { + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallUpdating, + marie_stat2.number_of_LinphoneCallUpdating + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallStreamsRunning, + marie_stat2.number_of_LinphoneCallStreamsRunning + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_LinphoneCallUpdating, + laure_stat2.number_of_LinphoneCallUpdating + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_LinphoneCallStreamsRunning, + laure_stat2.number_of_LinphoneCallStreamsRunning + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallUpdatedByRemote, + focus_stat2.number_of_LinphoneCallUpdatedByRemote + 3, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallStreamsRunning, + focus_stat2.number_of_LinphoneCallStreamsRunning + 3, + liblinphone_tester_sip_timeout)); + } + + BC_ASSERT_TRUE(wait_for_list(coresList, + &focus.getStats().number_of_participant_devices_media_capability_changed, + focus_stat2.number_of_participant_devices_media_capability_changed + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, + &marie.getStats().number_of_participant_devices_media_capability_changed, + marie_stat2.number_of_participant_devices_media_capability_changed + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, + &pauline.getStats().number_of_participant_devices_media_capability_changed, + pauline_stat2.number_of_participant_devices_media_capability_changed + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, + &laure.getStats().number_of_participant_devices_media_capability_changed, + laure_stat2.number_of_participant_devices_media_capability_changed + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_EQUAL(focus.getStats().number_of_participants_added, focus_stat2.number_of_participants_added, + int, "%0d"); + BC_ASSERT_EQUAL(focus.getStats().number_of_participant_devices_added, + focus_stat2.number_of_participant_devices_added, int, "%0d"); + BC_ASSERT_EQUAL(focus.getStats().number_of_participant_devices_joined, + focus_stat2.number_of_participant_devices_joined, int, "%0d"); + BC_ASSERT_EQUAL(marie.getStats().number_of_participants_added, marie_stat2.number_of_participants_added, + int, "%0d"); + BC_ASSERT_EQUAL(marie.getStats().number_of_participant_devices_added, + marie_stat2.number_of_participant_devices_added, int, "%0d"); + BC_ASSERT_EQUAL(marie.getStats().number_of_participant_devices_joined, + marie_stat2.number_of_participant_devices_joined, int, "%0d"); + BC_ASSERT_EQUAL(pauline.getStats().number_of_participants_added, + pauline_stat2.number_of_participants_added, int, "%0d"); + BC_ASSERT_EQUAL(pauline.getStats().number_of_participant_devices_added, + pauline_stat2.number_of_participant_devices_added, int, "%0d"); + BC_ASSERT_EQUAL(pauline.getStats().number_of_participant_devices_joined, + pauline_stat2.number_of_participant_devices_joined, int, "%0d"); + BC_ASSERT_EQUAL(laure.getStats().number_of_participants_added, laure_stat2.number_of_participants_added, + int, "%0d"); + BC_ASSERT_EQUAL(laure.getStats().number_of_participant_devices_added, + laure_stat2.number_of_participant_devices_added, int, "%0d"); + BC_ASSERT_EQUAL(laure.getStats().number_of_participant_devices_joined, + laure_stat2.number_of_participant_devices_joined, int, "%0d"); + + for (auto mgr : conferenceMgrs) { + LinphoneAddress *uri = linphone_address_new(linphone_core_get_identity(mgr->lc)); + LinphoneConference *pconference = + linphone_core_search_conference(mgr->lc, NULL, uri, confAddr, NULL); + linphone_address_unref(uri); + BC_ASSERT_PTR_NOT_NULL(pconference); + if (pconference) { + LinphoneParticipant *p = + (mgr == pauline.getCMgr()) + ? linphone_conference_get_me(pconference) + : linphone_conference_find_participant(pconference, pauline.getCMgr()->identity); + BC_ASSERT_PTR_NOT_NULL(p); + if (p) { + bctbx_list_t *devices = linphone_participant_get_devices(p); + for (bctbx_list_t *itd = devices; itd; itd = bctbx_list_next(itd)) { + LinphoneParticipantDevice *d = (LinphoneParticipantDevice *)bctbx_list_get_data(itd); + LinphoneMediaDirection expected_video_direction = video_direction; + if (enable == TRUE) { + if ((video_direction == LinphoneMediaDirectionRecvOnly) && + (layout == LinphoneConferenceLayoutGrid)) { + expected_video_direction = LinphoneMediaDirectionSendOnly; + } else { + expected_video_direction = video_direction; + } + } else { + expected_video_direction = LinphoneMediaDirectionInactive; + } + BC_ASSERT_EQUAL( + linphone_participant_device_get_stream_capability(d, LinphoneStreamTypeVideo), + expected_video_direction, int, "%0d"); + } + if (devices) { + bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); + } + } + } + } + + if (pauline_call) { + const LinphoneCallParams *call_lparams = linphone_call_get_params(pauline_call); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_lparams), enable, int, "%0d"); + const LinphoneCallParams *call_rparams = linphone_call_get_remote_params(pauline_call); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_rparams), enable, int, "%0d"); + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(pauline_call); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_cparams), enable, int, "%0d"); + } + if (focus_call) { + const LinphoneCallParams *call_lparams = linphone_call_get_params(focus_call); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_lparams), enable, int, "%0d"); + const LinphoneCallParams *call_rparams = linphone_call_get_remote_params(focus_call); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_rparams), enable, int, "%0d"); + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(focus_call); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_cparams), enable, int, "%0d"); + } + + if (paulineConference) { + bctbx_list_t *devices = linphone_conference_get_participant_device_list(paulineConference); + for (bctbx_list_t *itd = devices; itd; itd = bctbx_list_next(itd)) { + LinphoneParticipantDevice *d = (LinphoneParticipantDevice *)bctbx_list_get_data(itd); + if (enable) { + if (linphone_conference_is_me(paulineConference, + linphone_participant_device_get_address(d))) { + BC_ASSERT_TRUE( + linphone_participant_device_get_stream_availability(d, LinphoneStreamTypeVideo) == + (new_video_direction == LinphoneMediaDirectionSendRecv)); + } else { + BC_ASSERT_TRUE( + linphone_participant_device_get_stream_availability(d, LinphoneStreamTypeVideo) == + (linphone_participant_device_get_stream_capability(d, LinphoneStreamTypeVideo) == + LinphoneMediaDirectionSendRecv)); + } + } else { + BC_ASSERT_FALSE( + linphone_participant_device_get_stream_availability(d, LinphoneStreamTypeVideo)); + } + } + + if (devices) { + bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); + } + + stats focus_stat3 = focus.getStats(); + stats marie_stat3 = marie.getStats(); + stats pauline_stat3 = pauline.getStats(); + stats laure_stat3 = laure.getStats(); + + LinphoneCall *pauline_call = linphone_core_get_call_by_remote_address2(pauline.getLc(), confAddr); + BC_ASSERT_PTR_NOT_NULL(pauline_call); + + bool_t video_enabled = FALSE; + if (pauline_call) { + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(pauline_call); + video_enabled = linphone_call_params_video_enabled(call_cparams); + } + + linphone_conference_leave(paulineConference); + + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneCallPausing, + pauline_stat3.number_of_LinphoneCallPausing + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneCallPaused, + pauline_stat3.number_of_LinphoneCallPaused + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallPausedByRemote, + focus_stat3.number_of_LinphoneCallPausedByRemote + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_on_hold, + focus_stat3.number_of_participant_devices_on_hold + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_participant_devices_on_hold, + laure_stat3.number_of_participant_devices_on_hold + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list( + coresList, &laure.getStats().number_of_participant_devices_media_capability_changed, + laure_stat3.number_of_participant_devices_media_capability_changed + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_participant_devices_on_hold, + marie_stat3.number_of_participant_devices_on_hold + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list( + coresList, &marie.getStats().number_of_participant_devices_media_capability_changed, + marie_stat3.number_of_participant_devices_media_capability_changed + 1, + liblinphone_tester_sip_timeout)); + + for (auto mgr : conferenceMgrs) { + LinphoneAddress *uri = linphone_address_new(linphone_core_get_identity(mgr->lc)); + LinphoneConference *pconference = + linphone_core_search_conference(mgr->lc, NULL, uri, confAddr, NULL); + linphone_address_unref(uri); + BC_ASSERT_PTR_NOT_NULL(pconference); + if (pconference) { + if (mgr == pauline.getCMgr()) { + BC_ASSERT_FALSE(linphone_conference_is_in(pconference)); + } else { + LinphoneParticipant *participant = + linphone_conference_find_participant(pconference, pauline.getCMgr()->identity); + BC_ASSERT_PTR_NOT_NULL(participant); + if (participant) { + bctbx_list_t *devices = linphone_participant_get_devices(participant); + for (bctbx_list_t *it_d = devices; it_d != NULL; it_d = it_d->next) { + LinphoneParticipantDevice *d = (LinphoneParticipantDevice *)it_d->data; + BC_ASSERT_PTR_NOT_NULL(d); + if (d) { + BC_ASSERT_EQUAL(linphone_participant_device_get_state(d), + LinphoneParticipantDeviceStateOnHold, int, "%0d"); + } + } + bctbx_list_free_with_data(devices, + (void (*)(void *))linphone_participant_device_unref); + } + } + } + } + + linphone_conference_enter(paulineConference); + + int participant_streams_running = 0; + int pauline_streams_running = 0; + int focus_streams_running = 0; + if (video_direction == LinphoneMediaDirectionRecvOnly) { + if (layout == LinphoneConferenceLayoutGrid) { + /* + * If the participant video direction is set to RecvOnly, the conference server will see + * it as if everybody had disabled the video streams. The test explicitely changes + * Pauline's video direction to SendRecv to trigger events such as media capability and + * availability changed Leaving and rejoining a conference, therefore, triggers media + * events on participant devices only when Pauline enables video capabilities with + * direction SendRecv + */ + if (enable) { + participant_streams_running = 1; + focus_streams_running = static_cast<int>(members.size() + 1); + pauline_streams_running = 2; + } else { + focus_streams_running = 1; + pauline_streams_running = 1; + } + } else if (layout == LinphoneConferenceLayoutActiveSpeaker) { + focus_streams_running = 1; + pauline_streams_running = 1; + } + } else { + participant_streams_running = ((enable) ? 1 : 0); + focus_streams_running = static_cast<int>((enable) ? (members.size() + 1) : 1); + pauline_streams_running = ((enable) ? 2 : 1); + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneCallResuming, + pauline_stat3.number_of_LinphoneCallResuming + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(coresList, &pauline.getStats().number_of_LinphoneCallStreamsRunning, + (pauline_stat3.number_of_LinphoneCallStreamsRunning + pauline_streams_running), + liblinphone_tester_sip_timeout)); + // 2 streams running for Pauline and one for each participant + BC_ASSERT_TRUE( + wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallStreamsRunning, + focus_stat3.number_of_LinphoneCallStreamsRunning + focus_streams_running, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_joined, + focus_stat3.number_of_participant_devices_joined + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_participant_devices_joined, + laure_stat3.number_of_participant_devices_joined + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list( + coresList, &laure.getStats().number_of_participant_devices_media_capability_changed, + laure_stat3.number_of_participant_devices_media_capability_changed + 2, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE( + wait_for_list(coresList, &laure.getStats().number_of_LinphoneCallStreamsRunning, + laure_stat3.number_of_LinphoneCallStreamsRunning + participant_streams_running, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_participant_devices_joined, + marie_stat3.number_of_participant_devices_joined + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list( + coresList, &marie.getStats().number_of_participant_devices_media_capability_changed, + marie_stat3.number_of_participant_devices_media_capability_changed + 2, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallStreamsRunning, + marie_stat3.number_of_LinphoneCallStreamsRunning + participant_streams_running, + liblinphone_tester_sip_timeout)); + if (pauline_call) { + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(pauline_call); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_cparams), video_enabled, int, "%0d"); + } + + for (auto mgr : conferenceMgrs) { + LinphoneAddress *uri = linphone_address_new(linphone_core_get_identity(mgr->lc)); + LinphoneConference *pconference = + linphone_core_search_conference(mgr->lc, NULL, uri, confAddr, NULL); + linphone_address_unref(uri); + BC_ASSERT_PTR_NOT_NULL(pconference); + if (pconference) { + if (mgr == pauline.getCMgr()) { + BC_ASSERT_TRUE(linphone_conference_is_in(pconference)); + } else { + LinphoneParticipant *participant = + linphone_conference_find_participant(pconference, pauline.getCMgr()->identity); + BC_ASSERT_PTR_NOT_NULL(participant); + if (participant) { + bctbx_list_t *devices = linphone_participant_get_devices(participant); + for (bctbx_list_t *it_d = devices; it_d != NULL; it_d = it_d->next) { + LinphoneParticipantDevice *d = (LinphoneParticipantDevice *)it_d->data; + BC_ASSERT_PTR_NOT_NULL(d); + if (d) { + BC_ASSERT_EQUAL(linphone_participant_device_get_state(d), + LinphoneParticipantDeviceStatePresent, int, "%0d"); + } + } + bctbx_list_free_with_data(devices, + (void (*)(void *))linphone_participant_device_unref); + } + } + } + } + } + // Wait a little bit + wait_for_list(coresList, NULL, 0, 1000); + + enable = !enable; + } + } + + std::list<LinphoneCoreManager *> extraParticipantMgrs; + int no_local_participants = 3; + if (uninvited_participant_dials) { + stats marie_stat2 = marie.getStats(); + stats focus_stat2 = focus.getStats(); + stats pauline_stat2 = pauline.getStats(); + stats laure_stat2 = laure.getStats(); + + extraParticipantMgrs.push_back(michelle.getCMgr()); + + ms_message("%s is entering conference %s", linphone_core_get_identity(michelle.getLc()), + conference_address_str); + + LinphoneCallParams *params = linphone_core_create_call_params(michelle.getLc(), nullptr); + LinphoneCall *michelle_call = linphone_core_invite_address_with_params(michelle.getLc(), confAddr, params); + BC_ASSERT_PTR_NOT_NULL(michelle_call); + linphone_call_params_unref(params); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_LinphoneCallOutgoingProgress, 1, + liblinphone_tester_sip_timeout)); + int extra_participants = 0; + if (participant_list_type == LinphoneConferenceParticipantListTypeOpen) { + + if (network_restart) { + ms_message("%s switches off network before %s is added to conference %s", + linphone_core_get_identity(marie.getLc()), linphone_core_get_identity(michelle.getLc()), + conference_address_str); + linphone_core_set_network_reachable(marie.getLc(), FALSE); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneSubscriptionTerminated, + marie_stat2.number_of_LinphoneSubscriptionTerminated + 1, + liblinphone_tester_sip_timeout)); + } + + conferenceMgrs.push_back(michelle.getCMgr()); + members.push_back(michelle.getCMgr()); + + extra_participants = 1; + + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_LinphoneCallStreamsRunning, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_LinphoneCallUpdating, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_LinphoneCallStreamsRunning, 2, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_LinphoneConferenceStateCreated, + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list( + coresList, &michelle.getStats().number_of_LinphoneSubscriptionOutgoingProgress, 1, 5000)); + BC_ASSERT_TRUE( + wait_for_list(coresList, &michelle.getStats().number_of_LinphoneSubscriptionActive, 1, 5000)); + BC_ASSERT_TRUE(wait_for_list( + coresList, &focus.getStats().number_of_LinphoneSubscriptionIncomingReceived, + focus_stat2.number_of_LinphoneSubscriptionIncomingReceived + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneSubscriptionActive, + focus_stat2.number_of_LinphoneSubscriptionActive + 1, + liblinphone_tester_sip_timeout)); + + if (enable_video) { + if ((audio_only_participant == FALSE) && ((video_direction != LinphoneMediaDirectionRecvOnly) || + (layout == LinphoneConferenceLayoutActiveSpeaker))) { + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneCallUpdating, + pauline_stat2.number_of_LinphoneCallUpdating + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list( + coresList, &pauline.getStats().number_of_LinphoneCallStreamsRunning, + pauline_stat2.number_of_LinphoneCallStreamsRunning + 1, liblinphone_tester_sip_timeout)); + } + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_LinphoneCallUpdating, + laure_stat2.number_of_LinphoneCallUpdating + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_LinphoneCallStreamsRunning, + laure_stat2.number_of_LinphoneCallStreamsRunning + 1, + liblinphone_tester_sip_timeout)); + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participants_added, + focus_stat2.number_of_participants_added + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_added, + focus_stat2.number_of_participant_devices_added + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_joined, + focus_stat2.number_of_participant_devices_joined + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_participants_added, + pauline_stat2.number_of_participants_added + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_participant_devices_added, + pauline_stat2.number_of_participant_devices_added + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_participant_devices_joined, + pauline_stat2.number_of_participant_devices_joined + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_participants_added, + laure_stat2.number_of_participants_added + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_participant_devices_added, + laure_stat2.number_of_participant_devices_added + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_participant_devices_joined, + laure_stat2.number_of_participant_devices_joined + 1, + liblinphone_tester_sip_timeout)); + + if (network_restart) { + ms_message("%s is back online after %s is added to conference %s", + linphone_core_get_identity(marie.getLc()), linphone_core_get_identity(michelle.getLc()), + conference_address_str); + linphone_core_set_network_reachable(marie.getLc(), TRUE); + BC_ASSERT_TRUE(wait_for_list(coresList, + &marie.getStats().number_of_LinphoneSubscriptionOutgoingProgress, + marie_stat2.number_of_LinphoneSubscriptionOutgoingProgress + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, + &focus.getStats().number_of_LinphoneSubscriptionIncomingReceived, + focus_stat2.number_of_LinphoneSubscriptionIncomingReceived + 2, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneSubscriptionActive, + marie_stat2.number_of_LinphoneSubscriptionActive + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneSubscriptionActive, + focus_stat2.number_of_LinphoneSubscriptionActive + 2, + liblinphone_tester_sip_timeout)); + } + + if (enable_video) { + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallUpdating, + marie_stat2.number_of_LinphoneCallUpdating + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallStreamsRunning, + marie_stat2.number_of_LinphoneCallStreamsRunning + 1, + liblinphone_tester_sip_timeout)); + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_participants_added, + marie_stat2.number_of_participants_added + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_participant_devices_added, + marie_stat2.number_of_participant_devices_added + 1, + liblinphone_tester_sip_timeout)); + if (!network_restart) { + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_participant_devices_joined, + marie_stat2.number_of_participant_devices_joined + 1, + liblinphone_tester_sip_timeout)); + } + + std::map<LinphoneCoreManager *, LinphoneParticipantRole> memberList; + for (const auto &member : members) { + try { + const auto &participantInfo = participantList.at(member); + memberList.insert(std::make_pair(member, participantInfo)); + } catch (std::out_of_range &) { + if (member == marie.getCMgr()) { + memberList.insert(std::make_pair(member, LinphoneParticipantRoleSpeaker)); + } else if (member == michelle.getCMgr()) { + memberList.insert(std::make_pair(member, LinphoneParticipantRoleListener)); + } else { + ms_fatal("Unable to find active participant %s in the participant list", + linphone_core_get_identity(member->lc)); + } + } + } + wait_for_conference_streams({focus, marie, pauline, laure, michelle, berthe}, conferenceMgrs, + focus.getCMgr(), memberList, confAddr, enable_video); + } else if (participant_list_type == LinphoneConferenceParticipantListTypeClosed) { + extra_participants = 0; + + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_LinphoneCallError, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_LinphoneCallReleased, 1, + liblinphone_tester_sip_timeout)); + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, laure, michelle, berthe}).waitUntil(chrono::seconds(2), [] { + return false; + }); + + BC_ASSERT_EQUAL(michelle.getStats().number_of_LinphoneConferenceStateCreated, 0, int, "%0d"); + BC_ASSERT_EQUAL(michelle.getStats().number_of_LinphoneSubscriptionOutgoingProgress, 0, int, "%0d"); + BC_ASSERT_EQUAL(michelle.getStats().number_of_LinphoneSubscriptionActive, 0, int, "%0d"); + BC_ASSERT_EQUAL(focus.getStats().number_of_participants_added, focus_stat2.number_of_participants_added, + int, "%0d"); + BC_ASSERT_EQUAL(focus.getStats().number_of_participant_devices_added, + focus_stat2.number_of_participant_devices_added, int, "%0d"); + BC_ASSERT_EQUAL(focus.getStats().number_of_participant_devices_joined, + focus_stat2.number_of_participant_devices_joined, int, "%0d"); + BC_ASSERT_EQUAL(marie.getStats().number_of_participants_added, marie_stat2.number_of_participants_added, + int, "%0d"); + BC_ASSERT_EQUAL(marie.getStats().number_of_participant_devices_added, + marie_stat2.number_of_participant_devices_added, int, "%0d"); + BC_ASSERT_EQUAL(marie.getStats().number_of_participant_devices_joined, + marie_stat2.number_of_participant_devices_joined, int, "%0d"); + BC_ASSERT_EQUAL(pauline.getStats().number_of_participants_added, + pauline_stat2.number_of_participants_added, int, "%0d"); + BC_ASSERT_EQUAL(pauline.getStats().number_of_participant_devices_added, + pauline_stat2.number_of_participant_devices_added, int, "%0d"); + BC_ASSERT_EQUAL(pauline.getStats().number_of_participant_devices_joined, + pauline_stat2.number_of_participant_devices_joined, int, "%0d"); + BC_ASSERT_EQUAL(laure.getStats().number_of_participants_added, laure_stat2.number_of_participants_added, + int, "%0d"); + BC_ASSERT_EQUAL(laure.getStats().number_of_participant_devices_added, + laure_stat2.number_of_participant_devices_added, int, "%0d"); + BC_ASSERT_EQUAL(laure.getStats().number_of_participant_devices_joined, + laure_stat2.number_of_participant_devices_joined, int, "%0d"); + } + + for (auto mgr : conferenceMgrs) { + LinphoneAddress *uri = linphone_address_new(linphone_core_get_identity(mgr->lc)); + LinphoneConference *pconference = linphone_core_search_conference(mgr->lc, NULL, uri, confAddr, NULL); + linphone_address_unref(uri); + if (participant_list_type == LinphoneConferenceParticipantListTypeOpen) { + BC_ASSERT_PTR_NOT_NULL(pconference); + } else if (mgr == michelle.getCMgr()) { + BC_ASSERT_PTR_NULL(pconference); + } + if (pconference) { + const LinphoneConferenceParams *conference_params = + linphone_conference_get_current_params(pconference); + int no_participants = 0; + if (start_time >= 0) { + BC_ASSERT_EQUAL((long long)linphone_conference_params_get_start_time(conference_params), + (long long)start_time, long long, "%lld"); + } + BC_ASSERT_EQUAL((long long)linphone_conference_params_get_end_time(conference_params), + (long long)end_time, long long, "%lld"); + if (mgr == focus.getCMgr()) { + no_participants = no_local_participants + extra_participants; + BC_ASSERT_FALSE(linphone_conference_is_in(pconference)); + } else { + // Substracting one because we conference server is not in the conference + no_participants = (no_local_participants - 1) + extra_participants; + BC_ASSERT_TRUE(linphone_conference_is_in(pconference)); + + LinphoneCall *pcall = linphone_core_get_call_by_remote_address2(mgr->lc, confAddr); + BC_ASSERT_PTR_NOT_NULL(pcall); + + int no_streams_audio = 1; + int no_streams_video = 0; + int no_active_streams_video = 0; + int no_streams_text = 0; + if (pcall) { + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(pcall); + const bool_t enabled = linphone_call_params_video_enabled(call_cparams); + no_streams_video = + (enabled && (participant_list_type == LinphoneConferenceParticipantListTypeOpen)) ? 5 + : (enable_video) ? 4 + : 0; + no_active_streams_video = + static_cast<int>(compute_no_video_streams(enabled, pcall, pconference)); + _linphone_call_check_max_nb_streams(pcall, no_streams_audio, no_streams_video, + no_streams_text); + _linphone_call_check_nb_active_streams(pcall, no_streams_audio, no_active_streams_video, + no_streams_text); + } + + LinphoneCall *fcall = linphone_core_get_call_by_remote_address2(focus.getLc(), mgr->identity); + BC_ASSERT_PTR_NOT_NULL(fcall); + if (fcall) { + _linphone_call_check_max_nb_streams(fcall, no_streams_audio, no_streams_video, + no_streams_text); + _linphone_call_check_nb_active_streams(fcall, no_streams_audio, no_active_streams_video, + no_streams_text); + } + } + BC_ASSERT_EQUAL(linphone_conference_get_participant_count(pconference), no_participants, int, + "%0d"); + bctbx_list_t *devices = linphone_conference_get_participant_device_list(pconference); + BC_ASSERT_EQUAL(bctbx_list_size(devices), + static_cast<size_t>(no_local_participants + extra_participants), size_t, "%zu"); + if (devices) { + bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); + } + BC_ASSERT_STRING_EQUAL(linphone_conference_get_subject(pconference), initialSubject); + } + } + } + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, laure, michelle, berthe}).waitUntil(chrono::seconds(2), [] { + return false; + }); + + stats marie_stat = marie.getStats(); + + std::list<LinphoneCoreManager *> mgrsToRemove{pauline.getCMgr()}; + if (remove_participant) { + stats pauline_stat = pauline.getStats(); + stats michelle_stat = michelle.getStats(); + + LinphoneAddress *uri = linphone_address_new(linphone_core_get_identity(marie.getLc())); + LinphoneConference *pconference = linphone_core_search_conference(marie.getLc(), NULL, uri, confAddr, NULL); + linphone_address_unref(uri); + + ms_message("%s is removing %s from conference %s", linphone_core_get_identity(marie.getLc()), + linphone_core_get_identity(laure.getLc()), conference_address_str); + + BC_ASSERT_PTR_NOT_NULL(pconference); + LinphoneAddress *puri = linphone_address_new(linphone_core_get_identity(laure.getLc())); + if (pconference) { + LinphoneParticipant *participant = linphone_conference_find_participant(pconference, puri); + BC_ASSERT_PTR_NOT_NULL(participant); + linphone_conference_remove_participant_2(pconference, participant); + } + + LinphoneCoreManager *laureMgr = laure.getCMgr(); + auto itConferenceMgrs = std::find(conferenceMgrs.begin(), conferenceMgrs.end(), laureMgr); + if (itConferenceMgrs != conferenceMgrs.end()) { + conferenceMgrs.erase(itConferenceMgrs); + } + + auto itMembers = std::find(members.begin(), members.end(), laureMgr); + if (itMembers != members.end()) { + members.erase(itMembers); + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_LinphoneCallEnd, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_LinphoneCallReleased, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_LinphoneSubscriptionTerminated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, + &laure.getStats().number_of_LinphoneConferenceStateTerminationPending, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_LinphoneConferenceStateTerminated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_LinphoneConferenceStateDeleted, 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participants_removed, + focus_stat.number_of_participants_removed + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_removed, + focus_stat.number_of_participant_devices_removed + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_participants_removed, + marie_stat.number_of_participants_removed + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_participant_devices_removed, + marie_stat.number_of_participant_devices_removed + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_participants_removed, + pauline_stat.number_of_participants_removed + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_participant_devices_removed, + pauline_stat.number_of_participant_devices_removed + 1, + liblinphone_tester_sip_timeout)); + + if (enable_video) { + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallUpdating, + marie_stat.number_of_LinphoneCallUpdating + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallStreamsRunning, + marie_stat.number_of_LinphoneCallStreamsRunning + 1, + liblinphone_tester_sip_timeout)); + + if ((audio_only_participant == FALSE) && ((video_direction != LinphoneMediaDirectionRecvOnly) || + (layout == LinphoneConferenceLayoutActiveSpeaker))) { + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneCallUpdating, + pauline_stat.number_of_LinphoneCallUpdating + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneCallStreamsRunning, + pauline_stat.number_of_LinphoneCallStreamsRunning + 1, + liblinphone_tester_sip_timeout)); + } + } + + if ((uninvited_participant_dials) && (participant_list_type == LinphoneConferenceParticipantListTypeOpen)) { + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_participants_removed, + michelle_stat.number_of_participants_removed + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_participant_devices_removed, + michelle_stat.number_of_participant_devices_removed + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_LinphoneCallUpdating, + michelle_stat.number_of_LinphoneCallUpdating + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_LinphoneCallStreamsRunning, + michelle_stat.number_of_LinphoneCallStreamsRunning + 1, + liblinphone_tester_sip_timeout)); + } + + LinphoneConference *conference = linphone_core_search_conference(laure.getLc(), NULL, puri, confAddr, NULL); + BC_ASSERT_PTR_NULL(conference); + linphone_address_unref(puri); + + no_local_participants = 3; + if (uninvited_participant_dials) { + stats marie_stat2 = marie.getStats(); + stats focus_stat2 = focus.getStats(); + stats pauline_stat2 = pauline.getStats(); + stats michelle_stat2 = michelle.getStats(); + + extraParticipantMgrs.push_back(berthe.getCMgr()); + conferenceMgrs.push_back(berthe.getCMgr()); + members.push_back(berthe.getCMgr()); + ms_message("%s is entering conference %s", linphone_core_get_identity(berthe.getLc()), + conference_address_str); + + LinphoneCallParams *params = linphone_core_create_call_params(berthe.getLc(), nullptr); + LinphoneCall *berthe_call = linphone_core_invite_address_with_params(berthe.getLc(), confAddr, params); + BC_ASSERT_PTR_NOT_NULL(berthe_call); + linphone_call_params_unref(params); + BC_ASSERT_TRUE(wait_for_list(coresList, &berthe.getStats().number_of_LinphoneCallOutgoingProgress, 1, + liblinphone_tester_sip_timeout)); + int extra_participants = 0; + if (participant_list_type == LinphoneConferenceParticipantListTypeOpen) { + extra_participants = 1; + + BC_ASSERT_TRUE(wait_for_list(coresList, &berthe.getStats().number_of_LinphoneCallStreamsRunning, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &berthe.getStats().number_of_LinphoneCallUpdating, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &berthe.getStats().number_of_LinphoneCallStreamsRunning, 2, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &berthe.getStats().number_of_LinphoneConferenceStateCreated, + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list( + coresList, &berthe.getStats().number_of_LinphoneSubscriptionOutgoingProgress, 1, 5000)); + BC_ASSERT_TRUE( + wait_for_list(coresList, &berthe.getStats().number_of_LinphoneSubscriptionActive, 1, 5000)); + + if ((audio_only_participant == FALSE) && ((video_direction != LinphoneMediaDirectionRecvOnly) || + (layout == LinphoneConferenceLayoutActiveSpeaker))) { + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneCallUpdating, + pauline_stat2.number_of_LinphoneCallUpdating + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list( + coresList, &pauline.getStats().number_of_LinphoneCallStreamsRunning, + pauline_stat2.number_of_LinphoneCallStreamsRunning + 1, liblinphone_tester_sip_timeout)); + } + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallUpdating, + marie_stat2.number_of_LinphoneCallUpdating + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallStreamsRunning, + marie_stat2.number_of_LinphoneCallStreamsRunning + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_LinphoneCallUpdating, + michelle_stat2.number_of_LinphoneCallUpdating + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_LinphoneCallStreamsRunning, + michelle_stat2.number_of_LinphoneCallStreamsRunning + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list( + coresList, &focus.getStats().number_of_LinphoneCallUpdatedByRemote, + focus_stat2.number_of_LinphoneCallUpdatedByRemote + (audio_only_participant) ? 3 : 4, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list( + coresList, &focus.getStats().number_of_LinphoneCallStreamsRunning, + focus_stat2.number_of_LinphoneCallStreamsRunning + (audio_only_participant) ? 4 : 5, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participants_added, + focus_stat2.number_of_participants_added + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_added, + focus_stat2.number_of_participant_devices_added + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_joined, + focus_stat2.number_of_participant_devices_joined + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_participants_added, + marie_stat2.number_of_participants_added + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_participant_devices_added, + marie_stat2.number_of_participant_devices_added + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_participant_devices_joined, + marie_stat2.number_of_participant_devices_joined + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_participants_added, + pauline_stat2.number_of_participants_added + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_participant_devices_added, + pauline_stat2.number_of_participant_devices_added + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_participant_devices_joined, + pauline_stat2.number_of_participant_devices_joined + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_participants_added, + michelle_stat2.number_of_participants_added + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_participant_devices_added, + michelle_stat2.number_of_participant_devices_added + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_participant_devices_joined, + michelle_stat2.number_of_participant_devices_joined + 1, + liblinphone_tester_sip_timeout)); + } else if (participant_list_type == LinphoneConferenceParticipantListTypeClosed) { + extra_participants = 0; + + BC_ASSERT_TRUE(wait_for_list(coresList, &berthe.getStats().number_of_LinphoneCallError, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &berthe.getStats().number_of_LinphoneCallReleased, 1, + liblinphone_tester_sip_timeout)); + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, laure, michelle, berthe}) + .waitUntil(chrono::seconds(2), [] { return false; }); + + BC_ASSERT_EQUAL(berthe.getStats().number_of_LinphoneConferenceStateCreated, 0, int, "%0d"); + BC_ASSERT_EQUAL(berthe.getStats().number_of_LinphoneSubscriptionOutgoingProgress, 0, int, "%0d"); + BC_ASSERT_EQUAL(berthe.getStats().number_of_LinphoneSubscriptionActive, 0, int, "%0d"); + BC_ASSERT_EQUAL(focus.getStats().number_of_participants_added, + focus_stat2.number_of_participants_added, int, "%0d"); + BC_ASSERT_EQUAL(focus.getStats().number_of_participant_devices_added, + focus_stat2.number_of_participant_devices_added, int, "%0d"); + BC_ASSERT_EQUAL(focus.getStats().number_of_participant_devices_joined, + focus_stat2.number_of_participant_devices_joined, int, "%0d"); + BC_ASSERT_EQUAL(marie.getStats().number_of_participants_added, + marie_stat2.number_of_participants_added, int, "%0d"); + BC_ASSERT_EQUAL(marie.getStats().number_of_participant_devices_added, + marie_stat2.number_of_participant_devices_added, int, "%0d"); + BC_ASSERT_EQUAL(marie.getStats().number_of_participant_devices_joined, + marie_stat2.number_of_participant_devices_joined, int, "%0d"); + BC_ASSERT_EQUAL(pauline.getStats().number_of_participants_added, + pauline_stat2.number_of_participants_added, int, "%0d"); + BC_ASSERT_EQUAL(pauline.getStats().number_of_participant_devices_added, + pauline_stat2.number_of_participant_devices_added, int, "%0d"); + BC_ASSERT_EQUAL(pauline.getStats().number_of_participant_devices_joined, + pauline_stat2.number_of_participant_devices_joined, int, "%0d"); + BC_ASSERT_EQUAL(michelle.getStats().number_of_participants_added, + michelle_stat2.number_of_participants_added, int, "%0d"); + BC_ASSERT_EQUAL(michelle.getStats().number_of_participant_devices_added, + michelle_stat2.number_of_participant_devices_added, int, "%0d"); + BC_ASSERT_EQUAL(michelle.getStats().number_of_participant_devices_joined, + michelle_stat2.number_of_participant_devices_joined, int, "%0d"); + } + + for (auto mgr : conferenceMgrs) { + LinphoneAddress *uri = linphone_address_new(linphone_core_get_identity(mgr->lc)); + LinphoneConference *pconference = + linphone_core_search_conference(mgr->lc, NULL, uri, confAddr, NULL); + linphone_address_unref(uri); + if ((participant_list_type == LinphoneConferenceParticipantListTypeOpen) || + ((mgr != berthe.getCMgr()) && (mgr != michelle.getCMgr()))) { + BC_ASSERT_PTR_NOT_NULL(pconference); + } else if ((mgr == berthe.getCMgr()) || (mgr == michelle.getCMgr())) { + BC_ASSERT_PTR_NULL(pconference); + } + if (pconference) { + const LinphoneConferenceParams *conference_params = + linphone_conference_get_current_params(pconference); + int no_participants = 0; + if (start_time >= 0) { + BC_ASSERT_EQUAL((long long)linphone_conference_params_get_start_time(conference_params), + (long long)start_time, long long, "%lld"); + } + BC_ASSERT_EQUAL((long long)linphone_conference_params_get_end_time(conference_params), + (long long)end_time, long long, "%lld"); + if (mgr == focus.getCMgr()) { + no_participants = no_local_participants + extra_participants; + BC_ASSERT_FALSE(linphone_conference_is_in(pconference)); + } else { + LinphoneCall *pcall = linphone_core_get_call_by_remote_address2(mgr->lc, confAddr); + BC_ASSERT_PTR_NOT_NULL(pcall); + + int no_streams_audio = 1; + int no_streams_video = 0; + int no_active_streams_video = 0; + int no_streams_text = 0; + if (pcall) { + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(pcall); + const bool_t enabled = linphone_call_params_video_enabled(call_cparams); + no_streams_video = + (enabled && (participant_list_type == LinphoneConferenceParticipantListTypeOpen)) + ? 5 + : (enable_video) ? 4 + : 0; + no_active_streams_video = + static_cast<int>(compute_no_video_streams(enabled, pcall, pconference)); + _linphone_call_check_max_nb_streams(pcall, no_streams_audio, no_streams_video, + no_streams_text); + _linphone_call_check_nb_active_streams(pcall, no_streams_audio, no_active_streams_video, + no_streams_text); + } + + LinphoneCall *fcall = + linphone_core_get_call_by_remote_address2(focus.getLc(), mgr->identity); + BC_ASSERT_PTR_NOT_NULL(fcall); + if (fcall) { + _linphone_call_check_max_nb_streams(fcall, no_streams_audio, no_streams_video, + no_streams_text); + _linphone_call_check_nb_active_streams(fcall, no_streams_audio, no_active_streams_video, + no_streams_text); + } + + // Substracting one because we conference server is not in the conference + no_participants = (no_local_participants - 1) + extra_participants; + BC_ASSERT_TRUE(linphone_conference_is_in(pconference)); + } + BC_ASSERT_EQUAL(linphone_conference_get_participant_count(pconference), no_participants, int, + "%0d"); + bctbx_list_t *devices = linphone_conference_get_participant_device_list(pconference); + BC_ASSERT_EQUAL(bctbx_list_size(devices), + static_cast<size_t>(no_local_participants + extra_participants), size_t, "%zu"); + if (devices) { + bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); + } + BC_ASSERT_STRING_EQUAL(linphone_conference_get_subject(pconference), initialSubject); + } + } + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, laure, michelle, berthe}).waitUntil(chrono::seconds(2), [] { + return false; + }); + } + + } else { + mgrsToRemove.push_back(laure.getCMgr()); + } + + LinphoneAddress *paulineUri = linphone_address_new(linphone_core_get_identity(pauline.getLc())); + LinphoneConference *paulineConference = + linphone_core_search_conference(pauline.getLc(), NULL, paulineUri, confAddr, NULL); + linphone_address_unref(paulineUri); + BC_ASSERT_PTR_NOT_NULL(paulineConference); + + if (paulineConference) { + stats focus_stat2 = focus.getStats(); + stats marie_stat2 = marie.getStats(); + stats pauline_stat2 = pauline.getStats(); + stats laure_stat2 = laure.getStats(); + stats michelle_stat2 = michelle.getStats(); + + LinphoneCall *pauline_call = linphone_core_get_call_by_remote_address2(pauline.getLc(), confAddr); + BC_ASSERT_PTR_NOT_NULL(pauline_call); + + bool_t video_enabled = FALSE; + if (pauline_call) { + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(pauline_call); + video_enabled = linphone_call_params_video_enabled(call_cparams); + } + + linphone_conference_leave(paulineConference); + + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneCallPausing, + pauline_stat2.number_of_LinphoneCallPausing + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneCallPaused, + pauline_stat2.number_of_LinphoneCallPaused + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallPausedByRemote, + focus_stat2.number_of_LinphoneCallPausedByRemote + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_on_hold, + focus_stat2.number_of_participant_devices_on_hold + 1, + liblinphone_tester_sip_timeout)); + + if (!remove_participant) { + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_participant_devices_on_hold, + laure_stat2.number_of_participant_devices_on_hold + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, + &laure.getStats().number_of_participant_devices_media_capability_changed, + laure_stat2.number_of_participant_devices_media_capability_changed + 1, + liblinphone_tester_sip_timeout)); + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_participant_devices_on_hold, + marie_stat2.number_of_participant_devices_on_hold + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, + &marie.getStats().number_of_participant_devices_media_capability_changed, + marie_stat2.number_of_participant_devices_media_capability_changed + 1, + liblinphone_tester_sip_timeout)); + + if (uninvited_participant_dials && (participant_list_type == LinphoneConferenceParticipantListTypeOpen)) { + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_participant_devices_on_hold, + michelle_stat2.number_of_participant_devices_on_hold + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list( + coresList, &michelle.getStats().number_of_participant_devices_media_capability_changed, + michelle_stat2.number_of_participant_devices_media_capability_changed + 1, + liblinphone_tester_sip_timeout)); + } + + linphone_conference_enter(paulineConference); + + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneCallResuming, + pauline_stat2.number_of_LinphoneCallResuming + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneCallStreamsRunning, + pauline_stat2.number_of_LinphoneCallStreamsRunning + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallStreamsRunning, + focus_stat2.number_of_LinphoneCallStreamsRunning + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_joined, + focus_stat2.number_of_participant_devices_joined + 1, + liblinphone_tester_sip_timeout)); + + if (!remove_participant) { + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_participant_devices_joined, + laure_stat2.number_of_participant_devices_joined + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, + &laure.getStats().number_of_participant_devices_media_capability_changed, + laure_stat2.number_of_participant_devices_media_capability_changed + 2, + liblinphone_tester_sip_timeout)); + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_participant_devices_joined, + marie_stat2.number_of_participant_devices_joined + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, + &marie.getStats().number_of_participant_devices_media_capability_changed, + marie_stat2.number_of_participant_devices_media_capability_changed + 2, + liblinphone_tester_sip_timeout)); + + if (uninvited_participant_dials && (participant_list_type == LinphoneConferenceParticipantListTypeOpen)) { + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_participant_devices_joined, + michelle_stat2.number_of_participant_devices_joined + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list( + coresList, &michelle.getStats().number_of_participant_devices_media_capability_changed, + michelle_stat2.number_of_participant_devices_media_capability_changed + 1, + liblinphone_tester_sip_timeout)); + } + + if (pauline_call) { + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(pauline_call); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_cparams), video_enabled, int, "%0d"); + } + } + + for (auto mgr : mgrsToRemove) { + + auto itConferenceMgrs = std::find(conferenceMgrs.begin(), conferenceMgrs.end(), mgr); + if (itConferenceMgrs != conferenceMgrs.end()) { + conferenceMgrs.erase(itConferenceMgrs); + } + + auto itMembers = std::find(members.begin(), members.end(), mgr); + if (itMembers != members.end()) { + members.erase(itMembers); + } + + LinphoneCall *call = linphone_core_get_current_call(mgr->lc); + BC_ASSERT_PTR_NOT_NULL(call); + if (call) { + stats marie_stat2 = marie.getStats(); + stats focus_stat2 = focus.getStats(); + if (network_restart) { + ms_message("%s switches off network before %s leaves conference %s", + linphone_core_get_identity(marie.getLc()), linphone_core_get_identity(mgr->lc), + conference_address_str); + linphone_core_set_network_reachable(marie.getLc(), FALSE); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneSubscriptionTerminated, + marie_stat2.number_of_LinphoneSubscriptionTerminated + 1, + liblinphone_tester_sip_timeout)); + } + + linphone_call_terminate(call); + BC_ASSERT_TRUE( + wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallEnd, 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallReleased, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionTerminated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateTerminationPending, + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateTerminated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateDeleted, 1, + liblinphone_tester_sip_timeout)); + + LinphoneAddress *uri = linphone_address_new(linphone_core_get_identity(mgr->lc)); + LinphoneConference *pconference = linphone_core_search_conference(mgr->lc, NULL, uri, confAddr, NULL); + BC_ASSERT_PTR_NULL(pconference); + linphone_address_unref(uri); + + if (network_restart) { + ms_message("%s is back online after %s leaves conference %s", + linphone_core_get_identity(marie.getLc()), linphone_core_get_identity(mgr->lc), + conference_address_str); + linphone_core_set_network_reachable(marie.getLc(), TRUE); + BC_ASSERT_TRUE(wait_for_list(coresList, + &marie.getStats().number_of_LinphoneSubscriptionOutgoingProgress, + marie_stat2.number_of_LinphoneSubscriptionOutgoingProgress + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, + &focus.getStats().number_of_LinphoneSubscriptionIncomingReceived, + focus_stat2.number_of_LinphoneSubscriptionIncomingReceived + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneSubscriptionActive, + marie_stat2.number_of_LinphoneSubscriptionActive + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneSubscriptionActive, + focus_stat2.number_of_LinphoneSubscriptionActive + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_participants_removed, + marie_stat2.number_of_participants_removed + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_participant_devices_removed, + marie_stat2.number_of_participant_devices_removed + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallStreamsRunning, + marie_stat2.number_of_LinphoneCallStreamsRunning + 2, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallStreamsRunning, + focus_stat2.number_of_LinphoneCallStreamsRunning + 2, + liblinphone_tester_sip_timeout)); + } + } + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participants_removed, + focus_stat.number_of_participants_removed + 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_removed, + focus_stat.number_of_participant_devices_removed + 2, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_participants_removed, + marie_stat.number_of_participants_removed + 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_participant_devices_removed, + marie_stat.number_of_participant_devices_removed + 2, + liblinphone_tester_sip_timeout)); + + if (uninvited_participant_dials) { + if (participant_list_type == LinphoneConferenceParticipantListTypeOpen) { + int extra_participants = static_cast<int>(extraParticipantMgrs.size()); + + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_participants_removed, 2, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_participant_devices_removed, 2, + liblinphone_tester_sip_timeout)); + + if (remove_participant) { + BC_ASSERT_TRUE(wait_for_list(coresList, &berthe.getStats().number_of_participants_removed, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &berthe.getStats().number_of_participant_devices_removed, 1, + liblinphone_tester_sip_timeout)); + } + + for (auto mgr : {focus.getCMgr(), marie.getCMgr(), michelle.getCMgr(), berthe.getCMgr()}) { + LinphoneAddress *uri = linphone_address_new(linphone_core_get_identity(mgr->lc)); + LinphoneConference *pconference = + linphone_core_search_conference(mgr->lc, NULL, uri, confAddr, NULL); + BC_ASSERT_PTR_NOT_NULL(pconference); + linphone_address_unref(uri); + if (pconference) { + BC_ASSERT_EQUAL(linphone_conference_get_participant_count(pconference), + ((mgr == focus.getCMgr()) ? (extra_participants + 1) : extra_participants), int, + "%0d"); + bctbx_list_t *devices = linphone_conference_get_participant_device_list(pconference); + BC_ASSERT_EQUAL(bctbx_list_size(devices), (extra_participants + 1), size_t, "%zu"); + if (devices) { + bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); + } + BC_ASSERT_STRING_EQUAL(linphone_conference_get_subject(pconference), initialSubject); + } + } + + stats marie_stat2 = marie.getStats(); + stats focus_stat2 = focus.getStats(); + + for (auto mgr : extraParticipantMgrs) { + LinphoneCall *call = linphone_core_get_call_by_remote_address2(mgr->lc, confAddr); + + auto itConferenceMgrs = std::find(conferenceMgrs.begin(), conferenceMgrs.end(), mgr); + if (itConferenceMgrs != conferenceMgrs.end()) { + conferenceMgrs.erase(itConferenceMgrs); + } + + auto itMembers = std::find(members.begin(), members.end(), mgr); + if (itMembers != members.end()) { + members.erase(itMembers); + } + + BC_ASSERT_PTR_NOT_NULL(call); + if (call) { + linphone_call_terminate(call); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallEnd, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallReleased, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionTerminated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, + &mgr->stat.number_of_LinphoneConferenceStateTerminationPending, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateTerminated, + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateDeleted, 1, + liblinphone_tester_sip_timeout)); + } + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participants_removed, + focus_stat2.number_of_participants_removed + extra_participants, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_removed, + focus_stat2.number_of_participant_devices_removed + extra_participants, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_participants_removed, + marie_stat2.number_of_participants_removed + extra_participants, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_participant_devices_removed, + marie_stat2.number_of_participant_devices_removed + extra_participants, + liblinphone_tester_sip_timeout)); + } else if (participant_list_type == LinphoneConferenceParticipantListTypeClosed) { + LinphoneCall *call = linphone_core_get_current_call(michelle.getLc()); + BC_ASSERT_PTR_NULL(call); + + LinphoneAddress *uri = linphone_address_new(linphone_core_get_identity(michelle.getLc())); + LinphoneConference *pconference = + linphone_core_search_conference(michelle.getLc(), NULL, uri, confAddr, NULL); + BC_ASSERT_PTR_NULL(pconference); + linphone_address_unref(uri); + } + } + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, laure, michelle, berthe}).waitUntil(chrono::seconds(2), [] { + return false; + }); + + BC_ASSERT_EQUAL(marie.getStats().number_of_LinphoneConferenceStateTerminationPending, + marie_stat.number_of_LinphoneConferenceStateTerminationPending, int, "%d"); + BC_ASSERT_EQUAL(marie.getStats().number_of_LinphoneConferenceStateTerminated, + marie_stat.number_of_LinphoneConferenceStateTerminated, int, "%d"); + BC_ASSERT_EQUAL(marie.getStats().number_of_LinphoneConferenceStateDeleted, + marie_stat.number_of_LinphoneConferenceStateDeleted, int, "%d"); + + BC_ASSERT_EQUAL(focus.getStats().number_of_LinphoneConferenceStateTerminationPending, + focus_stat.number_of_LinphoneConferenceStateTerminationPending, int, "%d"); + BC_ASSERT_EQUAL(focus.getStats().number_of_LinphoneConferenceStateTerminated, + focus_stat.number_of_LinphoneConferenceStateTerminated, int, "%d"); + BC_ASSERT_EQUAL(focus.getStats().number_of_LinphoneConferenceStateDeleted, + focus_stat.number_of_LinphoneConferenceStateDeleted, int, "%d"); + + for (auto mgr : conferenceMgrs) { + LinphoneConference *pconference = linphone_core_search_conference_2(mgr->lc, confAddr); + BC_ASSERT_PTR_NOT_NULL(pconference); + if (pconference) { + BC_ASSERT_EQUAL(linphone_conference_get_participant_count(pconference), + ((mgr == focus.getCMgr()) ? 1 : 0), int, "%0d"); + bctbx_list_t *devices = linphone_conference_get_participant_device_list(pconference); + BC_ASSERT_EQUAL(bctbx_list_size(devices), 1, size_t, "%zu"); + if (devices) { + bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); + } + BC_ASSERT_STRING_EQUAL(linphone_conference_get_subject(pconference), initialSubject); + } + } + + const bctbx_list_t *calls = linphone_core_get_calls(marie.getLc()); + BC_ASSERT_EQUAL(bctbx_list_size(calls), 1, size_t, "%zu"); + + LinphoneCall *call = linphone_core_get_call_by_remote_address2(marie.getLc(), focus.getCMgr()->identity); + BC_ASSERT_PTR_NOT_NULL(call); + if (call) { + linphone_call_terminate(call); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallEnd, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallReleased, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneSubscriptionTerminated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, + &marie.getStats().number_of_LinphoneConferenceStateTerminationPending, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateTerminated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateDeleted, 1, + liblinphone_tester_sip_timeout)); + + // Explicitely terminate conference as those on server are static by default + LinphoneConference *pconference = linphone_core_search_conference_2(focus.getLc(), confAddr); + BC_ASSERT_PTR_NOT_NULL(pconference); + if (pconference) { + linphone_conference_terminate(pconference); + } + BC_ASSERT_TRUE(wait_for_list(coresList, + &focus.getStats().number_of_LinphoneConferenceStateTerminationPending, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateTerminated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateDeleted, 1, + liblinphone_tester_sip_timeout)); + } + + std::list<LinphoneCoreManager *> allMembers{marie.getCMgr(), pauline.getCMgr(), laure.getCMgr()}; + if ((participant_list_type == LinphoneConferenceParticipantListTypeOpen) && uninvited_participant_dials) { + allMembers.push_back(michelle.getCMgr()); + if (remove_participant) { + allMembers.push_back(berthe.getCMgr()); + } + } + for (auto mgr : allMembers) { + const bctbx_list_t *call_logs = linphone_core_get_call_logs(mgr->lc); + BC_ASSERT_EQUAL((unsigned int)bctbx_list_size(call_logs), + ((client_restart && (mgr == marie.getCMgr())) ? 2 : 1), unsigned int, "%u"); + + bctbx_list_t *mgr_focus_call_log = + linphone_core_get_call_history_2(mgr->lc, focus.getCMgr()->identity, mgr->identity); + BC_ASSERT_PTR_NOT_NULL(mgr_focus_call_log); + if (mgr_focus_call_log) { + BC_ASSERT_EQUAL((unsigned int)bctbx_list_size(mgr_focus_call_log), + ((client_restart && (mgr == marie.getCMgr())) ? 2 : 1), unsigned int, "%u"); + for (bctbx_list_t *it = mgr_focus_call_log; it; it = bctbx_list_next(it)) { + LinphoneCallLog *call_log = (LinphoneCallLog *)it->data; + BC_ASSERT_TRUE(linphone_call_log_was_conference(call_log)); + } + bctbx_list_free_with_data(mgr_focus_call_log, (bctbx_list_free_func)linphone_call_log_unref); + } + } + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, laure, michelle}).waitUntil(chrono::seconds(2), [] { return false; }); + + ms_free(conference_address_str); + linphone_address_unref(confAddr); + bctbx_list_free(coresList); + } +} + +static void create_simple_conference(void) { + create_conference_base(ms_time(NULL), -1, FALSE, LinphoneConferenceParticipantListTypeOpen, FALSE, + LinphoneMediaEncryptionNone, FALSE, LinphoneConferenceLayoutGrid, FALSE, FALSE, FALSE, FALSE, + FALSE, FALSE, LinphoneMediaDirectionRecvOnly, FALSE); +} + +static void create_simple_conference_with_server_restart(void) { + create_conference_base(ms_time(NULL), -1, FALSE, LinphoneConferenceParticipantListTypeOpen, FALSE, + LinphoneMediaEncryptionNone, FALSE, LinphoneConferenceLayoutGrid, FALSE, FALSE, FALSE, TRUE, + FALSE, FALSE, LinphoneMediaDirectionRecvOnly, TRUE); +} + +static void create_simple_conference_with_client_restart(void) { + create_conference_base(ms_time(NULL), -1, FALSE, LinphoneConferenceParticipantListTypeOpen, FALSE, + LinphoneMediaEncryptionNone, FALSE, LinphoneConferenceLayoutGrid, FALSE, FALSE, FALSE, FALSE, + TRUE, FALSE, LinphoneMediaDirectionRecvOnly, TRUE); +} + +static void create_simple_ice_conference(void) { + create_conference_base(ms_time(NULL), -1, FALSE, LinphoneConferenceParticipantListTypeOpen, FALSE, + LinphoneMediaEncryptionNone, TRUE, LinphoneConferenceLayoutGrid, TRUE, FALSE, FALSE, FALSE, + FALSE, FALSE, LinphoneMediaDirectionRecvOnly, TRUE); +} + +static void create_simple_stun_ice_conference(void) { + create_conference_base(ms_time(NULL), -1, FALSE, LinphoneConferenceParticipantListTypeOpen, FALSE, + LinphoneMediaEncryptionNone, TRUE, LinphoneConferenceLayoutGrid, TRUE, TRUE, FALSE, FALSE, + FALSE, FALSE, LinphoneMediaDirectionSendRecv, FALSE); +} + +static void create_simple_zrtp_conference(void) { + create_conference_base(ms_time(NULL), -1, FALSE, LinphoneConferenceParticipantListTypeOpen, FALSE, + LinphoneMediaEncryptionZRTP, TRUE, LinphoneConferenceLayoutActiveSpeaker, FALSE, FALSE, + FALSE, FALSE, FALSE, FALSE, LinphoneMediaDirectionRecvOnly, FALSE); +} + +static void create_simple_dtls_conference(void) { + create_conference_base(ms_time(NULL), -1, FALSE, LinphoneConferenceParticipantListTypeOpen, FALSE, + LinphoneMediaEncryptionDTLS, TRUE, LinphoneConferenceLayoutActiveSpeaker, FALSE, FALSE, + FALSE, FALSE, FALSE, FALSE, LinphoneMediaDirectionRecvOnly, FALSE); +} + +static void create_simple_srtp_conference(void) { + create_conference_base(ms_time(NULL), -1, FALSE, LinphoneConferenceParticipantListTypeOpen, FALSE, + LinphoneMediaEncryptionSRTP, TRUE, LinphoneConferenceLayoutGrid, FALSE, FALSE, FALSE, FALSE, + FALSE, FALSE, LinphoneMediaDirectionRecvOnly, FALSE); +} + +static void create_simple_ice_srtp_conference(void) { + create_conference_base(ms_time(NULL), -1, FALSE, LinphoneConferenceParticipantListTypeOpen, FALSE, + LinphoneMediaEncryptionSRTP, TRUE, LinphoneConferenceLayoutGrid, TRUE, FALSE, FALSE, FALSE, + FALSE, FALSE, LinphoneMediaDirectionSendRecv, FALSE); +} + +static void create_simple_ice_dtls_conference(void) { + create_conference_base(ms_time(NULL), -1, FALSE, LinphoneConferenceParticipantListTypeOpen, FALSE, + LinphoneMediaEncryptionDTLS, TRUE, LinphoneConferenceLayoutGrid, TRUE, FALSE, FALSE, FALSE, + FALSE, FALSE, LinphoneMediaDirectionSendRecv, FALSE); +} + +static void create_simple_stun_ice_srtp_conference(void) { + create_conference_base(ms_time(NULL), -1, FALSE, LinphoneConferenceParticipantListTypeOpen, FALSE, + LinphoneMediaEncryptionSRTP, TRUE, LinphoneConferenceLayoutActiveSpeaker, TRUE, TRUE, FALSE, + FALSE, FALSE, FALSE, LinphoneMediaDirectionSendRecv, FALSE); +} + +static void create_conference_with_uninvited_participant(void) { + create_conference_base(ms_time(NULL), -1, TRUE, LinphoneConferenceParticipantListTypeOpen, TRUE, + LinphoneMediaEncryptionNone, TRUE, LinphoneConferenceLayoutGrid, FALSE, FALSE, FALSE, FALSE, + FALSE, FALSE, LinphoneMediaDirectionRecvOnly, TRUE); +} + +static void create_conference_with_uninvited_participant_not_allowed(void) { + create_conference_base(ms_time(NULL), -1, TRUE, LinphoneConferenceParticipantListTypeClosed, FALSE, + LinphoneMediaEncryptionNone, FALSE, LinphoneConferenceLayoutActiveSpeaker, FALSE, FALSE, + FALSE, FALSE, FALSE, FALSE, LinphoneMediaDirectionSendRecv, FALSE); +} + +static void create_conference_starting_immediately(void) { + create_conference_base(ms_time(NULL), 0, FALSE, LinphoneConferenceParticipantListTypeClosed, FALSE, + LinphoneMediaEncryptionNone, FALSE, LinphoneConferenceLayoutGrid, FALSE, FALSE, FALSE, FALSE, + FALSE, FALSE, LinphoneMediaDirectionRecvOnly, FALSE); +} + +static void create_conference_starting_in_the_past(void) { + create_conference_base(ms_time(NULL) - 600, 900, FALSE, LinphoneConferenceParticipantListTypeClosed, TRUE, + LinphoneMediaEncryptionNone, FALSE, LinphoneConferenceLayoutActiveSpeaker, FALSE, FALSE, + FALSE, FALSE, FALSE, FALSE, LinphoneMediaDirectionSendRecv, FALSE); +} + +static void create_simple_conference_with_audio_only_participant(void) { + create_conference_base(ms_time(NULL), -1, FALSE, LinphoneConferenceParticipantListTypeOpen, FALSE, + LinphoneMediaEncryptionNone, TRUE, LinphoneConferenceLayoutGrid, FALSE, FALSE, TRUE, FALSE, + FALSE, FALSE, LinphoneMediaDirectionSendRecv, FALSE); +} + +static void create_simple_ice_conference_with_audio_only_participant(void) { + create_conference_base(ms_time(NULL), -1, FALSE, LinphoneConferenceParticipantListTypeOpen, FALSE, + LinphoneMediaEncryptionNone, TRUE, LinphoneConferenceLayoutGrid, TRUE, TRUE, TRUE, FALSE, + FALSE, FALSE, LinphoneMediaDirectionRecvOnly, FALSE); +} + +static void create_simple_stun_ice_conference_with_audio_only_participant(void) { + create_conference_base(ms_time(NULL), -1, FALSE, LinphoneConferenceParticipantListTypeOpen, FALSE, + LinphoneMediaEncryptionNone, TRUE, LinphoneConferenceLayoutGrid, TRUE, TRUE, TRUE, FALSE, + FALSE, FALSE, LinphoneMediaDirectionSendRecv, FALSE); +} + +static void create_simple_stun_ice_srtp_conference_with_audio_only_participant(void) { + create_conference_base(ms_time(NULL), -1, FALSE, LinphoneConferenceParticipantListTypeOpen, FALSE, + LinphoneMediaEncryptionSRTP, TRUE, LinphoneConferenceLayoutGrid, TRUE, TRUE, TRUE, FALSE, + FALSE, FALSE, LinphoneMediaDirectionRecvOnly, FALSE); +} + +static void create_conference_with_audio_only_and_uninvited_participant(void) { + create_conference_base(ms_time(NULL), -1, TRUE, LinphoneConferenceParticipantListTypeOpen, TRUE, + LinphoneMediaEncryptionNone, TRUE, LinphoneConferenceLayoutGrid, FALSE, FALSE, TRUE, FALSE, + FALSE, FALSE, LinphoneMediaDirectionSendRecv, FALSE); +} + +static void create_simple_conference_with_audio_only_participant_enabling_video(void) { + create_conference_base(ms_time(NULL), -1, FALSE, LinphoneConferenceParticipantListTypeOpen, FALSE, + LinphoneMediaEncryptionNone, TRUE, LinphoneConferenceLayoutGrid, FALSE, FALSE, TRUE, FALSE, + FALSE, FALSE, LinphoneMediaDirectionSendRecv, FALSE); +} + +static void create_conference_with_server_restart_base(bool_t organizer_first) { + Focus focus("chloe_rc"); + { // to make sure focus is destroyed after clients. + ClientConference marie("marie_rc", focus.getIdentity()); + ClientConference pauline("pauline_rc", focus.getIdentity()); + ClientConference laure("laure_tcp_rc", focus.getIdentity()); + + focus.registerAsParticipantDevice(marie); + focus.registerAsParticipantDevice(pauline); + focus.registerAsParticipantDevice(laure); + + setup_conference_info_cbs(marie.getCMgr()); + + bctbx_list_t *coresList = NULL; + + for (auto mgr : {focus.getCMgr(), marie.getCMgr(), pauline.getCMgr(), laure.getCMgr()}) { + LinphoneVideoActivationPolicy *pol = + linphone_factory_create_video_activation_policy(linphone_factory_get()); + linphone_video_activation_policy_set_automatically_accept(pol, TRUE); + linphone_video_activation_policy_set_automatically_initiate(pol, TRUE); + linphone_core_set_video_activation_policy(mgr->lc, pol); + linphone_video_activation_policy_unref(pol); + + linphone_core_set_video_device(mgr->lc, liblinphone_tester_mire_id); + linphone_core_enable_video_capture(mgr->lc, TRUE); + linphone_core_enable_video_display(mgr->lc, TRUE); + + coresList = bctbx_list_append(coresList, mgr->lc); + } + + linphone_core_set_file_transfer_server(marie.getLc(), file_transfer_url); + + stats focus_stat = focus.getStats(); + + std::list<LinphoneCoreManager *> participants{pauline.getCMgr(), laure.getCMgr()}; + std::list<LinphoneCoreManager *> conferenceMgrs{focus.getCMgr(), marie.getCMgr(), pauline.getCMgr(), + laure.getCMgr()}; + std::list<LinphoneCoreManager *> members{marie.getCMgr(), pauline.getCMgr(), laure.getCMgr()}; + + time_t start_time = ms_time(NULL) + 60; + int duration = 30; + time_t end_time = (duration <= 0) ? -1 : (start_time + duration * 60); + const char *initialSubject = "Test characters: ^ :) ¤ çà @"; + const char *description = "London Pub"; + std::map<LinphoneCoreManager *, LinphoneParticipantRole> participantList; + LinphoneParticipantRole role = LinphoneParticipantRoleSpeaker; + for (auto &p : participants) { + participantList.insert(std::make_pair(p, role)); + role = (role == LinphoneParticipantRoleSpeaker) ? LinphoneParticipantRoleListener + : LinphoneParticipantRoleSpeaker; + } + participantList.insert(std::make_pair(marie.getCMgr(), LinphoneParticipantRoleSpeaker)); + LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, + initialSubject, description, TRUE); + + BC_ASSERT_PTR_NOT_NULL(confAddr); + char *conference_address_str = (confAddr) ? linphone_address_as_string(confAddr) : ms_strdup("<unknown>"); + + // Chat room creation to send ICS + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateCreated, 2, + liblinphone_tester_sip_timeout)); + + coresList = bctbx_list_remove(coresList, focus.getLc()); + // Restart flexisip + focus.reStart(); + + LinphoneVideoActivationPolicy *pol = linphone_factory_create_video_activation_policy(linphone_factory_get()); + linphone_video_activation_policy_set_automatically_accept(pol, TRUE); + linphone_video_activation_policy_set_automatically_initiate(pol, TRUE); + linphone_core_set_video_activation_policy(focus.getLc(), pol); + linphone_video_activation_policy_unref(pol); + + linphone_core_enable_video_capture(focus.getLc(), TRUE); + linphone_core_enable_video_display(focus.getLc(), TRUE); + + coresList = bctbx_list_append(coresList, focus.getLc()); + + LinphoneCoreManager *first_to_join = NULL; + std::list<LinphoneCoreManager *> other_members{pauline.getCMgr()}; + if (organizer_first) { + first_to_join = marie.getCMgr(); + other_members.push_back(laure.getCMgr()); + } else { + first_to_join = laure.getCMgr(); + other_members.push_back(marie.getCMgr()); + } + + ms_message("First participant %s is calling conference %s", linphone_core_get_identity(first_to_join->lc), + conference_address_str); + LinphoneCallParams *first_part_new_params = linphone_core_create_call_params(first_to_join->lc, nullptr); + linphone_core_invite_address_with_params_2(first_to_join->lc, confAddr, first_part_new_params, NULL, nullptr); + linphone_call_params_unref(first_part_new_params); + + BC_ASSERT_TRUE(wait_for_list(coresList, &first_to_join->stat.number_of_LinphoneCallOutgoingProgress, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &first_to_join->stat.number_of_LinphoneCallStreamsRunning, 1, + liblinphone_tester_sip_timeout)); + + for (auto mgr : other_members) { + LinphoneCallParams *new_params = linphone_core_create_call_params(mgr->lc, nullptr); + ms_message("%s is calling conference %s", linphone_core_get_identity(mgr->lc), conference_address_str); + linphone_core_invite_address_with_params_2(mgr->lc, confAddr, new_params, NULL, nullptr); + linphone_call_params_unref(new_params); + } + + for (auto mgr : members) { + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallOutgoingProgress, 1, + liblinphone_tester_sip_timeout)); + int no_streams_running = 2; + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallUpdating, (no_streams_running - 1), + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallStreamsRunning, no_streams_running, + liblinphone_tester_sip_timeout)); + // Update to add to conference. + // If ICE is enabled, the addition to a conference may go through a resume of the call + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateCreated, + ((mgr == marie.getCMgr()) ? 3 : 2), liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionOutgoingProgress, 1, 5000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionActive, 1, 5000)); + BC_ASSERT_TRUE( + wait_for_list(coresList, &mgr->stat.number_of_NotifyReceived, 1, liblinphone_tester_sip_timeout)); + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallIncomingReceived, + focus_stat.number_of_LinphoneCallIncomingReceived + 3, + liblinphone_tester_sip_timeout)); + int focus_no_streams_running = 6; + // Update to end ICE negotiations + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallUpdatedByRemote, + focus_stat.number_of_LinphoneCallUpdatedByRemote + (focus_no_streams_running - 3), + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallStreamsRunning, + focus_stat.number_of_LinphoneCallStreamsRunning + focus_no_streams_running, + liblinphone_tester_sip_timeout)); + // Update to add to conference. + // If ICE is enabled, the addition to a conference may go through a resume of the call + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateCreated, + focus_stat.number_of_LinphoneConferenceStateCreated + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneSubscriptionIncomingReceived, + focus_stat.number_of_LinphoneSubscriptionIncomingReceived + 3, 5000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneSubscriptionActive, + focus_stat.number_of_LinphoneSubscriptionActive + 3, 5000)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participants_added, + focus_stat.number_of_participants_added + 3, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_added, + focus_stat.number_of_participant_devices_added + 3, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_joined, + focus_stat.number_of_participant_devices_joined + 3, + liblinphone_tester_sip_timeout)); + + std::map<LinphoneCoreManager *, LinphoneParticipantRole> memberList; + for (const auto &member : members) { + try { + const auto &participantInfo = participantList.at(member); + memberList.insert(std::make_pair(member, participantInfo)); + } catch (std::out_of_range &) { + if (member == marie.getCMgr()) { + memberList.insert(std::make_pair(marie.getCMgr(), LinphoneParticipantRoleSpeaker)); + } else { + ms_fatal("Unable to find active participant %s in the participant list", + linphone_core_get_identity(member->lc)); + } + } + } + wait_for_conference_streams({focus, marie, pauline, laure}, conferenceMgrs, focus.getCMgr(), memberList, + confAddr, TRUE); + + LinphoneConference *fconference = linphone_core_search_conference_2(focus.getLc(), confAddr); + BC_ASSERT_PTR_NOT_NULL(fconference); + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, laure}).waitUntil(chrono::seconds(2), [] { return false; }); + + for (auto mgr : conferenceMgrs) { + LinphoneConference *pconference = linphone_core_search_conference_2(mgr->lc, confAddr); + BC_ASSERT_PTR_NOT_NULL(pconference); + if (pconference) { + const LinphoneConferenceParams *conference_params = linphone_conference_get_current_params(pconference); + int no_participants = 0; + if (start_time >= 0) { + BC_ASSERT_EQUAL((long long)linphone_conference_params_get_start_time(conference_params), + (long long)start_time, long long, "%lld"); + } + BC_ASSERT_EQUAL((long long)linphone_conference_params_get_end_time(conference_params), + (long long)end_time, long long, "%lld"); + bctbx_list_t *participant_device_list = linphone_conference_get_participant_device_list(pconference); + BC_ASSERT_EQUAL(bctbx_list_size(participant_device_list), 3, size_t, "%zu"); + bctbx_list_free_with_data(participant_device_list, (void (*)(void *))linphone_participant_device_unref); + + if (mgr == focus.getCMgr()) { + no_participants = 3; + BC_ASSERT_FALSE(linphone_conference_is_in(pconference)); + } else { + no_participants = 2; + BC_ASSERT_TRUE(linphone_conference_is_in(pconference)); + LinphoneCall *current_call = linphone_core_get_current_call(mgr->lc); + BC_ASSERT_PTR_NOT_NULL(current_call); + if (current_call) { + BC_ASSERT_EQUAL((int)linphone_call_get_state(current_call), + (int)LinphoneCallStateStreamsRunning, int, "%0d"); + } + + LinphoneVideoActivationPolicy *pol = linphone_core_get_video_activation_policy(mgr->lc); + bool_t enabled = !!linphone_video_activation_policy_get_automatically_initiate(pol); + linphone_video_activation_policy_unref(pol); + + int no_streams_audio = 1; + int no_streams_video = (enabled) ? 4 : 0; + int no_active_streams_video = (enabled) ? no_streams_video : 0; + int no_streams_text = 0; + + LinphoneCall *pcall = linphone_core_get_call_by_remote_address2(mgr->lc, confAddr); + BC_ASSERT_PTR_NOT_NULL(pcall); + if (pcall) { + _linphone_call_check_nb_streams(pcall, no_streams_audio, no_streams_video, no_streams_text); + _linphone_call_check_nb_active_streams(pcall, no_streams_audio, no_active_streams_video, + no_streams_text); + const LinphoneCallParams *call_lparams = linphone_call_get_params(pcall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_lparams), enabled, int, "%0d"); + const LinphoneCallParams *call_rparams = linphone_call_get_remote_params(pcall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_rparams), enabled, int, "%0d"); + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(pcall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_cparams), enabled, int, "%0d"); + } + LinphoneCall *ccall = linphone_core_get_call_by_remote_address2(focus.getLc(), mgr->identity); + BC_ASSERT_PTR_NOT_NULL(ccall); + if (ccall) { + _linphone_call_check_nb_streams(ccall, no_streams_audio, no_streams_video, no_streams_text); + _linphone_call_check_nb_active_streams(ccall, no_streams_audio, no_active_streams_video, + no_streams_text); + const LinphoneCallParams *call_lparams = linphone_call_get_params(ccall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_lparams), enabled, int, "%0d"); + const LinphoneCallParams *call_rparams = linphone_call_get_remote_params(ccall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_rparams), enabled, int, "%0d"); + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(ccall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_cparams), enabled, int, "%0d"); + } + } + BC_ASSERT_EQUAL(linphone_conference_get_participant_count(pconference), no_participants, int, "%0d"); + BC_ASSERT_STRING_EQUAL(linphone_conference_get_subject(pconference), initialSubject); + LinphoneParticipant *me = linphone_conference_get_me(pconference); + BC_ASSERT_TRUE(linphone_participant_is_admin(me) == + ((mgr == marie.getCMgr()) || (mgr == focus.getCMgr()))); + BC_ASSERT_TRUE(linphone_address_weak_equal(linphone_participant_get_address(me), mgr->identity)); + bctbx_list_t *participants = linphone_conference_get_participant_list(pconference); + for (bctbx_list_t *itp = participants; itp; itp = bctbx_list_next(itp)) { + LinphoneParticipant *p = (LinphoneParticipant *)bctbx_list_get_data(itp); + BC_ASSERT_TRUE( + linphone_participant_is_admin(p) == + linphone_address_weak_equal(linphone_participant_get_address(p), marie.getCMgr()->identity)); + } + bctbx_list_free_with_data(participants, (void (*)(void *))linphone_participant_unref); + + if (mgr != focus.getCMgr()) { + check_conference_ssrc(fconference, pconference); + } + } + } + + const int total_marie_calls = + marie.getStats().number_of_LinphoneCallEnd + (int)bctbx_list_size(linphone_core_get_calls(marie.getLc())); + const int total_focus_calls = + focus.getStats().number_of_LinphoneCallEnd + (int)bctbx_list_size(linphone_core_get_calls(focus.getLc())); + const int total_pauline_calls = pauline.getStats().number_of_LinphoneCallEnd + + (int)bctbx_list_size(linphone_core_get_calls(pauline.getLc())); + const int total_laure_calls = + laure.getStats().number_of_LinphoneCallEnd + (int)bctbx_list_size(linphone_core_get_calls(laure.getLc())); + + linphone_core_terminate_all_calls(pauline.getLc()); + linphone_core_terminate_all_calls(laure.getLc()); + linphone_core_terminate_all_calls(marie.getLc()); + + // Wait for calls to be terminated + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallEnd, total_marie_calls, 30000)); + BC_ASSERT_TRUE( + wait_for_list(coresList, &pauline.getStats().number_of_LinphoneCallEnd, total_pauline_calls, 30000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_LinphoneCallEnd, total_laure_calls, 30000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallEnd, total_focus_calls, 40000)); + + BC_ASSERT_TRUE( + wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallReleased, total_marie_calls, 30000)); + BC_ASSERT_TRUE( + wait_for_list(coresList, &pauline.getStats().number_of_LinphoneCallReleased, total_pauline_calls, 30000)); + BC_ASSERT_TRUE( + wait_for_list(coresList, &laure.getStats().number_of_LinphoneCallReleased, total_laure_calls, 30000)); + BC_ASSERT_TRUE( + wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallReleased, total_focus_calls, 40000)); + + if (confAddr && fconference) { + linphone_conference_terminate(fconference); + } + + for (auto mgr : {focus.getCMgr(), marie.getCMgr(), pauline.getCMgr(), laure.getCMgr()}) { + + // Wait for all conferences to be terminated + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateTerminationPending, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateTerminated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateDeleted, 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionTerminated, + (mgr == focus.getCMgr()) ? 3 : 1, liblinphone_tester_sip_timeout)); + + if (mgr && (mgr != focus.getCMgr())) { + LinphoneCall *participant_call = + linphone_core_get_call_by_remote_address2(mgr->lc, focus.getCMgr()->identity); + BC_ASSERT_PTR_NULL(participant_call); + LinphoneCall *conference_call = linphone_core_get_call_by_remote_address2(focus.getLc(), mgr->identity); + BC_ASSERT_PTR_NULL(conference_call); + + const bctbx_list_t *call_logs = linphone_core_get_call_logs(mgr->lc); + BC_ASSERT_EQUAL((unsigned int)bctbx_list_size(call_logs), 1, unsigned int, "%u"); + + bctbx_list_t *mgr_focus_call_log = + linphone_core_get_call_history_2(mgr->lc, focus.getCMgr()->identity, mgr->identity); + BC_ASSERT_PTR_NOT_NULL(mgr_focus_call_log); + if (mgr_focus_call_log) { + BC_ASSERT_EQUAL((unsigned int)bctbx_list_size(mgr_focus_call_log), 1, unsigned int, "%u"); + for (bctbx_list_t *it = mgr_focus_call_log; it; it = bctbx_list_next(it)) { + LinphoneCallLog *call_log = (LinphoneCallLog *)it->data; + BC_ASSERT_TRUE(linphone_call_log_was_conference(call_log)); + } + bctbx_list_free_with_data(mgr_focus_call_log, (bctbx_list_free_func)linphone_call_log_unref); + } + } + } + + ms_free(conference_address_str); + linphone_address_unref(confAddr); + bctbx_list_free(coresList); + } +} + +static void create_conference_with_server_restart_organizer_first(void) { + create_conference_with_server_restart_base(TRUE); +} + +static void create_conference_with_server_restart_participant_first(void) { + create_conference_with_server_restart_base(FALSE); +} + +static void create_conference_with_codec_mismatch_base(bool_t organizer_codec_mismatch) { + Focus focus("chloe_rc"); + { // to make sure focus is destroyed after clients. + ClientConference marie("marie_rc", focus.getIdentity()); + ClientConference pauline("pauline_rc", focus.getIdentity()); + ClientConference laure("laure_tcp_rc", focus.getIdentity()); + ClientConference michelle("michelle_rc", focus.getIdentity()); + ClientConference berthe("berthe_rc", focus.getIdentity()); + + focus.registerAsParticipantDevice(marie); + focus.registerAsParticipantDevice(pauline); + focus.registerAsParticipantDevice(laure); + focus.registerAsParticipantDevice(michelle); + focus.registerAsParticipantDevice(berthe); + + setup_conference_info_cbs(marie.getCMgr()); + + bctbx_list_t *coresList = NULL; + + for (auto mgr : {focus.getCMgr(), marie.getCMgr(), pauline.getCMgr(), laure.getCMgr(), michelle.getCMgr(), + berthe.getCMgr()}) { + LinphoneVideoActivationPolicy *pol = + linphone_factory_create_video_activation_policy(linphone_factory_get()); + linphone_video_activation_policy_set_automatically_accept(pol, TRUE); + linphone_video_activation_policy_set_automatically_initiate(pol, TRUE); + linphone_core_set_video_activation_policy(mgr->lc, pol); + linphone_video_activation_policy_unref(pol); + + linphone_core_set_video_device(mgr->lc, liblinphone_tester_mire_id); + linphone_core_enable_video_capture(mgr->lc, TRUE); + linphone_core_enable_video_display(mgr->lc, TRUE); + + if (mgr != focus.getCMgr()) { + linphone_core_set_default_conference_layout(mgr->lc, LinphoneConferenceLayoutGrid); + linphone_core_set_media_encryption(mgr->lc, LinphoneMediaEncryptionSRTP); + } + + enable_stun_in_core(mgr, TRUE, TRUE); + linphone_core_manager_wait_for_stun_resolution(mgr); + + if ((organizer_codec_mismatch && (mgr == marie.getCMgr())) || + (!organizer_codec_mismatch && (mgr == michelle.getCMgr()))) { + disable_all_audio_codecs_except_one(mgr->lc, "pcmu", -1); + } else { + disable_all_audio_codecs_except_one(mgr->lc, "pcma", -1); + } + + coresList = bctbx_list_append(coresList, mgr->lc); + } + + linphone_core_set_file_transfer_server(marie.getLc(), file_transfer_url); + + stats focus_stat = focus.getStats(); + + std::list<LinphoneCoreManager *> participants{pauline.getCMgr(), laure.getCMgr(), michelle.getCMgr(), + berthe.getCMgr()}; + std::list<LinphoneCoreManager *> conferenceMgrs{focus.getCMgr(), marie.getCMgr(), pauline.getCMgr(), + laure.getCMgr(), michelle.getCMgr(), berthe.getCMgr()}; + std::list<LinphoneCoreManager *> members{marie.getCMgr(), pauline.getCMgr(), laure.getCMgr(), + michelle.getCMgr(), berthe.getCMgr()}; + + time_t start_time = ms_time(NULL); + time_t end_time = -1; + const char *initialSubject = "Test characters: ^ :) ¤ çà @"; + const char *description = "Paris Baker"; + std::map<LinphoneCoreManager *, LinphoneParticipantRole> participantList; + LinphoneParticipantRole role = LinphoneParticipantRoleSpeaker; + for (auto &p : participants) { + participantList.insert(std::make_pair(p, role)); + role = (role == LinphoneParticipantRoleSpeaker) ? LinphoneParticipantRoleListener + : LinphoneParticipantRoleSpeaker; + } + participantList.insert(std::make_pair(marie.getCMgr(), LinphoneParticipantRoleListener)); + LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, + initialSubject, description, TRUE); + BC_ASSERT_PTR_NOT_NULL(confAddr); + char *conference_address_str = (confAddr) ? linphone_address_as_string(confAddr) : ms_strdup("<unknown>"); + + // Chat room creation to send ICS + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateCreated, 2, + liblinphone_tester_sip_timeout)); + + LinphoneMediaEncryption encryption = LinphoneMediaEncryptionNone; + for (auto mgr : members) { + LinphoneCallParams *new_params = linphone_core_create_call_params(mgr->lc, nullptr); + linphone_call_params_set_media_encryption(new_params, encryption); + linphone_core_invite_address_with_params_2(mgr->lc, confAddr, new_params, NULL, nullptr); + linphone_call_params_unref(new_params); + } + + std::list<LinphoneCoreManager *> codec_mismatch_members; + if (organizer_codec_mismatch) { + codec_mismatch_members.push_back(marie.getCMgr()); + } else { + codec_mismatch_members.push_back(michelle.getCMgr()); + } + + for (const auto &m : codec_mismatch_members) { + auto itConferenceMgrs = std::find(conferenceMgrs.begin(), conferenceMgrs.end(), m); + if (itConferenceMgrs != conferenceMgrs.end()) { + conferenceMgrs.erase(itConferenceMgrs); + } + + auto itParticipants = std::find(participants.begin(), participants.end(), m); + if (itParticipants != participants.end()) { + participants.erase(itParticipants); + } + + auto itMembers = std::find(members.begin(), members.end(), m); + if (itMembers != members.end()) { + members.erase(itMembers); + } + } + + for (auto mgr : members) { + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallOutgoingProgress, 1, + liblinphone_tester_sip_timeout)); + int no_streams_running = 3; + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallUpdating, (no_streams_running - 1), + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallStreamsRunning, no_streams_running, + liblinphone_tester_sip_timeout)); + // Update to add to conference. + // If ICE is enabled, the addition to a conference may go through a resume of the call + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateCreated, + ((mgr == marie.getCMgr()) ? 3 : 2), liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionOutgoingProgress, 1, 5000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionActive, 1, 5000)); + BC_ASSERT_TRUE( + wait_for_list(coresList, &mgr->stat.number_of_NotifyReceived, 1, liblinphone_tester_sip_timeout)); + + LinphoneCall *pcall = linphone_core_get_call_by_remote_address2(mgr->lc, confAddr); + BC_ASSERT_PTR_NOT_NULL(pcall); + if (pcall) { + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(pcall); + const LinphoneMediaEncryption pcall_enc = linphone_call_params_get_media_encryption(call_cparams); + BC_ASSERT_EQUAL(pcall_enc, encryption, int, "%d"); + } + LinphoneCall *ccall = linphone_core_get_call_by_remote_address2(focus.getLc(), mgr->identity); + BC_ASSERT_PTR_NOT_NULL(ccall); + if (ccall) { + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(ccall); + const LinphoneMediaEncryption ccall_enc = linphone_call_params_get_media_encryption(call_cparams); + BC_ASSERT_EQUAL(ccall_enc, encryption, int, "%d"); + } + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallIncomingReceived, + focus_stat.number_of_LinphoneCallIncomingReceived + 3, + liblinphone_tester_sip_timeout)); + int focus_no_streams_running = 9; + // Update to end ICE negotiations + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallUpdatedByRemote, + focus_stat.number_of_LinphoneCallUpdatedByRemote + (focus_no_streams_running - 3), + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallStreamsRunning, + focus_stat.number_of_LinphoneCallStreamsRunning + focus_no_streams_running, + liblinphone_tester_sip_timeout)); + // Update to add to conference. + // If ICE is enabled, the addition to a conference may go through a resume of the call + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateCreated, + focus_stat.number_of_LinphoneConferenceStateCreated + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneSubscriptionIncomingReceived, + focus_stat.number_of_LinphoneSubscriptionIncomingReceived + 3, 5000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneSubscriptionActive, + focus_stat.number_of_LinphoneSubscriptionActive + 3, 5000)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participants_added, + focus_stat.number_of_participants_added + 3, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_added, + focus_stat.number_of_participant_devices_added + 3, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_joined, + focus_stat.number_of_participant_devices_joined + 3, + liblinphone_tester_sip_timeout)); + if (organizer_codec_mismatch) { + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participants_removed, + focus_stat.number_of_participants_removed + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_removed, + focus_stat.number_of_participant_devices_removed + 1, + liblinphone_tester_sip_timeout)); + } + + std::map<LinphoneCoreManager *, LinphoneParticipantRole> memberList; + for (const auto &member : members) { + try { + const auto &participantInfo = participantList.at(member); + memberList.insert(std::make_pair(member, participantInfo)); + } catch (std::out_of_range &) { + if (member == marie.getCMgr()) { + memberList.insert(std::make_pair(marie.getCMgr(), LinphoneParticipantRoleSpeaker)); + } else { + ms_fatal("Unable to find active participant %s in the participant list", + linphone_core_get_identity(member->lc)); + } + } + } + wait_for_conference_streams({focus, marie, pauline, laure, michelle, berthe}, conferenceMgrs, focus.getCMgr(), + memberList, confAddr, TRUE); + + LinphoneConference *fconference = linphone_core_search_conference_2(focus.getLc(), confAddr); + BC_ASSERT_PTR_NOT_NULL(fconference); + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, laure, michelle, berthe}).waitUntil(chrono::seconds(2), [] { + return false; + }); + + for (auto mgr : conferenceMgrs) { + LinphoneConference *pconference = linphone_core_search_conference_2(mgr->lc, confAddr); + BC_ASSERT_PTR_NOT_NULL(pconference); + if (pconference) { + const LinphoneConferenceParams *conference_params = linphone_conference_get_current_params(pconference); + int no_participants = 0; + if (start_time >= 0) { + BC_ASSERT_EQUAL((long long)linphone_conference_params_get_start_time(conference_params), + (long long)start_time, long long, "%lld"); + } + BC_ASSERT_EQUAL((long long)linphone_conference_params_get_end_time(conference_params), + (long long)end_time, long long, "%lld"); + + bctbx_list_t *participant_device_list = linphone_conference_get_participant_device_list(pconference); + BC_ASSERT_EQUAL(bctbx_list_size(participant_device_list), members.size(), size_t, "%zu"); + for (bctbx_list_t *d_it = participant_device_list; d_it; d_it = bctbx_list_next(d_it)) { + LinphoneParticipantDevice *d = (LinphoneParticipantDevice *)bctbx_list_get_data(d_it); + BC_ASSERT_PTR_NOT_NULL(d); + if (d) { + BC_ASSERT_TRUE(!!!linphone_participant_device_get_is_muted(d)); + } + } + bctbx_list_free_with_data(participant_device_list, (void (*)(void *))linphone_participant_device_unref); + + if (mgr == focus.getCMgr()) { + no_participants = static_cast<int>(members.size()); + BC_ASSERT_FALSE(linphone_conference_is_in(pconference)); + } else { + no_participants = static_cast<int>(members.size() - 1); + BC_ASSERT_TRUE(linphone_conference_is_in(pconference)); + LinphoneCall *current_call = linphone_core_get_current_call(mgr->lc); + BC_ASSERT_PTR_NOT_NULL(current_call); + if (current_call) { + BC_ASSERT_EQUAL((int)linphone_call_get_state(current_call), + (int)LinphoneCallStateStreamsRunning, int, "%0d"); + } + BC_ASSERT_TRUE(check_ice(mgr, focus.getCMgr(), LinphoneIceStateHostConnection)); + + LinphoneVideoActivationPolicy *pol = linphone_core_get_video_activation_policy(mgr->lc); + bool_t enabled = !!linphone_video_activation_policy_get_automatically_initiate(pol); + linphone_video_activation_policy_unref(pol); + + int no_streams_audio = 1; + int no_active_streams_video = 0; + int no_streams_text = 0; + + LinphoneCall *pcall = linphone_core_get_call_by_remote_address2(mgr->lc, confAddr); + BC_ASSERT_PTR_NOT_NULL(pcall); + if (pcall) { + no_active_streams_video = + static_cast<int>(compute_no_video_streams(enabled, pcall, pconference)); + _linphone_call_check_nb_active_streams(pcall, no_streams_audio, no_active_streams_video, + no_streams_text); + const LinphoneCallParams *call_lparams = linphone_call_get_params(pcall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_lparams), enabled, int, "%0d"); + const LinphoneCallParams *call_rparams = linphone_call_get_remote_params(pcall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_rparams), + enabled && (no_active_streams_video > 0), int, "%0d"); + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(pcall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_cparams), + enabled && (no_active_streams_video > 0), int, "%0d"); + } + LinphoneCall *ccall = linphone_core_get_call_by_remote_address2(focus.getLc(), mgr->identity); + BC_ASSERT_PTR_NOT_NULL(ccall); + if (ccall) { + _linphone_call_check_nb_active_streams(ccall, no_streams_audio, no_active_streams_video, + no_streams_text); + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(ccall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_cparams), + enabled && (no_active_streams_video > 0), int, "%0d"); + } + } + BC_ASSERT_EQUAL(linphone_conference_get_participant_count(pconference), no_participants, int, "%0d"); + BC_ASSERT_STRING_EQUAL(linphone_conference_get_subject(pconference), initialSubject); + LinphoneParticipant *me = linphone_conference_get_me(pconference); + BC_ASSERT_TRUE(linphone_participant_is_admin(me) == + ((mgr == marie.getCMgr()) || (mgr == focus.getCMgr()))); + BC_ASSERT_TRUE(linphone_address_weak_equal(linphone_participant_get_address(me), mgr->identity)); + bctbx_list_t *participants = linphone_conference_get_participant_list(pconference); + for (bctbx_list_t *itp = participants; itp; itp = bctbx_list_next(itp)) { + LinphoneParticipant *p = (LinphoneParticipant *)bctbx_list_get_data(itp); + BC_ASSERT_TRUE( + linphone_participant_is_admin(p) == + linphone_address_weak_equal(linphone_participant_get_address(p), marie.getCMgr()->identity)); + } + bctbx_list_free_with_data(participants, (void (*)(void *))linphone_participant_unref); + + if (mgr != focus.getCMgr()) { + check_conference_ssrc(fconference, pconference); + } + } + } + + for (auto mgr : members) { + LinphoneCall *call = linphone_core_get_current_call(mgr->lc); + BC_ASSERT_PTR_NOT_NULL(call); + if (call) { + linphone_call_terminate(call); + BC_ASSERT_TRUE( + wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallEnd, 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallReleased, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionTerminated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateTerminationPending, + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateTerminated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateDeleted, 1, + liblinphone_tester_sip_timeout)); + + LinphoneAddress *uri = linphone_address_new(linphone_core_get_identity(mgr->lc)); + LinphoneConference *pconference = linphone_core_search_conference(mgr->lc, NULL, uri, confAddr, NULL); + BC_ASSERT_PTR_NULL(pconference); + linphone_address_unref(uri); + } + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participants_removed, + focus_stat.number_of_participants_removed + static_cast<int>(members.size()), + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(coresList, &focus.getStats().number_of_participant_devices_removed, + focus_stat.number_of_participant_devices_removed + static_cast<int>(members.size()), + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallEnd, + focus_stat.number_of_LinphoneCallEnd + static_cast<int>(members.size()), + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallReleased, + focus_stat.number_of_LinphoneCallReleased + static_cast<int>(members.size()), + liblinphone_tester_sip_timeout)); + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, laure, michelle, berthe}).waitUntil(chrono::seconds(2), [] { + return false; + }); + + BC_ASSERT_EQUAL(focus.getStats().number_of_LinphoneConferenceStateTerminationPending, + focus_stat.number_of_LinphoneConferenceStateTerminationPending, int, "%d"); + BC_ASSERT_EQUAL(focus.getStats().number_of_LinphoneConferenceStateTerminated, + focus_stat.number_of_LinphoneConferenceStateTerminated, int, "%d"); + BC_ASSERT_EQUAL(focus.getStats().number_of_LinphoneConferenceStateDeleted, + focus_stat.number_of_LinphoneConferenceStateDeleted, int, "%d"); + + for (auto mgr : members) { + const bctbx_list_t *call_logs = linphone_core_get_call_logs(mgr->lc); + BC_ASSERT_EQUAL((unsigned int)bctbx_list_size(call_logs), 1, unsigned int, "%u"); + + bctbx_list_t *mgr_focus_call_log = + linphone_core_get_call_history_2(mgr->lc, focus.getCMgr()->identity, mgr->identity); + BC_ASSERT_PTR_NOT_NULL(mgr_focus_call_log); + if (mgr_focus_call_log) { + BC_ASSERT_EQUAL((unsigned int)bctbx_list_size(mgr_focus_call_log), 1, unsigned int, "%u"); + for (bctbx_list_t *it = mgr_focus_call_log; it; it = bctbx_list_next(it)) { + LinphoneCallLog *call_log = (LinphoneCallLog *)it->data; + BC_ASSERT_TRUE(linphone_call_log_was_conference(call_log)); + } + bctbx_list_free_with_data(mgr_focus_call_log, (bctbx_list_free_func)linphone_call_log_unref); + } + } + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, laure, michelle, berthe}).waitUntil(chrono::seconds(2), [] { + return false; + }); + + ms_free(conference_address_str); + linphone_address_unref(confAddr); + bctbx_list_free(coresList); + } +} + +static void create_conference_with_organizer_codec_mismatch(void) { + create_conference_with_codec_mismatch_base(TRUE); +} + +static void create_conference_with_participant_codec_mismatch(void) { + create_conference_with_codec_mismatch_base(FALSE); +} + +static void create_conference_dial_out_base(bool_t send_ics, + LinphoneConferenceLayout layout, + LinphoneVideoActivationPolicy *pol, + bool_t enable_stun, + bool_t enable_ice, + LinphoneConferenceParticipantListType participant_list_type, + bool_t accept, + bool_t participant_codec_mismatch) { + Focus focus("chloe_rc"); + { // to make sure focus is destroyed after clients. + ClientConference marie("marie_rc", focus.getIdentity()); + ClientConference pauline("pauline_rc", focus.getIdentity()); + ClientConference laure("laure_tcp_rc", focus.getIdentity()); + ClientConference michelle("michelle_rc", focus.getIdentity()); + ClientConference berthe("berthe_rc", focus.getIdentity()); + + focus.registerAsParticipantDevice(marie); + focus.registerAsParticipantDevice(pauline); + focus.registerAsParticipantDevice(laure); + focus.registerAsParticipantDevice(michelle); + focus.registerAsParticipantDevice(berthe); + + setup_conference_info_cbs(marie.getCMgr()); + + bctbx_list_t *coresList = NULL; + + bool_t enable_video = !!linphone_video_activation_policy_get_automatically_accept(pol) || + !!linphone_video_activation_policy_get_automatically_initiate(pol); + bool_t initiate_video = !!linphone_video_activation_policy_get_automatically_initiate(pol); + bool_t accept_video = !!linphone_video_activation_policy_get_automatically_accept(pol); + + for (auto mgr : {focus.getCMgr(), marie.getCMgr(), pauline.getCMgr(), laure.getCMgr(), michelle.getCMgr(), + berthe.getCMgr()}) { + if (participant_codec_mismatch) { + if (mgr == michelle.getCMgr()) { + disable_all_audio_codecs_except_one(mgr->lc, "pcmu", -1); + } else { + disable_all_audio_codecs_except_one(mgr->lc, "pcma", -1); + } + } + + linphone_core_set_video_activation_policy(mgr->lc, pol); + + linphone_core_set_video_device(mgr->lc, liblinphone_tester_mire_id); + linphone_core_enable_video_capture(mgr->lc, TRUE); + linphone_core_enable_video_display(mgr->lc, TRUE); + + if (mgr != focus.getCMgr()) { + linphone_core_set_default_conference_layout(mgr->lc, layout); + } + + enable_stun_in_core(mgr, enable_stun, enable_ice); + linphone_core_manager_wait_for_stun_resolution(mgr); + + coresList = bctbx_list_append(coresList, mgr->lc); + } + + linphone_core_set_file_transfer_server(marie.getLc(), file_transfer_url); + linphone_core_set_conference_participant_list_type(focus.getLc(), participant_list_type); + + stats focus_stat = focus.getStats(); + stats marie_stat = marie.getStats(); + + std::list<LinphoneCoreManager *> conferenceMgrs{focus.getCMgr(), marie.getCMgr(), pauline.getCMgr(), + laure.getCMgr(), michelle.getCMgr(), berthe.getCMgr()}; + std::list<LinphoneCoreManager *> members{marie.getCMgr(), pauline.getCMgr(), laure.getCMgr(), + michelle.getCMgr(), berthe.getCMgr()}; + std::list<LinphoneCoreManager *> participants{pauline.getCMgr(), laure.getCMgr(), michelle.getCMgr(), + berthe.getCMgr()}; + std::list<LinphoneCoreManager *> codec_mismatch_members; + + const char *initialSubject = "Schedule of the trip towards the top of Europe"; + const char *description = "To the top of the Mont Blanc!!!! :-)"; + std::map<LinphoneCoreManager *, LinphoneParticipantRole> participantList; + LinphoneParticipantRole role = LinphoneParticipantRoleSpeaker; + for (auto &p : participants) { + participantList.insert(std::make_pair(p, role)); + role = (role == LinphoneParticipantRoleSpeaker) ? LinphoneParticipantRoleListener + : LinphoneParticipantRoleSpeaker; + } + LinphoneAddress *confAddr = + create_conference_on_server(focus, marie, participantList, -1, -1, initialSubject, description, send_ics); + BC_ASSERT_PTR_NOT_NULL(confAddr); + + // Chat room creation to send ICS + int marie_conferences = ((send_ics) ? 1 : 0) + 1; + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateCreated, + marie_conferences, liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallOutgoingInit, + marie_stat.number_of_LinphoneCallOutgoingInit + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallOutgoingInit, + focus_stat.number_of_LinphoneCallOutgoingInit + 4, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallIncomingReceived, + focus_stat.number_of_LinphoneCallIncomingReceived + 1, + liblinphone_tester_sip_timeout)); + + if (participant_codec_mismatch) { + codec_mismatch_members.push_back(michelle.getCMgr()); + + for (const auto &m : codec_mismatch_members) { + auto itConferenceMgrs = std::find(conferenceMgrs.begin(), conferenceMgrs.end(), m); + if (itConferenceMgrs != conferenceMgrs.end()) { + conferenceMgrs.erase(itConferenceMgrs); + } + + auto itParticipants = std::find(participants.begin(), participants.end(), m); + if (itParticipants != participants.end()) { + participants.erase(itParticipants); + } + + auto itMembers = std::find(members.begin(), members.end(), m); + if (itMembers != members.end()) { + members.erase(itMembers); + } + + bctbx_list_t *focus_call_log = + linphone_core_get_call_history_2(focus.getLc(), m->identity, focus.getCMgr()->identity); + BC_ASSERT_PTR_NOT_NULL(focus_call_log); + if (focus_call_log) { + BC_ASSERT_EQUAL((unsigned int)bctbx_list_size(focus_call_log), 1, unsigned int, "%u"); + for (bctbx_list_t *it = focus_call_log; it; it = bctbx_list_next(it)) { + LinphoneCallLog *call_log = (LinphoneCallLog *)it->data; + BC_ASSERT_EQUAL(linphone_call_log_get_status(call_log), LinphoneCallAborted, int, "%d"); + } + bctbx_list_free_with_data(focus_call_log, (bctbx_list_free_func)linphone_call_log_unref); + } + + bctbx_list_t *member_call_log = + linphone_core_get_call_history_2(m->lc, focus.getCMgr()->identity, m->identity); + BC_ASSERT_PTR_NOT_NULL(member_call_log); + if (member_call_log) { + BC_ASSERT_EQUAL((unsigned int)bctbx_list_size(member_call_log), 1, unsigned int, "%u"); + for (bctbx_list_t *it = member_call_log; it; it = bctbx_list_next(it)) { + LinphoneCallLog *call_log = (LinphoneCallLog *)it->data; + BC_ASSERT_EQUAL(linphone_call_log_get_status(call_log), LinphoneCallAborted, int, "%d"); + } + bctbx_list_free_with_data(member_call_log, (bctbx_list_free_func)linphone_call_log_unref); + } + } + } + + for (auto mgr : participants) { + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallIncomingReceived, 1, + liblinphone_tester_sip_timeout)); + } + + LinphoneConference *oconference = linphone_core_search_conference_2(marie.getLc(), confAddr); + BC_ASSERT_PTR_NOT_NULL(oconference); + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallStreamsRunning, + marie_stat.number_of_LinphoneCallStreamsRunning + 1, + liblinphone_tester_sip_timeout)); + if (enable_ice) { + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallUpdating, + marie_stat.number_of_LinphoneCallUpdating + 1, 20000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallUpdatedByRemote, + focus_stat.number_of_LinphoneCallUpdatedByRemote + 1, + liblinphone_tester_sip_timeout)); + } + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallStreamsRunning, + marie_stat.number_of_LinphoneCallStreamsRunning + 2, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneSubscriptionOutgoingProgress, + marie_stat.number_of_LinphoneSubscriptionOutgoingProgress + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneSubscriptionActive, + marie_stat.number_of_LinphoneSubscriptionActive + 1, + liblinphone_tester_sip_timeout)); + + if (BC_ASSERT_PTR_NOT_NULL(oconference)) { + BC_ASSERT_EQUAL(linphone_conference_get_participant_count(oconference), 4, int, "%0d"); + bctbx_list_t *devices = linphone_conference_get_participant_device_list(oconference); + BC_ASSERT_EQUAL(bctbx_list_size(devices), members.size(), size_t, "%zu"); + if (devices) { + bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); + } + } + + if (confAddr) { + for (auto mgr : participants) { + check_conference_info(mgr, confAddr, marie.getCMgr(), 5, 0, 0, initialSubject, + (accept && send_ics) ? description : NULL, 0, LinphoneConferenceInfoStateNew); + + LinphoneCall *pcall = linphone_core_get_call_by_remote_address2(mgr->lc, confAddr); + BC_ASSERT_PTR_NOT_NULL(pcall); + if (pcall) { + LinphoneCallLog *call_log = linphone_call_get_call_log(pcall); + BC_ASSERT_TRUE(linphone_call_log_was_conference(call_log)); + if (accept) { + linphone_call_accept(pcall); + } else { + linphone_call_decline(pcall, LinphoneReasonDeclined); + } + } + } + } + + int participant_conference_info_participants = 5; + if (accept) { + int participant_no = static_cast<int>(participants.size()); + for (auto mgr : participants) { + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallStreamsRunning, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateCreated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionOutgoingProgress, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionActive, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(coresList, &mgr->stat.number_of_NotifyReceived, 1, liblinphone_tester_sip_timeout)); + + check_conference_info(mgr, confAddr, marie.getCMgr(), participant_conference_info_participants, 0, 0, + initialSubject, (send_ics) ? description : NULL, 0, + LinphoneConferenceInfoStateNew); + + LinphoneCall *pcall = linphone_core_get_call_by_remote_address2(mgr->lc, confAddr); + BC_ASSERT_PTR_NOT_NULL(pcall); + if (pcall) { + LinphoneCallLog *call_log = linphone_call_get_call_log(pcall); + BC_ASSERT_TRUE(linphone_call_log_was_conference(call_log)); + LinphoneConferenceInfo *call_log_info = linphone_call_log_get_conference_info(call_log); + if (BC_ASSERT_PTR_NOT_NULL(call_log_info)) { + BC_ASSERT_TRUE(linphone_address_weak_equal( + linphone_conference_info_get_organizer(call_log_info), marie.getCMgr()->identity)); + BC_ASSERT_TRUE( + linphone_address_weak_equal(linphone_conference_info_get_uri(call_log_info), confAddr)); + + const bctbx_list_t *info_participants = + linphone_conference_info_get_participants(call_log_info); + // Original participants + Marie who joined the conference + BC_ASSERT_EQUAL(bctbx_list_size(info_participants), participant_conference_info_participants, + size_t, "%zu"); + + BC_ASSERT_GREATER_STRICT((long long)linphone_conference_info_get_date_time(call_log_info), 0, + long long, "%lld"); + const int duration_m = linphone_conference_info_get_duration(call_log_info); + BC_ASSERT_EQUAL(duration_m, 0, int, "%d"); + if (initialSubject) { + BC_ASSERT_STRING_EQUAL(linphone_conference_info_get_subject(call_log_info), initialSubject); + } else { + BC_ASSERT_PTR_NULL(linphone_conference_info_get_subject(call_log_info)); + } + if (send_ics) { + BC_ASSERT_STRING_EQUAL(linphone_conference_info_get_description(call_log_info), + description); + } else { + BC_ASSERT_PTR_NULL(linphone_conference_info_get_description(call_log_info)); + } + } + } + } + + if (enable_ice) { + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallUpdating, + focus_stat.number_of_LinphoneCallUpdating + participant_no - 1, + liblinphone_tester_sip_timeout)); + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallStreamsRunning, + focus_stat.number_of_LinphoneCallStreamsRunning + + ((enable_ice) ? 2 : 1) * static_cast<int>(participants.size() + 1), + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateCreated, + marie_stat.number_of_LinphoneConferenceStateCreated + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneSubscriptionOutgoingProgress, + marie_stat.number_of_LinphoneSubscriptionOutgoingProgress + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneSubscriptionActive, + marie_stat.number_of_LinphoneSubscriptionActive + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_NotifyReceived, + marie_stat.number_of_NotifyReceived + 1, liblinphone_tester_sip_timeout)); + + // Update to add to conference. + // If ICE is enabled, the addition to a conference may go through a resume of the call + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateCreated, + focus_stat.number_of_LinphoneConferenceStateCreated + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneSubscriptionIncomingReceived, + focus_stat.number_of_LinphoneSubscriptionIncomingReceived + participant_no, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneSubscriptionActive, + focus_stat.number_of_LinphoneSubscriptionActive + participant_no, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participants_added, + focus_stat.number_of_participants_added + participant_no, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_added, + focus_stat.number_of_participant_devices_added + participant_no, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_joined, + focus_stat.number_of_participant_devices_joined + participant_no, + liblinphone_tester_sip_timeout)); + + LinphoneConference *fconference = linphone_core_search_conference_2(focus.getLc(), confAddr); + BC_ASSERT_PTR_NOT_NULL(fconference); + + std::map<LinphoneCoreManager *, LinphoneParticipantRole> memberList; + for (const auto &member : members) { + try { + const auto &participantInfo = participantList.at(member); + memberList.insert(std::make_pair(member, participantInfo)); + } catch (std::out_of_range &) { + if (member == marie.getCMgr()) { + memberList.insert(std::make_pair(marie.getCMgr(), LinphoneParticipantRoleSpeaker)); + } else { + ms_fatal("Unable to find active participant %s in the participant list", + linphone_core_get_identity(member->lc)); + } + } + } + wait_for_conference_streams({focus, marie, pauline, laure, michelle, berthe}, conferenceMgrs, + focus.getCMgr(), memberList, confAddr, enable_video); + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, laure, michelle, berthe}).waitUntil(chrono::seconds(15), [] { + return false; + }); + + for (auto mgr : conferenceMgrs) { + LinphoneAddress *uri = linphone_address_new(linphone_core_get_identity(mgr->lc)); + LinphoneConference *pconference = linphone_core_search_conference_2(mgr->lc, confAddr); + linphone_address_unref(uri); + BC_ASSERT_PTR_NOT_NULL(pconference); + + LinphoneVideoActivationPolicy *mgr_pol = linphone_core_get_video_activation_policy(mgr->lc); + bool_t video_enabled = + !!((mgr == marie.getCMgr()) ? linphone_video_activation_policy_get_automatically_initiate(mgr_pol) + : linphone_video_activation_policy_get_automatically_accept(mgr_pol)); + linphone_video_activation_policy_unref(mgr_pol); + + if (pconference) { + int no_participants = 0; + if (mgr == focus.getCMgr()) { + no_participants = static_cast<int>(members.size()); + BC_ASSERT_FALSE(linphone_conference_is_in(pconference)); + } else { + no_participants = participant_no; + BC_ASSERT_TRUE(linphone_conference_is_in(pconference)); + LinphoneCall *current_call = linphone_core_get_current_call(mgr->lc); + BC_ASSERT_PTR_NOT_NULL(current_call); + if (current_call) { + BC_ASSERT_EQUAL((int)linphone_call_get_state(current_call), + (int)LinphoneCallStateStreamsRunning, int, "%0d"); + } + if (enable_ice) { + BC_ASSERT_TRUE(check_ice(mgr, focus.getCMgr(), LinphoneIceStateHostConnection)); + } + + int no_streams_audio = 1; + int no_streams_video = 0; + int no_active_streams_video = 0; + if (initiate_video && accept_video) { + no_streams_video = (no_participants + 2); + } else if (initiate_video) { + no_streams_video = (mgr == marie.getCMgr()) ? 2 : 1; + } else if (accept_video) { + no_streams_video = (mgr == marie.getCMgr()) ? 0 : (no_participants + 1); + } else { + no_streams_video = (mgr == marie.getCMgr()) ? 0 : 1; + } + + if (video_enabled) { + if (initiate_video && accept_video) { + no_active_streams_video = (no_participants + 2); + } else if (initiate_video) { + no_active_streams_video = (mgr == marie.getCMgr()) ? 2 : 0; + } else if (accept_video) { + no_active_streams_video = (no_participants + 1); + } + } + int no_streams_text = 0; + + LinphoneCall *pcall = linphone_core_get_call_by_remote_address2(mgr->lc, confAddr); + BC_ASSERT_PTR_NOT_NULL(pcall); + if (pcall) { + _linphone_call_check_nb_streams(pcall, no_streams_audio, no_streams_video, no_streams_text); + _linphone_call_check_nb_active_streams(pcall, no_streams_audio, no_active_streams_video, + no_streams_text); + const LinphoneCallParams *call_lparams = linphone_call_get_params(pcall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_lparams), video_enabled, int, + "%0d"); + const LinphoneCallParams *call_rparams = linphone_call_get_remote_params(pcall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_rparams), video_enabled, int, + "%0d"); + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(pcall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_cparams), video_enabled, int, + "%0d"); + } + + LinphoneCall *ccall = linphone_core_get_call_by_remote_address2(focus.getLc(), mgr->identity); + BC_ASSERT_PTR_NOT_NULL(ccall); + if (ccall) { + _linphone_call_check_nb_streams(ccall, no_streams_audio, no_streams_video, no_streams_text); + _linphone_call_check_nb_active_streams(ccall, no_streams_audio, no_active_streams_video, + no_streams_text); + const LinphoneCallParams *call_lparams = linphone_call_get_params(ccall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_lparams), video_enabled, int, + "%0d"); + const LinphoneCallParams *call_rparams = linphone_call_get_remote_params(ccall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_rparams), video_enabled, int, + "%0d"); + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(ccall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_cparams), video_enabled, int, + "%0d"); + } + } + BC_ASSERT_EQUAL(linphone_conference_get_participant_count(pconference), no_participants, int, + "%0d"); + bctbx_list_t *devices = linphone_conference_get_participant_device_list(pconference); + BC_ASSERT_EQUAL(bctbx_list_size(devices), members.size(), size_t, "%zu"); + if (devices) { + bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); + } + BC_ASSERT_STRING_EQUAL(linphone_conference_get_subject(pconference), initialSubject); + LinphoneParticipant *me = linphone_conference_get_me(pconference); + BC_ASSERT_TRUE(linphone_participant_is_admin(me) == + ((mgr == marie.getCMgr()) || (mgr == focus.getCMgr()))); + BC_ASSERT_TRUE(linphone_address_weak_equal(linphone_participant_get_address(me), mgr->identity)); + bctbx_list_t *participants = linphone_conference_get_participant_list(pconference); + for (bctbx_list_t *itp = participants; itp; itp = bctbx_list_next(itp)) { + LinphoneParticipant *p = (LinphoneParticipant *)bctbx_list_get_data(itp); + BC_ASSERT_TRUE(linphone_participant_is_admin(p) == + linphone_address_weak_equal(linphone_participant_get_address(p), + marie.getCMgr()->identity)); + } + bctbx_list_free_with_data(participants, (void (*)(void *))linphone_participant_unref); + + if (mgr != focus.getCMgr()) { + check_conference_ssrc(fconference, pconference); + } + + LinphoneConference *conference = linphone_core_search_conference_2(mgr->lc, confAddr); + BC_ASSERT_PTR_NOT_NULL(conference); + if (conference) { + bctbx_list_t *devices = linphone_conference_get_participant_device_list(conference); + for (bctbx_list_t *itd = devices; itd; itd = bctbx_list_next(itd)) { + LinphoneParticipantDevice *d = (LinphoneParticipantDevice *)bctbx_list_get_data(itd); + // If we are currently carrying out checks on the conference server side, the must set the + // value of video enabled flag for each participant. In fact each call session may have the + // video enabled or not and this is taken into account to compute the video availablity + // flag. Nonetheless, this is not required for the participants as they only have one call + // session towards the conference server therefore we can reuse the value computed earlier + // on. + if (mgr == focus.getCMgr()) { + if (linphone_address_weak_equal(marie.getIdentity().toC(), + linphone_participant_device_get_address(d))) { + // The organizer will not offer video streams in its INVITE to join a conference if + // the policy doesn't allow it + if (!initiate_video) { + video_enabled = FALSE; + } else { + video_enabled = TRUE; + } + } else { + // The participants will not accept video streams to answer the conference server + // INVITE to join a conference if the policy doesn't allow it + if (!accept_video) { + video_enabled = FALSE; + } else { + video_enabled = TRUE; + } + } + } + + if (enable_video) { + if (linphone_conference_is_me(conference, linphone_participant_device_get_address(d))) { + BC_ASSERT_TRUE(linphone_participant_device_get_stream_availability( + d, LinphoneStreamTypeVideo) == video_enabled); + } else { + bool_t video_available = + linphone_participant_device_get_stream_availability(d, LinphoneStreamTypeVideo); + LinphoneMediaDirection video_direction = + linphone_participant_device_get_stream_capability(d, LinphoneStreamTypeVideo); + BC_ASSERT_TRUE(video_available == + (((video_direction == LinphoneMediaDirectionSendOnly) || + (video_direction == LinphoneMediaDirectionSendRecv)) && + video_enabled)); + } + } else { + BC_ASSERT_FALSE( + linphone_participant_device_get_stream_availability(d, LinphoneStreamTypeVideo)); + } + } + + if (devices) { + bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); + } + } + } + } + + if (enable_video) { + LinphoneCall *pauline_call = linphone_core_get_call_by_remote_address2(pauline.getLc(), confAddr); + BC_ASSERT_PTR_NOT_NULL(pauline_call); + + Address paulineAddr = pauline.getIdentity(); + LinphoneCall *focus_call = linphone_core_get_call_by_remote_address2(focus.getLc(), paulineAddr.toC()); + BC_ASSERT_PTR_NOT_NULL(focus_call); + + LinphoneVideoActivationPolicy *pol = linphone_core_get_video_activation_policy(pauline.getLc()); + bool_t enable = !!!linphone_video_activation_policy_get_automatically_accept(pol); + linphone_video_activation_policy_unref(pol); + + LinphoneAddress *paulineUri = linphone_address_new(linphone_core_get_identity(pauline.getLc())); + LinphoneConference *paulineConference = + linphone_core_search_conference(pauline.getLc(), NULL, paulineUri, confAddr, NULL); + linphone_address_unref(paulineUri); + BC_ASSERT_PTR_NOT_NULL(paulineConference); + + for (int i = 0; i < 4; i++) { + set_video_settings_in_conference(focus.getCMgr(), pauline.getCMgr(), members, confAddr, enable, + LinphoneMediaDirectionSendRecv, enable, + LinphoneMediaDirectionSendRecv); + + if (paulineConference) { + bctbx_list_t *devices = linphone_conference_get_participant_device_list(paulineConference); + for (bctbx_list_t *itd = devices; itd; itd = bctbx_list_next(itd)) { + LinphoneParticipantDevice *d = (LinphoneParticipantDevice *)bctbx_list_get_data(itd); + if (enable) { + if (linphone_conference_is_me(paulineConference, + linphone_participant_device_get_address(d))) { + BC_ASSERT_TRUE(linphone_participant_device_get_stream_availability( + d, LinphoneStreamTypeVideo)); + } else { + BC_ASSERT_TRUE(linphone_participant_device_get_stream_availability( + d, LinphoneStreamTypeVideo) == + (linphone_participant_device_get_stream_capability( + d, LinphoneStreamTypeVideo) == LinphoneMediaDirectionSendRecv)); + } + } else { + BC_ASSERT_TRUE(linphone_participant_device_get_stream_availability( + d, LinphoneStreamTypeVideo) == enable); + } + } + + if (devices) { + bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); + } + } + // Wait a little bit + wait_for_list(coresList, NULL, 0, 1000); + + enable = !enable; + } + } + + focus_stat = focus.getStats(); + for (auto mgr : members) { + LinphoneCall *call = linphone_core_get_call_by_remote_address2(mgr->lc, focus.getCMgr()->identity); + BC_ASSERT_PTR_NOT_NULL(call); + if (call) { + ms_message("%s is terminating call with %s", linphone_core_get_identity(mgr->lc), + linphone_core_get_identity(focus.getLc())); + linphone_call_terminate(call); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallEnd, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallReleased, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionTerminated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, + &mgr->stat.number_of_LinphoneConferenceStateTerminationPending, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateTerminated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateDeleted, 1, + liblinphone_tester_sip_timeout)); + + LinphoneAddress *uri = linphone_address_new(linphone_core_get_identity(mgr->lc)); + LinphoneConference *pconference = + linphone_core_search_conference(mgr->lc, NULL, uri, confAddr, NULL); + BC_ASSERT_PTR_NULL(pconference); + linphone_address_unref(uri); + } + } + + int members_no = static_cast<int>(members.size()); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallEnd, + focus_stat.number_of_LinphoneCallEnd + members_no, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallReleased, + focus_stat.number_of_LinphoneCallReleased + members_no, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneSubscriptionTerminated, + focus_stat.number_of_LinphoneSubscriptionTerminated + members_no, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participants_removed, + focus_stat.number_of_participants_removed + members_no, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_removed, + focus_stat.number_of_participant_devices_removed + members_no, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_EQUAL(focus.getStats().number_of_LinphoneConferenceStateTerminationPending, + focus_stat.number_of_LinphoneConferenceStateTerminationPending, int, "%d"); + BC_ASSERT_EQUAL(focus.getStats().number_of_LinphoneConferenceStateTerminated, + focus_stat.number_of_LinphoneConferenceStateTerminated, int, "%d"); + BC_ASSERT_EQUAL(focus.getStats().number_of_LinphoneConferenceStateDeleted, + focus_stat.number_of_LinphoneConferenceStateDeleted, int, "%d"); + + for (auto mgr : {focus.getCMgr()}) { + LinphoneConference *pconference = linphone_core_search_conference_2(mgr->lc, confAddr); + BC_ASSERT_PTR_NOT_NULL(pconference); + if (pconference) { + BC_ASSERT_EQUAL(linphone_conference_get_participant_count(pconference), 0, int, "%0d"); + bctbx_list_t *devices = linphone_conference_get_participant_device_list(pconference); + BC_ASSERT_EQUAL(bctbx_list_size(devices), 0, size_t, "%zu"); + if (devices) { + bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); + } + BC_ASSERT_STRING_EQUAL(linphone_conference_get_subject(pconference), initialSubject); + } + } + + focus_stat = focus.getStats(); + const bctbx_list_t *calls = linphone_core_get_calls(focus.getLc()); + BC_ASSERT_EQUAL(bctbx_list_size(calls), 0, size_t, "%zu"); + + // Explicitely terminate conference as those on server are static by default + if (fconference) { + linphone_conference_terminate(fconference); + } + BC_ASSERT_TRUE(wait_for_list(coresList, + &focus.getStats().number_of_LinphoneConferenceStateTerminationPending, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateTerminated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateDeleted, 1, + liblinphone_tester_sip_timeout)); + } else { + int members_no = static_cast<int>(members.size()); + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallEnd, + focus_stat.number_of_LinphoneCallEnd + 4, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallReleased, + focus_stat.number_of_LinphoneCallReleased + 4, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participants_removed, + focus_stat.number_of_participants_removed + 4, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_removed, + focus_stat.number_of_participant_devices_removed + 4, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_participants_removed, 4, + 2 * liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_participant_devices_removed, 4, + liblinphone_tester_sip_timeout)); + + for (auto mgr : participants) { + BC_ASSERT_TRUE( + wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallEnd, 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallReleased, 1, + liblinphone_tester_sip_timeout)); + } + + ms_message("%s is terminating call with %s", linphone_core_get_identity(marie.getLc()), + linphone_core_get_identity(focus.getLc())); + LinphoneCall *call = linphone_core_get_call_by_remote_address2(marie.getLc(), focus.getCMgr()->identity); + linphone_call_terminate(call); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallEnd, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallReleased, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallEnd, + focus_stat.number_of_LinphoneCallEnd + members_no, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallReleased, + focus_stat.number_of_LinphoneCallReleased + members_no, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participants_removed, + focus_stat.number_of_participants_removed + members_no, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_removed, + focus_stat.number_of_participant_devices_removed + members_no, + liblinphone_tester_sip_timeout)); + } + + for (auto mgr : members) { + const bctbx_list_t *call_logs = linphone_core_get_call_logs(mgr->lc); + BC_ASSERT_EQUAL((unsigned int)bctbx_list_size(call_logs), 1, unsigned int, "%u"); + + bctbx_list_t *mgr_focus_call_log = + linphone_core_get_call_history_2(mgr->lc, focus.getCMgr()->identity, mgr->identity); + BC_ASSERT_PTR_NOT_NULL(mgr_focus_call_log); + if (mgr_focus_call_log) { + BC_ASSERT_EQUAL((unsigned int)bctbx_list_size(mgr_focus_call_log), 1, unsigned int, "%u"); + for (bctbx_list_t *it = mgr_focus_call_log; it; it = bctbx_list_next(it)) { + LinphoneCallLog *call_log = (LinphoneCallLog *)it->data; + BC_ASSERT_TRUE(linphone_call_log_was_conference(call_log)); + } + bctbx_list_free_with_data(mgr_focus_call_log, (bctbx_list_free_func)linphone_call_log_unref); + } + check_conference_info( + mgr, confAddr, marie.getCMgr(), (mgr == marie.getCMgr()) ? 5 : participant_conference_info_participants, + 0, 0, initialSubject, ((accept && send_ics) || (mgr == marie.getCMgr())) ? description : NULL, 0, + LinphoneConferenceInfoStateNew); + } + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, laure, michelle, berthe}).waitUntil(chrono::seconds(2), [] { + return false; + }); + + linphone_address_unref(confAddr); + bctbx_list_free(coresList); + } +} + +static void create_simple_conference_dial_out(void) { + LinphoneVideoActivationPolicy *pol = linphone_factory_create_video_activation_policy(linphone_factory_get()); + linphone_video_activation_policy_set_automatically_accept(pol, FALSE); + linphone_video_activation_policy_set_automatically_initiate(pol, FALSE); + create_conference_dial_out_base(FALSE, LinphoneConferenceLayoutActiveSpeaker, pol, FALSE, FALSE, + LinphoneConferenceParticipantListTypeClosed, TRUE, FALSE); + linphone_video_activation_policy_unref(pol); +} + +static void create_simple_conference_dial_out_and_ics(void) { + LinphoneVideoActivationPolicy *pol = linphone_factory_create_video_activation_policy(linphone_factory_get()); + linphone_video_activation_policy_set_automatically_accept(pol, TRUE); + linphone_video_activation_policy_set_automatically_initiate(pol, TRUE); + create_conference_dial_out_base(TRUE, LinphoneConferenceLayoutGrid, pol, TRUE, TRUE, + LinphoneConferenceParticipantListTypeOpen, TRUE, FALSE); + linphone_video_activation_policy_unref(pol); +} + +static void create_simple_conference_dial_out_with_calls_declined(void) { + LinphoneVideoActivationPolicy *pol = linphone_factory_create_video_activation_policy(linphone_factory_get()); + linphone_video_activation_policy_set_automatically_accept(pol, TRUE); + linphone_video_activation_policy_set_automatically_initiate(pol, TRUE); + create_conference_dial_out_base(FALSE, LinphoneConferenceLayoutGrid, pol, TRUE, TRUE, + LinphoneConferenceParticipantListTypeOpen, FALSE, FALSE); + linphone_video_activation_policy_unref(pol); +} + +static void create_simple_conference_dial_out_participant_codec_mismatch(void) { + LinphoneVideoActivationPolicy *pol = linphone_factory_create_video_activation_policy(linphone_factory_get()); + linphone_video_activation_policy_set_automatically_accept(pol, FALSE); + linphone_video_activation_policy_set_automatically_initiate(pol, FALSE); + create_conference_dial_out_base(FALSE, LinphoneConferenceLayoutActiveSpeaker, pol, FALSE, FALSE, + LinphoneConferenceParticipantListTypeClosed, TRUE, TRUE); + linphone_video_activation_policy_unref(pol); +} + +static void create_simple_conference_dial_out_with_video_not_accepted(void) { + LinphoneVideoActivationPolicy *pol = linphone_factory_create_video_activation_policy(linphone_factory_get()); + linphone_video_activation_policy_set_automatically_accept(pol, FALSE); + linphone_video_activation_policy_set_automatically_initiate(pol, TRUE); + create_conference_dial_out_base(FALSE, LinphoneConferenceLayoutActiveSpeaker, pol, FALSE, FALSE, + LinphoneConferenceParticipantListTypeClosed, TRUE, FALSE); + linphone_video_activation_policy_unref(pol); +} + +static void create_simple_conference_dial_out_with_video_not_initiated(void) { + LinphoneVideoActivationPolicy *pol = linphone_factory_create_video_activation_policy(linphone_factory_get()); + linphone_video_activation_policy_set_automatically_accept(pol, TRUE); + linphone_video_activation_policy_set_automatically_initiate(pol, FALSE); + create_conference_dial_out_base(FALSE, LinphoneConferenceLayoutGrid, pol, FALSE, FALSE, + LinphoneConferenceParticipantListTypeClosed, TRUE, FALSE); + linphone_video_activation_policy_unref(pol); +} + +static void create_simple_conference_dial_out_organizer_codec_mismatch(void) { + Focus focus("chloe_rc"); + { // to make sure focus is destroyed after clients. + ClientConference marie("marie_rc", focus.getIdentity()); + ClientConference pauline("pauline_rc", focus.getIdentity()); + ClientConference laure("laure_tcp_rc", focus.getIdentity()); + ClientConference michelle("michelle_rc", focus.getIdentity()); + ClientConference berthe("berthe_rc", focus.getIdentity()); + + focus.registerAsParticipantDevice(marie); + focus.registerAsParticipantDevice(pauline); + focus.registerAsParticipantDevice(laure); + focus.registerAsParticipantDevice(michelle); + focus.registerAsParticipantDevice(berthe); + + setup_conference_info_cbs(marie.getCMgr()); + + bctbx_list_t *coresList = NULL; + + for (auto mgr : {focus.getCMgr(), marie.getCMgr(), pauline.getCMgr(), laure.getCMgr(), michelle.getCMgr(), + berthe.getCMgr()}) { + if (mgr == marie.getCMgr()) { + disable_all_audio_codecs_except_one(mgr->lc, "pcmu", -1); + } else { + disable_all_audio_codecs_except_one(mgr->lc, "pcma", -1); + } + + LinphoneVideoActivationPolicy *pol = + linphone_factory_create_video_activation_policy(linphone_factory_get()); + linphone_video_activation_policy_set_automatically_accept(pol, TRUE); + linphone_video_activation_policy_set_automatically_initiate(pol, TRUE); + linphone_core_set_video_activation_policy(mgr->lc, pol); + linphone_video_activation_policy_unref(pol); + + linphone_core_set_video_device(mgr->lc, liblinphone_tester_mire_id); + linphone_core_enable_video_capture(mgr->lc, TRUE); + linphone_core_enable_video_display(mgr->lc, TRUE); + + coresList = bctbx_list_append(coresList, mgr->lc); + } + + linphone_core_set_file_transfer_server(marie.getLc(), file_transfer_url); + + stats focus_stat = focus.getStats(); + stats marie_stat = marie.getStats(); + + std::list<LinphoneCoreManager *> conferenceMgrs{focus.getCMgr(), marie.getCMgr(), pauline.getCMgr(), + laure.getCMgr(), michelle.getCMgr(), berthe.getCMgr()}; + std::list<LinphoneCoreManager *> members{marie.getCMgr(), pauline.getCMgr(), laure.getCMgr(), + michelle.getCMgr(), berthe.getCMgr()}; + std::list<LinphoneCoreManager *> participants{pauline.getCMgr(), laure.getCMgr(), michelle.getCMgr(), + berthe.getCMgr()}; + + const char *initialSubject = "Schedule of the trip towards the top of Europe"; + const char *description = "To the Goutier mountain hut!!!! :-)"; + std::map<LinphoneCoreManager *, LinphoneParticipantRole> participantList; + LinphoneParticipantRole role = LinphoneParticipantRoleSpeaker; + for (auto &p : participants) { + participantList.insert(std::make_pair(p, role)); + role = (role == LinphoneParticipantRoleSpeaker) ? LinphoneParticipantRoleListener + : LinphoneParticipantRoleSpeaker; + } + LinphoneAddress *confAddr = + create_conference_on_server(focus, marie, participantList, -1, -1, initialSubject, description, TRUE); + BC_ASSERT_PTR_NULL(confAddr); + + // Chat room creation to send ICS + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateCreationFailed, + focus_stat.number_of_LinphoneConferenceStateCreationFailed + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_FALSE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateCreated, + marie_stat.number_of_LinphoneConferenceStateCreated + 1, 3000)); + BC_ASSERT_FALSE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallIncomingReceived, + focus_stat.number_of_LinphoneCallIncomingReceived + 1, 1000)); + + LinphoneConference *fconference = linphone_core_search_conference_2(focus.getLc(), confAddr); + BC_ASSERT_PTR_NULL(fconference); + + if (confAddr) linphone_address_unref(confAddr); + bctbx_list_free(coresList); + } +} + +static void create_simple_conference_dial_out_with_some_calls_declined_base(LinphoneReason reason) { + Focus focus("chloe_rc"); + { // to make sure focus is destroyed after clients. + ClientConference marie("marie_rc", focus.getIdentity()); + ClientConference pauline("pauline_rc", focus.getIdentity()); + ClientConference laure("laure_tcp_rc", focus.getIdentity()); + ClientConference michelle("michelle_rc", focus.getIdentity()); + ClientConference berthe("berthe_rc", focus.getIdentity()); + + focus.registerAsParticipantDevice(marie); + focus.registerAsParticipantDevice(pauline); + focus.registerAsParticipantDevice(laure); + focus.registerAsParticipantDevice(michelle); + focus.registerAsParticipantDevice(berthe); + + setup_conference_info_cbs(marie.getCMgr()); + + bctbx_list_t *coresList = NULL; + + for (auto mgr : {focus.getCMgr(), marie.getCMgr(), pauline.getCMgr(), laure.getCMgr(), michelle.getCMgr(), + berthe.getCMgr()}) { + LinphoneVideoActivationPolicy *pol = + linphone_factory_create_video_activation_policy(linphone_factory_get()); + linphone_video_activation_policy_set_automatically_accept(pol, TRUE); + linphone_video_activation_policy_set_automatically_initiate(pol, TRUE); + linphone_core_set_video_activation_policy(mgr->lc, pol); + linphone_video_activation_policy_unref(pol); + + linphone_core_set_video_device(mgr->lc, liblinphone_tester_mire_id); + linphone_core_enable_video_capture(mgr->lc, TRUE); + linphone_core_enable_video_display(mgr->lc, TRUE); + + if (mgr != focus.getCMgr()) { + linphone_core_set_default_conference_layout(mgr->lc, LinphoneConferenceLayoutActiveSpeaker); + } + + enable_stun_in_core(mgr, TRUE, TRUE); + linphone_core_manager_wait_for_stun_resolution(mgr); + + coresList = bctbx_list_append(coresList, mgr->lc); + } + + linphone_core_set_file_transfer_server(marie.getLc(), file_transfer_url); + linphone_core_set_conference_participant_list_type(focus.getLc(), LinphoneConferenceParticipantListTypeOpen); + + stats focus_stat = focus.getStats(); + stats marie_stat = marie.getStats(); + + std::list<LinphoneCoreManager *> active_participants{pauline.getCMgr(), michelle.getCMgr()}; + std::list<LinphoneCoreManager *> declining_participants{laure.getCMgr(), berthe.getCMgr()}; + std::list<LinphoneCoreManager *> participants = active_participants; + for (auto mgr : declining_participants) { + participants.push_back(mgr); + } + + std::list<LinphoneCoreManager *> all_active_participants = active_participants; + all_active_participants.push_back(marie.getCMgr()); + + std::list<LinphoneCoreManager *> conference_members = all_active_participants; + conference_members.push_back(focus.getCMgr()); + + const char *initialSubject = "Team building hike to the mountain hut"; + const char *description = "Having fun!!!! :-)"; + std::map<LinphoneCoreManager *, LinphoneParticipantRole> participantList; + LinphoneParticipantRole role = LinphoneParticipantRoleSpeaker; + for (auto &p : participants) { + participantList.insert(std::make_pair(p, role)); + role = (role == LinphoneParticipantRoleSpeaker) ? LinphoneParticipantRoleListener + : LinphoneParticipantRoleSpeaker; + } + LinphoneAddress *confAddr = + create_conference_on_server(focus, marie, participantList, -1, -1, initialSubject, description, FALSE); + BC_ASSERT_PTR_NOT_NULL(confAddr); + + // Chat room creation to send ICS + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateCreated, 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallOutgoingInit, + marie_stat.number_of_LinphoneCallOutgoingInit + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallOutgoingInit, + focus_stat.number_of_LinphoneCallOutgoingInit + 4, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallIncomingReceived, + focus_stat.number_of_LinphoneCallIncomingReceived + 1, + liblinphone_tester_sip_timeout)); + + for (auto mgr : participants) { + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallIncomingReceived, 1, + liblinphone_tester_sip_timeout)); + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallStreamsRunning, + marie_stat.number_of_LinphoneCallStreamsRunning + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallUpdating, + marie_stat.number_of_LinphoneCallUpdating + 1, 20000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallUpdatedByRemote, + focus_stat.number_of_LinphoneCallUpdatedByRemote + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallStreamsRunning, + marie_stat.number_of_LinphoneCallStreamsRunning + 2, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateCreated, + marie_stat.number_of_LinphoneConferenceStateCreated + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneSubscriptionOutgoingProgress, + marie_stat.number_of_LinphoneSubscriptionOutgoingProgress + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneSubscriptionActive, + marie_stat.number_of_LinphoneSubscriptionActive + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_NotifyReceived, + marie_stat.number_of_NotifyReceived + 1, liblinphone_tester_sip_timeout)); + + LinphoneConference *oconference = linphone_core_search_conference_2(marie.getLc(), confAddr); + if (BC_ASSERT_PTR_NOT_NULL(oconference)) { + BC_ASSERT_EQUAL(linphone_conference_get_participant_count(oconference), 4, int, "%0d"); + bctbx_list_t *devices = linphone_conference_get_participant_device_list(oconference); + BC_ASSERT_EQUAL(bctbx_list_size(devices), 5, size_t, "%zu"); + if (devices) { + bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); + } + } + + if (confAddr) { + for (auto mgr : participants) { + check_conference_info(mgr, confAddr, marie.getCMgr(), 5, 0, 0, initialSubject, NULL, 0, + LinphoneConferenceInfoStateNew); + + LinphoneCall *pcall = linphone_core_get_call_by_remote_address2(mgr->lc, confAddr); + BC_ASSERT_PTR_NOT_NULL(pcall); + if (pcall) { + LinphoneCallLog *call_log = linphone_call_get_call_log(pcall); + BC_ASSERT_TRUE(linphone_call_log_was_conference(call_log)); + if (std::find(active_participants.cbegin(), active_participants.cend(), mgr) != + active_participants.cend()) { + linphone_call_accept(pcall); + } else { + linphone_call_decline(pcall, reason); + } + } + } + } + + for (auto mgr : active_participants) { + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallStreamsRunning, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallUpdatedByRemote, 1, 20000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallStreamsRunning, 2, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateCreated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionOutgoingProgress, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionActive, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(coresList, &mgr->stat.number_of_NotifyReceived, 1, liblinphone_tester_sip_timeout)); + + check_conference_info(mgr, confAddr, marie.getCMgr(), 5, 0, 0, initialSubject, NULL, 0, + LinphoneConferenceInfoStateNew); + + LinphoneCall *pcall = linphone_core_get_call_by_remote_address2(mgr->lc, confAddr); + BC_ASSERT_PTR_NOT_NULL(pcall); + if (pcall) { + LinphoneCallLog *call_log = linphone_call_get_call_log(pcall); + BC_ASSERT_TRUE(linphone_call_log_was_conference(call_log)); + LinphoneConferenceInfo *call_log_info = linphone_call_log_get_conference_info(call_log); + if (BC_ASSERT_PTR_NOT_NULL(call_log_info)) { + BC_ASSERT_TRUE(linphone_address_weak_equal(linphone_conference_info_get_organizer(call_log_info), + marie.getCMgr()->identity)); + BC_ASSERT_TRUE( + linphone_address_weak_equal(linphone_conference_info_get_uri(call_log_info), confAddr)); + + const bctbx_list_t *info_participants = linphone_conference_info_get_participants(call_log_info); + // Original participants + Marie who joined the conference + BC_ASSERT_EQUAL(bctbx_list_size(info_participants), 5, size_t, "%zu"); + + BC_ASSERT_GREATER_STRICT((long long)linphone_conference_info_get_date_time(call_log_info), 0, + long long, "%lld"); + const int duration_m = linphone_conference_info_get_duration(call_log_info); + BC_ASSERT_EQUAL(duration_m, 0, int, "%d"); + if (initialSubject) { + BC_ASSERT_STRING_EQUAL(linphone_conference_info_get_subject(call_log_info), initialSubject); + } else { + BC_ASSERT_PTR_NULL(linphone_conference_info_get_subject(call_log_info)); + } + BC_ASSERT_PTR_NULL(linphone_conference_info_get_description(call_log_info)); + } + } + } + + BC_ASSERT_TRUE( + wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallUpdating, + focus_stat.number_of_LinphoneCallUpdating + static_cast<int>(active_participants.size()), + liblinphone_tester_sip_timeout) + + 1); + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallStreamsRunning, + focus_stat.number_of_LinphoneCallStreamsRunning + + 2 * static_cast<int>(active_participants.size() + 1), + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateCreated, + focus_stat.number_of_LinphoneConferenceStateCreated + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneSubscriptionIncomingReceived, + focus_stat.number_of_LinphoneSubscriptionIncomingReceived + + static_cast<int>(active_participants.size()) + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneSubscriptionActive, + focus_stat.number_of_LinphoneSubscriptionActive + + static_cast<int>(active_participants.size()) + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participants_added, + focus_stat.number_of_participants_added + 5, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_added, + focus_stat.number_of_participant_devices_added + 5, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_joined, + focus_stat.number_of_participant_devices_joined + + static_cast<int>(all_active_participants.size()), + liblinphone_tester_sip_timeout)); + + // Participants that declined the call + for (auto mgr : declining_participants) { + BC_ASSERT_TRUE( + wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallEnd, 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallReleased, 1, liblinphone_tester_sip_timeout)); + } + + if (reason == LinphoneReasonBusy) { + BC_ASSERT_TRUE( + wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallError, + focus_stat.number_of_LinphoneCallError + static_cast<int>(declining_participants.size()), + liblinphone_tester_sip_timeout)); + BC_ASSERT_FALSE(wait_for_list(coresList, &marie.getStats().number_of_participants_removed, + marie_stat.number_of_participants_removed + 1, 3000)); + BC_ASSERT_FALSE(wait_for_list(coresList, &focus.getStats().number_of_participants_removed, + focus_stat.number_of_participants_removed + 1, 1000)); + } else { + BC_ASSERT_TRUE( + wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallEnd, + focus_stat.number_of_LinphoneCallEnd + static_cast<int>(declining_participants.size()), + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_participants_removed, + marie_stat.number_of_participants_removed + 2, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participants_removed, + focus_stat.number_of_participants_removed + 2, + liblinphone_tester_sip_timeout)); + } + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_participant_devices_removed, + marie_stat.number_of_participant_devices_removed + 2, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_removed, + focus_stat.number_of_participant_devices_removed + 2, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE( + wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallReleased, + focus_stat.number_of_LinphoneCallReleased + static_cast<int>(declining_participants.size()), + liblinphone_tester_sip_timeout)); + + LinphoneConference *fconference = linphone_core_search_conference_2(focus.getLc(), confAddr); + BC_ASSERT_PTR_NOT_NULL(fconference); + + std::map<LinphoneCoreManager *, LinphoneParticipantRole> memberList; + for (const auto &member : all_active_participants) { + try { + const auto &participantInfo = participantList.at(member); + memberList.insert(std::make_pair(member, participantInfo)); + } catch (std::out_of_range &) { + if (member == marie.getCMgr()) { + memberList.insert(std::make_pair(marie.getCMgr(), LinphoneParticipantRoleSpeaker)); + } else { + ms_fatal("Unable to find active participant %s in the participant list", + linphone_core_get_identity(member->lc)); + } + } + } + wait_for_conference_streams({focus, marie, pauline, laure, michelle, berthe}, conference_members, + focus.getCMgr(), memberList, confAddr, TRUE); + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, laure, michelle, berthe}).waitUntil(chrono::seconds(15), [] { + return false; + }); + + for (auto mgr : conference_members) { + LinphoneAddress *uri = linphone_address_new(linphone_core_get_identity(mgr->lc)); + LinphoneConference *pconference = linphone_core_search_conference_2(mgr->lc, confAddr); + linphone_address_unref(uri); + BC_ASSERT_PTR_NOT_NULL(pconference); + if (pconference) { + size_t no_participants = 0; + if (mgr == focus.getCMgr()) { + no_participants = all_active_participants.size(); + BC_ASSERT_FALSE(linphone_conference_is_in(pconference)); + } else { + no_participants = active_participants.size(); + BC_ASSERT_TRUE(linphone_conference_is_in(pconference)); + LinphoneCall *current_call = linphone_core_get_current_call(mgr->lc); + BC_ASSERT_PTR_NOT_NULL(current_call); + if (current_call) { + BC_ASSERT_EQUAL((int)linphone_call_get_state(current_call), + (int)LinphoneCallStateStreamsRunning, int, "%0d"); + } + BC_ASSERT_TRUE(check_ice(mgr, focus.getCMgr(), LinphoneIceStateHostConnection)); + + LinphoneVideoActivationPolicy *pol = linphone_core_get_video_activation_policy(mgr->lc); + bool_t enabled = !!linphone_video_activation_policy_get_automatically_initiate(pol); + linphone_video_activation_policy_unref(pol); + + int no_streams_audio = 1; + int no_streams_video = (enabled) ? (static_cast<int>(all_active_participants.size()) + 1) : 0; + int no_streams_text = 0; + + LinphoneCall *pcall = linphone_core_get_call_by_remote_address2(mgr->lc, confAddr); + BC_ASSERT_PTR_NOT_NULL(pcall); + if (pcall) { + _linphone_call_check_nb_streams(pcall, no_streams_audio, no_streams_video, no_streams_text); + _linphone_call_check_nb_active_streams(pcall, no_streams_audio, no_streams_video, + no_streams_text); + const LinphoneCallParams *call_lparams = linphone_call_get_params(pcall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_lparams), enabled, int, "%0d"); + const LinphoneCallParams *call_rparams = linphone_call_get_remote_params(pcall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_rparams), enabled, int, "%0d"); + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(pcall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_cparams), enabled, int, "%0d"); + } + + LinphoneCall *ccall = linphone_core_get_call_by_remote_address2(focus.getLc(), mgr->identity); + BC_ASSERT_PTR_NOT_NULL(ccall); + if (ccall) { + _linphone_call_check_nb_streams(ccall, no_streams_audio, no_streams_video, no_streams_text); + _linphone_call_check_nb_active_streams(ccall, no_streams_audio, no_streams_video, + no_streams_text); + const LinphoneCallParams *call_lparams = linphone_call_get_params(ccall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_lparams), enabled, int, "%0d"); + const LinphoneCallParams *call_rparams = linphone_call_get_remote_params(ccall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_rparams), enabled, int, "%0d"); + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(ccall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_cparams), enabled, int, "%0d"); + } + } + BC_ASSERT_STRING_EQUAL(linphone_conference_get_subject(pconference), initialSubject); + LinphoneParticipant *me = linphone_conference_get_me(pconference); + BC_ASSERT_TRUE(linphone_participant_is_admin(me) == + ((mgr == marie.getCMgr()) || (mgr == focus.getCMgr()))); + BC_ASSERT_TRUE(linphone_address_weak_equal(linphone_participant_get_address(me), mgr->identity)); + bctbx_list_t *participants_list = linphone_conference_get_participant_list(pconference); + if (reason == LinphoneReasonBusy) { + no_participants += declining_participants.size(); + } + BC_ASSERT_EQUAL(bctbx_list_size(participants_list), no_participants, size_t, "%zu"); + BC_ASSERT_EQUAL(linphone_conference_get_participant_count(pconference), + static_cast<int>(no_participants), int, "%0d"); + for (bctbx_list_t *itp = participants_list; itp; itp = bctbx_list_next(itp)) { + LinphoneParticipant *p = (LinphoneParticipant *)bctbx_list_get_data(itp); + BC_ASSERT_TRUE( + linphone_participant_is_admin(p) == + linphone_address_weak_equal(linphone_participant_get_address(p), marie.getCMgr()->identity)); + } + bctbx_list_free_with_data(participants_list, (void (*)(void *))linphone_participant_unref); + + if (mgr != focus.getCMgr()) { + check_conference_ssrc(fconference, pconference); + } + + bctbx_list_t *devices = linphone_conference_get_participant_device_list(pconference); + BC_ASSERT_EQUAL(bctbx_list_size(devices), all_active_participants.size(), size_t, "%zu"); + for (bctbx_list_t *itd = devices; itd; itd = bctbx_list_next(itd)) { + LinphoneParticipantDevice *d = (LinphoneParticipantDevice *)bctbx_list_get_data(itd); + if (linphone_conference_is_me(pconference, linphone_participant_device_get_address(d))) { + BC_ASSERT_TRUE(linphone_participant_device_get_stream_availability(d, LinphoneStreamTypeVideo)); + } else { + BC_ASSERT_TRUE( + linphone_participant_device_get_stream_availability(d, LinphoneStreamTypeVideo) == + (linphone_participant_device_get_stream_capability(d, LinphoneStreamTypeVideo) == + LinphoneMediaDirectionSendRecv)); + } + } + + if (devices) { + bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); + } + } + } + + LinphoneCall *pauline_call = linphone_core_get_call_by_remote_address2(pauline.getLc(), confAddr); + BC_ASSERT_PTR_NOT_NULL(pauline_call); + + Address paulineAddr = pauline.getIdentity(); + LinphoneCall *focus_call = linphone_core_get_call_by_remote_address2(focus.getLc(), paulineAddr.toC()); + BC_ASSERT_PTR_NOT_NULL(focus_call); + + LinphoneVideoActivationPolicy *pol = linphone_core_get_video_activation_policy(pauline.getLc()); + bool_t enable = !!!linphone_video_activation_policy_get_automatically_initiate(pol); + linphone_video_activation_policy_unref(pol); + + LinphoneAddress *paulineUri = linphone_address_new(linphone_core_get_identity(pauline.getLc())); + LinphoneConference *paulineConference = + linphone_core_search_conference(pauline.getLc(), NULL, paulineUri, confAddr, NULL); + linphone_address_unref(paulineUri); + BC_ASSERT_PTR_NOT_NULL(paulineConference); + + for (int i = 0; i < 4; i++) { + set_video_settings_in_conference(focus.getCMgr(), pauline.getCMgr(), all_active_participants, confAddr, + enable, LinphoneMediaDirectionSendRecv, enable, + LinphoneMediaDirectionSendRecv); + + if (paulineConference) { + bctbx_list_t *devices = linphone_conference_get_participant_device_list(paulineConference); + for (bctbx_list_t *itd = devices; itd; itd = bctbx_list_next(itd)) { + LinphoneParticipantDevice *d = (LinphoneParticipantDevice *)bctbx_list_get_data(itd); + if (enable) { + if (linphone_conference_is_me(paulineConference, linphone_participant_device_get_address(d))) { + BC_ASSERT_TRUE( + linphone_participant_device_get_stream_availability(d, LinphoneStreamTypeVideo)); + } else { + BC_ASSERT_TRUE( + linphone_participant_device_get_stream_availability(d, LinphoneStreamTypeVideo) == + (linphone_participant_device_get_stream_capability(d, LinphoneStreamTypeVideo) == + LinphoneMediaDirectionSendRecv)); + } + } else { + BC_ASSERT_TRUE( + linphone_participant_device_get_stream_availability(d, LinphoneStreamTypeVideo) == enable); + } + } + + if (devices) { + bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); + } + } + // Wait a little bit + wait_for_list(coresList, NULL, 0, 1000); + + enable = !enable; + } + + focus_stat = focus.getStats(); + for (auto mgr : all_active_participants) { + LinphoneCall *call = linphone_core_get_call_by_remote_address2(mgr->lc, focus.getCMgr()->identity); + BC_ASSERT_PTR_NOT_NULL(call); + if (call) { + ms_message("%s is terminating call with %s", linphone_core_get_identity(mgr->lc), + linphone_core_get_identity(focus.getLc())); + linphone_call_terminate(call); + BC_ASSERT_TRUE( + wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallEnd, 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallReleased, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionTerminated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateTerminationPending, + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateTerminated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateDeleted, 1, + liblinphone_tester_sip_timeout)); + + LinphoneAddress *uri = linphone_address_new(linphone_core_get_identity(mgr->lc)); + LinphoneConference *pconference = linphone_core_search_conference(mgr->lc, NULL, uri, confAddr, NULL); + BC_ASSERT_PTR_NULL(pconference); + linphone_address_unref(uri); + } + } + + BC_ASSERT_TRUE( + wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallEnd, + focus_stat.number_of_LinphoneCallEnd + static_cast<int>(all_active_participants.size()), + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallReleased, + focus_stat.number_of_LinphoneCallReleased + static_cast<int>(all_active_participants.size()), + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneSubscriptionTerminated, + focus_stat.number_of_LinphoneSubscriptionTerminated + + static_cast<int>(all_active_participants.size()), + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(coresList, &focus.getStats().number_of_participants_removed, + focus_stat.number_of_participants_removed + static_cast<int>(all_active_participants.size()), + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_removed, + focus_stat.number_of_participant_devices_removed + + static_cast<int>(all_active_participants.size()), + liblinphone_tester_sip_timeout)); + + BC_ASSERT_EQUAL(focus.getStats().number_of_LinphoneConferenceStateTerminationPending, + focus_stat.number_of_LinphoneConferenceStateTerminationPending, int, "%d"); + BC_ASSERT_EQUAL(focus.getStats().number_of_LinphoneConferenceStateTerminated, + focus_stat.number_of_LinphoneConferenceStateTerminated, int, "%d"); + BC_ASSERT_EQUAL(focus.getStats().number_of_LinphoneConferenceStateDeleted, + focus_stat.number_of_LinphoneConferenceStateDeleted, int, "%d"); + + BC_ASSERT_EQUAL(focus.getStats().number_of_participant_devices_joined, + static_cast<int>(all_active_participants.size()), int, "%d"); + BC_ASSERT_EQUAL(marie.getStats().number_of_participant_devices_joined, + static_cast<int>(all_active_participants.size()), int, "%d"); + + for (auto mgr : {focus.getCMgr()}) { + LinphoneConference *pconference = linphone_core_search_conference_2(mgr->lc, confAddr); + BC_ASSERT_PTR_NOT_NULL(pconference); + if (pconference) { + BC_ASSERT_EQUAL(linphone_conference_get_participant_count(pconference), + static_cast<int>((reason == LinphoneReasonBusy) ? declining_participants.size() : 0), + int, "%0d"); + bctbx_list_t *devices = linphone_conference_get_participant_device_list(pconference); + BC_ASSERT_EQUAL(bctbx_list_size(devices), 0, size_t, "%zu"); + if (devices) { + bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); + } + BC_ASSERT_STRING_EQUAL(linphone_conference_get_subject(pconference), initialSubject); + } + } + + focus_stat = focus.getStats(); + const bctbx_list_t *calls = linphone_core_get_calls(focus.getLc()); + BC_ASSERT_EQUAL(bctbx_list_size(calls), 0, size_t, "%zu"); + + // Explicitely terminate conference as those on server are static by default + if (fconference) { + linphone_conference_terminate(fconference); + } + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateTerminationPending, + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateTerminated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateDeleted, 1, + liblinphone_tester_sip_timeout)); + + for (auto mgr : {marie.getCMgr(), pauline.getCMgr(), laure.getCMgr(), michelle.getCMgr(), berthe.getCMgr()}) { + const bctbx_list_t *call_logs = linphone_core_get_call_logs(mgr->lc); + BC_ASSERT_EQUAL((unsigned int)bctbx_list_size(call_logs), 1, unsigned int, "%u"); + + bctbx_list_t *mgr_focus_call_log = + linphone_core_get_call_history_2(mgr->lc, focus.getCMgr()->identity, mgr->identity); + BC_ASSERT_PTR_NOT_NULL(mgr_focus_call_log); + if (mgr_focus_call_log) { + BC_ASSERT_EQUAL((unsigned int)bctbx_list_size(mgr_focus_call_log), 1, unsigned int, "%u"); + for (bctbx_list_t *it = mgr_focus_call_log; it; it = bctbx_list_next(it)) { + LinphoneCallLog *call_log = (LinphoneCallLog *)it->data; + BC_ASSERT_TRUE(linphone_call_log_was_conference(call_log)); + } + bctbx_list_free_with_data(mgr_focus_call_log, (bctbx_list_free_func)linphone_call_log_unref); + } + check_conference_info(mgr, confAddr, marie.getCMgr(), 5, 0, 0, initialSubject, + (mgr == marie.getCMgr()) ? description : NULL, 0, LinphoneConferenceInfoStateNew); + } + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, laure, michelle, berthe}).waitUntil(chrono::seconds(2), [] { + return false; + }); + + linphone_address_unref(confAddr); + bctbx_list_free(coresList); + } +} + +static void create_simple_conference_dial_out_with_some_calls_declined(void) { + create_simple_conference_dial_out_with_some_calls_declined_base(LinphoneReasonDeclined); +} + +static void create_simple_conference_dial_out_with_some_calls_busy(void) { + create_simple_conference_dial_out_with_some_calls_declined_base(LinphoneReasonBusy); +} + +static void +create_conference_with_late_participant_addition_base(time_t start_time, + int duration, + LinphoneConferenceLayout layout, + LinphoneConferenceParticipantListType participant_list_type, + bool_t accept, + bool_t one_addition) { + Focus focus("chloe_rc"); + { // to make sure focus is destroyed after clients. + ClientConference marie("marie_rc", focus.getIdentity()); + ClientConference pauline("pauline_rc", focus.getIdentity()); + ClientConference laure("laure_tcp_rc", focus.getIdentity()); + ClientConference michelle("michelle_rc", focus.getIdentity()); + ClientConference berthe("berthe_rc", focus.getIdentity()); + + focus.registerAsParticipantDevice(marie); + focus.registerAsParticipantDevice(pauline); + focus.registerAsParticipantDevice(laure); + focus.registerAsParticipantDevice(michelle); + focus.registerAsParticipantDevice(berthe); + + setup_conference_info_cbs(marie.getCMgr()); + + bctbx_list_t *coresList = NULL; + + for (auto mgr : {focus.getCMgr(), marie.getCMgr(), pauline.getCMgr(), laure.getCMgr(), michelle.getCMgr(), + berthe.getCMgr()}) { + LinphoneVideoActivationPolicy *pol = + linphone_factory_create_video_activation_policy(linphone_factory_get()); + linphone_video_activation_policy_set_automatically_accept(pol, TRUE); + linphone_video_activation_policy_set_automatically_initiate(pol, TRUE); + linphone_core_set_video_activation_policy(mgr->lc, pol); + linphone_video_activation_policy_unref(pol); + + linphone_core_set_video_device(mgr->lc, liblinphone_tester_mire_id); + linphone_core_enable_video_capture(mgr->lc, TRUE); + linphone_core_enable_video_display(mgr->lc, TRUE); + + if (mgr != focus.getCMgr()) { + linphone_core_set_default_conference_layout(mgr->lc, layout); + } + + coresList = bctbx_list_append(coresList, mgr->lc); + } + + linphone_core_set_file_transfer_server(marie.getLc(), file_transfer_url); + linphone_core_set_conference_participant_list_type(focus.getLc(), participant_list_type); + + stats focus_stat = focus.getStats(); + stats marie_stat = marie.getStats(); + + std::list<LinphoneCoreManager *> participants{pauline.getCMgr(), laure.getCMgr()}; + if (one_addition) { + participants.push_back(michelle.getCMgr()); + } + + time_t end_time = (duration <= 0) ? -1 : (start_time + duration * 60); + const char *initialSubject = "Weekly recap"; + const char *description = "What happened in the past week"; + std::map<LinphoneCoreManager *, LinphoneParticipantRole> participantList; + LinphoneParticipantRole role = LinphoneParticipantRoleSpeaker; + for (auto &p : participants) { + participantList.insert(std::make_pair(p, role)); + role = (role == LinphoneParticipantRoleSpeaker) ? LinphoneParticipantRoleListener + : LinphoneParticipantRoleSpeaker; + } + LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, + initialSubject, description, TRUE); + BC_ASSERT_PTR_NOT_NULL(confAddr); + + // Chat room creation to send ICS + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateCreated, 2, + liblinphone_tester_sip_timeout)); + + auto members = participants; + members.push_back(marie.getCMgr()); + auto conferenceMgrs = members; + conferenceMgrs.push_back(focus.getCMgr()); + + if (start_time < 0) { + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallOutgoingInit, + marie_stat.number_of_LinphoneCallOutgoingInit + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE( + wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallOutgoingInit, + focus_stat.number_of_LinphoneCallOutgoingInit + static_cast<int>(participants.size()), + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallIncomingReceived, + focus_stat.number_of_LinphoneCallIncomingReceived + 1, + liblinphone_tester_sip_timeout)); + + for (auto mgr : participants) { + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallIncomingReceived, 1, + liblinphone_tester_sip_timeout)); + } + } else if (confAddr) { + for (auto mgr : members) { + LinphoneCallParams *new_params = linphone_core_create_call_params(mgr->lc, nullptr); + linphone_call_params_set_video_direction(new_params, LinphoneMediaDirectionSendRecv); + if (mgr == laure.getCMgr()) { + linphone_call_params_enable_mic(new_params, FALSE); + } + linphone_core_invite_address_with_params_2(mgr->lc, confAddr, new_params, NULL, nullptr); + linphone_call_params_unref(new_params); + } + + for (auto mgr : members) { + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallOutgoingProgress, 1, + liblinphone_tester_sip_timeout)); + } + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallStreamsRunning, + marie_stat.number_of_LinphoneCallStreamsRunning + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallStreamsRunning, + marie_stat.number_of_LinphoneCallStreamsRunning + 2, + liblinphone_tester_sip_timeout)); + + LinphoneConference *oconference = linphone_core_search_conference_2(marie.getLc(), confAddr); + if (BC_ASSERT_PTR_NOT_NULL(oconference)) { + BC_ASSERT_EQUAL(linphone_conference_get_participant_count(oconference), + static_cast<int>(participants.size()), int, "%0d"); + bctbx_list_t *devices = linphone_conference_get_participant_device_list(oconference); + BC_ASSERT_EQUAL(bctbx_list_size(devices), (participants.size() + 1), size_t, "%zu"); + if (devices) { + bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); + } + } + + if (confAddr) { + for (auto mgr : participants) { + check_conference_info(mgr, confAddr, marie.getCMgr(), members.size(), start_time, duration, + initialSubject, description, 0, LinphoneConferenceInfoStateNew); + + LinphoneCall *pcall = linphone_core_get_call_by_remote_address2(mgr->lc, confAddr); + BC_ASSERT_PTR_NOT_NULL(pcall); + if (pcall) { + LinphoneCallLog *call_log = linphone_call_get_call_log(pcall); + BC_ASSERT_TRUE(linphone_call_log_was_conference(call_log)); + linphone_call_accept(pcall); + } + } + } + + for (auto mgr : participants) { + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallStreamsRunning, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallStreamsRunning, 2, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateCreated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionOutgoingProgress, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionActive, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(coresList, &mgr->stat.number_of_NotifyReceived, 1, liblinphone_tester_sip_timeout)); + + check_conference_info(mgr, confAddr, marie.getCMgr(), members.size(), start_time, duration, initialSubject, + description, 0, LinphoneConferenceInfoStateNew); + + LinphoneCall *pcall = linphone_core_get_call_by_remote_address2(mgr->lc, confAddr); + BC_ASSERT_PTR_NOT_NULL(pcall); + if (pcall) { + LinphoneCallLog *call_log = linphone_call_get_call_log(pcall); + BC_ASSERT_TRUE(linphone_call_log_was_conference(call_log)); + LinphoneConferenceInfo *call_log_info = linphone_call_log_get_conference_info(call_log); + if (BC_ASSERT_PTR_NOT_NULL(call_log_info)) { + BC_ASSERT_TRUE(linphone_address_weak_equal(linphone_conference_info_get_organizer(call_log_info), + marie.getCMgr()->identity)); + BC_ASSERT_TRUE( + linphone_address_weak_equal(linphone_conference_info_get_uri(call_log_info), confAddr)); + + const bctbx_list_t *info_participants = linphone_conference_info_get_participants(call_log_info); + // Original participants + Marie who joined the conference + BC_ASSERT_EQUAL(bctbx_list_size(info_participants), static_cast<int>(members.size()), size_t, + "%zu"); + + if (start_time > 0) { + BC_ASSERT_EQUAL((long long)linphone_conference_info_get_date_time(call_log_info), start_time, + long long, "%lld"); + } + const int duration_m = linphone_conference_info_get_duration(call_log_info); + BC_ASSERT_EQUAL(duration_m, ((duration < 0) ? 0 : duration), int, "%d"); + if (initialSubject) { + BC_ASSERT_STRING_EQUAL(linphone_conference_info_get_subject(call_log_info), initialSubject); + } else { + BC_ASSERT_PTR_NULL(linphone_conference_info_get_subject(call_log_info)); + } + BC_ASSERT_STRING_EQUAL(linphone_conference_info_get_description(call_log_info), description); + } + } + } + + BC_ASSERT_TRUE( + wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallStreamsRunning, + focus_stat.number_of_LinphoneCallStreamsRunning + static_cast<int>(participants.size() + 1), + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateCreated, + marie_stat.number_of_LinphoneConferenceStateCreated + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneSubscriptionOutgoingProgress, + marie_stat.number_of_LinphoneSubscriptionOutgoingProgress + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneSubscriptionActive, + marie_stat.number_of_LinphoneSubscriptionActive + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_NotifyReceived, + marie_stat.number_of_NotifyReceived + 1, liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateCreated, + focus_stat.number_of_LinphoneConferenceStateCreated + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(coresList, &focus.getStats().number_of_LinphoneSubscriptionIncomingReceived, + focus_stat.number_of_LinphoneSubscriptionIncomingReceived + static_cast<int>(members.size()), + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneSubscriptionActive, + focus_stat.number_of_LinphoneSubscriptionActive + static_cast<int>(members.size()), + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participants_added, + focus_stat.number_of_participants_added + static_cast<int>(members.size()), + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_added, + focus_stat.number_of_participant_devices_added + static_cast<int>(members.size()), + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_joined, + focus_stat.number_of_participant_devices_joined + static_cast<int>(members.size()), + liblinphone_tester_sip_timeout)); + + LinphoneConference *fconference = linphone_core_search_conference_2(focus.getLc(), confAddr); + BC_ASSERT_PTR_NOT_NULL(fconference); + + std::map<LinphoneCoreManager *, LinphoneParticipantRole> memberList; + for (const auto &member : members) { + try { + const auto &participantInfo = participantList.at(member); + memberList.insert(std::make_pair(member, participantInfo)); + } catch (std::out_of_range &) { + if (member == marie.getCMgr()) { + memberList.insert(std::make_pair(marie.getCMgr(), LinphoneParticipantRoleSpeaker)); + } else { + ms_fatal("Unable to find active participant %s in the participant list", + linphone_core_get_identity(member->lc)); + } + } + } + wait_for_conference_streams({focus, marie, pauline, laure, michelle, berthe}, conferenceMgrs, focus.getCMgr(), + memberList, confAddr, TRUE); + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, laure, michelle, berthe}).waitUntil(chrono::seconds(15), [] { + return false; + }); + + for (auto mgr : conferenceMgrs) { + LinphoneAddress *uri = linphone_address_new(linphone_core_get_identity(mgr->lc)); + LinphoneConference *pconference = linphone_core_search_conference_2(mgr->lc, confAddr); + linphone_address_unref(uri); + BC_ASSERT_PTR_NOT_NULL(pconference); + if (pconference) { + int no_participants = 0; + if (mgr == focus.getCMgr()) { + no_participants = static_cast<int>(members.size()); + BC_ASSERT_FALSE(linphone_conference_is_in(pconference)); + } else { + no_participants = static_cast<int>(participants.size()); + BC_ASSERT_TRUE(linphone_conference_is_in(pconference)); + LinphoneCall *current_call = linphone_core_get_current_call(mgr->lc); + BC_ASSERT_PTR_NOT_NULL(current_call); + if (current_call) { + BC_ASSERT_EQUAL((int)linphone_call_get_state(current_call), + (int)LinphoneCallStateStreamsRunning, int, "%0d"); + } + + LinphoneVideoActivationPolicy *pol = linphone_core_get_video_activation_policy(mgr->lc); + bool_t enabled = !!linphone_video_activation_policy_get_automatically_initiate(pol); + linphone_video_activation_policy_unref(pol); + + int no_streams_audio = 1; + int no_streams_video = (enabled) ? (static_cast<int>(members.size()) + 1) : 0; + int no_streams_text = 0; + + LinphoneCall *pcall = linphone_core_get_call_by_remote_address2(mgr->lc, confAddr); + BC_ASSERT_PTR_NOT_NULL(pcall); + if (pcall) { + _linphone_call_check_nb_streams(pcall, no_streams_audio, no_streams_video, no_streams_text); + _linphone_call_check_nb_active_streams(pcall, no_streams_audio, no_streams_video, + no_streams_text); + const LinphoneCallParams *call_lparams = linphone_call_get_params(pcall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_lparams), enabled, int, "%0d"); + const LinphoneCallParams *call_rparams = linphone_call_get_remote_params(pcall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_rparams), enabled, int, "%0d"); + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(pcall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_cparams), enabled, int, "%0d"); + } + + LinphoneCall *ccall = linphone_core_get_call_by_remote_address2(focus.getLc(), mgr->identity); + BC_ASSERT_PTR_NOT_NULL(ccall); + if (ccall) { + _linphone_call_check_nb_streams(ccall, no_streams_audio, no_streams_video, no_streams_text); + _linphone_call_check_nb_active_streams(ccall, no_streams_audio, no_streams_video, + no_streams_text); + const LinphoneCallParams *call_lparams = linphone_call_get_params(ccall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_lparams), enabled, int, "%0d"); + const LinphoneCallParams *call_rparams = linphone_call_get_remote_params(ccall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_rparams), enabled, int, "%0d"); + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(ccall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_cparams), enabled, int, "%0d"); + } + } + BC_ASSERT_EQUAL(linphone_conference_get_participant_count(pconference), no_participants, int, "%0d"); + bctbx_list_t *devices = linphone_conference_get_participant_device_list(pconference); + BC_ASSERT_EQUAL(bctbx_list_size(devices), members.size(), size_t, "%zu"); + if (devices) { + bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); + } + BC_ASSERT_STRING_EQUAL(linphone_conference_get_subject(pconference), initialSubject); + LinphoneParticipant *me = linphone_conference_get_me(pconference); + BC_ASSERT_TRUE(linphone_participant_is_admin(me) == + ((mgr == marie.getCMgr()) || (mgr == focus.getCMgr()))); + BC_ASSERT_TRUE(linphone_address_weak_equal(linphone_participant_get_address(me), mgr->identity)); + bctbx_list_t *participants = linphone_conference_get_participant_list(pconference); + for (bctbx_list_t *itp = participants; itp; itp = bctbx_list_next(itp)) { + LinphoneParticipant *p = (LinphoneParticipant *)bctbx_list_get_data(itp); + BC_ASSERT_TRUE( + linphone_participant_is_admin(p) == + linphone_address_weak_equal(linphone_participant_get_address(p), marie.getCMgr()->identity)); + } + bctbx_list_free_with_data(participants, (void (*)(void *))linphone_participant_unref); + + if (mgr != focus.getCMgr()) { + check_conference_ssrc(fconference, pconference); + } + + LinphoneConference *conference = linphone_core_search_conference_2(mgr->lc, confAddr); + BC_ASSERT_PTR_NOT_NULL(conference); + if (conference) { + bctbx_list_t *devices = linphone_conference_get_participant_device_list(conference); + for (bctbx_list_t *itd = devices; itd; itd = bctbx_list_next(itd)) { + LinphoneParticipantDevice *d = (LinphoneParticipantDevice *)bctbx_list_get_data(itd); + if (linphone_conference_is_me(conference, linphone_participant_device_get_address(d))) { + BC_ASSERT_TRUE( + linphone_participant_device_get_stream_availability(d, LinphoneStreamTypeVideo)); + } else { + BC_ASSERT_TRUE( + linphone_participant_device_get_stream_availability(d, LinphoneStreamTypeVideo) == + (linphone_participant_device_get_stream_capability(d, LinphoneStreamTypeVideo) == + LinphoneMediaDirectionSendRecv)); + } + } + + if (devices) { + bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); + } + } + } + } + + focus_stat = focus.getStats(); + marie_stat = marie.getStats(); + stats michelle_stat = michelle.getStats(); + stats berthe_stat = berthe.getStats(); + stats pauline_stat = pauline.getStats(); + stats laure_stat = laure.getStats(); + + if (one_addition) { + linphone_conference_add_participant_2(oconference, berthe.getCMgr()->identity); + } else { + bctbx_list_t *addresses = NULL; + addresses = bctbx_list_append(addresses, berthe.getCMgr()->identity); + addresses = bctbx_list_append(addresses, michelle.getCMgr()->identity); + linphone_conference_add_participants_2(oconference, addresses); + bctbx_list_free(addresses); + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallOutgoingInit, + focus_stat.number_of_LinphoneCallOutgoingInit + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallOutgoingProgress, + focus_stat.number_of_LinphoneCallOutgoingProgress + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &berthe.getStats().number_of_LinphoneCallIncomingReceived, + berthe_stat.number_of_LinphoneCallIncomingReceived + 1, + liblinphone_tester_sip_timeout)); + + if (!one_addition) { + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallOutgoingInit, + focus_stat.number_of_LinphoneCallOutgoingInit + 2, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallOutgoingProgress, + focus_stat.number_of_LinphoneCallOutgoingProgress + 2, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_LinphoneCallIncomingReceived, + michelle_stat.number_of_LinphoneCallIncomingReceived + 1, + liblinphone_tester_sip_timeout)); + } + + LinphoneCall *berthe_call = + linphone_core_get_call_by_remote_address2(berthe.getLc(), focus.getCMgr()->identity); + BC_ASSERT_PTR_NOT_NULL(berthe_call); + + LinphoneCall *michelle_call = + linphone_core_get_call_by_remote_address2(michelle.getLc(), focus.getCMgr()->identity); + BC_ASSERT_PTR_NOT_NULL(michelle_call); + + int participant_added = ((one_addition) ? 1 : 2); + + if (accept) { + if (berthe_call) { + linphone_call_accept(berthe_call); + } + + conferenceMgrs.push_back(berthe.getCMgr()); + members.push_back(berthe.getCMgr()); + memberList.insert(std::make_pair(berthe.getCMgr(), LinphoneParticipantRoleListener)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallStreamsRunning, + focus_stat.number_of_LinphoneCallStreamsRunning + 2, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &berthe.getStats().number_of_LinphoneCallStreamsRunning, + berthe_stat.number_of_LinphoneCallStreamsRunning + 2, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &berthe.getStats().number_of_LinphoneConferenceStateCreated, + berthe_stat.number_of_LinphoneConferenceStateCreated + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &berthe.getStats().number_of_LinphoneSubscriptionOutgoingProgress, + berthe_stat.number_of_LinphoneSubscriptionOutgoingProgress + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &berthe.getStats().number_of_LinphoneSubscriptionActive, + berthe_stat.number_of_LinphoneSubscriptionActive + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &berthe.getStats().number_of_NotifyReceived, + berthe_stat.number_of_NotifyReceived + 1, liblinphone_tester_sip_timeout)); + + if (!one_addition) { + if (michelle_call) { + linphone_call_accept(michelle_call); + } + + conferenceMgrs.push_back(michelle.getCMgr()); + members.push_back(michelle.getCMgr()); + memberList.insert(std::make_pair(michelle.getCMgr(), LinphoneParticipantRoleListener)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallStreamsRunning, + focus_stat.number_of_LinphoneCallStreamsRunning + 4, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_LinphoneCallStreamsRunning, + michelle_stat.number_of_LinphoneCallStreamsRunning + 2, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_LinphoneConferenceStateCreated, + michelle_stat.number_of_LinphoneConferenceStateCreated + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list( + coresList, &michelle.getStats().number_of_LinphoneSubscriptionOutgoingProgress, + michelle_stat.number_of_LinphoneSubscriptionOutgoingProgress + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_LinphoneSubscriptionActive, + michelle_stat.number_of_LinphoneSubscriptionActive + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_NotifyReceived, + michelle_stat.number_of_NotifyReceived + 1, + liblinphone_tester_sip_timeout)); + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneSubscriptionIncomingReceived, + focus_stat.number_of_LinphoneSubscriptionIncomingReceived + participant_added, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneSubscriptionActive, + focus_stat.number_of_LinphoneSubscriptionActive + participant_added, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participants_added, + focus_stat.number_of_participants_added + participant_added, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_participants_added, + marie_stat.number_of_participants_added + participant_added, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_participants_added, + pauline_stat.number_of_participants_added + participant_added, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_participants_added, + laure_stat.number_of_participants_added + participant_added, + liblinphone_tester_sip_timeout)); + if (one_addition) { + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_participants_added, + michelle_stat.number_of_participants_added + 1, + liblinphone_tester_sip_timeout)); + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_alerting, + focus_stat.number_of_participant_devices_alerting + participant_added, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_participant_devices_alerting, + marie_stat.number_of_participant_devices_alerting + participant_added, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_participant_devices_alerting, + pauline_stat.number_of_participant_devices_alerting + participant_added, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_participant_devices_alerting, + laure_stat.number_of_participant_devices_alerting + participant_added, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_added, + focus_stat.number_of_participant_devices_added + participant_added, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_participant_devices_added, + marie_stat.number_of_participant_devices_added + participant_added, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_participant_devices_added, + pauline_stat.number_of_participant_devices_added + participant_added, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_participant_devices_added, + laure_stat.number_of_participant_devices_added + participant_added, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_joined, + focus_stat.number_of_participant_devices_joined + participant_added, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_participant_devices_joined, + marie_stat.number_of_participant_devices_joined + participant_added, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_participant_devices_joined, + pauline_stat.number_of_participant_devices_joined + participant_added, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_participant_devices_joined, + laure_stat.number_of_participant_devices_joined + participant_added, + liblinphone_tester_sip_timeout)); + if (one_addition) { + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_participant_devices_alerting, + michelle_stat.number_of_participant_devices_alerting + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_participant_devices_added, + michelle_stat.number_of_participant_devices_added + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_participant_devices_joined, + michelle_stat.number_of_participant_devices_joined + 1, + liblinphone_tester_sip_timeout)); + } + } else { + if (berthe_call) { + linphone_call_decline(berthe_call, LinphoneReasonDeclined); + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &berthe.getStats().number_of_LinphoneCallEnd, + berthe_stat.number_of_LinphoneCallEnd + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &berthe.getStats().number_of_LinphoneCallReleased, + berthe_stat.number_of_LinphoneCallReleased + 1, + liblinphone_tester_sip_timeout)); + + if (!one_addition) { + if (michelle_call) { + linphone_call_decline(michelle_call, LinphoneReasonDeclined); + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_LinphoneCallEnd, + michelle_stat.number_of_LinphoneCallEnd + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_LinphoneCallReleased, + michelle_stat.number_of_LinphoneCallReleased + 1, + liblinphone_tester_sip_timeout)); + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallEnd, + focus_stat.number_of_LinphoneCallEnd + participant_added, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallReleased, + focus_stat.number_of_LinphoneCallReleased + participant_added, + liblinphone_tester_sip_timeout)); + } + wait_for_conference_streams({focus, marie, pauline, laure, michelle, berthe}, conferenceMgrs, focus.getCMgr(), + memberList, confAddr, TRUE); + + LinphoneCall *pauline_call = linphone_core_get_call_by_remote_address2(pauline.getLc(), confAddr); + BC_ASSERT_PTR_NOT_NULL(pauline_call); + + Address paulineAddr = pauline.getIdentity(); + LinphoneCall *focus_call = linphone_core_get_call_by_remote_address2(focus.getLc(), paulineAddr.toC()); + BC_ASSERT_PTR_NOT_NULL(focus_call); + + LinphoneVideoActivationPolicy *pol = linphone_core_get_video_activation_policy(pauline.getLc()); + bool_t enable = !!!linphone_video_activation_policy_get_automatically_initiate(pol); + linphone_video_activation_policy_unref(pol); + + LinphoneAddress *paulineUri = linphone_address_new(linphone_core_get_identity(pauline.getLc())); + LinphoneConference *paulineConference = + linphone_core_search_conference(pauline.getLc(), NULL, paulineUri, confAddr, NULL); + linphone_address_unref(paulineUri); + BC_ASSERT_PTR_NOT_NULL(paulineConference); + + for (int i = 0; i < 4; i++) { + set_video_settings_in_conference(focus.getCMgr(), pauline.getCMgr(), members, confAddr, enable, + LinphoneMediaDirectionSendRecv, enable, LinphoneMediaDirectionSendRecv); + + if (paulineConference) { + bctbx_list_t *devices = linphone_conference_get_participant_device_list(paulineConference); + for (bctbx_list_t *itd = devices; itd; itd = bctbx_list_next(itd)) { + LinphoneParticipantDevice *d = (LinphoneParticipantDevice *)bctbx_list_get_data(itd); + if (enable) { + if (linphone_conference_is_me(paulineConference, linphone_participant_device_get_address(d))) { + BC_ASSERT_TRUE( + linphone_participant_device_get_stream_availability(d, LinphoneStreamTypeVideo)); + } else { + BC_ASSERT_TRUE( + linphone_participant_device_get_stream_availability(d, LinphoneStreamTypeVideo) == + (linphone_participant_device_get_stream_capability(d, LinphoneStreamTypeVideo) == + LinphoneMediaDirectionSendRecv)); + } + } else { + BC_ASSERT_TRUE( + linphone_participant_device_get_stream_availability(d, LinphoneStreamTypeVideo) == enable); + } + } + + if (devices) { + bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); + } + } + // Wait a little bit + wait_for_list(coresList, NULL, 0, 1000); + + enable = !enable; + } + + focus_stat = focus.getStats(); + for (auto mgr : members) { + LinphoneCall *call = linphone_core_get_call_by_remote_address2(mgr->lc, focus.getCMgr()->identity); + BC_ASSERT_PTR_NOT_NULL(call); + if (call) { + ms_message("%s is terminating call with %s", linphone_core_get_identity(mgr->lc), + linphone_core_get_identity(focus.getLc())); + linphone_call_terminate(call); + BC_ASSERT_TRUE( + wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallEnd, 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallReleased, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionTerminated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateTerminationPending, + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateTerminated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateDeleted, 1, + liblinphone_tester_sip_timeout)); + + LinphoneAddress *uri = linphone_address_new(linphone_core_get_identity(mgr->lc)); + LinphoneConference *pconference = linphone_core_search_conference(mgr->lc, NULL, uri, confAddr, NULL); + BC_ASSERT_PTR_NULL(pconference); + linphone_address_unref(uri); + } + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallEnd, + focus_stat.number_of_LinphoneCallEnd + ((accept) ? 5 : 4), + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallReleased, + focus_stat.number_of_LinphoneCallReleased + ((accept) ? 5 : 4), + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneSubscriptionTerminated, + focus_stat.number_of_LinphoneSubscriptionTerminated + ((accept) ? 5 : 4), + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participants_removed, + focus_stat.number_of_participants_removed + ((accept) ? 5 : 4), + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_removed, + focus_stat.number_of_participant_devices_removed + ((accept) ? 5 : 4), + liblinphone_tester_sip_timeout)); + + BC_ASSERT_EQUAL(focus.getStats().number_of_LinphoneConferenceStateTerminationPending, + focus_stat.number_of_LinphoneConferenceStateTerminationPending, int, "%d"); + BC_ASSERT_EQUAL(focus.getStats().number_of_LinphoneConferenceStateTerminated, + focus_stat.number_of_LinphoneConferenceStateTerminated, int, "%d"); + BC_ASSERT_EQUAL(focus.getStats().number_of_LinphoneConferenceStateDeleted, + focus_stat.number_of_LinphoneConferenceStateDeleted, int, "%d"); + + for (auto mgr : {focus.getCMgr()}) { + LinphoneConference *pconference = linphone_core_search_conference_2(mgr->lc, confAddr); + BC_ASSERT_PTR_NOT_NULL(pconference); + if (pconference) { + BC_ASSERT_EQUAL(linphone_conference_get_participant_count(pconference), 0, int, "%0d"); + bctbx_list_t *devices = linphone_conference_get_participant_device_list(pconference); + BC_ASSERT_EQUAL(bctbx_list_size(devices), 0, size_t, "%zu"); + if (devices) { + bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); + } + BC_ASSERT_STRING_EQUAL(linphone_conference_get_subject(pconference), initialSubject); + } + } + + focus_stat = focus.getStats(); + const bctbx_list_t *calls = linphone_core_get_calls(focus.getLc()); + BC_ASSERT_EQUAL(bctbx_list_size(calls), 0, size_t, "%zu"); + + // Explicitely terminate conference as those on server are static by default + if (fconference) { + linphone_conference_terminate(fconference); + } + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateTerminationPending, + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateTerminated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateDeleted, 1, + liblinphone_tester_sip_timeout)); + + for (auto mgr : members) { + const bctbx_list_t *call_logs = linphone_core_get_call_logs(mgr->lc); + BC_ASSERT_EQUAL((unsigned int)bctbx_list_size(call_logs), 1, unsigned int, "%u"); + + bctbx_list_t *mgr_focus_call_log = + linphone_core_get_call_history_2(mgr->lc, focus.getCMgr()->identity, mgr->identity); + BC_ASSERT_PTR_NOT_NULL(mgr_focus_call_log); + if (mgr_focus_call_log) { + BC_ASSERT_EQUAL((unsigned int)bctbx_list_size(mgr_focus_call_log), 1, unsigned int, "%u"); + for (bctbx_list_t *it = mgr_focus_call_log; it; it = bctbx_list_next(it)) { + LinphoneCallLog *call_log = (LinphoneCallLog *)it->data; + BC_ASSERT_TRUE(linphone_call_log_was_conference(call_log)); + } + bctbx_list_free_with_data(mgr_focus_call_log, (bctbx_list_free_func)linphone_call_log_unref); + } + + check_conference_info( + mgr, confAddr, marie.getCMgr(), 5, 0, 0, initialSubject, + ((!one_addition && (mgr == michelle.getCMgr())) || (mgr == berthe.getCMgr())) ? NULL : description, 0, + LinphoneConferenceInfoStateNew); + } + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, laure, michelle, berthe}).waitUntil(chrono::seconds(2), [] { + return false; + }); + + linphone_address_unref(confAddr); + bctbx_list_free(coresList); + } +} + +static void create_conference_with_late_participant_addition(void) { + create_conference_with_late_participant_addition_base(ms_time(NULL), -1, LinphoneConferenceLayoutGrid, + LinphoneConferenceParticipantListTypeClosed, TRUE, TRUE); +} + +static void create_conference_with_late_participant_addition_declined(void) { + create_conference_with_late_participant_addition_base(ms_time(NULL), -1, LinphoneConferenceLayoutActiveSpeaker, + LinphoneConferenceParticipantListTypeClosed, FALSE, TRUE); +} + +static void create_simple_conference_dial_out_with_late_participant_addition(void) { + create_conference_with_late_participant_addition_base(-1, -1, LinphoneConferenceLayoutActiveSpeaker, + LinphoneConferenceParticipantListTypeOpen, TRUE, TRUE); +} + +static void create_simple_conference_dial_out_with_many_late_participant_additions(void) { + create_conference_with_late_participant_addition_base(-1, -1, LinphoneConferenceLayoutGrid, + LinphoneConferenceParticipantListTypeOpen, TRUE, FALSE); +} + +static void simple_dial_out_conference_with_no_payloads(void) { + Focus focus("chloe_rc"); + { // to make sure focus is destroyed after clients. + ClientConference marie("marie_rc", focus.getIdentity()); + ClientConference pauline("pauline_rc", focus.getIdentity()); + ClientConference laure("laure_tcp_rc", focus.getIdentity()); + ClientConference michelle("michelle_rc", focus.getIdentity()); + ClientConference berthe("berthe_rc", focus.getIdentity()); + + focus.registerAsParticipantDevice(marie); + focus.registerAsParticipantDevice(pauline); + focus.registerAsParticipantDevice(laure); + focus.registerAsParticipantDevice(michelle); + focus.registerAsParticipantDevice(berthe); + + const bctbx_list_t *elem = linphone_core_get_audio_codecs(pauline.getLc()); + disable_all_codecs(elem, pauline.getCMgr()); + + setup_conference_info_cbs(marie.getCMgr()); + + bctbx_list_t *coresList = NULL; + + linphone_core_set_avpf_mode(focus.getCMgr()->lc, LinphoneAVPFEnabled); + for (auto mgr : {focus.getCMgr(), marie.getCMgr(), pauline.getCMgr(), laure.getCMgr(), michelle.getCMgr(), + berthe.getCMgr()}) { + coresList = bctbx_list_append(coresList, mgr->lc); + } + + linphone_core_set_file_transfer_server(marie.getLc(), file_transfer_url); + + stats focus_stat = focus.getStats(); + + std::list<LinphoneCoreManager *> participants{pauline.getCMgr(), laure.getCMgr(), michelle.getCMgr(), + berthe.getCMgr()}; + + const char *initialSubject = "Schedule of the trip towards the top of Europe"; + const char *description = "To the top of the Mont Blanc!!!! :-)"; + std::map<LinphoneCoreManager *, LinphoneParticipantRole> participantList; + LinphoneParticipantRole role = LinphoneParticipantRoleSpeaker; + for (auto &p : participants) { + participantList.insert(std::make_pair(p, role)); + role = (role == LinphoneParticipantRoleSpeaker) ? LinphoneParticipantRoleListener + : LinphoneParticipantRoleSpeaker; + } + LinphoneAddress *confAddr = + create_conference_on_server(focus, marie, participantList, -1, -1, initialSubject, description, FALSE); + BC_ASSERT_PTR_NOT_NULL(confAddr); + + LinphoneConference *fconference = + (confAddr) ? linphone_core_search_conference_2(focus.getLc(), confAddr) : NULL; + + // Chat room creation to send ICS + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateCreated, 1, + liblinphone_tester_sip_timeout)); + + std::list<LinphoneCoreManager *> mgr_in_conference{marie.getCMgr(), laure.getCMgr(), michelle.getCMgr(), + berthe.getCMgr()}; + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallOutgoingInit, + focus_stat.number_of_LinphoneCallOutgoingInit + 5, + liblinphone_tester_sip_timeout)); + for (auto mgr : participants) { + if (mgr == pauline.getCMgr()) { + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallError, + focus_stat.number_of_LinphoneCallError + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallReleased, + focus_stat.number_of_LinphoneCallReleased + 1, + liblinphone_tester_sip_timeout)); + } else { + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallIncomingReceived, 1, + liblinphone_tester_sip_timeout)); + } + } + + if (confAddr) { + for (auto mgr : participants) { + LinphoneCall *pcall = linphone_core_get_call_by_remote_address2(mgr->lc, confAddr); + if (mgr == pauline.getCMgr()) { + BC_ASSERT_PTR_NULL(pcall); + } else { + BC_ASSERT_PTR_NOT_NULL(pcall); + if (pcall) { + linphone_call_accept(pcall); + } + } + } + } + + for (auto mgr : participants) { + if (mgr != pauline.getCMgr()) { + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallStreamsRunning, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallStreamsRunning, 2, + liblinphone_tester_sip_timeout)); + + // If ICE is enabled, the addition to a conference may go through a resume of the call + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateCreated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionOutgoingProgress, 1, 5000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionActive, 1, 5000)); + BC_ASSERT_TRUE( + wait_for_list(coresList, &mgr->stat.number_of_NotifyReceived, 1, liblinphone_tester_sip_timeout)); + } + } + + focus_stat = focus.getStats(); + for (auto mgr : {marie.getCMgr(), laure.getCMgr(), michelle.getCMgr(), berthe.getCMgr()}) { + LinphoneCall *call = linphone_core_get_call_by_remote_address2(mgr->lc, focus.getCMgr()->identity); + BC_ASSERT_PTR_NOT_NULL(call); + if (call) { + ms_message("%s is terminating call with %s", linphone_core_get_identity(mgr->lc), + linphone_core_get_identity(focus.getLc())); + linphone_call_terminate(call); + BC_ASSERT_TRUE( + wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallEnd, 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallReleased, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionTerminated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateTerminationPending, + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateTerminated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateDeleted, 1, + liblinphone_tester_sip_timeout)); + + LinphoneAddress *uri = linphone_address_new(linphone_core_get_identity(mgr->lc)); + LinphoneConference *pconference = linphone_core_search_conference(mgr->lc, NULL, uri, confAddr, NULL); + BC_ASSERT_PTR_NULL(pconference); + linphone_address_unref(uri); + } + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallEnd, + focus_stat.number_of_LinphoneCallEnd + 4, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallReleased, + focus_stat.number_of_LinphoneCallReleased + 4, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneSubscriptionTerminated, + focus_stat.number_of_LinphoneSubscriptionTerminated + 4, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participants_removed, + focus_stat.number_of_participants_removed + 4, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_removed, + focus_stat.number_of_participant_devices_removed + 4, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_EQUAL(focus.getStats().number_of_LinphoneConferenceStateTerminationPending, + focus_stat.number_of_LinphoneConferenceStateTerminationPending, int, "%d"); + BC_ASSERT_EQUAL(focus.getStats().number_of_LinphoneConferenceStateTerminated, + focus_stat.number_of_LinphoneConferenceStateTerminated, int, "%d"); + BC_ASSERT_EQUAL(focus.getStats().number_of_LinphoneConferenceStateDeleted, + focus_stat.number_of_LinphoneConferenceStateDeleted, int, "%d"); + + for (auto mgr : {focus.getCMgr()}) { + LinphoneConference *pconference = linphone_core_search_conference_2(mgr->lc, confAddr); + BC_ASSERT_PTR_NOT_NULL(pconference); + if (pconference) { + BC_ASSERT_EQUAL(linphone_conference_get_participant_count(pconference), 0, int, "%0d"); + BC_ASSERT_STRING_EQUAL(linphone_conference_get_subject(pconference), initialSubject); + } + } + + focus_stat = focus.getStats(); + const bctbx_list_t *calls = linphone_core_get_calls(focus.getLc()); + BC_ASSERT_EQUAL(bctbx_list_size(calls), 0, size_t, "%zu"); + + // Explicitely terminate conference as those on server are static by default + if (fconference) { + linphone_conference_terminate(fconference); + } + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateTerminationPending, + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateTerminated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateDeleted, 1, + liblinphone_tester_sip_timeout)); + + linphone_address_unref(confAddr); + bctbx_list_free(coresList); + } +} + +static void abort_call_to_ice_conference(void) { + Focus focus("chloe_rc"); + { // to make sure focus is destroyed after clients. + ClientConference marie("marie_rc", focus.getIdentity()); + ClientConference pauline("pauline_rc", focus.getIdentity()); + ClientConference laure("laure_tcp_rc", focus.getIdentity()); + + focus.registerAsParticipantDevice(marie); + focus.registerAsParticipantDevice(pauline); + focus.registerAsParticipantDevice(laure); + + setup_conference_info_cbs(marie.getCMgr()); + + const LinphoneConferenceLayout layout = LinphoneConferenceLayoutGrid; + + bctbx_list_t *coresList = NULL; + + for (auto mgr : {focus.getCMgr(), marie.getCMgr(), pauline.getCMgr(), laure.getCMgr()}) { + if (mgr != focus.getCMgr()) { + linphone_core_set_default_conference_layout(mgr->lc, layout); + } + + enable_stun_in_core(mgr, TRUE, TRUE); + linphone_core_manager_wait_for_stun_resolution(mgr); + coresList = bctbx_list_append(coresList, mgr->lc); + } + + linphone_core_set_file_transfer_server(marie.getLc(), file_transfer_url); + + std::list<LinphoneCoreManager *> participants{pauline.getCMgr(), laure.getCMgr()}; + + time_t start_time = ms_time(NULL); + int duration = -1; + time_t end_time = (duration <= 0) ? -1 : (start_time + duration * 60); + const char *initialSubject = "Test aborted ICE call"; + const char *description = "Grenoble"; + + stats focus_stat = focus.getStats(); + std::map<LinphoneCoreManager *, LinphoneParticipantRole> participantList; + LinphoneParticipantRole role = LinphoneParticipantRoleSpeaker; + for (auto &p : participants) { + participantList.insert(std::make_pair(p, role)); + role = (role == LinphoneParticipantRoleSpeaker) ? LinphoneParticipantRoleListener + : LinphoneParticipantRoleSpeaker; + } + LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, + initialSubject, description, TRUE); + BC_ASSERT_PTR_NOT_NULL(confAddr); + + // Chat room creation to send ICS + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateCreated, 2, + liblinphone_tester_sip_timeout)); + + for (auto mgr : {marie.getCMgr(), pauline.getCMgr(), laure.getCMgr()}) { + LinphoneCallParams *new_params = linphone_core_create_call_params(mgr->lc, nullptr); + LinphoneCall *call = + linphone_core_invite_address_with_params_2(mgr->lc, confAddr, new_params, NULL, nullptr); + BC_ASSERT_PTR_NOT_NULL(call); + linphone_call_params_unref(new_params); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallOutgoingProgress, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallStreamsRunning, 1, + liblinphone_tester_sip_timeout)); + if (call) { + linphone_call_terminate(call); + } + BC_ASSERT_TRUE( + wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallEnd, 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallReleased, 1, liblinphone_tester_sip_timeout)); + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallIncomingReceived, 3, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallEnd, 3, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallReleased, 3, + liblinphone_tester_sip_timeout)); + + for (auto mgr : {marie.getCMgr(), pauline.getCMgr(), laure.getCMgr()}) { + reset_counters(&mgr->stat); + LinphoneCallParams *new_params = linphone_core_create_call_params(mgr->lc, nullptr); + linphone_core_invite_address_with_params_2(mgr->lc, confAddr, new_params, NULL, nullptr); + linphone_call_params_unref(new_params); + } + + for (auto mgr : {marie.getCMgr(), pauline.getCMgr(), laure.getCMgr()}) { + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallOutgoingProgress, 1, + liblinphone_tester_sip_timeout)); + int no_streams_running = 3; + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallUpdating, (no_streams_running - 1), + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallStreamsRunning, no_streams_running, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateCreated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionOutgoingProgress, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionActive, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(coresList, &mgr->stat.number_of_NotifyReceived, 1, liblinphone_tester_sip_timeout)); + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallIncomingReceived, + focus_stat.number_of_LinphoneCallIncomingReceived + 3, + liblinphone_tester_sip_timeout)); + int focus_no_streams_running = 9; + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallUpdatedByRemote, + focus_stat.number_of_LinphoneCallUpdatedByRemote + (focus_no_streams_running - 3), + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallStreamsRunning, + focus_stat.number_of_LinphoneCallStreamsRunning + focus_no_streams_running, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateCreated, + focus_stat.number_of_LinphoneConferenceStateCreated + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneSubscriptionIncomingReceived, + focus_stat.number_of_LinphoneSubscriptionIncomingReceived + 3, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneSubscriptionActive, + focus_stat.number_of_LinphoneSubscriptionActive + 3, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participants_added, + focus_stat.number_of_participants_added + 3, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_added, + focus_stat.number_of_participant_devices_added + 3, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_joined, + focus_stat.number_of_participant_devices_joined + 3, + liblinphone_tester_sip_timeout)); + + LinphoneConference *fconference = linphone_core_search_conference_2(focus.getLc(), confAddr); + BC_ASSERT_PTR_NOT_NULL(fconference); + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, laure}).waitUntil(chrono::seconds(2), [] { return false; }); + + for (auto mgr : {focus.getCMgr(), marie.getCMgr(), pauline.getCMgr(), laure.getCMgr()}) { + LinphoneAddress *uri = linphone_address_new(linphone_core_get_identity(mgr->lc)); + LinphoneConference *pconference = linphone_core_search_conference_2(mgr->lc, confAddr); + linphone_address_unref(uri); + BC_ASSERT_PTR_NOT_NULL(pconference); + if (pconference) { + const LinphoneConferenceParams *conference_params = linphone_conference_get_current_params(pconference); + int no_participants = 0; + if (start_time >= 0) { + BC_ASSERT_EQUAL((long long)linphone_conference_params_get_start_time(conference_params), + (long long)start_time, long long, "%lld"); + } + BC_ASSERT_EQUAL((long long)linphone_conference_params_get_end_time(conference_params), + (long long)end_time, long long, "%lld"); + if (mgr == focus.getCMgr()) { + no_participants = 3; + BC_ASSERT_FALSE(linphone_conference_is_in(pconference)); + } else { + no_participants = 2; + BC_ASSERT_TRUE(linphone_conference_is_in(pconference)); + LinphoneCall *current_call = linphone_core_get_current_call(mgr->lc); + BC_ASSERT_PTR_NOT_NULL(current_call); + if (current_call) { + BC_ASSERT_EQUAL((int)linphone_call_get_state(current_call), + (int)LinphoneCallStateStreamsRunning, int, "%0d"); + } + BC_ASSERT_TRUE(check_ice(mgr, focus.getCMgr(), LinphoneIceStateHostConnection)); + + LinphoneVideoActivationPolicy *pol = linphone_core_get_video_activation_policy(mgr->lc); + bool_t enabled = !!linphone_video_activation_policy_get_automatically_initiate(pol); + linphone_video_activation_policy_unref(pol); + LinphoneCall *pcall = linphone_core_get_call_by_remote_address2(mgr->lc, confAddr); + BC_ASSERT_PTR_NOT_NULL(pcall); + if (pcall) { + const LinphoneCallParams *call_lparams = linphone_call_get_params(pcall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_lparams), enabled, int, "%0d"); + const LinphoneCallParams *call_rparams = linphone_call_get_remote_params(pcall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_rparams), enabled, int, "%0d"); + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(pcall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_cparams), enabled, int, "%0d"); + } + LinphoneCall *ccall = linphone_core_get_call_by_remote_address2(focus.getLc(), mgr->identity); + BC_ASSERT_PTR_NOT_NULL(ccall); + if (ccall) { + const LinphoneCallParams *call_lparams = linphone_call_get_params(ccall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_lparams), enabled, int, "%0d"); + const LinphoneCallParams *call_rparams = linphone_call_get_remote_params(ccall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_rparams), enabled, int, "%0d"); + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(ccall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_cparams), enabled, int, "%0d"); + } + } + BC_ASSERT_EQUAL(linphone_conference_get_participant_count(pconference), no_participants, int, "%0d"); + bctbx_list_t *devices = linphone_conference_get_participant_device_list(pconference); + BC_ASSERT_EQUAL(bctbx_list_size(devices), 3, size_t, "%zu"); + if (devices) { + bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); + } + BC_ASSERT_STRING_EQUAL(linphone_conference_get_subject(pconference), initialSubject); + LinphoneParticipant *me = linphone_conference_get_me(pconference); + BC_ASSERT_TRUE(linphone_participant_is_admin(me) == + ((mgr == marie.getCMgr()) || (mgr == focus.getCMgr()))); + BC_ASSERT_TRUE(linphone_address_weak_equal(linphone_participant_get_address(me), mgr->identity)); + bctbx_list_t *participants = linphone_conference_get_participant_list(pconference); + for (bctbx_list_t *itp = participants; itp; itp = bctbx_list_next(itp)) { + LinphoneParticipant *p = (LinphoneParticipant *)bctbx_list_get_data(itp); + BC_ASSERT_TRUE( + linphone_participant_is_admin(p) == + linphone_address_weak_equal(linphone_participant_get_address(p), marie.getCMgr()->identity)); + } + bctbx_list_free_with_data(participants, (void (*)(void *))linphone_participant_unref); + + if (mgr != focus.getCMgr()) { + check_conference_ssrc(fconference, pconference); + } + } + } + + // Wait a little bit + wait_for_list(coresList, NULL, 0, 3000); + + std::list<LinphoneCoreManager *> mgrsToRemove{pauline.getCMgr()}; + mgrsToRemove.push_back(laure.getCMgr()); + + stats marie_stat = marie.getStats(); + + for (auto mgr : mgrsToRemove) { + LinphoneCall *call = linphone_core_get_current_call(mgr->lc); + BC_ASSERT_PTR_NOT_NULL(call); + if (call) { + linphone_call_terminate(call); + BC_ASSERT_TRUE( + wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallEnd, 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallReleased, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionTerminated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateTerminationPending, + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateTerminated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateDeleted, 1, + liblinphone_tester_sip_timeout)); + + LinphoneAddress *uri = linphone_address_new(linphone_core_get_identity(mgr->lc)); + LinphoneConference *pconference = linphone_core_search_conference(mgr->lc, NULL, uri, confAddr, NULL); + BC_ASSERT_PTR_NULL(pconference); + linphone_address_unref(uri); + } + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participants_removed, + focus_stat.number_of_participants_removed + 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_removed, + focus_stat.number_of_participant_devices_removed + 2, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_participants_removed, + marie_stat.number_of_participants_removed + 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_participant_devices_removed, + marie_stat.number_of_participant_devices_removed + 2, + liblinphone_tester_sip_timeout)); + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, laure}).waitUntil(chrono::seconds(2), [] { return false; }); + + BC_ASSERT_EQUAL(marie.getStats().number_of_LinphoneConferenceStateTerminationPending, + marie_stat.number_of_LinphoneConferenceStateTerminationPending, int, "%d"); + BC_ASSERT_EQUAL(marie.getStats().number_of_LinphoneConferenceStateTerminated, + marie_stat.number_of_LinphoneConferenceStateTerminated, int, "%d"); + BC_ASSERT_EQUAL(marie.getStats().number_of_LinphoneConferenceStateDeleted, + marie_stat.number_of_LinphoneConferenceStateDeleted, int, "%d"); + + BC_ASSERT_EQUAL(focus.getStats().number_of_LinphoneConferenceStateTerminationPending, + focus_stat.number_of_LinphoneConferenceStateTerminationPending, int, "%d"); + BC_ASSERT_EQUAL(focus.getStats().number_of_LinphoneConferenceStateTerminated, + focus_stat.number_of_LinphoneConferenceStateTerminated, int, "%d"); + BC_ASSERT_EQUAL(focus.getStats().number_of_LinphoneConferenceStateDeleted, + focus_stat.number_of_LinphoneConferenceStateDeleted, int, "%d"); + + for (auto mgr : {focus.getCMgr(), marie.getCMgr()}) { + LinphoneConference *pconference = linphone_core_search_conference_2(mgr->lc, confAddr); + BC_ASSERT_PTR_NOT_NULL(pconference); + if (pconference) { + BC_ASSERT_EQUAL(linphone_conference_get_participant_count(pconference), + ((mgr == focus.getCMgr()) ? 1 : 0), int, "%0d"); + bctbx_list_t *devices = linphone_conference_get_participant_device_list(pconference); + BC_ASSERT_EQUAL(bctbx_list_size(devices), 1, size_t, "%zu"); + if (devices) { + bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); + } + BC_ASSERT_STRING_EQUAL(linphone_conference_get_subject(pconference), initialSubject); + } + } + + const bctbx_list_t *calls = linphone_core_get_calls(marie.getLc()); + BC_ASSERT_EQUAL(bctbx_list_size(calls), 1, size_t, "%zu"); + + LinphoneCall *call = linphone_core_get_call_by_remote_address2(marie.getLc(), focus.getCMgr()->identity); + BC_ASSERT_PTR_NOT_NULL(call); + if (call) { + linphone_call_terminate(call); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallEnd, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallReleased, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneSubscriptionTerminated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, + &marie.getStats().number_of_LinphoneConferenceStateTerminationPending, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateTerminated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateDeleted, 1, + liblinphone_tester_sip_timeout)); + + // Explicitely terminate conference as those on server are static by default + LinphoneConference *pconference = linphone_core_search_conference_2(focus.getLc(), confAddr); + BC_ASSERT_PTR_NOT_NULL(pconference); + if (pconference) { + linphone_conference_terminate(pconference); + } + BC_ASSERT_TRUE(wait_for_list(coresList, + &focus.getStats().number_of_LinphoneConferenceStateTerminationPending, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateTerminated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateDeleted, 1, + liblinphone_tester_sip_timeout)); + } + + for (auto mgr : {marie.getCMgr(), pauline.getCMgr(), laure.getCMgr()}) { + const bctbx_list_t *call_logs = linphone_core_get_call_logs(mgr->lc); + BC_ASSERT_EQUAL((unsigned int)bctbx_list_size(call_logs), 2, unsigned int, "%u"); + + bctbx_list_t *mgr_focus_call_log = + linphone_core_get_call_history_2(mgr->lc, focus.getCMgr()->identity, mgr->identity); + BC_ASSERT_PTR_NOT_NULL(mgr_focus_call_log); + if (mgr_focus_call_log) { + BC_ASSERT_EQUAL((unsigned int)bctbx_list_size(mgr_focus_call_log), 2, unsigned int, "%u"); + for (bctbx_list_t *it = mgr_focus_call_log; it; it = bctbx_list_next(it)) { + LinphoneCallLog *call_log = (LinphoneCallLog *)it->data; + BC_ASSERT_TRUE(linphone_call_log_was_conference(call_log)); + } + bctbx_list_free_with_data(mgr_focus_call_log, (bctbx_list_free_func)linphone_call_log_unref); + } + } + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, laure}).waitUntil(chrono::seconds(2), [] { return false; }); + + linphone_address_unref(confAddr); + bctbx_list_free(coresList); + } +} + +static void edit_simple_conference_base(bool_t from_organizer, + bool_t use_default_account, + bool_t enable_bundle_mode, + bool_t join, + bool_t enable_encryption, + bool_t server_restart, + bool_t role_changed) { + Focus focus("chloe_rc"); + { // to make sure focus is destroyed after clients. + ClientConference marie("marie_rc", focus.getIdentity()); + ClientConference pauline("pauline_rc", focus.getIdentity()); + ClientConference laure("laure_tcp_rc", focus.getIdentity()); + ClientConference michelle("michelle_rc", focus.getIdentity()); + ClientConference lise("lise_rc", focus.getIdentity()); + + LinphoneCoreManager *manager_editing = (from_organizer) ? marie.getCMgr() : laure.getCMgr(); + linphone_core_enable_rtp_bundle(manager_editing->lc, enable_bundle_mode); + int default_index = + linphone_config_get_int(linphone_core_get_config(manager_editing->lc), "sip", "default_proxy", 0); + LinphoneAccountParams *params = linphone_account_params_new_with_config(manager_editing->lc, default_index); + LinphoneAddress *alternative_address = linphone_address_new("sip:toto@sip.linphone.org"); + linphone_account_params_set_identity_address(params, alternative_address); + LinphoneAccount *new_account = linphone_account_new(manager_editing->lc, params); + linphone_core_add_account(manager_editing->lc, new_account); + linphone_account_params_unref(params); + linphone_account_unref(new_account); + + focus.registerAsParticipantDevice(marie); + focus.registerAsParticipantDevice(pauline); + focus.registerAsParticipantDevice(laure); + focus.registerAsParticipantDevice(michelle); + focus.registerAsParticipantDevice(lise); + + setup_conference_info_cbs(marie.getCMgr()); + linphone_core_set_file_transfer_server(marie.getLc(), file_transfer_url); + + for (auto mgr : {focus.getCMgr(), marie.getCMgr(), pauline.getCMgr(), laure.getCMgr()}) { + LinphoneVideoActivationPolicy *pol = + linphone_factory_create_video_activation_policy(linphone_factory_get()); + linphone_video_activation_policy_set_automatically_accept(pol, TRUE); + linphone_video_activation_policy_set_automatically_initiate(pol, TRUE); + linphone_core_set_video_activation_policy(mgr->lc, pol); + linphone_video_activation_policy_unref(pol); + + linphone_core_set_video_device(mgr->lc, liblinphone_tester_mire_id); + linphone_core_enable_video_capture(mgr->lc, TRUE); + linphone_core_enable_video_display(mgr->lc, TRUE); + + if ((mgr != focus.getCMgr()) && enable_encryption) { + linphone_config_set_int(linphone_core_get_config(mgr->lc), "rtp", "accept_any_encryption", 1); + linphone_core_set_media_encryption_mandatory(mgr->lc, TRUE); + linphone_core_set_media_encryption(mgr->lc, LinphoneMediaEncryptionZRTP); + } + } + + bctbx_list_t *coresList = bctbx_list_append(NULL, focus.getLc()); + coresList = bctbx_list_append(coresList, marie.getLc()); + coresList = bctbx_list_append(coresList, pauline.getLc()); + coresList = bctbx_list_append(coresList, laure.getLc()); + coresList = bctbx_list_append(coresList, michelle.getLc()); + coresList = bctbx_list_append(coresList, lise.getLc()); + + std::list<LinphoneCoreManager *> invitedParticipants{pauline.getCMgr(), laure.getCMgr()}; + + time_t start_time = time(NULL) + 600; // Start in 10 minutes + int duration = 60; // minutes + time_t end_time = (duration <= 0) ? -1 : (start_time + duration * 60); + const char *initialSubject = "Test characters: <S-F12><S-F11><S-F6> £$%§"; + const char *description = "Testing characters"; + std::map<LinphoneCoreManager *, LinphoneParticipantRole> participantList; + LinphoneParticipantRole role = LinphoneParticipantRoleSpeaker; + for (auto &p : invitedParticipants) { + participantList.insert(std::make_pair(p, role)); + role = (role == LinphoneParticipantRoleSpeaker) ? LinphoneParticipantRoleListener + : LinphoneParticipantRoleSpeaker; + } + LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, + initialSubject, description, TRUE); + BC_ASSERT_PTR_NOT_NULL(confAddr); + + // Chat room creation to send ICS + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateCreated, 2, + liblinphone_tester_sip_timeout)); + + char *conference_address_str = (confAddr) ? linphone_address_as_string(confAddr) : ms_strdup("<unknown>"); + + std::list<LinphoneCoreManager *> participants{pauline.getCMgr(), laure.getCMgr()}; + + char *uid = NULL; + unsigned int sequence = 0; + LinphoneConferenceInfo *conf_info = linphone_core_find_conference_information_from_uri(marie.getLc(), confAddr); + BC_ASSERT_PTR_NOT_NULL(conf_info); + if (conf_info) { + uid = ms_strdup(linphone_conference_info_get_ics_uid(conf_info)); + BC_ASSERT_PTR_NOT_NULL(uid); + sequence = linphone_conference_info_get_ics_sequence(conf_info); + + ms_message("%s is trying to update conference %s - adding %s", linphone_core_get_identity(marie.getLc()), + conference_address_str, linphone_core_get_identity(lise.getLc())); + + stats focus_stat = focus.getStats(); + stats marie_stat = marie.getStats(); + linphone_conference_info_set_subject(conf_info, initialSubject); + linphone_conference_info_set_description(conf_info, description); + LinphoneParticipantInfo *lise_participant_info = linphone_participant_info_new(lise.getCMgr()->identity); + linphone_participant_info_set_role(lise_participant_info, LinphoneParticipantRoleListener); + linphone_conference_info_add_participant_2(conf_info, lise_participant_info); + linphone_participant_info_unref(lise_participant_info); + + if (role_changed) { + LinphoneParticipantRole current_pauline_role = participantList[pauline.getCMgr()]; + LinphoneParticipantRole new_pauline_role = (current_pauline_role == LinphoneParticipantRoleListener) + ? LinphoneParticipantRoleSpeaker + : LinphoneParticipantRoleListener; + ms_message("%s is trying to update conference %s - changing role of %s from %s to %s", + linphone_core_get_identity(marie.getLc()), conference_address_str, + linphone_core_get_identity(pauline.getLc()), + linphone_participant_role_to_string(current_pauline_role), + linphone_participant_role_to_string(new_pauline_role)); + LinphoneParticipantInfo *pauline_participant_info = + linphone_participant_info_new(pauline.getCMgr()->identity); + linphone_participant_info_set_role(pauline_participant_info, new_pauline_role); + linphone_conference_info_add_participant_2(conf_info, pauline_participant_info); + linphone_participant_info_unref(pauline_participant_info); + participantList[pauline.getCMgr()] = new_pauline_role; + } + + participants.push_back(lise.getCMgr()); + + const auto ics_participant_number = 3; + const bctbx_list_t *ics_participants = linphone_conference_info_get_participants(conf_info); + BC_ASSERT_EQUAL(bctbx_list_size(ics_participants), ics_participant_number, size_t, "%zu"); + + std::list<stats> participant_stats; + for (auto mgr : {focus.getCMgr(), marie.getCMgr(), laure.getCMgr(), pauline.getCMgr(), lise.getCMgr()}) { + participant_stats.push_back(mgr->stat); + } + + LinphoneConferenceScheduler *conference_scheduler = + linphone_core_create_conference_scheduler(marie.getLc()); + LinphoneConferenceSchedulerCbs *cbs = + linphone_factory_create_conference_scheduler_cbs(linphone_factory_get()); + linphone_conference_scheduler_cbs_set_state_changed(cbs, conference_scheduler_state_changed); + linphone_conference_scheduler_cbs_set_invitations_sent(cbs, conference_scheduler_invitations_sent); + linphone_conference_scheduler_add_callbacks(conference_scheduler, cbs); + linphone_conference_scheduler_cbs_unref(cbs); + linphone_conference_scheduler_set_info(conference_scheduler, conf_info); + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_ConferenceSchedulerStateUpdating, + marie_stat.number_of_ConferenceSchedulerStateUpdating + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_ConferenceSchedulerStateReady, + marie_stat.number_of_ConferenceSchedulerStateReady + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallIncomingReceived, + focus_stat.number_of_LinphoneCallIncomingReceived + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallStreamsRunning, + focus_stat.number_of_LinphoneCallStreamsRunning + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallEnd, + focus_stat.number_of_LinphoneCallEnd + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallReleased, + focus_stat.number_of_LinphoneCallReleased + 1, + liblinphone_tester_sip_timeout)); + + LinphoneChatRoomParams *chat_room_params = linphone_core_create_default_chat_room_params(marie.getLc()); + linphone_chat_room_params_set_backend(chat_room_params, LinphoneChatRoomBackendBasic); + linphone_conference_scheduler_send_invitations(conference_scheduler, chat_room_params); + linphone_chat_room_params_unref(chat_room_params); + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_ConferenceSchedulerInvitationsSent, + marie_stat.number_of_ConferenceSchedulerInvitationsSent + 1, + liblinphone_tester_sip_timeout)); + + for (auto mgr : {focus.getCMgr(), marie.getCMgr(), laure.getCMgr(), pauline.getCMgr(), lise.getCMgr()}) { + auto old_stats = participant_stats.front(); + if ((mgr != focus.getCMgr()) && (mgr != marie.getCMgr())) { + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneMessageReceived, + old_stats.number_of_LinphoneMessageReceived + 1, + liblinphone_tester_sip_timeout)); + if (!linphone_core_conference_ics_in_message_body_enabled(marie.getLc())) { + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneMessageReceivedWithFile, + old_stats.number_of_LinphoneMessageReceivedWithFile + 1, + liblinphone_tester_sip_timeout)); + } + + BC_ASSERT_PTR_NOT_NULL(mgr->stat.last_received_chat_message); + if (mgr->stat.last_received_chat_message != NULL) { + const string expected = ContentType::Icalendar.getMediaType(); + BC_ASSERT_STRING_EQUAL( + linphone_chat_message_get_content_type(mgr->stat.last_received_chat_message), + expected.c_str()); + } + + bctbx_list_t *participant_chat_room_participants = + bctbx_list_append(NULL, marie.getCMgr()->identity); + LinphoneChatRoom *pcr = linphone_core_search_chat_room(mgr->lc, NULL, mgr->identity, NULL, + participant_chat_room_participants); + bctbx_list_free(participant_chat_room_participants); + BC_ASSERT_PTR_NOT_NULL(pcr); + bctbx_list_t *chat_room_participants = bctbx_list_append(NULL, mgr->identity); + + LinphoneChatRoom *cr = linphone_core_search_chat_room( + marie.getLc(), NULL, marie.getCMgr()->identity, NULL, chat_room_participants); + bctbx_list_free(chat_room_participants); + BC_ASSERT_PTR_NOT_NULL(cr); + + BC_ASSERT_EQUAL((int)bctbx_list_size(linphone_core_get_chat_rooms(mgr->lc)), 1, int, "%d"); + + if (cr) { + LinphoneChatMessage *msg = linphone_chat_room_get_last_message_in_history(cr); + BC_ASSERT_PTR_NOT_NULL(msg); + + const bctbx_list_t *original_contents = linphone_chat_message_get_contents(msg); + BC_ASSERT_EQUAL((int)bctbx_list_size(original_contents), 1, int, "%d"); + LinphoneContent *original_content = (LinphoneContent *)bctbx_list_get_data(original_contents); + if (BC_ASSERT_PTR_NOT_NULL(original_content)) { + LinphoneConferenceInfo *conf_info_from_original_content = + linphone_factory_create_conference_info_from_icalendar_content(linphone_factory_get(), + original_content); + if (BC_ASSERT_PTR_NOT_NULL(conf_info_from_original_content)) { + BC_ASSERT_TRUE(linphone_address_weak_equal( + marie.getCMgr()->identity, + linphone_conference_info_get_organizer(conf_info_from_original_content))); + BC_ASSERT_TRUE(linphone_address_weak_equal( + confAddr, linphone_conference_info_get_uri(conf_info_from_original_content))); + + const bctbx_list_t *ics_participants = + linphone_conference_info_get_participants(conf_info_from_original_content); + BC_ASSERT_EQUAL(bctbx_list_size(ics_participants), ics_participant_number, size_t, + "%zu"); + + if (start_time > 0) { + BC_ASSERT_EQUAL((long long)linphone_conference_info_get_date_time( + conf_info_from_original_content), + (long long)start_time, long long, "%lld"); + if (end_time > 0) { + const int duration_m = + linphone_conference_info_get_duration(conf_info_from_original_content); + const int duration_s = duration_m * 60; + BC_ASSERT_EQUAL(duration_s, (int)(end_time - start_time), int, "%d"); + BC_ASSERT_EQUAL(duration_m, duration, int, "%d"); + } + } + if (initialSubject) { + BC_ASSERT_STRING_EQUAL( + linphone_conference_info_get_subject(conf_info_from_original_content), + initialSubject); + } else { + BC_ASSERT_PTR_NULL( + linphone_conference_info_get_subject(conf_info_from_original_content)); + } + if (description) { + BC_ASSERT_STRING_EQUAL( + linphone_conference_info_get_description(conf_info_from_original_content), + description); + } else { + BC_ASSERT_PTR_NULL( + linphone_conference_info_get_description(conf_info_from_original_content)); + } + BC_ASSERT_STRING_EQUAL( + linphone_conference_info_get_ics_uid(conf_info_from_original_content), uid); + const unsigned int ics_sequence = + linphone_conference_info_get_ics_sequence(conf_info_from_original_content); + int exp_sequence = 0; + if (mgr == lise.getCMgr()) { + exp_sequence = 0; + } else { + exp_sequence = (sequence + 1); + } + + BC_ASSERT_EQUAL(ics_sequence, exp_sequence, int, "%d"); + + LinphoneConferenceInfoState exp_state = LinphoneConferenceInfoStateNew; + + if (mgr == lise.getCMgr()) { + exp_state = LinphoneConferenceInfoStateNew; + } else { + exp_state = LinphoneConferenceInfoStateUpdated; + } + BC_ASSERT_EQUAL( + (int)linphone_conference_info_get_state(conf_info_from_original_content), + (int)exp_state, int, "%d"); + + linphone_conference_info_unref(conf_info_from_original_content); + } + } + linphone_chat_message_unref(msg); + } + } + participant_stats.pop_front(); + } + linphone_conference_info_unref(conf_info); + linphone_conference_scheduler_unref(conference_scheduler); + } + + if (join) { + std::list<LinphoneCoreManager *> conferenceMgrs{focus.getCMgr(), marie.getCMgr(), pauline.getCMgr()}; + std::list<LinphoneCoreManager *> members{marie.getCMgr(), pauline.getCMgr()}; + + stats focus_stat = focus.getStats(); + for (auto mgr : members) { + LinphoneCallParams *new_params = linphone_core_create_call_params(mgr->lc, nullptr); + linphone_core_invite_address_with_params_2(mgr->lc, confAddr, new_params, NULL, nullptr); + linphone_call_params_unref(new_params); + } + + for (auto mgr : members) { + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallOutgoingProgress, 1, + liblinphone_tester_sip_timeout)); + int no_streams_running = 2; + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallUpdating, + (no_streams_running - 1), liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallStreamsRunning, + no_streams_running, liblinphone_tester_sip_timeout)); + // Update to add to conference. + // If ICE is enabled, the addition to a conference may go through a resume of the call + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateCreated, + ((mgr == marie.getCMgr()) ? 3 : 2), liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionOutgoingProgress, 1, 5000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionActive, 1, 5000)); + BC_ASSERT_TRUE( + wait_for_list(coresList, &mgr->stat.number_of_NotifyReceived, 1, liblinphone_tester_sip_timeout)); + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallIncomingReceived, + focus_stat.number_of_LinphoneCallIncomingReceived + 2, + liblinphone_tester_sip_timeout)); + int focus_no_streams_running = 4; + // Update to end ICE negotiations + BC_ASSERT_TRUE( + wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallUpdatedByRemote, + focus_stat.number_of_LinphoneCallUpdatedByRemote + (focus_no_streams_running - 2), + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallStreamsRunning, + focus_stat.number_of_LinphoneCallStreamsRunning + focus_no_streams_running, + liblinphone_tester_sip_timeout)); + // Update to add to conference. + // If ICE is enabled, the addition to a conference may go through a resume of the call + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateCreated, + focus_stat.number_of_LinphoneConferenceStateCreated + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneSubscriptionIncomingReceived, + focus_stat.number_of_LinphoneSubscriptionIncomingReceived + 2, 5000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneSubscriptionActive, + focus_stat.number_of_LinphoneSubscriptionActive + 2, 5000)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participants_added, + focus_stat.number_of_participants_added + 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_added, + focus_stat.number_of_participant_devices_added + 2, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_joined, + focus_stat.number_of_participant_devices_joined + 2, + liblinphone_tester_sip_timeout)); + + std::map<LinphoneCoreManager *, LinphoneParticipantRole> memberList; + for (const auto &member : members) { + try { + const auto &participantInfo = participantList.at(member); + memberList.insert(std::make_pair(member, participantInfo)); + } catch (std::out_of_range &) { + if (member == marie.getCMgr()) { + memberList.insert(std::make_pair(marie.getCMgr(), LinphoneParticipantRoleSpeaker)); + } else { + ms_fatal("Unable to find active participant %s in the participant list", + linphone_core_get_identity(member->lc)); + } + } + } + wait_for_conference_streams({focus, marie, pauline, laure, michelle, lise}, conferenceMgrs, focus.getCMgr(), + memberList, confAddr, TRUE); + + LinphoneConference *fconference = linphone_core_search_conference_2(focus.getLc(), confAddr); + BC_ASSERT_PTR_NOT_NULL(fconference); + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, laure, michelle, lise}).waitUntil(chrono::seconds(2), [] { + return false; + }); + + for (auto mgr : conferenceMgrs) { + LinphoneConference *pconference = linphone_core_search_conference_2(mgr->lc, confAddr); + BC_ASSERT_PTR_NOT_NULL(pconference); + if (pconference) { + const LinphoneConferenceParams *conference_params = + linphone_conference_get_current_params(pconference); + int no_participants = 0; + if (start_time >= 0) { + BC_ASSERT_EQUAL((long long)linphone_conference_params_get_start_time(conference_params), + (long long)start_time, long long, "%lld"); + } + BC_ASSERT_EQUAL((long long)linphone_conference_params_get_end_time(conference_params), + (long long)end_time, long long, "%lld"); + + bctbx_list_t *participant_device_list = + linphone_conference_get_participant_device_list(pconference); + BC_ASSERT_EQUAL(bctbx_list_size(participant_device_list), 2, size_t, "%zu"); + for (bctbx_list_t *d_it = participant_device_list; d_it; d_it = bctbx_list_next(d_it)) { + LinphoneParticipantDevice *d = (LinphoneParticipantDevice *)bctbx_list_get_data(d_it); + BC_ASSERT_PTR_NOT_NULL(d); + if (d) { + BC_ASSERT_TRUE((!!linphone_participant_device_get_is_muted(d)) == + (linphone_address_weak_equal(linphone_participant_device_get_address(d), + laure.getCMgr()->identity))); + linphone_participant_device_set_user_data(d, mgr->lc); + LinphoneParticipantDeviceCbs *cbs = + linphone_factory_create_participant_device_cbs(linphone_factory_get()); + linphone_participant_device_cbs_set_is_muted(cbs, on_muted_notified); + linphone_participant_device_add_callbacks(d, cbs); + linphone_participant_device_cbs_unref(cbs); + } + } + bctbx_list_free_with_data(participant_device_list, + (void (*)(void *))linphone_participant_device_unref); + + if (mgr == focus.getCMgr()) { + no_participants = 2; + BC_ASSERT_FALSE(linphone_conference_is_in(pconference)); + } else { + no_participants = 1; + BC_ASSERT_TRUE(linphone_conference_is_in(pconference)); + LinphoneCall *current_call = linphone_core_get_current_call(mgr->lc); + BC_ASSERT_PTR_NOT_NULL(current_call); + if (current_call) { + BC_ASSERT_EQUAL((int)linphone_call_get_state(current_call), + (int)LinphoneCallStateStreamsRunning, int, "%0d"); + } + + LinphoneVideoActivationPolicy *pol = linphone_core_get_video_activation_policy(mgr->lc); + bool_t enabled = !!linphone_video_activation_policy_get_automatically_initiate(pol); + linphone_video_activation_policy_unref(pol); + + int no_streams_audio = 1; + int no_streams_video = 3; + int no_active_streams_video = no_streams_video; + int no_streams_text = 0; + + LinphoneCall *pcall = linphone_core_get_call_by_remote_address2(mgr->lc, confAddr); + BC_ASSERT_PTR_NOT_NULL(pcall); + if (pcall) { + _linphone_call_check_nb_streams(pcall, no_streams_audio, no_streams_video, no_streams_text); + _linphone_call_check_nb_active_streams(pcall, no_streams_audio, no_active_streams_video, + no_streams_text); + const LinphoneCallParams *call_lparams = linphone_call_get_params(pcall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_lparams), enabled, int, "%0d"); + const LinphoneCallParams *call_rparams = linphone_call_get_remote_params(pcall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_rparams), enabled, int, "%0d"); + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(pcall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_cparams), enabled, int, "%0d"); + } + LinphoneCall *ccall = linphone_core_get_call_by_remote_address2(focus.getLc(), mgr->identity); + BC_ASSERT_PTR_NOT_NULL(ccall); + if (ccall) { + _linphone_call_check_nb_streams(ccall, no_streams_audio, no_streams_video, no_streams_text); + _linphone_call_check_nb_active_streams(ccall, no_streams_audio, no_active_streams_video, + no_streams_text); + const LinphoneCallParams *call_lparams = linphone_call_get_params(ccall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_lparams), enabled, int, "%0d"); + const LinphoneCallParams *call_rparams = linphone_call_get_remote_params(ccall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_rparams), enabled, int, "%0d"); + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(ccall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_cparams), enabled, int, "%0d"); + } + } + BC_ASSERT_EQUAL(linphone_conference_get_participant_count(pconference), no_participants, int, + "%0d"); + BC_ASSERT_STRING_EQUAL(linphone_conference_get_subject(pconference), initialSubject); + LinphoneParticipant *me = linphone_conference_get_me(pconference); + BC_ASSERT_TRUE(linphone_participant_is_admin(me) == + ((mgr == marie.getCMgr()) || (mgr == focus.getCMgr()))); + BC_ASSERT_TRUE(linphone_address_weak_equal(linphone_participant_get_address(me), mgr->identity)); + bctbx_list_t *participants = linphone_conference_get_participant_list(pconference); + for (bctbx_list_t *itp = participants; itp; itp = bctbx_list_next(itp)) { + LinphoneParticipant *p = (LinphoneParticipant *)bctbx_list_get_data(itp); + BC_ASSERT_TRUE(linphone_participant_is_admin(p) == + linphone_address_weak_equal(linphone_participant_get_address(p), + marie.getCMgr()->identity)); + } + bctbx_list_free_with_data(participants, (void (*)(void *))linphone_participant_unref); + + if (mgr != focus.getCMgr()) { + check_conference_ssrc(fconference, pconference); + } + } + } + } + + if (server_restart) { + ms_message("%s is restarting", linphone_core_get_identity(focus.getLc())); + coresList = bctbx_list_remove(coresList, focus.getLc()); + // Restart flexisip + focus.reStart(); + + LinphoneVideoActivationPolicy *pol = + linphone_factory_create_video_activation_policy(linphone_factory_get()); + linphone_video_activation_policy_set_automatically_accept(pol, TRUE); + linphone_video_activation_policy_set_automatically_initiate(pol, TRUE); + linphone_core_set_video_activation_policy(focus.getLc(), pol); + linphone_video_activation_policy_unref(pol); + + linphone_core_enable_video_capture(focus.getLc(), TRUE); + linphone_core_enable_video_display(focus.getLc(), TRUE); + coresList = bctbx_list_append(coresList, focus.getLc()); + } + + bool_t add = TRUE; + const char *subject = "Test characters: <S-F12><S-F11><S-F6> £$%§ (+edits)"; + const char *description2 = "Testing characters (+edits)"; + + LinphoneAccount *editing_account = NULL; + if (use_default_account) { + editing_account = linphone_core_get_default_account(manager_editing->lc); + } else { + editing_account = linphone_core_lookup_known_account(manager_editing->lc, alternative_address); + } + BC_ASSERT_PTR_NOT_NULL(editing_account); + if (editing_account) { + LinphoneAccountParams *account_params = + linphone_account_params_clone(linphone_account_get_params(editing_account)); + linphone_account_params_enable_rtp_bundle(account_params, enable_bundle_mode); + linphone_account_set_params(editing_account, account_params); + linphone_account_params_unref(account_params); + } + + for (int attempt = 0; attempt < 3; attempt++) { + ms_message("%s is trying to update conference %s - attempt %0d - %s %s", + linphone_core_get_identity(manager_editing->lc), conference_address_str, attempt, + (add) ? "adding" : "removing", linphone_core_get_identity(michelle.getLc())); + + stats focus_stat = focus.getStats(); + stats manager_editing_stat = manager_editing->stat; + + std::list<LinphoneCoreManager *> participants{pauline.getCMgr(), laure.getCMgr(), lise.getCMgr()}; + LinphoneConferenceInfo *conf_info = + linphone_core_find_conference_information_from_uri(manager_editing->lc, confAddr); + BC_ASSERT_PTR_NOT_NULL(conf_info); + if (conf_info) { + linphone_conference_info_set_subject(conf_info, subject); + linphone_conference_info_set_description(conf_info, description2); + if (add) { + linphone_conference_info_add_participant(conf_info, michelle.getCMgr()->identity); + participants.push_back(michelle.getCMgr()); + } else { + linphone_conference_info_remove_participant(conf_info, michelle.getCMgr()->identity); + } + + const auto ics_participant_number = ((add) ? 4 : 3) + ((join) ? 1 : 0); + const bctbx_list_t *ics_participants = linphone_conference_info_get_participants(conf_info); + BC_ASSERT_EQUAL(bctbx_list_size(ics_participants), ics_participant_number, size_t, "%zu"); + + std::list<stats> participant_stats; + for (auto mgr : {focus.getCMgr(), marie.getCMgr(), laure.getCMgr(), pauline.getCMgr(), + michelle.getCMgr(), lise.getCMgr()}) { + participant_stats.push_back(mgr->stat); + } + + LinphoneConferenceScheduler *conference_scheduler = + linphone_core_create_conference_scheduler(manager_editing->lc); + linphone_conference_scheduler_set_account(conference_scheduler, editing_account); + LinphoneConferenceSchedulerCbs *cbs = + linphone_factory_create_conference_scheduler_cbs(linphone_factory_get()); + linphone_conference_scheduler_cbs_set_state_changed(cbs, conference_scheduler_state_changed); + linphone_conference_scheduler_cbs_set_invitations_sent(cbs, conference_scheduler_invitations_sent); + linphone_conference_scheduler_add_callbacks(conference_scheduler, cbs); + linphone_conference_scheduler_cbs_unref(cbs); + linphone_conference_scheduler_set_info(conference_scheduler, conf_info); + + if (use_default_account) { + BC_ASSERT_TRUE(wait_for_list(coresList, + &manager_editing->stat.number_of_ConferenceSchedulerStateUpdating, + manager_editing_stat.number_of_ConferenceSchedulerStateUpdating + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, + &manager_editing->stat.number_of_ConferenceSchedulerStateReady, + manager_editing_stat.number_of_ConferenceSchedulerStateReady + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallIncomingReceived, + focus_stat.number_of_LinphoneCallIncomingReceived + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallStreamsRunning, + focus_stat.number_of_LinphoneCallStreamsRunning + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallEnd, + focus_stat.number_of_LinphoneCallEnd + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallReleased, + focus_stat.number_of_LinphoneCallReleased + 1, + liblinphone_tester_sip_timeout)); + + LinphoneChatRoomParams *chat_room_params = + linphone_core_create_default_chat_room_params(manager_editing->lc); + linphone_chat_room_params_set_backend(chat_room_params, LinphoneChatRoomBackendBasic); + linphone_conference_scheduler_send_invitations(conference_scheduler, chat_room_params); + linphone_chat_room_params_unref(chat_room_params); + + BC_ASSERT_TRUE(wait_for_list(coresList, + &manager_editing->stat.number_of_ConferenceSchedulerInvitationsSent, + manager_editing_stat.number_of_ConferenceSchedulerInvitationsSent + 1, + liblinphone_tester_sip_timeout)); + + for (auto mgr : {focus.getCMgr(), marie.getCMgr(), laure.getCMgr(), pauline.getCMgr(), + michelle.getCMgr(), lise.getCMgr()}) { + auto old_stats = participant_stats.front(); + if ((mgr != focus.getCMgr()) && (mgr != manager_editing)) { + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneMessageReceived, + old_stats.number_of_LinphoneMessageReceived + 1, + liblinphone_tester_sip_timeout)); + if (!linphone_core_conference_ics_in_message_body_enabled(manager_editing->lc)) { + BC_ASSERT_TRUE(wait_for_list(coresList, + &mgr->stat.number_of_LinphoneMessageReceivedWithFile, + old_stats.number_of_LinphoneMessageReceivedWithFile + 1, + liblinphone_tester_sip_timeout)); + } + + BC_ASSERT_PTR_NOT_NULL(mgr->stat.last_received_chat_message); + if (mgr->stat.last_received_chat_message != NULL) { + const string expected = ContentType::Icalendar.getMediaType(); + BC_ASSERT_STRING_EQUAL( + linphone_chat_message_get_content_type(mgr->stat.last_received_chat_message), + expected.c_str()); + } + + bctbx_list_t *participant_chat_room_participants = + bctbx_list_append(NULL, manager_editing->identity); + LinphoneChatRoom *pcr = linphone_core_search_chat_room(mgr->lc, NULL, mgr->identity, NULL, + participant_chat_room_participants); + bctbx_list_free(participant_chat_room_participants); + BC_ASSERT_PTR_NOT_NULL(pcr); + bctbx_list_t *chat_room_participants = bctbx_list_append(NULL, mgr->identity); + + LinphoneChatRoom *cr = linphone_core_search_chat_room( + manager_editing->lc, NULL, manager_editing->identity, NULL, chat_room_participants); + bctbx_list_free(chat_room_participants); + BC_ASSERT_PTR_NOT_NULL(cr); + + int number_chat_rooms = 0; + if (mgr == marie.getCMgr()) { + number_chat_rooms = 3; + } else if (from_organizer || (mgr == michelle.getCMgr())) { + number_chat_rooms = 1; + } else { + number_chat_rooms = 2; + } + BC_ASSERT_EQUAL((int)bctbx_list_size(linphone_core_get_chat_rooms(mgr->lc)), + number_chat_rooms, int, "%d"); + + if (cr) { + LinphoneChatMessage *msg = linphone_chat_room_get_last_message_in_history(cr); + BC_ASSERT_PTR_NOT_NULL(msg); + + const bctbx_list_t *original_contents = linphone_chat_message_get_contents(msg); + BC_ASSERT_EQUAL((int)bctbx_list_size(original_contents), 1, int, "%d"); + LinphoneContent *original_content = + (LinphoneContent *)bctbx_list_get_data(original_contents); + if (BC_ASSERT_PTR_NOT_NULL(original_content)) { + LinphoneConferenceInfo *conf_info_from_original_content = + linphone_factory_create_conference_info_from_icalendar_content( + linphone_factory_get(), original_content); + if (BC_ASSERT_PTR_NOT_NULL(conf_info_from_original_content)) { + BC_ASSERT_TRUE(linphone_address_weak_equal( + marie.getCMgr()->identity, + linphone_conference_info_get_organizer(conf_info_from_original_content))); + BC_ASSERT_TRUE(linphone_address_weak_equal( + confAddr, + linphone_conference_info_get_uri(conf_info_from_original_content))); + + const bctbx_list_t *ics_participants = + linphone_conference_info_get_participants(conf_info_from_original_content); + BC_ASSERT_EQUAL(bctbx_list_size(ics_participants), ics_participant_number, + size_t, "%zu"); + + if (start_time > 0) { + BC_ASSERT_EQUAL((long long)linphone_conference_info_get_date_time( + conf_info_from_original_content), + (long long)start_time, long long, "%lld"); + if (end_time > 0) { + const int duration_m = linphone_conference_info_get_duration( + conf_info_from_original_content); + const int duration_s = duration_m * 60; + BC_ASSERT_EQUAL(duration_s, (int)(end_time - start_time), int, "%d"); + BC_ASSERT_EQUAL(duration_m, duration, int, "%d"); + } + } + if (subject) { + BC_ASSERT_STRING_EQUAL( + linphone_conference_info_get_subject(conf_info_from_original_content), + subject); + } else { + BC_ASSERT_PTR_NULL( + linphone_conference_info_get_subject(conf_info_from_original_content)); + } + if (description2) { + BC_ASSERT_STRING_EQUAL(linphone_conference_info_get_description( + conf_info_from_original_content), + description2); + } else { + BC_ASSERT_PTR_NULL(linphone_conference_info_get_description( + conf_info_from_original_content)); + } + BC_ASSERT_STRING_EQUAL( + linphone_conference_info_get_ics_uid(conf_info_from_original_content), uid); + const unsigned int ics_sequence = + linphone_conference_info_get_ics_sequence(conf_info_from_original_content); + int exp_sequence = 0; + if (mgr == michelle.getCMgr()) { + if (add) { + exp_sequence = 0; + } else { + exp_sequence = 1; + } + } else if (mgr == lise.getCMgr()) { + exp_sequence = (sequence + attempt + 1); + } else { + exp_sequence = (sequence + attempt + 2); + } + + BC_ASSERT_EQUAL(ics_sequence, exp_sequence, int, "%d"); + + LinphoneConferenceInfoState exp_state = LinphoneConferenceInfoStateNew; + + if (mgr == michelle.getCMgr()) { + if (add) { + exp_state = LinphoneConferenceInfoStateNew; + } else { + exp_state = LinphoneConferenceInfoStateCancelled; + } + } else { + exp_state = LinphoneConferenceInfoStateUpdated; + } + BC_ASSERT_EQUAL( + (int)linphone_conference_info_get_state(conf_info_from_original_content), + (int)exp_state, int, "%d"); + + linphone_conference_info_unref(conf_info_from_original_content); + } + } + linphone_chat_message_unref(msg); + } + } + participant_stats.pop_front(); + } + } else { + BC_ASSERT_TRUE(wait_for_list(coresList, + &manager_editing->stat.number_of_ConferenceSchedulerStateError, + manager_editing_stat.number_of_ConferenceSchedulerStateError + 1, + liblinphone_tester_sip_timeout)); + } + linphone_conference_scheduler_unref(conference_scheduler); + + for (auto mgr : {focus.getCMgr(), marie.getCMgr(), laure.getCMgr(), pauline.getCMgr(), + michelle.getCMgr(), lise.getCMgr()}) { + LinphoneConferenceInfo *info = + linphone_core_find_conference_information_from_uri(mgr->lc, confAddr); + if (!use_default_account && (mgr == michelle.getCMgr())) { + BC_ASSERT_PTR_NULL(info); + } else if (BC_ASSERT_PTR_NOT_NULL(info)) { + + const char *exp_subject = NULL; + if (use_default_account) { + exp_subject = subject; + } else { + exp_subject = initialSubject; + } + + const char *exp_description = NULL; + if (mgr != focus.getCMgr()) { + if (use_default_account) { + exp_description = description2; + } else { + exp_description = description; + } + } + + unsigned int exp_sequence = 0; + LinphoneConferenceInfoState exp_state = LinphoneConferenceInfoStateNew; + if (mgr == focus.getCMgr()) { + exp_sequence = 0; + exp_state = LinphoneConferenceInfoStateUpdated; + } else { + if (mgr == michelle.getCMgr()) { + if (add) { + exp_state = LinphoneConferenceInfoStateNew; + } else { + exp_state = LinphoneConferenceInfoStateCancelled; + } + } else if (mgr == lise.getCMgr()) { + exp_state = use_default_account ? LinphoneConferenceInfoStateUpdated + : LinphoneConferenceInfoStateNew; + } else { + exp_state = LinphoneConferenceInfoStateUpdated; + } + if (mgr == michelle.getCMgr()) { + if (add) { + exp_sequence = 0; + } else { + exp_sequence = 1; + } + } else if (mgr == lise.getCMgr()) { + exp_sequence = use_default_account ? (sequence + attempt + 1) : 0; + } else { + exp_sequence = use_default_account ? (sequence + attempt + 2) : 1; + } + } + check_conference_info( + mgr, confAddr, marie.getCMgr(), + ((use_default_account && add) ? 4 : 3) + ((join || (mgr == focus.getCMgr())) ? 1 : 0), + start_time, duration, exp_subject, exp_description, exp_sequence, exp_state); + + if (mgr != focus.getCMgr()) { + for (auto &p : participants) { + int exp_participant_sequence = 0; + // If not using the default account (which was used to create the conference), the + // conference scheduler errors out and Michelle is not added + if ((use_default_account) || (p != michelle.getCMgr())) { + if (!use_default_account) { + exp_participant_sequence = (p == lise.getCMgr()) ? 0 : 1; + } else if (p == michelle.getCMgr()) { + exp_participant_sequence = 0; + } else if (p == lise.getCMgr()) { + exp_participant_sequence = attempt + 1; + } else { + exp_participant_sequence = attempt + 2; + } + linphone_conference_info_check_participant(info, p->identity, + exp_participant_sequence); + } + } + int exp_organizer_sequence = 0; + if (use_default_account) { + exp_organizer_sequence = attempt + 2; + } else { + exp_organizer_sequence = 1; + } + linphone_conference_info_check_organizer(info, exp_organizer_sequence); + } + } + if (info) { + linphone_conference_info_unref(info); + } + } + linphone_conference_info_unref(conf_info); + } + add = !add; + } + ms_free(conference_address_str); + ms_free(uid); + linphone_address_unref(alternative_address); + linphone_address_unref(confAddr); + bctbx_list_free(coresList); + } +} + +static void organizer_edits_simple_conference(void) { + edit_simple_conference_base(TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE); +} +static void organizer_edits_simple_conference_using_different_account(void) { + edit_simple_conference_base(TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE); +} + +static void organizer_edits_simple_conference_while_active(void) { + edit_simple_conference_base(TRUE, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE); +} + +static void participant_edits_simple_conference(void) { + edit_simple_conference_base(FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE); +} + +static void participant_edits_simple_conference_using_different_account(void) { + edit_simple_conference_base(FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE); +} + +static void organizer_edits_simple_conference_with_server_restart(void) { + edit_simple_conference_base(TRUE, TRUE, FALSE, FALSE, TRUE, TRUE, FALSE); +} + +static void conference_edition_with_participant_role_changed(void) { + edit_simple_conference_base(TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, TRUE); +} + +static void conference_edition_with_simultaneous_participant_add_remove_base(bool_t codec_mismatch) { + Focus focus("chloe_rc"); + { // to make sure focus is destroyed after clients. + linphone_core_enable_lime_x3dh(focus.getLc(), true); + + ClientConference marie("marie_rc", focus.getIdentity(), true); + ClientConference pauline("pauline_rc", focus.getIdentity(), true); + ClientConference laure("laure_tcp_rc", focus.getIdentity(), true); + ClientConference michelle("michelle_rc", focus.getIdentity(), true); + + focus.registerAsParticipantDevice(marie); + focus.registerAsParticipantDevice(pauline); + focus.registerAsParticipantDevice(laure); + focus.registerAsParticipantDevice(michelle); + + setup_conference_info_cbs(marie.getCMgr()); + linphone_core_set_file_transfer_server(marie.getLc(), file_transfer_url); + + bctbx_list_t *coresList = bctbx_list_append(NULL, focus.getLc()); + coresList = bctbx_list_append(coresList, marie.getLc()); + coresList = bctbx_list_append(coresList, pauline.getLc()); + coresList = bctbx_list_append(coresList, laure.getLc()); + coresList = bctbx_list_append(coresList, michelle.getLc()); + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_X3dhUserCreationSuccess, 1, + x3dhServer_creationTimeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_X3dhUserCreationSuccess, 1, + x3dhServer_creationTimeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_X3dhUserCreationSuccess, 1, + x3dhServer_creationTimeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_X3dhUserCreationSuccess, 1, + x3dhServer_creationTimeout)); + + BC_ASSERT_TRUE(linphone_core_lime_x3dh_enabled(marie.getLc())); + BC_ASSERT_TRUE(linphone_core_lime_x3dh_enabled(pauline.getLc())); + BC_ASSERT_TRUE(linphone_core_lime_x3dh_enabled(laure.getLc())); + BC_ASSERT_TRUE(linphone_core_lime_x3dh_enabled(michelle.getLc())); + + for (auto mgr : {focus.getCMgr(), marie.getCMgr(), pauline.getCMgr(), laure.getCMgr(), michelle.getCMgr()}) { + if (codec_mismatch) { + if (mgr == marie.getCMgr()) { + disable_all_audio_codecs_except_one(mgr->lc, "pcmu", -1); + } else { + disable_all_audio_codecs_except_one(mgr->lc, "pcma", -1); + } + } + } + + std::list<LinphoneCoreManager *> participants{pauline.getCMgr(), marie.getCMgr(), laure.getCMgr()}; + + time_t start_time = time(NULL) + 600; // Start in 10 minutes + int duration = 60; // minutes + time_t end_time = (duration <= 0) ? -1 : (start_time + duration * 60); + const char *initialSubject = "Test characters: <S-F12><S-F11><S-F6> £$%§"; + const char *description = "Testing characters"; + std::map<LinphoneCoreManager *, LinphoneParticipantRole> participantList; + LinphoneParticipantRole role = LinphoneParticipantRoleSpeaker; + for (auto &p : participants) { + participantList.insert(std::make_pair(p, role)); + role = (role == LinphoneParticipantRoleSpeaker) ? LinphoneParticipantRoleListener + : LinphoneParticipantRoleSpeaker; + } + LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, + initialSubject, description, TRUE); + BC_ASSERT_PTR_NOT_NULL(confAddr); + + // Chat room creation to send ICS + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateCreated, 2, + liblinphone_tester_sip_timeout)); + + const char *subject = "Test characters: <S-F12><S-F11><S-F6> £$%§ (+edits)"; + const char *description2 = "Testing characters (+edits)"; + + stats marie_stat = marie.getStats(); + LinphoneAccount *editing_account = linphone_core_get_default_account(marie.getLc()); + BC_ASSERT_PTR_NOT_NULL(editing_account); + + char *uid = NULL; + unsigned int sequence = 0; + LinphoneConferenceInfo *info = linphone_core_find_conference_information_from_uri(marie.getLc(), confAddr); + if (BC_ASSERT_PTR_NOT_NULL(info)) { + uid = ms_strdup(linphone_conference_info_get_ics_uid(info)); + BC_ASSERT_PTR_NOT_NULL(uid); + sequence = linphone_conference_info_get_ics_sequence(info); + linphone_conference_info_unref(info); + } + + char *conference_address_str = (confAddr) ? linphone_address_as_string(confAddr) : ms_strdup("<unknown>"); + ms_message("%s is trying to update conference %s - adding %s and removing %s", + linphone_core_get_identity(marie.getLc()), conference_address_str, + linphone_core_get_identity(michelle.getLc()), linphone_core_get_identity(laure.getLc())); + ms_free(conference_address_str); + + stats focus_stat = focus.getStats(); + + LinphoneConferenceInfo *conf_info = linphone_core_find_conference_information_from_uri(marie.getLc(), confAddr); + linphone_conference_info_set_subject(conf_info, subject); + linphone_conference_info_set_description(conf_info, description2); + linphone_conference_info_add_participant(conf_info, michelle.getCMgr()->identity); + linphone_conference_info_remove_participant(conf_info, laure.getCMgr()->identity); + + const bctbx_list_t *ics_participants = linphone_conference_info_get_participants(conf_info); + BC_ASSERT_EQUAL(bctbx_list_size(ics_participants), participants.size(), size_t, "%zu"); + + std::list<stats> participant_stats; + for (auto mgr : {focus.getCMgr(), marie.getCMgr(), laure.getCMgr(), pauline.getCMgr(), michelle.getCMgr()}) { + participant_stats.push_back(mgr->stat); + } + + LinphoneConferenceScheduler *conference_scheduler = linphone_core_create_conference_scheduler(marie.getLc()); + linphone_conference_scheduler_set_account(conference_scheduler, editing_account); + LinphoneConferenceSchedulerCbs *cbs = linphone_factory_create_conference_scheduler_cbs(linphone_factory_get()); + linphone_conference_scheduler_cbs_set_state_changed(cbs, conference_scheduler_state_changed); + linphone_conference_scheduler_cbs_set_invitations_sent(cbs, conference_scheduler_invitations_sent); + linphone_conference_scheduler_add_callbacks(conference_scheduler, cbs); + linphone_conference_scheduler_cbs_unref(cbs); + linphone_conference_scheduler_set_info(conference_scheduler, conf_info); + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_ConferenceSchedulerStateUpdating, + marie_stat.number_of_ConferenceSchedulerStateUpdating + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_ConferenceSchedulerStateReady, + marie_stat.number_of_ConferenceSchedulerStateReady + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallIncomingReceived, + focus_stat.number_of_LinphoneCallIncomingReceived + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallStreamsRunning, + focus_stat.number_of_LinphoneCallStreamsRunning + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallEnd, + focus_stat.number_of_LinphoneCallEnd + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallReleased, + focus_stat.number_of_LinphoneCallReleased + 1, liblinphone_tester_sip_timeout)); + + LinphoneChatRoomParams *chat_room_params = linphone_core_create_default_chat_room_params(marie.getLc()); + linphone_chat_room_params_set_subject(chat_room_params, "Conference"); + linphone_chat_room_params_enable_encryption(chat_room_params, TRUE); + linphone_chat_room_params_set_backend(chat_room_params, LinphoneChatRoomBackendFlexisipChat); + linphone_conference_scheduler_send_invitations(conference_scheduler, chat_room_params); + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_ConferenceSchedulerInvitationsSent, + marie_stat.number_of_ConferenceSchedulerInvitationsSent + 1, + liblinphone_tester_sip_timeout)); + + for (auto mgr : {focus.getCMgr(), marie.getCMgr(), laure.getCMgr(), pauline.getCMgr(), michelle.getCMgr()}) { + auto old_stats = participant_stats.front(); + if ((mgr != focus.getCMgr()) && (mgr != marie.getCMgr())) { + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneMessageReceived, + old_stats.number_of_LinphoneMessageReceived + 1, + liblinphone_tester_sip_timeout)); + if (!linphone_core_conference_ics_in_message_body_enabled(marie.getLc())) { + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneMessageReceivedWithFile, + old_stats.number_of_LinphoneMessageReceivedWithFile + 1, + liblinphone_tester_sip_timeout)); + } + + BC_ASSERT_PTR_NOT_NULL(mgr->stat.last_received_chat_message); + if (mgr->stat.last_received_chat_message != NULL) { + const string expected = ContentType::Icalendar.getMediaType(); + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_content_type(mgr->stat.last_received_chat_message), + expected.c_str()); + } + + bctbx_list_t *participant_chat_room_participants = bctbx_list_append(NULL, marie.getCMgr()->identity); + LinphoneChatRoom *pcr = linphone_core_search_chat_room(mgr->lc, chat_room_params, mgr->identity, NULL, + participant_chat_room_participants); + bctbx_list_free(participant_chat_room_participants); + BC_ASSERT_PTR_NOT_NULL(pcr); + bctbx_list_t *chat_room_participants = bctbx_list_append(NULL, mgr->identity); + + LinphoneChatRoom *cr = linphone_core_search_chat_room( + marie.getLc(), chat_room_params, marie.getCMgr()->identity, NULL, chat_room_participants); + bctbx_list_free(chat_room_participants); + BC_ASSERT_PTR_NOT_NULL(cr); + + BC_ASSERT_EQUAL((int)bctbx_list_size(linphone_core_get_chat_rooms(mgr->lc)), + (mgr == michelle.getCMgr()) ? 1 : 2, int, "%d"); + + if (cr) { + LinphoneChatMessage *msg = linphone_chat_room_get_last_message_in_history(cr); + BC_ASSERT_PTR_NOT_NULL(msg); + + const bctbx_list_t *original_contents = linphone_chat_message_get_contents(msg); + BC_ASSERT_EQUAL((int)bctbx_list_size(original_contents), 1, int, "%d"); + LinphoneContent *original_content = (LinphoneContent *)bctbx_list_get_data(original_contents); + if (BC_ASSERT_PTR_NOT_NULL(original_content)) { + LinphoneConferenceInfo *conf_info_from_original_content = + linphone_factory_create_conference_info_from_icalendar_content(linphone_factory_get(), + original_content); + if (BC_ASSERT_PTR_NOT_NULL(conf_info_from_original_content)) { + BC_ASSERT_TRUE(linphone_address_weak_equal( + marie.getCMgr()->identity, + linphone_conference_info_get_organizer(conf_info_from_original_content))); + BC_ASSERT_TRUE(linphone_address_weak_equal( + confAddr, linphone_conference_info_get_uri(conf_info_from_original_content))); + + const bctbx_list_t *ics_participants = + linphone_conference_info_get_participants(conf_info_from_original_content); + BC_ASSERT_EQUAL(bctbx_list_size(ics_participants), participants.size(), size_t, "%zu"); + + if (start_time > 0) { + BC_ASSERT_EQUAL( + (long long)linphone_conference_info_get_date_time(conf_info_from_original_content), + (long long)start_time, long long, "%lld"); + if (end_time > 0) { + const int duration_m = + linphone_conference_info_get_duration(conf_info_from_original_content); + const int duration_s = duration_m * 60; + BC_ASSERT_EQUAL(duration_s, (int)(end_time - start_time), int, "%d"); + BC_ASSERT_EQUAL(duration_m, duration, int, "%d"); + } + } + if (subject) { + BC_ASSERT_STRING_EQUAL( + linphone_conference_info_get_subject(conf_info_from_original_content), subject); + } else { + BC_ASSERT_PTR_NULL( + linphone_conference_info_get_subject(conf_info_from_original_content)); + } + if (description2) { + BC_ASSERT_STRING_EQUAL( + linphone_conference_info_get_description(conf_info_from_original_content), + description2); + } else { + BC_ASSERT_PTR_NULL( + linphone_conference_info_get_description(conf_info_from_original_content)); + } + BC_ASSERT_STRING_EQUAL( + linphone_conference_info_get_ics_uid(conf_info_from_original_content), uid); + const unsigned int ics_sequence = + linphone_conference_info_get_ics_sequence(conf_info_from_original_content); + BC_ASSERT_EQUAL(ics_sequence, (mgr == michelle.getCMgr()) ? 0 : (sequence + 1), int, "%d"); + + LinphoneConferenceInfoState exp_state = LinphoneConferenceInfoStateNew; + if (mgr == laure.getCMgr()) { + exp_state = LinphoneConferenceInfoStateCancelled; + } else if (mgr == michelle.getCMgr()) { + exp_state = LinphoneConferenceInfoStateNew; + } else { + exp_state = LinphoneConferenceInfoStateUpdated; + } + BC_ASSERT_EQUAL((int)linphone_conference_info_get_state(conf_info_from_original_content), + (int)exp_state, int, "%d"); + + linphone_conference_info_unref(conf_info_from_original_content); + } + } + linphone_chat_message_unref(msg); + } + } + participant_stats.pop_front(); + } + linphone_chat_room_params_unref(chat_room_params); + linphone_conference_scheduler_unref(conference_scheduler); + + for (auto mgr : {focus.getCMgr(), marie.getCMgr(), laure.getCMgr(), pauline.getCMgr(), michelle.getCMgr()}) { + LinphoneConferenceInfo *info = linphone_core_find_conference_information_from_uri(mgr->lc, confAddr); + if (BC_ASSERT_PTR_NOT_NULL(info)) { + + const char *exp_subject = subject; + + const char *exp_description = NULL; + if (mgr != focus.getCMgr()) { + exp_description = description2; + } + + unsigned int exp_sequence = 0; + LinphoneConferenceInfoState exp_state = LinphoneConferenceInfoStateNew; + if (mgr == focus.getCMgr()) { + exp_sequence = 0; + exp_state = LinphoneConferenceInfoStateUpdated; + } else { + exp_sequence = (mgr == michelle.getCMgr()) ? 0 : (sequence + 1); + if (mgr == laure.getCMgr()) { + exp_state = LinphoneConferenceInfoStateCancelled; + } else if (mgr == michelle.getCMgr()) { + exp_state = LinphoneConferenceInfoStateNew; + } else { + exp_state = LinphoneConferenceInfoStateUpdated; + } + } + check_conference_info(mgr, confAddr, marie.getCMgr(), participants.size(), start_time, duration, + exp_subject, exp_description, exp_sequence, exp_state); + } + if (info) { + linphone_conference_info_unref(info); + } + } + linphone_conference_info_unref(conf_info); + ms_free(uid); + linphone_address_unref(confAddr); + bctbx_list_free(coresList); + } +} + +static void conference_edition_with_simultaneous_participant_add_remove(void) { + conference_edition_with_simultaneous_participant_add_remove_base(FALSE); +} + +static void conference_edition_with_organizer_codec_mismatch(void) { + conference_edition_with_simultaneous_participant_add_remove_base(TRUE); +} + +static void conference_cancelled_through_edit_base(bool_t server_restart) { + Focus focus("chloe_rc"); + { // to make sure focus is destroyed after clients. + ClientConference marie("marie_rc", focus.getIdentity()); + ClientConference pauline("pauline_rc", focus.getIdentity()); + ClientConference laure("laure_tcp_rc", focus.getIdentity()); + ClientConference michelle("michelle_rc", focus.getIdentity()); + + focus.registerAsParticipantDevice(marie); + focus.registerAsParticipantDevice(pauline); + focus.registerAsParticipantDevice(laure); + focus.registerAsParticipantDevice(michelle); + + setup_conference_info_cbs(marie.getCMgr()); + linphone_core_set_file_transfer_server(marie.getLc(), file_transfer_url); + + bctbx_list_t *coresList = bctbx_list_append(NULL, focus.getLc()); + coresList = bctbx_list_append(coresList, marie.getLc()); + coresList = bctbx_list_append(coresList, pauline.getLc()); + coresList = bctbx_list_append(coresList, laure.getLc()); + coresList = bctbx_list_append(coresList, michelle.getLc()); + + std::list<LinphoneCoreManager *> participants{michelle.getCMgr(), pauline.getCMgr(), laure.getCMgr()}; + + time_t start_time = time(NULL) + 600; // Start in 10 minutes + int duration = 60; // minutes + time_t end_time = (duration <= 0) ? -1 : (start_time + duration * 60); + const char *initialSubject = "Test characters: <S-F12><S-F11><S-F6> £$%§"; + const char *description = "Testing characters"; + std::map<LinphoneCoreManager *, LinphoneParticipantRole> participantList; + LinphoneParticipantRole role = LinphoneParticipantRoleSpeaker; + for (auto &p : participants) { + participantList.insert(std::make_pair(p, role)); + role = (role == LinphoneParticipantRoleSpeaker) ? LinphoneParticipantRoleListener + : LinphoneParticipantRoleSpeaker; + } + LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, + initialSubject, description, TRUE); + BC_ASSERT_PTR_NOT_NULL(confAddr); + + // Chat room creation to send ICS + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateCreated, 2, + liblinphone_tester_sip_timeout)); + + char *uid = NULL; + unsigned int sequence = 0; + LinphoneConferenceInfo *info = linphone_core_find_conference_information_from_uri(marie.getLc(), confAddr); + if (BC_ASSERT_PTR_NOT_NULL(info)) { + uid = ms_strdup(linphone_conference_info_get_ics_uid(info)); + BC_ASSERT_PTR_NOT_NULL(uid); + sequence = linphone_conference_info_get_ics_sequence(info); + linphone_conference_info_unref(info); + } + + stats focus_stat = focus.getStats(); + stats manager_editing_stat = marie.getStats(); + + std::list<stats> participant_stats; + for (auto mgr : {focus.getCMgr(), marie.getCMgr(), laure.getCMgr(), pauline.getCMgr(), michelle.getCMgr()}) { + participant_stats.push_back(mgr->stat); + } + + char *conference_address_str = (confAddr) ? linphone_address_as_string(confAddr) : ms_strdup("<unknown>"); + ms_message("%s is trying to change duration of conference %s", linphone_core_get_identity(marie.getLc()), + conference_address_str); + LinphoneConferenceInfo *conf_info = linphone_core_find_conference_information_from_uri(marie.getLc(), confAddr); + unsigned int new_duration = 2000; + linphone_conference_info_set_duration(conf_info, new_duration); + + LinphoneConferenceScheduler *conference_scheduler = linphone_core_create_conference_scheduler(marie.getLc()); + LinphoneAccount *editing_account = linphone_core_get_default_account(marie.getLc()); + BC_ASSERT_PTR_NOT_NULL(editing_account); + linphone_conference_scheduler_set_account(conference_scheduler, editing_account); + LinphoneConferenceSchedulerCbs *cbs = linphone_factory_create_conference_scheduler_cbs(linphone_factory_get()); + linphone_conference_scheduler_cbs_set_state_changed(cbs, conference_scheduler_state_changed); + linphone_conference_scheduler_cbs_set_invitations_sent(cbs, conference_scheduler_invitations_sent); + linphone_conference_scheduler_add_callbacks(conference_scheduler, cbs); + linphone_conference_scheduler_cbs_unref(cbs); + cbs = nullptr; + linphone_conference_scheduler_set_info(conference_scheduler, conf_info); + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_ConferenceSchedulerStateUpdating, + manager_editing_stat.number_of_ConferenceSchedulerStateUpdating + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_ConferenceSchedulerStateReady, + manager_editing_stat.number_of_ConferenceSchedulerStateReady + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallIncomingReceived, + focus_stat.number_of_LinphoneCallIncomingReceived + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallStreamsRunning, + focus_stat.number_of_LinphoneCallStreamsRunning + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallEnd, + focus_stat.number_of_LinphoneCallEnd + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallReleased, + focus_stat.number_of_LinphoneCallReleased + 1, liblinphone_tester_sip_timeout)); + + LinphoneChatRoomParams *chat_room_params = linphone_core_create_default_chat_room_params(marie.getLc()); + linphone_chat_room_params_set_backend(chat_room_params, LinphoneChatRoomBackendBasic); + linphone_conference_scheduler_send_invitations(conference_scheduler, chat_room_params); + linphone_chat_room_params_unref(chat_room_params); + chat_room_params = nullptr; + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_ConferenceSchedulerInvitationsSent, + manager_editing_stat.number_of_ConferenceSchedulerInvitationsSent + 1, + liblinphone_tester_sip_timeout)); + + for (auto mgr : {focus.getCMgr(), marie.getCMgr(), laure.getCMgr(), pauline.getCMgr(), michelle.getCMgr()}) { + auto old_stats = participant_stats.front(); + if ((mgr != focus.getCMgr()) && (mgr != marie.getCMgr())) { + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneMessageReceived, + old_stats.number_of_LinphoneMessageReceived + 1, + liblinphone_tester_sip_timeout)); + if (!linphone_core_conference_ics_in_message_body_enabled(marie.getLc())) { + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneMessageReceivedWithFile, + old_stats.number_of_LinphoneMessageReceivedWithFile + 1, + liblinphone_tester_sip_timeout)); + } + + BC_ASSERT_PTR_NOT_NULL(mgr->stat.last_received_chat_message); + if (mgr->stat.last_received_chat_message != NULL) { + const string expected = ContentType::Icalendar.getMediaType(); + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_content_type(mgr->stat.last_received_chat_message), + expected.c_str()); + } + + bctbx_list_t *participant_chat_room_participants = bctbx_list_append(NULL, marie.getCMgr()->identity); + LinphoneChatRoom *pcr = linphone_core_search_chat_room(mgr->lc, NULL, mgr->identity, NULL, + participant_chat_room_participants); + bctbx_list_free(participant_chat_room_participants); + BC_ASSERT_PTR_NOT_NULL(pcr); + bctbx_list_t *chat_room_participants = bctbx_list_append(NULL, mgr->identity); + + LinphoneChatRoom *cr = linphone_core_search_chat_room(marie.getLc(), NULL, marie.getCMgr()->identity, + NULL, chat_room_participants); + bctbx_list_free(chat_room_participants); + BC_ASSERT_PTR_NOT_NULL(cr); + + BC_ASSERT_EQUAL((int)bctbx_list_size(linphone_core_get_chat_rooms(mgr->lc)), 1, int, "%d"); + + if (cr) { + LinphoneChatMessage *msg = linphone_chat_room_get_last_message_in_history(cr); + BC_ASSERT_PTR_NOT_NULL(msg); + + const bctbx_list_t *original_contents = linphone_chat_message_get_contents(msg); + BC_ASSERT_EQUAL((int)bctbx_list_size(original_contents), 1, int, "%d"); + LinphoneContent *original_content = (LinphoneContent *)bctbx_list_get_data(original_contents); + if (BC_ASSERT_PTR_NOT_NULL(original_content)) { + LinphoneConferenceInfo *conf_info_from_original_content = + linphone_factory_create_conference_info_from_icalendar_content(linphone_factory_get(), + original_content); + if (BC_ASSERT_PTR_NOT_NULL(conf_info_from_original_content)) { + BC_ASSERT_TRUE(linphone_address_weak_equal( + marie.getCMgr()->identity, + linphone_conference_info_get_organizer(conf_info_from_original_content))); + BC_ASSERT_TRUE(linphone_address_weak_equal( + confAddr, linphone_conference_info_get_uri(conf_info_from_original_content))); + + const bctbx_list_t *ics_participants = + linphone_conference_info_get_participants(conf_info_from_original_content); + BC_ASSERT_EQUAL(bctbx_list_size(ics_participants), 3, size_t, "%zu"); + + if (start_time > 0) { + BC_ASSERT_EQUAL( + (long long)linphone_conference_info_get_date_time(conf_info_from_original_content), + (long long)start_time, long long, "%lld"); + if (end_time > 0) { + const int duration_m = + linphone_conference_info_get_duration(conf_info_from_original_content); + BC_ASSERT_EQUAL(duration_m, new_duration, int, "%d"); + } + } + if (initialSubject) { + BC_ASSERT_STRING_EQUAL( + linphone_conference_info_get_subject(conf_info_from_original_content), + initialSubject); + } else { + BC_ASSERT_PTR_NULL( + linphone_conference_info_get_subject(conf_info_from_original_content)); + } + if (description) { + BC_ASSERT_STRING_EQUAL( + linphone_conference_info_get_description(conf_info_from_original_content), + description); + } else { + BC_ASSERT_PTR_NULL( + linphone_conference_info_get_description(conf_info_from_original_content)); + } + BC_ASSERT_STRING_EQUAL( + linphone_conference_info_get_ics_uid(conf_info_from_original_content), uid); + const unsigned int ics_sequence = + linphone_conference_info_get_ics_sequence(conf_info_from_original_content); + BC_ASSERT_EQUAL(ics_sequence, (sequence + 1), int, "%d"); + + LinphoneConferenceInfoState exp_state = LinphoneConferenceInfoStateUpdated; + BC_ASSERT_EQUAL((int)linphone_conference_info_get_state(conf_info_from_original_content), + (int)exp_state, int, "%d"); + + linphone_conference_info_unref(conf_info_from_original_content); + } + } + linphone_chat_message_unref(msg); + } + } + participant_stats.pop_front(); + } + linphone_conference_scheduler_unref(conference_scheduler); + conference_scheduler = nullptr; + linphone_conference_info_unref(conf_info); + conf_info = nullptr; + + if (server_restart) { + ms_message("%s is restarting", linphone_core_get_identity(focus.getLc())); + coresList = bctbx_list_remove(coresList, focus.getLc()); + // Restart flexisip + focus.reStart(); + + LinphoneVideoActivationPolicy *pol = + linphone_factory_create_video_activation_policy(linphone_factory_get()); + linphone_video_activation_policy_set_automatically_accept(pol, TRUE); + linphone_video_activation_policy_set_automatically_initiate(pol, TRUE); + linphone_core_set_video_activation_policy(focus.getLc(), pol); + linphone_video_activation_policy_unref(pol); + + linphone_core_enable_video_capture(focus.getLc(), TRUE); + linphone_core_enable_video_display(focus.getLc(), TRUE); + coresList = bctbx_list_append(coresList, focus.getLc()); + } + + const char *subject = "Test characters: <S-F12><S-F11><S-F6> £$%§ (+cancelled)"; + const char *description2 = "Testing characters (+cancelled)"; + + info = linphone_core_find_conference_information_from_uri(marie.getLc(), confAddr); + if (BC_ASSERT_PTR_NOT_NULL(info)) { + sequence = linphone_conference_info_get_ics_sequence(info); + linphone_conference_info_unref(info); + } + + ms_message("%s is trying to cancel conference %s", linphone_core_get_identity(marie.getLc()), + conference_address_str); + ms_free(conference_address_str); + + focus_stat = focus.getStats(); + manager_editing_stat = marie.getStats(); + + participant_stats.clear(); + for (auto mgr : {focus.getCMgr(), marie.getCMgr(), laure.getCMgr(), pauline.getCMgr(), michelle.getCMgr()}) { + participant_stats.push_back(mgr->stat); + } + + conf_info = linphone_core_find_conference_information_from_uri(marie.getLc(), confAddr); + linphone_conference_info_set_subject(conf_info, subject); + linphone_conference_info_set_description(conf_info, description2); + + const bctbx_list_t *ics_participants = linphone_conference_info_get_participants(conf_info); + BC_ASSERT_EQUAL(bctbx_list_size(ics_participants), 3, size_t, "%zu"); + + conference_scheduler = linphone_core_create_conference_scheduler(marie.getLc()); + linphone_conference_scheduler_set_account(conference_scheduler, editing_account); + cbs = linphone_factory_create_conference_scheduler_cbs(linphone_factory_get()); + linphone_conference_scheduler_cbs_set_state_changed(cbs, conference_scheduler_state_changed); + linphone_conference_scheduler_cbs_set_invitations_sent(cbs, conference_scheduler_invitations_sent); + linphone_conference_scheduler_add_callbacks(conference_scheduler, cbs); + linphone_conference_scheduler_cbs_unref(cbs); + linphone_conference_scheduler_cancel_conference(conference_scheduler, conf_info); + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_ConferenceSchedulerStateUpdating, + manager_editing_stat.number_of_ConferenceSchedulerStateUpdating + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_ConferenceSchedulerStateReady, + manager_editing_stat.number_of_ConferenceSchedulerStateReady + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallIncomingReceived, + focus_stat.number_of_LinphoneCallIncomingReceived + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallStreamsRunning, + focus_stat.number_of_LinphoneCallStreamsRunning + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallEnd, + focus_stat.number_of_LinphoneCallEnd + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallReleased, + focus_stat.number_of_LinphoneCallReleased + 1, liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateTerminationPending, + focus_stat.number_of_LinphoneConferenceStateTerminationPending + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateTerminated, + focus_stat.number_of_LinphoneConferenceStateTerminated + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateDeleted, + focus_stat.number_of_LinphoneConferenceStateDeleted + 1, + liblinphone_tester_sip_timeout)); + + chat_room_params = linphone_core_create_default_chat_room_params(marie.getLc()); + linphone_chat_room_params_set_backend(chat_room_params, LinphoneChatRoomBackendBasic); + linphone_conference_scheduler_send_invitations(conference_scheduler, chat_room_params); + linphone_chat_room_params_unref(chat_room_params); + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_ConferenceSchedulerInvitationsSent, + manager_editing_stat.number_of_ConferenceSchedulerInvitationsSent + 1, + liblinphone_tester_sip_timeout)); + + for (auto mgr : {focus.getCMgr(), marie.getCMgr(), laure.getCMgr(), pauline.getCMgr(), michelle.getCMgr()}) { + auto old_stats = participant_stats.front(); + if ((mgr != focus.getCMgr()) && (mgr != marie.getCMgr())) { + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneMessageReceived, + old_stats.number_of_LinphoneMessageReceived + 1, + liblinphone_tester_sip_timeout)); + if (!linphone_core_conference_ics_in_message_body_enabled(marie.getLc())) { + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneMessageReceivedWithFile, + old_stats.number_of_LinphoneMessageReceivedWithFile + 1, + liblinphone_tester_sip_timeout)); + } + + BC_ASSERT_PTR_NOT_NULL(mgr->stat.last_received_chat_message); + if (mgr->stat.last_received_chat_message != NULL) { + const string expected = ContentType::Icalendar.getMediaType(); + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_content_type(mgr->stat.last_received_chat_message), + expected.c_str()); + } + + bctbx_list_t *participant_chat_room_participants = bctbx_list_append(NULL, marie.getCMgr()->identity); + LinphoneChatRoom *pcr = linphone_core_search_chat_room(mgr->lc, NULL, mgr->identity, NULL, + participant_chat_room_participants); + bctbx_list_free(participant_chat_room_participants); + BC_ASSERT_PTR_NOT_NULL(pcr); + bctbx_list_t *chat_room_participants = bctbx_list_append(NULL, mgr->identity); + + LinphoneChatRoom *cr = linphone_core_search_chat_room(marie.getLc(), NULL, marie.getCMgr()->identity, + NULL, chat_room_participants); + bctbx_list_free(chat_room_participants); + BC_ASSERT_PTR_NOT_NULL(cr); + + BC_ASSERT_EQUAL((int)bctbx_list_size(linphone_core_get_chat_rooms(mgr->lc)), 1, int, "%d"); + + if (cr) { + LinphoneChatMessage *msg = linphone_chat_room_get_last_message_in_history(cr); + BC_ASSERT_PTR_NOT_NULL(msg); + + const bctbx_list_t *original_contents = linphone_chat_message_get_contents(msg); + BC_ASSERT_EQUAL((int)bctbx_list_size(original_contents), 1, int, "%d"); + LinphoneContent *original_content = (LinphoneContent *)bctbx_list_get_data(original_contents); + if (BC_ASSERT_PTR_NOT_NULL(original_content)) { + LinphoneConferenceInfo *conf_info_from_original_content = + linphone_factory_create_conference_info_from_icalendar_content(linphone_factory_get(), + original_content); + if (BC_ASSERT_PTR_NOT_NULL(conf_info_from_original_content)) { + BC_ASSERT_TRUE(linphone_address_weak_equal( + marie.getCMgr()->identity, + linphone_conference_info_get_organizer(conf_info_from_original_content))); + BC_ASSERT_TRUE(linphone_address_weak_equal( + confAddr, linphone_conference_info_get_uri(conf_info_from_original_content))); + + const bctbx_list_t *ics_participants = + linphone_conference_info_get_participants(conf_info_from_original_content); + BC_ASSERT_EQUAL(bctbx_list_size(ics_participants), 0, size_t, "%zu"); + + if (start_time > 0) { + BC_ASSERT_EQUAL( + (long long)linphone_conference_info_get_date_time(conf_info_from_original_content), + (long long)start_time, long long, "%lld"); + if (end_time > 0) { + const int duration_m = + linphone_conference_info_get_duration(conf_info_from_original_content); + BC_ASSERT_EQUAL(duration_m, new_duration, int, "%d"); + } + } + if (subject) { + BC_ASSERT_STRING_EQUAL( + linphone_conference_info_get_subject(conf_info_from_original_content), subject); + } else { + BC_ASSERT_PTR_NULL( + linphone_conference_info_get_subject(conf_info_from_original_content)); + } + if (description2) { + BC_ASSERT_STRING_EQUAL( + linphone_conference_info_get_description(conf_info_from_original_content), + description2); + } else { + BC_ASSERT_PTR_NULL( + linphone_conference_info_get_description(conf_info_from_original_content)); + } + BC_ASSERT_STRING_EQUAL( + linphone_conference_info_get_ics_uid(conf_info_from_original_content), uid); + const unsigned int ics_sequence = + linphone_conference_info_get_ics_sequence(conf_info_from_original_content); + BC_ASSERT_EQUAL(ics_sequence, (sequence + 1), int, "%d"); + + LinphoneConferenceInfoState exp_state = LinphoneConferenceInfoStateNew; + if (mgr == focus.getCMgr()) { + exp_state = LinphoneConferenceInfoStateUpdated; + } else { + exp_state = LinphoneConferenceInfoStateCancelled; + } + BC_ASSERT_EQUAL((int)linphone_conference_info_get_state(conf_info_from_original_content), + (int)exp_state, int, "%d"); + + linphone_conference_info_unref(conf_info_from_original_content); + } + } + linphone_chat_message_unref(msg); + } + } + participant_stats.pop_front(); + } + linphone_conference_scheduler_unref(conference_scheduler); + + for (auto mgr : {focus.getCMgr(), marie.getCMgr(), laure.getCMgr(), pauline.getCMgr(), michelle.getCMgr()}) { + LinphoneConferenceInfo *info = linphone_core_find_conference_information_from_uri(mgr->lc, confAddr); + if (BC_ASSERT_PTR_NOT_NULL(info)) { + + const char *exp_subject = subject; + + const char *exp_description = NULL; + if (mgr != focus.getCMgr()) { + exp_description = description2; + } + + unsigned int exp_sequence = 0; + LinphoneConferenceInfoState exp_state = LinphoneConferenceInfoStateCancelled; + unsigned int exp_participant_number = 0; + if (mgr == focus.getCMgr()) { + exp_sequence = 0; + } else { + exp_sequence = (sequence + 1); + } + check_conference_info(mgr, confAddr, marie.getCMgr(), exp_participant_number, start_time, new_duration, + exp_subject, exp_description, exp_sequence, exp_state); + } + if (info) { + linphone_conference_info_unref(info); + } + } + linphone_conference_info_unref(conf_info); + ms_free(uid); + linphone_address_unref(confAddr); + bctbx_list_free(coresList); + } +} + +static void conference_cancelled_through_edit(void) { + conference_cancelled_through_edit_base(FALSE); +} + +static void create_conference_with_server_restart_conference_cancelled(void) { + conference_cancelled_through_edit_base(TRUE); +} + +#if 0 +static void conference_with_participant_added_outside_valid_time_slot (bool_t before_start) { + Focus focus("chloe_rc"); + {//to make sure focus is destroyed after clients. + ClientConference marie("marie_rc", focus.getIdentity()); + ClientConference pauline("pauline_rc", focus.getIdentity()); + ClientConference laure("laure_tcp_rc", focus.getIdentity()); + + focus.registerAsParticipantDevice(marie); + focus.registerAsParticipantDevice(pauline); + focus.registerAsParticipantDevice(laure); + + setup_conference_info_cbs(marie.getCMgr()); + + linphone_core_set_file_transfer_server(marie.getLc(), file_transfer_url); + linphone_core_set_conference_participant_list_type(focus.getLc(), LinphoneConferenceParticipantListTypeOpen); + + bctbx_list_t * coresList = bctbx_list_append(NULL, focus.getLc()); + coresList = bctbx_list_append(coresList, marie.getLc()); + coresList = bctbx_list_append(coresList, pauline.getLc()); + coresList = bctbx_list_append(coresList, laure.getLc()); + + std::list<LinphoneCoreManager *> participants{pauline.getCMgr(), laure.getCMgr()}; + time_t start_time = (time_t)-1; + time_t end_time = (time_t)-1; + + if (before_start) { + start_time = ms_time(NULL) + 600; + } else { + start_time = ms_time(NULL) - 60; + } + end_time = start_time + 60; + const char *initialSubject = "Colleagues"; + const char *description = "Tom Black"; + std::map<LinphoneCoreManager *, LinphoneParticipantRole> participantList; + LinphoneParticipantRole role = LinphoneParticipantRoleSpeaker; + for (auto &p : participants) { + participantList.insert(std::make_pair(p, role)); + role = (role == LinphoneParticipantRoleSpeaker) ? LinphoneParticipantRoleListener : LinphoneParticipantRoleSpeaker; + } + LinphoneAddress* confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, initialSubject, description, TRUE); + BC_ASSERT_PTR_NOT_NULL(confAddr); + // Chat room creation to send ICS + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateCreated, 2, liblinphone_tester_sip_timeout)); + + for (auto mgr : {marie.getCMgr(), pauline.getCMgr(), laure.getCMgr()}) { + LinphoneCallParams *new_params = linphone_core_create_call_params(mgr->lc, nullptr); + linphone_core_invite_address_with_params_2(mgr->lc, confAddr, new_params, NULL, nullptr); + linphone_call_params_unref(new_params); + } + + for (auto mgr : {marie.getCMgr(), pauline.getCMgr(), laure.getCMgr()}) { + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallOutgoingProgress, 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallStreamsRunning, 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallEnd, 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallReleased, 1, liblinphone_tester_sip_timeout)); + } + + //wait bit more to detect side effect if any + CoreManagerAssert({focus,marie,pauline,laure}).waitUntil(chrono::seconds(2),[] { + return false; + }); + + linphone_address_unref(confAddr); + bctbx_list_free(coresList); + + } +} + +static void conference_with_participants_added_after_end (void) { + conference_with_participant_added_outside_valid_time_slot(FALSE); +} + +static void conference_with_participants_added_before_start (void) { + conference_with_participant_added_outside_valid_time_slot(TRUE); +} +#endif + +static void two_overlapping_conferences_base(bool_t same_organizer, bool_t dialout) { + Focus focus("chloe_rc"); + { // to make sure focus is destroyed after clients. + ClientConference marie("marie_rc", focus.getIdentity()); + ClientConference pauline("pauline_rc", focus.getIdentity()); + ClientConference laure("laure_tcp_rc", focus.getIdentity()); + ClientConference michelle("michelle_rc", focus.getIdentity()); + ClientConference berthe("berthe_rc", focus.getIdentity()); + + focus.registerAsParticipantDevice(marie); + focus.registerAsParticipantDevice(pauline); + focus.registerAsParticipantDevice(laure); + focus.registerAsParticipantDevice(michelle); + focus.registerAsParticipantDevice(berthe); + + linphone_core_set_file_transfer_server(marie.getLc(), file_transfer_url); + setup_conference_info_cbs(marie.getCMgr()); + if (!same_organizer) { + linphone_core_set_file_transfer_server(michelle.getLc(), file_transfer_url); + setup_conference_info_cbs(michelle.getCMgr()); + } + + linphone_core_set_conference_participant_list_type(focus.getLc(), LinphoneConferenceParticipantListTypeOpen); + linphone_core_set_default_conference_layout(focus.getLc(), LinphoneConferenceLayoutGrid); + + int i = 0; + for (auto mgr : {focus.getCMgr(), marie.getCMgr(), pauline.getCMgr(), laure.getCMgr(), michelle.getCMgr(), + berthe.getCMgr()}) { + LinphoneVideoActivationPolicy *pol = + linphone_factory_create_video_activation_policy(linphone_factory_get()); + linphone_video_activation_policy_set_automatically_accept(pol, TRUE); + linphone_video_activation_policy_set_automatically_initiate(pol, TRUE); + linphone_core_set_video_activation_policy(mgr->lc, pol); + linphone_video_activation_policy_unref(pol); + + linphone_core_set_video_device(mgr->lc, liblinphone_tester_mire_id); + linphone_core_enable_video_capture(mgr->lc, TRUE); + linphone_core_enable_video_display(mgr->lc, TRUE); + + linphone_core_set_default_conference_layout(mgr->lc, (i % 2) ? LinphoneConferenceLayoutGrid + : LinphoneConferenceLayoutActiveSpeaker); + } + + stats focus_stat = focus.getStats(); + + bctbx_list_t *coresList = bctbx_list_append(NULL, focus.getLc()); + coresList = bctbx_list_append(coresList, marie.getLc()); + coresList = bctbx_list_append(coresList, pauline.getLc()); + coresList = bctbx_list_append(coresList, laure.getLc()); + coresList = bctbx_list_append(coresList, michelle.getLc()); + coresList = bctbx_list_append(coresList, berthe.getLc()); + + std::list<LinphoneCoreManager *> participants1{pauline.getCMgr(), laure.getCMgr()}; + time_t start_time1 = ms_time(NULL); + time_t end_time1 = (start_time1 + 600); + const char *subject1 = "Colleagues"; + const char *description1 = NULL; + std::map<LinphoneCoreManager *, LinphoneParticipantRole> participantList1; + LinphoneParticipantRole role = LinphoneParticipantRoleSpeaker; + for (auto &p : participants1) { + participantList1.insert(std::make_pair(p, role)); + role = (role == LinphoneParticipantRoleSpeaker) ? LinphoneParticipantRoleListener + : LinphoneParticipantRoleSpeaker; + } + LinphoneAddress *confAddr1 = create_conference_on_server(focus, marie, participantList1, start_time1, end_time1, + subject1, description1, TRUE); + char *conference1_address_str = (confAddr1) ? linphone_address_as_string(confAddr1) : ms_strdup("<unknown>"); + BC_ASSERT_PTR_NOT_NULL(confAddr1); + // Chat room creation to send ICS + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateCreated, 2, + liblinphone_tester_sip_timeout)); + + for (auto mgr : {marie.getCMgr(), pauline.getCMgr(), laure.getCMgr()}) { + ms_message("%s is entering conference %s", linphone_core_get_identity(mgr->lc), conference1_address_str); + LinphoneCallParams *new_params = linphone_core_create_call_params(mgr->lc, nullptr); + linphone_core_invite_address_with_params_2(mgr->lc, confAddr1, new_params, NULL, nullptr); + linphone_call_params_unref(new_params); + } + + for (auto mgr : {marie.getCMgr(), pauline.getCMgr(), laure.getCMgr()}) { + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallOutgoingProgress, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallStreamsRunning, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateCreated, 2, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionOutgoingProgress, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionActive, 1, + liblinphone_tester_sip_timeout)); + LinphoneCall *currentCall = linphone_core_get_current_call(mgr->lc); + BC_ASSERT_PTR_NOT_NULL(currentCall); + LinphoneAddress *uri = linphone_address_new(linphone_core_get_identity(mgr->lc)); + LinphoneConference *pconference = linphone_core_search_conference(mgr->lc, NULL, uri, confAddr1, NULL); + BC_ASSERT_PTR_NOT_NULL(pconference); + linphone_address_unref(uri); + if (currentCall && pconference) { + BC_ASSERT_PTR_EQUAL(linphone_call_get_conference(currentCall), pconference); + } + BC_ASSERT_TRUE( + wait_for_list(coresList, &mgr->stat.number_of_NotifyReceived, 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallUpdating, 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallStreamsRunning, 2, + liblinphone_tester_sip_timeout)); + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallIncomingReceived, + focus_stat.number_of_LinphoneCallIncomingReceived + 3, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallUpdatedByRemote, + focus_stat.number_of_LinphoneCallUpdatedByRemote + 3, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallStreamsRunning, + focus_stat.number_of_LinphoneCallStreamsRunning + 6, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateCreated, + focus_stat.number_of_LinphoneConferenceStateCreated + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneSubscriptionIncomingReceived, + focus_stat.number_of_LinphoneSubscriptionIncomingReceived + 3, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneSubscriptionActive, + focus_stat.number_of_LinphoneSubscriptionActive + 3, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participants_added, + focus_stat.number_of_participants_added + 3, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_added, + focus_stat.number_of_participant_devices_added + 3, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_joined, + focus_stat.number_of_participant_devices_joined + 3, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallStreamsRunning, + focus_stat.number_of_LinphoneCallStreamsRunning + 3, + liblinphone_tester_sip_timeout)); + + LinphoneAddress *focus_uri1 = linphone_address_new(linphone_core_get_identity(focus.getLc())); + LinphoneConference *fconference1 = + linphone_core_search_conference(focus.getLc(), NULL, focus_uri1, confAddr1, NULL); + linphone_address_unref(focus_uri1); + BC_ASSERT_PTR_NOT_NULL(fconference1); + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, laure, michelle}).waitUntil(chrono::seconds(2), [] { return false; }); + + for (auto mgr : {focus.getCMgr(), marie.getCMgr(), pauline.getCMgr(), laure.getCMgr()}) { + LinphoneAddress *uri = linphone_address_new(linphone_core_get_identity(mgr->lc)); + LinphoneConference *pconference = linphone_core_search_conference(mgr->lc, NULL, uri, confAddr1, NULL); + linphone_address_unref(uri); + BC_ASSERT_PTR_NOT_NULL(pconference); + if (pconference) { + const LinphoneConferenceParams *conference_params = linphone_conference_get_current_params(pconference); + int no_participants = 0; + if (start_time1 >= 0) { + BC_ASSERT_EQUAL((long long)linphone_conference_params_get_start_time(conference_params), + (long long)start_time1, long long, "%lld"); + } + BC_ASSERT_EQUAL((long long)linphone_conference_params_get_end_time(conference_params), + (long long)end_time1, long long, "%lld"); + if (mgr == focus.getCMgr()) { + no_participants = 3; + BC_ASSERT_FALSE(linphone_conference_is_in(pconference)); + } else { + no_participants = 2; + BC_ASSERT_TRUE(linphone_conference_is_in(pconference)); + } + BC_ASSERT_EQUAL(linphone_conference_get_participant_count(pconference), no_participants, int, "%0d"); + bctbx_list_t *devices = linphone_conference_get_participant_device_list(pconference); + BC_ASSERT_EQUAL(bctbx_list_size(devices), 3, size_t, "%zu"); + if (devices) { + bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); + } + BC_ASSERT_STRING_EQUAL(linphone_conference_get_subject(pconference), subject1); + LinphoneParticipant *me = linphone_conference_get_me(pconference); + BC_ASSERT_TRUE(linphone_participant_is_admin(me) == + ((mgr == marie.getCMgr()) || (mgr == focus.getCMgr()))); + BC_ASSERT_TRUE(linphone_address_weak_equal(linphone_participant_get_address(me), mgr->identity)); + bctbx_list_t *participants = linphone_conference_get_participant_list(pconference); + for (bctbx_list_t *itp = participants; itp; itp = bctbx_list_next(itp)) { + LinphoneParticipant *p = (LinphoneParticipant *)bctbx_list_get_data(itp); + BC_ASSERT_TRUE( + linphone_participant_is_admin(p) == + linphone_address_weak_equal(linphone_participant_get_address(p), marie.getCMgr()->identity)); + } + bctbx_list_free_with_data(participants, (void (*)(void *))linphone_participant_unref); + + if (mgr != focus.getCMgr()) { + check_conference_ssrc(fconference1, pconference); + } + } + } + + time_t start_time2 = (dialout) ? -1 : ms_time(NULL); + time_t end_time2 = (dialout) ? -1 : (start_time2 + 600); + const char *subject2 = "All Hands Q3 FY2021 - Attendance Mandatory"; + const char *description2 = "Financial result - Internal only - Strictly confidential"; + std::list<LinphoneCoreManager *> participants2{pauline.getCMgr()}; + std::list<LinphoneCoreManager *> mgr_having_two_confs{}; + std::list<LinphoneCoreManager *> mgr_in_conf2{focus.getCMgr(), michelle.getCMgr()}; + ClientConference &confCreator2 = (same_organizer) ? marie : michelle; + + if (same_organizer) { + participants2.push_back(michelle.getCMgr()); + mgr_having_two_confs.push_back(marie.getCMgr()); + } else { + participants2.push_back(marie.getCMgr()); + if (!dialout) { + mgr_having_two_confs.push_back(marie.getCMgr()); + mgr_in_conf2.push_back(marie.getCMgr()); + } + } + + std::map<LinphoneCoreManager *, LinphoneParticipantRole> participantList2; + for (auto &p : participants2) { + role = (role == LinphoneParticipantRoleSpeaker) ? LinphoneParticipantRoleListener + : LinphoneParticipantRoleSpeaker; + participantList2.insert(std::make_pair(p, role)); + } + + LinphoneAddress *confAddr2 = create_conference_on_server(focus, confCreator2, participantList2, start_time2, + end_time2, subject2, description2, TRUE); + BC_ASSERT_PTR_NOT_NULL(confAddr2); + char *conference2_address_str = (confAddr2) ? linphone_address_as_string(confAddr2) : ms_strdup("<unknown>"); + + // Chat room creation to send ICS + if (same_organizer) { + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateCreated, 3, + liblinphone_tester_sip_timeout)); + } else { + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_LinphoneConferenceStateCreated, 2, + liblinphone_tester_sip_timeout)); + } + + if (confAddr2) { + if (dialout) { + for (auto mgr : participants2) { + LinphoneCall *pcall = linphone_core_get_call_by_remote_address2(mgr->lc, confAddr2); + BC_ASSERT_PTR_NOT_NULL(pcall); + auto it = std::find(participants1.cbegin(), participants1.cend(), mgr); + if (pcall && (it == participants1.cend())) { + LinphoneCallLog *call_log = linphone_call_get_call_log(pcall); + BC_ASSERT_TRUE(linphone_call_log_was_conference(call_log)); + ms_message("%s is entering conference %s", linphone_core_get_identity(mgr->lc), + conference2_address_str); + linphone_call_accept(pcall); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallStreamsRunning, 2, + liblinphone_tester_sip_timeout)); + } + } + } else { + mgr_having_two_confs.push_back(pauline.getCMgr()); + mgr_in_conf2.push_back(pauline.getCMgr()); + for (auto mgr : {marie.getCMgr(), pauline.getCMgr(), michelle.getCMgr()}) { + ms_message("%s is entering conference %s", linphone_core_get_identity(mgr->lc), + conference2_address_str); + LinphoneCallParams *new_params = linphone_core_create_call_params(mgr->lc, nullptr); + linphone_core_invite_address_with_params_2(mgr->lc, confAddr2, new_params, NULL, nullptr); + linphone_call_params_unref(new_params); + } + + for (auto mgr : {marie.getCMgr(), pauline.getCMgr()}) { + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallOutgoingProgress, 2, + liblinphone_tester_sip_timeout)); + } + + for (auto mgr : {michelle.getCMgr()}) { + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallOutgoingProgress, 1, + liblinphone_tester_sip_timeout)); + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallIncomingReceived, + focus_stat.number_of_LinphoneCallIncomingReceived + 6, + liblinphone_tester_sip_timeout)); + } + } + + for (auto mgr : mgr_having_two_confs) { + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallStreamsRunning, 3, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallPaused, 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateCreated, + ((mgr == marie.getCMgr()) ? 4 : 3), liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionOutgoingProgress, 2, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionActive, 2, + liblinphone_tester_sip_timeout)); + LinphoneCall *currentCall = linphone_core_get_current_call(mgr->lc); + BC_ASSERT_PTR_NOT_NULL(currentCall); + LinphoneAddress *uri = linphone_address_new(linphone_core_get_identity(mgr->lc)); + LinphoneConference *pconference = linphone_core_search_conference(mgr->lc, NULL, uri, confAddr2, NULL); + BC_ASSERT_PTR_NOT_NULL(pconference); + linphone_address_unref(uri); + if (currentCall && pconference) { + BC_ASSERT_PTR_EQUAL(linphone_call_get_conference(currentCall), pconference); + } + BC_ASSERT_TRUE( + wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallUpdating, 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallStreamsRunning, 4, + liblinphone_tester_sip_timeout)); + } + + for (auto mgr : {michelle.getCMgr()}) { + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallStreamsRunning, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateCreated, + ((same_organizer) ? 2 : 3), liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionOutgoingProgress, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionActive, 1, + liblinphone_tester_sip_timeout)); + LinphoneCall *currentCall = linphone_core_get_current_call(mgr->lc); + BC_ASSERT_PTR_NOT_NULL(currentCall); + LinphoneAddress *uri = linphone_address_new(linphone_core_get_identity(mgr->lc)); + LinphoneConference *pconference = linphone_core_search_conference(mgr->lc, NULL, uri, confAddr2, NULL); + BC_ASSERT_PTR_NOT_NULL(pconference); + linphone_address_unref(uri); + if (currentCall && pconference) { + BC_ASSERT_PTR_EQUAL(linphone_call_get_conference(currentCall), pconference); + } + BC_ASSERT_TRUE( + wait_for_list(coresList, &mgr->stat.number_of_NotifyReceived, 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallUpdating, 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallStreamsRunning, 2, + liblinphone_tester_sip_timeout)); + } + + const int subscription = (dialout) ? ((same_organizer) ? 5 : 4) : 6; + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallUpdatedByRemote, + focus_stat.number_of_LinphoneCallUpdatedByRemote + subscription, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallStreamsRunning, + focus_stat.number_of_LinphoneCallStreamsRunning + 2 * subscription, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateCreated, + focus_stat.number_of_LinphoneConferenceStateCreated + 2, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneSubscriptionIncomingReceived, + focus_stat.number_of_LinphoneSubscriptionIncomingReceived + subscription, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneSubscriptionActive, + focus_stat.number_of_LinphoneSubscriptionActive + subscription, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participants_added, + focus_stat.number_of_participants_added + subscription, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_added, + focus_stat.number_of_participant_devices_added + subscription, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_joined, + focus_stat.number_of_participant_devices_joined + subscription, + liblinphone_tester_sip_timeout)); + + // Marie and Pauline leave conference1 + const int onhold = (dialout) ? ((same_organizer) ? 1 : 0) : 2; + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_on_hold, + focus_stat.number_of_participant_devices_on_hold + onhold, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, + &laure.getStats().number_of_participant_devices_media_capability_changed, onhold, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_participant_devices_on_hold, onhold, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, + &marie.getStats().number_of_participant_devices_media_capability_changed, + onhold - 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_participant_devices_on_hold, onhold - 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, + &pauline.getStats().number_of_participant_devices_media_capability_changed, onhold, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_participant_devices_on_hold, onhold, + liblinphone_tester_sip_timeout)); + + LinphoneAddress *focus_uri2 = linphone_address_new(linphone_core_get_identity(focus.getLc())); + LinphoneConference *fconference2 = + linphone_core_search_conference(focus.getLc(), NULL, focus_uri2, confAddr2, NULL); + linphone_address_unref(focus_uri2); + BC_ASSERT_PTR_NOT_NULL(fconference2); + + for (auto mgr : {focus.getCMgr(), marie.getCMgr(), pauline.getCMgr(), laure.getCMgr()}) { + LinphoneAddress *uri = linphone_address_new(linphone_core_get_identity(mgr->lc)); + LinphoneConference *pconference = linphone_core_search_conference(mgr->lc, NULL, uri, confAddr1, NULL); + linphone_address_unref(uri); + BC_ASSERT_PTR_NOT_NULL(pconference); + if (pconference) { + bctbx_list_t *devices = linphone_conference_get_participant_device_list(pconference); + if (mgr == focus.getCMgr()) { + BC_ASSERT_EQUAL(bctbx_list_size(devices), 3, size_t, "%zu"); + BC_ASSERT_EQUAL(linphone_conference_get_participant_count(pconference), 3, int, "%0d"); + } else { + BC_ASSERT_EQUAL(linphone_conference_get_participant_count(pconference), 2, int, "%0d"); + if ((mgr == laure.getCMgr()) || + (dialout && ((mgr == pauline.getCMgr()) || (!same_organizer && (mgr == marie.getCMgr()))))) { + BC_ASSERT_EQUAL(bctbx_list_size(devices), 3, size_t, "%zu"); + BC_ASSERT_TRUE(linphone_conference_is_in(pconference)); + } else { + BC_ASSERT_EQUAL(bctbx_list_size(devices), 2, size_t, "%zu"); + BC_ASSERT_FALSE(linphone_conference_is_in(pconference)); + } + } + if (devices) { + bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); + } + } + } + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, laure, michelle}).waitUntil(chrono::seconds(2), [] { return false; }); + + auto &organizer2 = (same_organizer) ? marie : michelle; + for (auto mgr : mgr_in_conf2) { + LinphoneAddress *uri = linphone_address_new(linphone_core_get_identity(mgr->lc)); + LinphoneConference *pconference = linphone_core_search_conference_2(mgr->lc, confAddr2); + linphone_address_unref(uri); + BC_ASSERT_PTR_NOT_NULL(pconference); + if (pconference) { + const LinphoneConferenceParams *conference_params = linphone_conference_get_current_params(pconference); + int no_participants = 0; + if (start_time2 >= 0) { + BC_ASSERT_EQUAL((long long)linphone_conference_params_get_start_time(conference_params), + (long long)start_time2, long long, "%lld"); + } + BC_ASSERT_EQUAL((long long)linphone_conference_params_get_end_time(conference_params), + (long long)end_time2, long long, "%lld"); + if (mgr == focus.getCMgr()) { + no_participants = 3; + BC_ASSERT_FALSE(linphone_conference_is_in(pconference)); + } else { + no_participants = 2; + BC_ASSERT_TRUE(linphone_conference_is_in(pconference)); + } + BC_ASSERT_EQUAL(linphone_conference_get_participant_count(pconference), no_participants, int, "%0d"); + bctbx_list_t *devices = linphone_conference_get_participant_device_list(pconference); + BC_ASSERT_EQUAL(bctbx_list_size(devices), (dialout) ? ((same_organizer) ? 2 : 1) : 3, size_t, "%zu"); + if (devices) { + bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); + } + BC_ASSERT_STRING_EQUAL(linphone_conference_get_subject(pconference), subject2); + + LinphoneParticipant *me = linphone_conference_get_me(pconference); + + BC_ASSERT_TRUE(linphone_participant_is_admin(me) == + ((mgr == organizer2.getCMgr()) || (mgr == focus.getCMgr()))); + BC_ASSERT_TRUE(linphone_address_weak_equal(linphone_participant_get_address(me), mgr->identity)); + bctbx_list_t *participants = linphone_conference_get_participant_list(pconference); + for (bctbx_list_t *itp = participants; itp; itp = bctbx_list_next(itp)) { + LinphoneParticipant *p = (LinphoneParticipant *)bctbx_list_get_data(itp); + BC_ASSERT_TRUE(linphone_participant_is_admin(p) == + linphone_address_weak_equal(linphone_participant_get_address(p), + organizer2.getCMgr()->identity)); + } + bctbx_list_free_with_data(participants, (void (*)(void *))linphone_participant_unref); + + if (mgr != focus.getCMgr()) { + check_conference_ssrc(fconference2, pconference); + } + } + } + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, laure}).waitUntil(chrono::seconds(2), [] { return false; }); + + focus_stat = focus.getStats(); + stats pauline_stat = pauline.getStats(); + + std::list<LinphoneCoreManager *> mgr_conf2_to_remove{michelle.getCMgr()}; + if (!dialout || same_organizer) { + mgr_conf2_to_remove.push_back(marie.getCMgr()); + } + // Marie and Michelle leave conference2 + for (auto mgr : mgr_conf2_to_remove) { + LinphoneAddress *uri = linphone_address_new(linphone_core_get_identity(mgr->lc)); + LinphoneConference *pconference = linphone_core_search_conference(mgr->lc, NULL, uri, confAddr2, NULL); + BC_ASSERT_PTR_NOT_NULL(pconference); + if (pconference) { + ms_message("%s is terminating conference %s", linphone_core_get_identity(mgr->lc), + conference2_address_str); + linphone_conference_terminate(pconference); + BC_ASSERT_TRUE( + wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallEnd, 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallReleased, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionTerminated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateTerminationPending, + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateTerminated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateDeleted, 1, + liblinphone_tester_sip_timeout)); + + LinphoneConference *pconference1 = linphone_core_search_conference(mgr->lc, NULL, uri, confAddr2, NULL); + BC_ASSERT_PTR_NULL(pconference1); + } + auto it_having_two_confs = std::find(mgr_having_two_confs.begin(), mgr_having_two_confs.end(), mgr); + if (it_having_two_confs != mgr_having_two_confs.end()) { + mgr_having_two_confs.erase(it_having_two_confs); + } + + auto it_conf2 = std::find(mgr_in_conf2.begin(), mgr_in_conf2.end(), mgr); + if (it_conf2 != mgr_in_conf2.end()) { + mgr_in_conf2.erase(it_conf2); + } + linphone_address_unref(uri); + } + + BC_ASSERT_TRUE( + wait_for_list(coresList, &focus.getStats().number_of_participants_removed, + focus_stat.number_of_participants_removed + static_cast<int>(mgr_conf2_to_remove.size()), + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_removed, + focus_stat.number_of_participant_devices_removed + + static_cast<int>(mgr_conf2_to_remove.size()), + liblinphone_tester_sip_timeout)); + + if (!dialout) { + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_participants_removed, + pauline_stat.number_of_participants_removed + + static_cast<int>(mgr_conf2_to_remove.size()), + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_participant_devices_removed, + pauline_stat.number_of_participant_devices_removed + + static_cast<int>(mgr_conf2_to_remove.size()), + liblinphone_tester_sip_timeout)); + } + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, laure}).waitUntil(chrono::seconds(2), [] { return false; }); + + BC_ASSERT_EQUAL(pauline.getStats().number_of_LinphoneConferenceStateTerminationPending, + pauline_stat.number_of_LinphoneConferenceStateTerminationPending, int, "%d"); + BC_ASSERT_EQUAL(pauline.getStats().number_of_LinphoneConferenceStateTerminated, + pauline_stat.number_of_LinphoneConferenceStateTerminated, int, "%d"); + BC_ASSERT_EQUAL(pauline.getStats().number_of_LinphoneConferenceStateDeleted, + pauline_stat.number_of_LinphoneConferenceStateDeleted, int, "%d"); + + BC_ASSERT_EQUAL(focus.getStats().number_of_LinphoneConferenceStateTerminationPending, + focus_stat.number_of_LinphoneConferenceStateTerminationPending, int, "%d"); + BC_ASSERT_EQUAL(focus.getStats().number_of_LinphoneConferenceStateTerminated, + focus_stat.number_of_LinphoneConferenceStateTerminated, int, "%d"); + BC_ASSERT_EQUAL(focus.getStats().number_of_LinphoneConferenceStateDeleted, + focus_stat.number_of_LinphoneConferenceStateDeleted, int, "%d"); + + for (auto mgr : mgr_in_conf2) { + LinphoneAddress *uri = linphone_address_new(linphone_core_get_identity(mgr->lc)); + LinphoneConference *pconference = linphone_core_search_conference(mgr->lc, NULL, uri, confAddr2, NULL); + BC_ASSERT_PTR_NOT_NULL(pconference); + linphone_address_unref(uri); + if (pconference) { + BC_ASSERT_EQUAL(linphone_conference_get_participant_count(pconference), + ((mgr == focus.getCMgr()) ? ((!dialout || same_organizer) ? 1 : 2) : 0), int, "%0d"); + bctbx_list_t *devices = linphone_conference_get_participant_device_list(pconference); + BC_ASSERT_EQUAL(bctbx_list_size(devices), mgr_in_conf2.size() - 1, size_t, "%zu"); + if (devices) { + bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); + } + BC_ASSERT_STRING_EQUAL(linphone_conference_get_subject(pconference), subject2); + } + } + + // Marie and Pauline rejoin conference1 (Pauline leaves conference2) + focus_stat = focus.getStats(); + pauline_stat = pauline.getStats(); + stats marie_stat = marie.getStats(); + std::list<LinphoneCoreManager *> mgr_rejoining{marie.getCMgr()}; + if (!dialout) { + mgr_rejoining.push_back(pauline.getCMgr()); + } + + for (auto mgr : mgr_rejoining) { + LinphoneAddress *uri = linphone_address_new(linphone_core_get_identity(mgr->lc)); + LinphoneConference *pconference = linphone_core_search_conference(mgr->lc, NULL, uri, confAddr1, NULL); + BC_ASSERT_PTR_NOT_NULL(pconference); + linphone_address_unref(uri); + if (pconference) { + ms_message("%s is joining conference %s", linphone_core_get_identity(mgr->lc), conference1_address_str); + linphone_conference_enter(pconference); + } + } + + LinphoneAddress *focusUri = linphone_address_new(linphone_core_get_identity(focus.getLc())); + LinphoneConference *conference1 = + linphone_core_search_conference(focus.getLc(), NULL, focusUri, confAddr1, NULL); + + if (!dialout) { + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneCallPaused, + pauline_stat.number_of_LinphoneCallPaused + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneCallStreamsRunning, + pauline_stat.number_of_LinphoneCallStreamsRunning + 1, + liblinphone_tester_sip_timeout)); + } + if (!dialout || same_organizer) { + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallStreamsRunning, + marie_stat.number_of_LinphoneCallStreamsRunning + 1, + liblinphone_tester_sip_timeout)); + } + + BC_ASSERT_EQUAL(pauline.getStats().number_of_LinphoneConferenceStateTerminationPending, + pauline_stat.number_of_LinphoneConferenceStateTerminationPending, int, "%d"); + BC_ASSERT_EQUAL(pauline.getStats().number_of_LinphoneConferenceStateTerminated, + pauline_stat.number_of_LinphoneConferenceStateTerminated, int, "%d"); + BC_ASSERT_EQUAL(pauline.getStats().number_of_LinphoneConferenceStateDeleted, + pauline_stat.number_of_LinphoneConferenceStateDeleted, int, "%d"); + + BC_ASSERT_EQUAL(focus.getStats().number_of_LinphoneConferenceStateTerminationPending, + focus_stat.number_of_LinphoneConferenceStateTerminationPending, int, "%d"); + BC_ASSERT_EQUAL(focus.getStats().number_of_LinphoneConferenceStateTerminated, + focus_stat.number_of_LinphoneConferenceStateTerminated, int, "%d"); + BC_ASSERT_EQUAL(focus.getStats().number_of_LinphoneConferenceStateDeleted, + focus_stat.number_of_LinphoneConferenceStateDeleted, int, "%d"); + + if (!dialout) { + // Pauline leaves conference2 + // Pauline and Marie enter conference1 + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_on_hold, + focus_stat.number_of_participant_devices_on_hold + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_joined, + focus_stat.number_of_participant_devices_joined + 2, + liblinphone_tester_sip_timeout)); + } + + for (auto mgr : mgr_in_conf2) { + LinphoneAddress *uri = linphone_address_new(linphone_core_get_identity(mgr->lc)); + LinphoneConference *pconference = linphone_core_search_conference(mgr->lc, NULL, uri, confAddr2, NULL); + BC_ASSERT_PTR_NOT_NULL(pconference); + linphone_address_unref(uri); + if (pconference) { + BC_ASSERT_EQUAL(linphone_conference_get_participant_count(pconference), + ((mgr == focus.getCMgr()) ? ((!dialout || same_organizer) ? 1 : 2) : 0), int, "%0d"); + bctbx_list_t *devices = linphone_conference_get_participant_device_list(pconference); + BC_ASSERT_EQUAL(bctbx_list_size(devices), ((mgr == focus.getCMgr() && !dialout) ? 1 : 0), size_t, + "%zu"); + if (devices) { + bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); + } + BC_ASSERT_FALSE(linphone_conference_is_in(pconference)); + BC_ASSERT_STRING_EQUAL(linphone_conference_get_subject(pconference), subject2); + } + } + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, laure, michelle}).waitUntil(chrono::seconds(2), [] { return false; }); + + // Laure and Pauline are removed from conference1 + focus_stat = focus.getStats(); + marie_stat = marie.getStats(); + pauline_stat = pauline.getStats(); + stats laure_stat = laure.getStats(); + int cnt = 0; + for (auto mgr : {laure.getCMgr(), pauline.getCMgr()}) { + cnt++; + LinphoneParticipant *participant = linphone_conference_find_participant(conference1, mgr->identity); + BC_ASSERT_PTR_NOT_NULL(participant); + if (participant) { + char *conference1_me = linphone_address_as_string( + linphone_participant_get_address(linphone_conference_get_me(conference1))); + ms_message("%s is removing participant %s from conference %s", conference1_me, + linphone_core_get_identity(mgr->lc), conference1_address_str); + ms_free(conference1_me); + linphone_conference_remove_participant_2(conference1, participant); + } + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallEnd, + focus_stat.number_of_LinphoneCallEnd + cnt, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallReleased, + focus_stat.number_of_LinphoneCallReleased + cnt, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participants_removed, + focus_stat.number_of_participants_removed + cnt, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_removed, + focus_stat.number_of_participant_devices_removed + cnt, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_participants_removed, + marie_stat.number_of_participants_removed + cnt, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_participant_devices_removed, + marie_stat.number_of_participant_devices_removed + cnt, + liblinphone_tester_sip_timeout)); + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_LinphoneCallEnd, + laure_stat.number_of_LinphoneCallEnd + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_LinphoneCallReleased, + laure_stat.number_of_LinphoneCallReleased + 1, liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneCallEnd, + pauline_stat.number_of_LinphoneCallEnd + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneCallReleased, + pauline_stat.number_of_LinphoneCallReleased + 1, liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_LinphoneSubscriptionTerminated, + laure_stat.number_of_LinphoneSubscriptionTerminated + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_LinphoneConferenceStateTerminationPending, + laure_stat.number_of_LinphoneConferenceStateTerminationPending + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_LinphoneConferenceStateTerminated, + laure_stat.number_of_LinphoneConferenceStateTerminated + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_LinphoneConferenceStateDeleted, + laure_stat.number_of_LinphoneConferenceStateDeleted + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneSubscriptionTerminated, + pauline_stat.number_of_LinphoneSubscriptionTerminated + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneConferenceStateTerminationPending, + pauline_stat.number_of_LinphoneConferenceStateTerminationPending + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneConferenceStateTerminated, + pauline_stat.number_of_LinphoneConferenceStateTerminated + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneConferenceStateDeleted, + pauline_stat.number_of_LinphoneConferenceStateDeleted + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_EQUAL(marie.getStats().number_of_LinphoneConferenceStateTerminationPending, + marie_stat.number_of_LinphoneConferenceStateTerminationPending, int, "%d"); + BC_ASSERT_EQUAL(marie.getStats().number_of_LinphoneConferenceStateTerminated, + marie_stat.number_of_LinphoneConferenceStateTerminated, int, "%d"); + BC_ASSERT_EQUAL(marie.getStats().number_of_LinphoneConferenceStateDeleted, + marie_stat.number_of_LinphoneConferenceStateDeleted, int, "%d"); + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, laure, michelle}).waitUntil(chrono::seconds(2), [] { return false; }); + + if (!dialout) { + // Pauline rejoins and leaves conference2 (conference2 is destroyed on the server) + focus_stat = focus.getStats(); + pauline_stat = pauline.getStats(); + for (auto mgr : {pauline.getCMgr()}) { + LinphoneAddress *uri = linphone_address_new(linphone_core_get_identity(mgr->lc)); + LinphoneConference *pconference = linphone_core_search_conference(mgr->lc, NULL, uri, confAddr2, NULL); + BC_ASSERT_PTR_NOT_NULL(pconference); + linphone_address_unref(uri); + if (pconference) { + ms_message("%s is entering conference %s", linphone_core_get_identity(mgr->lc), + conference2_address_str); + linphone_conference_enter(pconference); + } + } + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneCallStreamsRunning, + pauline_stat.number_of_LinphoneCallStreamsRunning + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_EQUAL(pauline.getStats().number_of_LinphoneConferenceStateTerminationPending, + pauline_stat.number_of_LinphoneConferenceStateTerminationPending, int, "%d"); + BC_ASSERT_EQUAL(pauline.getStats().number_of_LinphoneConferenceStateTerminated, + pauline_stat.number_of_LinphoneConferenceStateTerminated, int, "%d"); + BC_ASSERT_EQUAL(pauline.getStats().number_of_LinphoneConferenceStateDeleted, + pauline_stat.number_of_LinphoneConferenceStateDeleted, int, "%d"); + + // Pauline enters conference2 + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_joined, + focus_stat.number_of_participant_devices_joined + 1, + liblinphone_tester_sip_timeout)); + + for (auto mgr : mgr_in_conf2) { + LinphoneAddress *uri = linphone_address_new(linphone_core_get_identity(mgr->lc)); + LinphoneConference *pconference = linphone_core_search_conference(mgr->lc, NULL, uri, confAddr2, NULL); + BC_ASSERT_PTR_NOT_NULL(pconference); + linphone_address_unref(uri); + if (pconference) { + BC_ASSERT_EQUAL(linphone_conference_get_participant_count(pconference), + ((mgr == focus.getCMgr()) ? 1 : 0), int, "%0d"); + bctbx_list_t *devices = linphone_conference_get_participant_device_list(pconference); + BC_ASSERT_EQUAL(bctbx_list_size(devices), 1, size_t, "%zu"); + if (devices) { + bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); + } + BC_ASSERT_TRUE(linphone_conference_is_in(pconference) == (mgr != focus.getCMgr())); + BC_ASSERT_STRING_EQUAL(linphone_conference_get_subject(pconference), subject2); + } + } + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, laure, michelle}).waitUntil(chrono::seconds(2), [] { + return false; + }); + + // Pauline leaves conference2 + focus_stat = focus.getStats(); + pauline_stat = pauline.getStats(); + for (auto mgr : {pauline.getCMgr()}) { + LinphoneAddress *uri = linphone_address_new(linphone_core_get_identity(mgr->lc)); + LinphoneConference *pconference = linphone_core_search_conference(mgr->lc, NULL, uri, confAddr2, NULL); + BC_ASSERT_PTR_NOT_NULL(pconference); + if (pconference) { + ms_message("%s is terminating conference %s", linphone_core_get_identity(mgr->lc), + conference2_address_str); + linphone_conference_terminate(pconference); + } + linphone_address_unref(uri); + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneCallEnd, + pauline_stat.number_of_LinphoneCallEnd + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneCallReleased, + pauline_stat.number_of_LinphoneCallReleased + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneSubscriptionTerminated, + pauline_stat.number_of_LinphoneSubscriptionTerminated + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list( + coresList, &pauline.getStats().number_of_LinphoneConferenceStateTerminationPending, + pauline_stat.number_of_LinphoneConferenceStateTerminationPending + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneConferenceStateTerminated, + pauline_stat.number_of_LinphoneConferenceStateTerminated + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneConferenceStateDeleted, + pauline_stat.number_of_LinphoneConferenceStateDeleted + 1, + liblinphone_tester_sip_timeout)); + } + + // Explicitely terminate conference as those on server are static by default + LinphoneConference *conference2 = + linphone_core_search_conference(focus.getLc(), NULL, focusUri, confAddr2, NULL); + linphone_conference_terminate(conference2); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateTerminationPending, + focus_stat.number_of_LinphoneConferenceStateTerminationPending + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateTerminated, + focus_stat.number_of_LinphoneConferenceStateTerminated + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateDeleted, + focus_stat.number_of_LinphoneConferenceStateDeleted + 1, + liblinphone_tester_sip_timeout)); + + // Marie terminates conference1 (conference1 is destroyed on the server) + focus_stat = focus.getStats(); + marie_stat = marie.getStats(); + for (auto mgr : {marie.getCMgr()}) { + LinphoneAddress *uri = linphone_address_new(linphone_core_get_identity(mgr->lc)); + LinphoneConference *pconference = linphone_core_search_conference(mgr->lc, NULL, uri, confAddr1, NULL); + BC_ASSERT_PTR_NOT_NULL(pconference); + if (pconference) { + char *conference_address_str = + (confAddr1) ? linphone_address_as_string(confAddr1) : ms_strdup("<unknown>"); + ms_message("%s is terminating conference %s", linphone_core_get_identity(mgr->lc), + conference_address_str); + ms_free(conference_address_str); + linphone_conference_terminate(pconference); + } + linphone_address_unref(uri); + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallEnd, + marie_stat.number_of_LinphoneCallEnd + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallReleased, + marie_stat.number_of_LinphoneCallReleased + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneSubscriptionTerminated, + marie_stat.number_of_LinphoneSubscriptionTerminated + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateTerminationPending, + marie_stat.number_of_LinphoneConferenceStateTerminationPending + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateTerminated, + marie_stat.number_of_LinphoneConferenceStateTerminated + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateDeleted, + marie_stat.number_of_LinphoneConferenceStateDeleted + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallEnd, + focus_stat.number_of_LinphoneCallEnd + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallReleased, + focus_stat.number_of_LinphoneCallReleased + 1, liblinphone_tester_sip_timeout)); + + // Explicitely terminate conference as those on server are static by default + linphone_conference_terminate(conference1); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateTerminationPending, + focus_stat.number_of_LinphoneConferenceStateTerminationPending + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateTerminated, + focus_stat.number_of_LinphoneConferenceStateTerminated + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateDeleted, + focus_stat.number_of_LinphoneConferenceStateDeleted + 1, + liblinphone_tester_sip_timeout)); + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, laure, michelle}).waitUntil(chrono::seconds(2), [] { return false; }); + + ms_free(conference1_address_str); + ms_free(conference2_address_str); + linphone_address_unref(focusUri); + linphone_address_unref(confAddr1); + linphone_address_unref(confAddr2); + bctbx_list_free(coresList); + } +} + +static void organizer_schedule_two_conferences(void) { + two_overlapping_conferences_base(TRUE, FALSE); +} + +static void two_overlapping_scheduled_conferences_from_different_organizers(void) { + two_overlapping_conferences_base(FALSE, FALSE); +} + +static void organizer_creates_two_dialout_conferences(void) { + two_overlapping_conferences_base(TRUE, TRUE); +} + +static void two_overlapping_dialout_conferences_from_different_organizers(void) { + two_overlapping_conferences_base(FALSE, TRUE); +} + +static void create_simple_conference_merging_calls_base(bool_t enable_ice, + LinphoneConferenceLayout layout, + bool_t toggle_video, + bool_t toggle_all_mananger_video, + bool_t change_layout) { + Focus focus("chloe_rc"); + { // to make sure focus is destroyed after clients. + ClientConference marie("marie_rc", focus.getIdentity()); + ClientConference pauline("pauline_rc", focus.getIdentity()); + ClientConference laure("laure_tcp_rc", focus.getIdentity()); + + focus.registerAsParticipantDevice(marie); + focus.registerAsParticipantDevice(pauline); + focus.registerAsParticipantDevice(laure); + + setup_conference_info_cbs(marie.getCMgr()); + + linphone_core_set_file_transfer_server(marie.getLc(), file_transfer_url); + + auto focus_proxy_config = focus.getDefaultProxyConfig(); + const char *focus_uri = linphone_proxy_config_get_identity(focus_proxy_config); + + auto marie_proxy = marie.getDefaultProxyConfig(); + linphone_proxy_config_edit(marie_proxy); + linphone_proxy_config_set_conference_factory_uri(marie_proxy, focus_uri); + linphone_proxy_config_done(marie_proxy); + + for (auto mgr : {focus.getCMgr(), marie.getCMgr(), pauline.getCMgr(), laure.getCMgr()}) { + if (toggle_video) { + LinphoneVideoActivationPolicy *pol = + linphone_factory_create_video_activation_policy(linphone_factory_get()); + linphone_video_activation_policy_set_automatically_accept(pol, TRUE); + linphone_video_activation_policy_set_automatically_initiate(pol, TRUE); + linphone_core_set_video_activation_policy(mgr->lc, pol); + linphone_video_activation_policy_unref(pol); + + linphone_core_set_video_device(mgr->lc, liblinphone_tester_mire_id); + linphone_core_enable_video_capture(mgr->lc, TRUE); + linphone_core_enable_video_display(mgr->lc, TRUE); + } + + if (mgr != focus.getCMgr()) { + linphone_core_set_default_conference_layout(mgr->lc, layout); + } + } + + bctbx_list_t *coresList = bctbx_list_append(NULL, focus.getLc()); + coresList = bctbx_list_append(coresList, marie.getLc()); + coresList = bctbx_list_append(coresList, pauline.getLc()); + coresList = bctbx_list_append(coresList, laure.getLc()); + + BC_ASSERT_TRUE(call(marie.getCMgr(), pauline.getCMgr())); + + for (auto mgr : {focus.getCMgr(), marie.getCMgr(), pauline.getCMgr(), laure.getCMgr()}) { + enable_stun_in_core(mgr, enable_ice, enable_ice); + } + + LinphoneCall *marie_call_pauline = linphone_core_get_current_call(marie.getLc()); + BC_ASSERT_PTR_NOT_NULL(marie_call_pauline); + LinphoneCall *pauline_called_by_marie = linphone_core_get_current_call(pauline.getLc()); + BC_ASSERT_PTR_NOT_NULL(pauline_called_by_marie); + // linphone_call_set_microphone_muted (pauline_called_by_marie, TRUE); + BC_ASSERT_TRUE(pause_call_1(marie.getCMgr(), marie_call_pauline, pauline.getCMgr(), pauline_called_by_marie)); + + BC_ASSERT_TRUE(call(marie.getCMgr(), laure.getCMgr())); + LinphoneCall *marie_call_laure = linphone_core_get_current_call(marie.getLc()); + BC_ASSERT_PTR_NOT_NULL(marie_call_laure); + + // marie creates the conference + LinphoneConferenceParams *conf_params = linphone_core_create_conference_params_2(marie.getLc(), NULL); + const char *initialSubject = "Test characters: ^ :) ¤ çà @"; + linphone_conference_params_set_subject(conf_params, initialSubject); + LinphoneConference *conf = linphone_core_create_conference_with_params(marie.getLc(), conf_params); + linphone_conference_params_unref(conf_params); + BC_ASSERT_PTR_NOT_NULL(conf); + + std::list<stats> participant_stats; + for (auto mgr : {marie.getCMgr(), pauline.getCMgr(), laure.getCMgr()}) { + participant_stats.push_back(mgr->stat); + } + + if (conf) { + const bctbx_list_t *calls = linphone_core_get_calls(marie.getLc()); + for (const bctbx_list_t *it = calls; it; it = bctbx_list_next(it)) { + LinphoneCall *call = (LinphoneCall *)it->data; + linphone_conference_add_participant(conf, call); + } + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateCreationPending, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateCreated, 1, 20000)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_participants_added, 2, + liblinphone_tester_sip_timeout)); + + LinphoneAddress *confAddr = + conf ? linphone_address_clone(linphone_conference_get_conference_address(conf)) : NULL; + BC_ASSERT_PTR_NOT_NULL(confAddr); + + int counter = 0; + for (auto mgr : {pauline.getCMgr(), laure.getCMgr()}) { + counter++; + auto old_stats = participant_stats.front(); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateCreationPending, + old_stats.number_of_LinphoneConferenceStateCreationPending + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateCreated, + old_stats.number_of_LinphoneConferenceStateCreated + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionOutgoingProgress, + old_stats.number_of_LinphoneSubscriptionOutgoingProgress + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionActive, + old_stats.number_of_LinphoneSubscriptionActive + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallStreamsRunning, counter + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneTransferCallConnected, counter, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallStreamsRunning, + old_stats.number_of_LinphoneCallStreamsRunning + 1, + liblinphone_tester_sip_timeout)); + + // End of call between conference and participant + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallEnd, counter, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallEnd, + old_stats.number_of_LinphoneCallEnd + 1, liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallReleased, counter, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallReleased, + old_stats.number_of_LinphoneCallReleased + 1, liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE( + wait_for_list(coresList, &mgr->stat.number_of_NotifyReceived, 1, liblinphone_tester_sip_timeout)); + participant_stats.pop_front(); + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participants_added, 3, + liblinphone_tester_sip_timeout)); + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, laure}).waitUntil(chrono::seconds(2), [] { return false; }); + + for (auto mgr : {focus.getCMgr(), marie.getCMgr(), pauline.getCMgr(), laure.getCMgr()}) { + LinphoneAddress *uri = linphone_address_new(linphone_core_get_identity(mgr->lc)); + LinphoneConference *pconference = linphone_core_search_conference(mgr->lc, NULL, uri, confAddr, NULL); + linphone_address_unref(uri); + BC_ASSERT_PTR_NOT_NULL(pconference); + + if (pconference) { + BC_ASSERT_EQUAL(linphone_conference_get_participant_count(pconference), + (mgr == focus.getCMgr()) ? 3 : 2, int, "%0d"); + bctbx_list_t *devices = linphone_conference_get_participant_device_list(pconference); + BC_ASSERT_EQUAL(bctbx_list_size(devices), 3, size_t, "%zu"); + if (devices) { + bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); + } + BC_ASSERT_STRING_EQUAL(linphone_conference_get_subject(pconference), initialSubject); + } + + if (mgr != focus.getCMgr()) { + // Local conference + LinphoneCall *focus_call = linphone_core_get_call_by_remote_address2(focus.getLc(), mgr->identity); + BC_ASSERT_PTR_NOT_NULL(focus_call); + if (focus_call) { + BC_ASSERT_PTR_NOT_NULL(linphone_call_get_conference(focus_call)); + BC_ASSERT_TRUE(linphone_call_is_in_conference(focus_call)); + _linphone_call_check_nb_active_streams(focus_call, 1, (toggle_video) ? 4 : 0, 0); + } + + // Remote conference + LinphoneCall *participant_call = + linphone_core_get_call_by_remote_address2(mgr->lc, focus.getCMgr()->identity); + BC_ASSERT_PTR_NOT_NULL(participant_call); + if (participant_call) { + BC_ASSERT_PTR_NOT_NULL(linphone_call_get_conference(participant_call)); + BC_ASSERT_FALSE(linphone_call_is_in_conference(participant_call)); + // BC_ASSERT_TRUE(linphone_call_get_microphone_muted(participant_call) == (mgr == + // pauline.getCMgr())); + _linphone_call_check_nb_active_streams(participant_call, 1, (toggle_video) ? 4 : 0, 0); + } + } + + if (confAddr) { + check_conference_info(mgr, confAddr, marie.getCMgr(), 3, 0, 0, initialSubject, NULL, 0, + LinphoneConferenceInfoStateNew); + } + } + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, laure}).waitUntil(chrono::seconds(2), [] { return false; }); + + participant_stats.clear(); + for (auto mgr : {marie.getCMgr(), pauline.getCMgr(), laure.getCMgr()}) { + participant_stats.push_back(mgr->stat); + } + + stats focus_stat = focus.getStats(); + stats pauline_stat = pauline.getStats(); + stats laure_stat = laure.getStats(); + const char *newSubject = "Let's go drink a beer"; + linphone_conference_set_subject(conf, newSubject); + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_subject_changed, + focus_stat.number_of_subject_changed + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_subject_changed, + pauline_stat.number_of_subject_changed + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_subject_changed, + laure_stat.number_of_subject_changed + 1, liblinphone_tester_sip_timeout)); + + for (auto mgr : {marie.getCMgr(), pauline.getCMgr(), laure.getCMgr()}) { + auto old_stats = participant_stats.front(); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_subject_changed, + old_stats.number_of_subject_changed + 1, liblinphone_tester_sip_timeout)); + if (confAddr) { + check_conference_info(mgr, confAddr, marie.getCMgr(), 3, 0, 0, newSubject, NULL, 0, + LinphoneConferenceInfoStateNew); + } + participant_stats.pop_front(); + } + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, laure}).waitUntil(chrono::seconds(2), [] { return false; }); + + LinphoneAddress *focus_addr = linphone_address_new(linphone_core_get_identity(focus.getLc())); + LinphoneConference *fconference = + linphone_core_search_conference(focus.getLc(), NULL, focus_addr, confAddr, NULL); + linphone_address_unref(focus_addr); + BC_ASSERT_PTR_NOT_NULL(fconference); + + for (auto mgr : {marie.getCMgr(), pauline.getCMgr(), laure.getCMgr()}) { + // Explicitely terminate conference as those on server are static by default + LinphoneAddress *uri = linphone_address_new(linphone_core_get_identity(mgr->lc)); + LinphoneConference *pconference = linphone_core_search_conference(mgr->lc, NULL, uri, confAddr, NULL); + linphone_address_unref(uri); + BC_ASSERT_PTR_NOT_NULL(pconference); + check_conference_ssrc(fconference, pconference); + } + + focus_stat = focus.getStats(); + pauline_stat = pauline.getStats(); + stats marie_stat = marie.getStats(); + + // Laure's core suddenly stops + ms_message("%s core suddently loses network and restarts", linphone_core_get_identity(laure.getLc())); + linphone_core_set_network_reachable(laure.getLc(), FALSE); + coresList = bctbx_list_remove(coresList, laure.getLc()); + + laure.reStart(false); + + if (toggle_video) { + LinphoneVideoActivationPolicy *pol = + linphone_factory_create_video_activation_policy(linphone_factory_get()); + linphone_video_activation_policy_set_automatically_accept(pol, TRUE); + linphone_video_activation_policy_set_automatically_initiate(pol, TRUE); + linphone_core_set_video_activation_policy(laure.getLc(), pol); + linphone_video_activation_policy_unref(pol); + + linphone_core_enable_video_capture(laure.getLc(), TRUE); + linphone_core_enable_video_display(laure.getLc(), TRUE); + } + + linphone_core_set_default_conference_layout(laure.getLc(), layout); + coresList = bctbx_list_append(coresList, laure.getLc()); + + linphone_core_set_network_reachable(laure.getLc(), TRUE); + + LinphoneCallParams *laure_params = linphone_core_create_call_params(laure.getLc(), nullptr); + linphone_core_invite_address_with_params_2(laure.getLc(), confAddr, laure_params, NULL, nullptr); + linphone_call_params_unref(laure_params); + + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_LinphoneRegistrationOk, 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_LinphoneConferenceStateCreationPending, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_LinphoneConferenceStateCreated, 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_LinphoneSubscriptionOutgoingProgress, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_LinphoneSubscriptionActive, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(coresList, &laure.getStats().number_of_NotifyReceived, 1, liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_LinphoneCallUpdating, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_LinphoneCallStreamsRunning, 2, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallStreamsRunning, + focus_stat.number_of_LinphoneCallStreamsRunning + 2, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_added, + focus_stat.number_of_participant_devices_added + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_participant_devices_added, + marie_stat.number_of_participant_devices_added + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_participant_devices_added, + pauline_stat.number_of_participant_devices_added + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_joined, + focus_stat.number_of_participant_devices_joined + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_participant_devices_joined, + marie_stat.number_of_participant_devices_joined + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_participant_devices_joined, + pauline_stat.number_of_participant_devices_joined + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_removed, + focus_stat.number_of_participant_devices_removed + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_participant_devices_removed, + marie_stat.number_of_participant_devices_removed + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_participant_devices_removed, + pauline_stat.number_of_participant_devices_removed + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_participant_devices_removed, 1, + liblinphone_tester_sip_timeout)); + + if (toggle_video) { + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallUpdating, + marie_stat.number_of_LinphoneCallUpdating + 2, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneCallUpdating, + pauline_stat.number_of_LinphoneCallUpdating + 2, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_LinphoneCallUpdating, 2, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallUpdatedByRemote, + focus_stat.number_of_LinphoneCallUpdatedByRemote + 6, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallStreamsRunning, + marie_stat.number_of_LinphoneCallStreamsRunning + 2, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneCallStreamsRunning, + pauline_stat.number_of_LinphoneCallStreamsRunning + 2, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_LinphoneCallStreamsRunning, 3, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallStreamsRunning, + focus_stat.number_of_LinphoneCallStreamsRunning + 7, + liblinphone_tester_sip_timeout)); + } + + for (auto mgr : {focus.getCMgr(), marie.getCMgr(), pauline.getCMgr(), laure.getCMgr()}) { + LinphoneAddress *uri = linphone_address_new(linphone_core_get_identity(mgr->lc)); + LinphoneConference *pconference = linphone_core_search_conference(mgr->lc, NULL, uri, confAddr, NULL); + linphone_address_unref(uri); + BC_ASSERT_PTR_NOT_NULL(pconference); + + if (pconference) { + BC_ASSERT_EQUAL(linphone_conference_get_participant_count(pconference), + (mgr == focus.getCMgr()) ? 3 : 2, int, "%0d"); + bctbx_list_t *devices = linphone_conference_get_participant_device_list(pconference); + BC_ASSERT_EQUAL(bctbx_list_size(devices), 3, size_t, "%zu"); + if (devices) { + bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); + } + BC_ASSERT_STRING_EQUAL(linphone_conference_get_subject(pconference), newSubject); + } + + if (mgr != focus.getCMgr()) { + // Local conference + LinphoneCall *focus_call = linphone_core_get_call_by_remote_address2(focus.getLc(), mgr->identity); + BC_ASSERT_PTR_NOT_NULL(focus_call); + if (focus_call) { + BC_ASSERT_PTR_NOT_NULL(linphone_call_get_conference(focus_call)); + BC_ASSERT_TRUE(linphone_call_is_in_conference(focus_call)); + } + + // Remote conference + LinphoneCall *participant_call = + linphone_core_get_call_by_remote_address2(mgr->lc, focus.getCMgr()->identity); + BC_ASSERT_PTR_NOT_NULL(participant_call); + if (participant_call) { + BC_ASSERT_PTR_NOT_NULL(linphone_call_get_conference(participant_call)); + BC_ASSERT_FALSE(linphone_call_is_in_conference(participant_call)); + // BC_ASSERT_TRUE(linphone_call_get_microphone_muted(participant_call) == (mgr == + // pauline.getCMgr())); + } + } + + if (confAddr) { + check_conference_info(mgr, confAddr, marie.getCMgr(), 3, 0, 0, newSubject, NULL, 0, + LinphoneConferenceInfoStateNew); + } + } + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, laure}).waitUntil(chrono::seconds(5), [] { return false; }); + + std::list<LinphoneCoreManager *> mgrList = {pauline.getCMgr()}; + if (toggle_all_mananger_video) { + mgrList.push_back(marie.getCMgr()); + mgrList.push_back(laure.getCMgr()); + } + + if (toggle_video) { + for (int i = 0; i < 4; i++) { + for (auto mgr : mgrList) { + LinphoneMediaDirection video_direction = LinphoneMediaDirectionSendRecv; + + LinphoneCall *participant_call = linphone_core_get_call_by_remote_address2(mgr->lc, confAddr); + BC_ASSERT_PTR_NOT_NULL(participant_call); + if (participant_call) { + const LinphoneCallParams *call_lparams = linphone_call_get_params(participant_call); + BC_ASSERT_TRUE(linphone_call_params_video_enabled(call_lparams)); + video_direction = linphone_call_params_get_video_direction(call_lparams); + + if (video_direction == LinphoneMediaDirectionRecvOnly) { + video_direction = LinphoneMediaDirectionSendRecv; + } else if (video_direction == LinphoneMediaDirectionSendRecv) { + video_direction = LinphoneMediaDirectionRecvOnly; + } + } + + set_video_settings_in_conference(focus.getCMgr(), mgr, mgrList, confAddr, TRUE, video_direction, + TRUE, video_direction); + + LinphoneAddress *uri = linphone_address_new(linphone_core_get_identity(mgr->lc)); + LinphoneConference *pconference = + linphone_core_search_conference(mgr->lc, NULL, uri, confAddr, NULL); + linphone_address_unref(uri); + BC_ASSERT_PTR_NOT_NULL(pconference); + + if (pconference) { + bctbx_list_t *devices = linphone_conference_get_participant_device_list(pconference); + for (bctbx_list_t *itd = devices; itd; itd = bctbx_list_next(itd)) { + LinphoneParticipantDevice *d = (LinphoneParticipantDevice *)bctbx_list_get_data(itd); + LinphoneMediaDirection video_dir = + linphone_participant_device_get_stream_capability(d, LinphoneStreamTypeVideo); + if (linphone_conference_is_me(pconference, linphone_participant_device_get_address(d))) { + BC_ASSERT_TRUE( + linphone_participant_device_get_stream_availability(d, LinphoneStreamTypeVideo) == + (video_direction == LinphoneMediaDirectionSendRecv)); + } else { + BC_ASSERT_TRUE( + linphone_participant_device_get_stream_availability(d, LinphoneStreamTypeVideo) == + ((video_dir == LinphoneMediaDirectionSendRecv) || + (video_dir == LinphoneMediaDirectionSendOnly))); + } + } + + if (devices) { + bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); + } + + if (change_layout) { + stats mgr_stat2 = mgr->stat; + stats focus_stat2 = focus.getStats(); + + LinphoneConferenceLayout new_layout = LinphoneConferenceLayoutActiveSpeaker; + LinphoneCall *pcall2 = linphone_conference_get_call(pconference); + BC_ASSERT_PTR_NOT_NULL(pcall2); + if (pcall2) { + const LinphoneCallParams *pcall2_local_params = linphone_call_get_params(pcall2); + const LinphoneConferenceLayout conference_layout = + linphone_call_params_get_conference_video_layout(pcall2_local_params); + + if (conference_layout == LinphoneConferenceLayoutGrid) { + new_layout = LinphoneConferenceLayoutActiveSpeaker; + } else { + new_layout = LinphoneConferenceLayoutGrid; + } + + LinphoneCallParams *call_params = linphone_core_create_call_params(mgr->lc, pcall2); + linphone_call_params_set_conference_video_layout(call_params, new_layout); + linphone_call_update(pcall2, call_params); + linphone_call_params_unref(call_params); + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallUpdating, + mgr_stat2.number_of_LinphoneCallUpdating + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list( + coresList, &focus.getStats().number_of_LinphoneCallUpdatedByRemote, + focus_stat2.number_of_LinphoneCallUpdatedByRemote + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallStreamsRunning, + mgr_stat2.number_of_LinphoneCallStreamsRunning + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list( + coresList, &focus.getStats().number_of_LinphoneCallStreamsRunning, + focus_stat2.number_of_LinphoneCallStreamsRunning + 1, liblinphone_tester_sip_timeout)); + if (pcall2) { + const LinphoneCallParams *pcall2_local_params = linphone_call_get_params(pcall2); + const LinphoneConferenceLayout remote_conf_layout = + linphone_call_params_get_conference_video_layout(pcall2_local_params); + BC_ASSERT_EQUAL(new_layout, remote_conf_layout, int, "%d"); + } + LinphoneConference *fconference = + linphone_core_search_conference(focus.getLc(), NULL, NULL, confAddr, NULL); + LinphoneParticipant *participant = + linphone_conference_find_participant(fconference, mgr->identity); + BC_ASSERT_PTR_NOT_NULL(participant); + if (participant) { + bctbx_list_t *devices = linphone_participant_get_devices(participant); + + for (bctbx_list_t *it_d = devices; it_d != NULL; it_d = it_d->next) { + LinphoneParticipantDevice *d = (LinphoneParticipantDevice *)it_d->data; + BC_ASSERT_PTR_NOT_NULL(d); + LinphoneCall *participant_call = linphone_core_get_call_by_remote_address2( + focus.getLc(), linphone_participant_device_get_address(d)); + BC_ASSERT_PTR_NOT_NULL(participant_call); + if (participant_call) { + const LinphoneCallParams *call_remote_params = + linphone_call_get_remote_params(participant_call); + const LinphoneConferenceLayout device_layout = + linphone_call_params_get_conference_video_layout(call_remote_params); + BC_ASSERT_EQUAL(device_layout, new_layout, int, "%d"); + } + } + bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); + } + + LinphoneCall *pcall = linphone_core_get_call_by_remote_address2(mgr->lc, confAddr); + BC_ASSERT_PTR_NOT_NULL(pcall); + if (pcall) { + const LinphoneCallParams *call_lparams = linphone_call_get_params(pcall); + BC_ASSERT_TRUE(linphone_call_params_video_enabled(call_lparams)); + const LinphoneCallParams *call_rparams = linphone_call_get_remote_params(pcall); + BC_ASSERT_TRUE(linphone_call_params_video_enabled(call_rparams)); + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(pcall); + BC_ASSERT_TRUE(linphone_call_params_video_enabled(call_cparams)); + } + + LinphoneAddress *uri = linphone_address_new(linphone_core_get_identity(mgr->lc)); + LinphoneCall *ccall = linphone_core_get_call_by_remote_address2(focus.getLc(), uri); + BC_ASSERT_PTR_NOT_NULL(ccall); + if (ccall) { + const LinphoneCallParams *call_lparams = linphone_call_get_params(ccall); + BC_ASSERT_TRUE(linphone_call_params_video_enabled(call_lparams)); + const LinphoneCallParams *call_rparams = linphone_call_get_remote_params(ccall); + BC_ASSERT_TRUE(linphone_call_params_video_enabled(call_rparams)); + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(ccall); + BC_ASSERT_TRUE(linphone_call_params_video_enabled(call_cparams)); + } + } + } + + // Wait a little bit + wait_for_list(coresList, NULL, 0, 1000); + } + } + } + + const int total_marie_calls = + marie.getStats().number_of_LinphoneCallEnd + (int)bctbx_list_size(linphone_core_get_calls(marie.getLc())); + const int total_focus_calls = + focus.getStats().number_of_LinphoneCallEnd + (int)bctbx_list_size(linphone_core_get_calls(focus.getLc())); + const int total_pauline_calls = pauline.getStats().number_of_LinphoneCallEnd + + (int)bctbx_list_size(linphone_core_get_calls(pauline.getLc())); + const int total_laure_calls = + laure.getStats().number_of_LinphoneCallEnd + (int)bctbx_list_size(linphone_core_get_calls(laure.getLc())); + + linphone_core_terminate_all_calls(pauline.getLc()); + linphone_core_terminate_all_calls(laure.getLc()); + linphone_core_terminate_all_calls(marie.getLc()); + + // Wait for calls to be terminated + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallEnd, total_marie_calls, 30000)); + BC_ASSERT_TRUE( + wait_for_list(coresList, &pauline.getStats().number_of_LinphoneCallEnd, total_pauline_calls, 30000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_LinphoneCallEnd, total_laure_calls, 30000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallEnd, total_focus_calls, 40000)); + + BC_ASSERT_TRUE( + wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallReleased, total_marie_calls, 30000)); + BC_ASSERT_TRUE( + wait_for_list(coresList, &pauline.getStats().number_of_LinphoneCallReleased, total_pauline_calls, 30000)); + BC_ASSERT_TRUE( + wait_for_list(coresList, &laure.getStats().number_of_LinphoneCallReleased, total_laure_calls, 30000)); + BC_ASSERT_TRUE( + wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallReleased, total_focus_calls, 40000)); + + if (confAddr && fconference) { + linphone_conference_terminate(fconference); + } + + for (auto mgr : {focus.getCMgr(), marie.getCMgr(), pauline.getCMgr(), laure.getCMgr()}) { + + // Wait for all conferences to be terminated + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateTerminationPending, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateTerminated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateDeleted, 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionTerminated, + (mgr == focus.getCMgr()) ? 3 : 1, liblinphone_tester_sip_timeout)); + + if (mgr && (mgr != focus.getCMgr())) { + LinphoneCall *participant_call = + linphone_core_get_call_by_remote_address2(mgr->lc, focus.getCMgr()->identity); + BC_ASSERT_PTR_NULL(participant_call); + LinphoneCall *conference_call = linphone_core_get_call_by_remote_address2(focus.getLc(), mgr->identity); + BC_ASSERT_PTR_NULL(conference_call); + + const bctbx_list_t *call_logs = linphone_core_get_call_logs(mgr->lc); + BC_ASSERT_EQUAL((unsigned int)bctbx_list_size(call_logs), + (((mgr == marie.getCMgr()) || (mgr == laure.getCMgr())) ? 3 : 2), unsigned int, "%u"); + + bctbx_list_t *mgr_focus_call_log = + linphone_core_get_call_history_2(mgr->lc, focus.getCMgr()->identity, mgr->identity); + BC_ASSERT_PTR_NOT_NULL(mgr_focus_call_log); + if (mgr_focus_call_log) { + BC_ASSERT_EQUAL((unsigned int)bctbx_list_size(mgr_focus_call_log), (mgr == laure.getCMgr()) ? 2 : 1, + unsigned int, "%u"); + for (bctbx_list_t *it = mgr_focus_call_log; it; it = bctbx_list_next(it)) { + LinphoneCallLog *call_log = (LinphoneCallLog *)it->data; + BC_ASSERT_TRUE(linphone_call_log_was_conference(call_log)); + } + bctbx_list_free_with_data(mgr_focus_call_log, (bctbx_list_free_func)linphone_call_log_unref); + } + } + } + + linphone_conference_unref(conf); + linphone_address_unref(confAddr); + bctbx_list_free(coresList); + } +} + +static void create_simple_conference_merging_calls(void) { + create_simple_conference_merging_calls_base(FALSE, LinphoneConferenceLayoutActiveSpeaker, FALSE, FALSE, FALSE); +} + +static void create_simple_conference_merging_calls_with_video_toggling(void) { + create_simple_conference_merging_calls_base(FALSE, LinphoneConferenceLayoutGrid, TRUE, TRUE, TRUE); +} + +static void create_simple_ice_conference_merging_calls(void) { + create_simple_conference_merging_calls_base(TRUE, LinphoneConferenceLayoutActiveSpeaker, TRUE, FALSE, TRUE); +} + +static void create_simple_conference_with_update_deferred(void) { + Focus focus("chloe_rc"); + { // to make sure focus is destroyed after clients. + ClientConference marie("marie_rc", focus.getIdentity()); + ClientConference pauline("pauline_rc", focus.getIdentity()); + ClientConference laure("laure_tcp_rc", focus.getIdentity()); + ClientConference michelle("michelle_rc", focus.getIdentity()); + + focus.registerAsParticipantDevice(marie); + focus.registerAsParticipantDevice(pauline); + focus.registerAsParticipantDevice(laure); + focus.registerAsParticipantDevice(michelle); + + setup_conference_info_cbs(marie.getCMgr()); + + for (auto mgr : {focus.getCMgr(), marie.getCMgr(), pauline.getCMgr(), laure.getCMgr(), michelle.getCMgr()}) { + LinphoneVideoActivationPolicy *pol = + linphone_factory_create_video_activation_policy(linphone_factory_get()); + linphone_video_activation_policy_set_automatically_accept(pol, TRUE); + linphone_video_activation_policy_set_automatically_initiate(pol, FALSE); + linphone_core_set_video_activation_policy(mgr->lc, pol); + linphone_video_activation_policy_unref(pol); + + linphone_core_set_video_device(mgr->lc, liblinphone_tester_mire_id); + linphone_core_enable_video_capture(mgr->lc, TRUE); + linphone_core_enable_video_display(mgr->lc, TRUE); + + if (mgr != focus.getCMgr()) { + linphone_core_set_default_conference_layout(mgr->lc, LinphoneConferenceLayoutGrid); + } + } + + linphone_core_set_file_transfer_server(marie.getLc(), file_transfer_url); + + bctbx_list_t *coresList = bctbx_list_append(NULL, focus.getLc()); + coresList = bctbx_list_append(coresList, marie.getLc()); + coresList = bctbx_list_append(coresList, pauline.getLc()); + coresList = bctbx_list_append(coresList, laure.getLc()); + coresList = bctbx_list_append(coresList, michelle.getLc()); + + stats marie_stat = marie.getStats(); + stats focus_stat = focus.getStats(); + + std::list<LinphoneCoreManager *> participants{pauline.getCMgr(), laure.getCMgr()}; + std::list<LinphoneCoreManager *> members{marie.getCMgr(), pauline.getCMgr(), laure.getCMgr()}; + + time_t start_time = ms_time(NULL) + 10; + time_t end_time = (start_time + 300); + const char *initialSubject = "Test characters: ^ :) ¤ çà @"; + const char *description = "Paris Baker"; + std::map<LinphoneCoreManager *, LinphoneParticipantRole> participantList; + LinphoneParticipantRole role = LinphoneParticipantRoleSpeaker; + for (auto &p : participants) { + participantList.insert(std::make_pair(p, role)); + role = (role == LinphoneParticipantRoleSpeaker) ? LinphoneParticipantRoleListener + : LinphoneParticipantRoleSpeaker; + } + LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, + initialSubject, description, TRUE); + BC_ASSERT_PTR_NOT_NULL(confAddr); + + // Chat room creation to send ICS + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateCreated, 2, + liblinphone_tester_sip_timeout)); + + for (auto mgr : members) { + LinphoneCallParams *new_params = linphone_core_create_call_params(mgr->lc, nullptr); + linphone_core_invite_address_with_params_2(mgr->lc, confAddr, new_params, NULL, nullptr); + linphone_call_params_unref(new_params); + } + + for (auto mgr : members) { + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallOutgoingProgress, 1, + liblinphone_tester_sip_timeout)); + int no_streams_running = 2; + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallUpdating, (no_streams_running - 1), + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallStreamsRunning, no_streams_running, + liblinphone_tester_sip_timeout)); + // Update to add to conference. + // If ICE is enabled, the addition to a conference may go through a resume of the call + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateCreated, + ((mgr == marie.getCMgr()) ? 3 : 2), liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionOutgoingProgress, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionActive, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(coresList, &mgr->stat.number_of_NotifyReceived, 1, liblinphone_tester_sip_timeout)); + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallIncomingReceived, + focus_stat.number_of_LinphoneCallIncomingReceived + 3, + liblinphone_tester_sip_timeout)); + int focus_no_streams_running = 6; + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallUpdatedByRemote, + focus_stat.number_of_LinphoneCallUpdatedByRemote + (focus_no_streams_running - 3), + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallStreamsRunning, + focus_stat.number_of_LinphoneCallStreamsRunning + focus_no_streams_running, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateCreated, + focus_stat.number_of_LinphoneConferenceStateCreated + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneSubscriptionIncomingReceived, + focus_stat.number_of_LinphoneSubscriptionIncomingReceived + 3, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneSubscriptionActive, + focus_stat.number_of_LinphoneSubscriptionActive + 3, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participants_added, + focus_stat.number_of_participants_added + 3, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_added, + focus_stat.number_of_participant_devices_added + 3, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_joined, + focus_stat.number_of_participant_devices_joined + 3, + liblinphone_tester_sip_timeout)); + + LinphoneConference *fconference = linphone_core_search_conference_2(focus.getLc(), confAddr); + BC_ASSERT_PTR_NOT_NULL(fconference); + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, laure, michelle}).waitUntil(chrono::seconds(2), [] { return false; }); + + for (auto mgr : {focus.getCMgr(), marie.getCMgr(), pauline.getCMgr(), laure.getCMgr()}) { + LinphoneConference *pconference = linphone_core_search_conference_2(mgr->lc, confAddr); + BC_ASSERT_PTR_NOT_NULL(pconference); + if (pconference) { + const LinphoneConferenceParams *conference_params = linphone_conference_get_current_params(pconference); + int no_participants = 0; + if (start_time >= 0) { + BC_ASSERT_EQUAL((long long)linphone_conference_params_get_start_time(conference_params), + (long long)start_time, long long, "%lld"); + } + BC_ASSERT_EQUAL((long long)linphone_conference_params_get_end_time(conference_params), + (long long)end_time, long long, "%lld"); + if (mgr == focus.getCMgr()) { + no_participants = 3; + BC_ASSERT_FALSE(linphone_conference_is_in(pconference)); + } else { + no_participants = 2; + BC_ASSERT_TRUE(linphone_conference_is_in(pconference)); + LinphoneCall *current_call = linphone_core_get_current_call(mgr->lc); + BC_ASSERT_PTR_NOT_NULL(current_call); + if (current_call) { + BC_ASSERT_EQUAL((int)linphone_call_get_state(current_call), + (int)LinphoneCallStateStreamsRunning, int, "%0d"); + } + + LinphoneVideoActivationPolicy *pol = linphone_core_get_video_activation_policy(mgr->lc); + bool_t enabled = !!linphone_video_activation_policy_get_automatically_initiate(pol); + LinphoneCall *pcall = linphone_core_get_call_by_remote_address2(mgr->lc, confAddr); + BC_ASSERT_PTR_NOT_NULL(pcall); + if (pcall) { + const LinphoneCallParams *call_lparams = linphone_call_get_params(pcall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_lparams), enabled, int, "%0d"); + const LinphoneCallParams *call_rparams = linphone_call_get_remote_params(pcall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_rparams), enabled, int, "%0d"); + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(pcall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_cparams), enabled, int, "%0d"); + } + LinphoneCall *ccall = linphone_core_get_call_by_remote_address2(focus.getLc(), mgr->identity); + BC_ASSERT_PTR_NOT_NULL(ccall); + if (ccall) { + const LinphoneCallParams *call_lparams = linphone_call_get_params(ccall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_lparams), enabled, int, "%0d"); + const LinphoneCallParams *call_rparams = linphone_call_get_remote_params(ccall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_rparams), enabled, int, "%0d"); + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(ccall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_cparams), enabled, int, "%0d"); + } + linphone_video_activation_policy_unref(pol); + } + BC_ASSERT_EQUAL(linphone_conference_get_participant_count(pconference), no_participants, int, "%0d"); + bctbx_list_t *devices = linphone_conference_get_participant_device_list(pconference); + BC_ASSERT_EQUAL(bctbx_list_size(devices), 3, size_t, "%zu"); + if (devices) { + bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); + } + BC_ASSERT_STRING_EQUAL(linphone_conference_get_subject(pconference), initialSubject); + LinphoneParticipant *me = linphone_conference_get_me(pconference); + BC_ASSERT_TRUE(linphone_participant_is_admin(me) == + ((mgr == marie.getCMgr()) || (mgr == focus.getCMgr()))); + BC_ASSERT_TRUE(linphone_address_weak_equal(linphone_participant_get_address(me), mgr->identity)); + bctbx_list_t *participants = linphone_conference_get_participant_list(pconference); + for (bctbx_list_t *itp = participants; itp; itp = bctbx_list_next(itp)) { + LinphoneParticipant *p = (LinphoneParticipant *)bctbx_list_get_data(itp); + BC_ASSERT_TRUE( + linphone_participant_is_admin(p) == + linphone_address_weak_equal(linphone_participant_get_address(p), marie.getCMgr()->identity)); + } + bctbx_list_free_with_data(participants, (void (*)(void *))linphone_participant_unref); + + if (mgr != focus.getCMgr()) { + check_conference_ssrc(fconference, pconference); + } + } + } + + // Wait a little bit + wait_for_list(coresList, NULL, 0, 3000); + + LinphoneCall *pauline_call = linphone_core_get_call_by_remote_address2(pauline.getLc(), confAddr); + BC_ASSERT_PTR_NOT_NULL(pauline_call); + + LinphoneVideoActivationPolicy *pol = linphone_core_get_video_activation_policy(pauline.getLc()); + bool_t enable = !!!linphone_video_activation_policy_get_automatically_initiate(pol); + linphone_video_activation_policy_unref(pol); + + if (pauline_call) { + const LinphoneCallParams *call_lparams = linphone_call_get_params(pauline_call); + BC_ASSERT_FALSE(linphone_call_params_video_enabled(call_lparams)); + const LinphoneCallParams *call_rparams = linphone_call_get_remote_params(pauline_call); + BC_ASSERT_FALSE(linphone_call_params_video_enabled(call_rparams)); + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(pauline_call); + BC_ASSERT_FALSE(linphone_call_params_video_enabled(call_cparams)); + } + Address paulineIdentity = pauline.getIdentity(); + LinphoneCall *focus_call = linphone_core_get_call_by_remote_address2(focus.getLc(), paulineIdentity.toC()); + BC_ASSERT_PTR_NOT_NULL(focus_call); + if (focus_call) { + const LinphoneCallParams *call_lparams = linphone_call_get_params(focus_call); + BC_ASSERT_FALSE(linphone_call_params_video_enabled(call_lparams)); + const LinphoneCallParams *call_rparams = linphone_call_get_remote_params(focus_call); + BC_ASSERT_FALSE(linphone_call_params_video_enabled(call_rparams)); + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(focus_call); + BC_ASSERT_FALSE(linphone_call_params_video_enabled(call_cparams)); + } + + LinphoneAddress *paulineUri = linphone_address_new(linphone_core_get_identity(pauline.getLc())); + LinphoneConference *paulineConference = + linphone_core_search_conference(pauline.getLc(), NULL, paulineUri, confAddr, NULL); + linphone_address_unref(paulineUri); + BC_ASSERT_PTR_NOT_NULL(paulineConference); + + linphone_config_set_int(linphone_core_get_config(focus.getLc()), "sip", "defer_update_default", TRUE); + + for (int i = 0; i < 4; i++) { + set_video_settings_in_conference(focus.getCMgr(), pauline.getCMgr(), members, confAddr, enable, + LinphoneMediaDirectionSendRecv, !enable, LinphoneMediaDirectionSendRecv); + + // Wait a little bit + wait_for_list(coresList, NULL, 0, 1000); + + enable = !enable; + } + + linphone_config_set_int(linphone_core_get_config(focus.getLc()), "sip", "defer_update_default", FALSE); + + if (paulineConference) { + + stats focus_stat2 = focus.getStats(); + stats marie_stat2 = marie.getStats(); + stats pauline_stat2 = pauline.getStats(); + stats laure_stat2 = laure.getStats(); + + LinphoneCall *pcall = linphone_core_get_call_by_remote_address2(pauline.getLc(), confAddr); + BC_ASSERT_PTR_NOT_NULL(pcall); + bool_t video_enabled = FALSE; + if (pcall) { + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(pcall); + video_enabled = linphone_call_params_video_enabled(call_cparams); + } + + linphone_conference_leave(paulineConference); + + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneCallPausing, + pauline_stat2.number_of_LinphoneCallPausing + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneCallPaused, + pauline_stat2.number_of_LinphoneCallPaused + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallPausedByRemote, + focus_stat2.number_of_LinphoneCallPausedByRemote + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_on_hold, + focus_stat2.number_of_participant_devices_on_hold + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_participant_devices_on_hold, + laure_stat2.number_of_participant_devices_on_hold + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, + &laure.getStats().number_of_participant_devices_media_capability_changed, + laure_stat2.number_of_participant_devices_media_capability_changed + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_participant_devices_on_hold, + marie_stat2.number_of_participant_devices_on_hold + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, + &marie.getStats().number_of_participant_devices_media_capability_changed, + marie_stat2.number_of_participant_devices_media_capability_changed + 1, + liblinphone_tester_sip_timeout)); + + linphone_conference_enter(paulineConference); + + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneCallResuming, + pauline_stat2.number_of_LinphoneCallResuming + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneCallStreamsRunning, + pauline_stat2.number_of_LinphoneCallStreamsRunning + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallStreamsRunning, + focus_stat2.number_of_LinphoneCallStreamsRunning + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_joined, + focus_stat2.number_of_participant_devices_joined + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_participant_devices_joined, + laure_stat2.number_of_participant_devices_joined + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, + &laure.getStats().number_of_participant_devices_media_capability_changed, + laure_stat2.number_of_participant_devices_media_capability_changed + 2, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_participant_devices_joined, + marie_stat2.number_of_participant_devices_joined + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, + &marie.getStats().number_of_participant_devices_media_capability_changed, + marie_stat2.number_of_participant_devices_media_capability_changed + 2, + liblinphone_tester_sip_timeout)); + + if (pcall) { + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(pcall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_cparams), video_enabled, int, "%0d"); + } + } + + std::list<LinphoneCoreManager *> mgrsToRemove{pauline.getCMgr()}; + mgrsToRemove.push_back(laure.getCMgr()); + + for (auto mgr : mgrsToRemove) { + LinphoneCall *call = linphone_core_get_current_call(mgr->lc); + BC_ASSERT_PTR_NOT_NULL(call); + if (call) { + linphone_call_terminate(call); + BC_ASSERT_TRUE( + wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallEnd, 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallReleased, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionTerminated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateTerminationPending, + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateTerminated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateDeleted, 1, + liblinphone_tester_sip_timeout)); + + LinphoneAddress *uri = linphone_address_new(linphone_core_get_identity(mgr->lc)); + LinphoneConference *pconference = linphone_core_search_conference(mgr->lc, NULL, uri, confAddr, NULL); + BC_ASSERT_PTR_NULL(pconference); + linphone_address_unref(uri); + } + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participants_removed, + focus_stat.number_of_participants_removed + 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_removed, + focus_stat.number_of_participant_devices_removed + 2, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_participants_removed, + marie_stat.number_of_participants_removed + 2, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_participant_devices_removed, + marie_stat.number_of_participant_devices_removed + 2, + liblinphone_tester_sip_timeout)); + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, laure}).waitUntil(chrono::seconds(2), [] { return false; }); + + BC_ASSERT_EQUAL(marie.getStats().number_of_LinphoneConferenceStateTerminationPending, + marie_stat.number_of_LinphoneConferenceStateTerminationPending, int, "%d"); + BC_ASSERT_EQUAL(marie.getStats().number_of_LinphoneConferenceStateTerminated, + marie_stat.number_of_LinphoneConferenceStateTerminated, int, "%d"); + BC_ASSERT_EQUAL(marie.getStats().number_of_LinphoneConferenceStateDeleted, + marie_stat.number_of_LinphoneConferenceStateDeleted, int, "%d"); + + BC_ASSERT_EQUAL(focus.getStats().number_of_LinphoneConferenceStateTerminationPending, + focus_stat.number_of_LinphoneConferenceStateTerminationPending, int, "%d"); + BC_ASSERT_EQUAL(focus.getStats().number_of_LinphoneConferenceStateTerminated, + focus_stat.number_of_LinphoneConferenceStateTerminated, int, "%d"); + BC_ASSERT_EQUAL(focus.getStats().number_of_LinphoneConferenceStateDeleted, + focus_stat.number_of_LinphoneConferenceStateDeleted, int, "%d"); + + for (auto mgr : {focus.getCMgr(), marie.getCMgr()}) { + LinphoneConference *pconference = linphone_core_search_conference_2(mgr->lc, confAddr); + BC_ASSERT_PTR_NOT_NULL(pconference); + if (pconference) { + BC_ASSERT_EQUAL(linphone_conference_get_participant_count(pconference), + ((mgr == focus.getCMgr()) ? 1 : 0), int, "%0d"); + bctbx_list_t *devices = linphone_conference_get_participant_device_list(pconference); + BC_ASSERT_EQUAL(bctbx_list_size(devices), 1, size_t, "%zu"); + if (devices) { + bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); + } + BC_ASSERT_STRING_EQUAL(linphone_conference_get_subject(pconference), initialSubject); + } + } + + const bctbx_list_t *calls = linphone_core_get_calls(marie.getLc()); + BC_ASSERT_EQUAL(bctbx_list_size(calls), 1, size_t, "%zu"); + + LinphoneCall *call = linphone_core_get_call_by_remote_address2(marie.getLc(), focus.getCMgr()->identity); + BC_ASSERT_PTR_NOT_NULL(call); + if (call) { + linphone_call_terminate(call); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallEnd, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallReleased, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneSubscriptionTerminated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, + &marie.getStats().number_of_LinphoneConferenceStateTerminationPending, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateTerminated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateDeleted, 1, + liblinphone_tester_sip_timeout)); + + // Explicitely terminate conference as those on server are static by default + LinphoneConference *pconference = linphone_core_search_conference_2(focus.getLc(), confAddr); + BC_ASSERT_PTR_NOT_NULL(pconference); + if (pconference) { + linphone_conference_terminate(pconference); + } + BC_ASSERT_TRUE(wait_for_list(coresList, + &focus.getStats().number_of_LinphoneConferenceStateTerminationPending, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateTerminated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateDeleted, 1, + liblinphone_tester_sip_timeout)); + } + + for (auto mgr : {marie.getCMgr(), pauline.getCMgr(), laure.getCMgr()}) { + const bctbx_list_t *call_logs = linphone_core_get_call_logs(mgr->lc); + BC_ASSERT_EQUAL((unsigned int)bctbx_list_size(call_logs), 1, unsigned int, "%u"); + + bctbx_list_t *mgr_focus_call_log = + linphone_core_get_call_history_2(mgr->lc, focus.getCMgr()->identity, mgr->identity); + BC_ASSERT_PTR_NOT_NULL(mgr_focus_call_log); + if (mgr_focus_call_log) { + BC_ASSERT_EQUAL((unsigned int)bctbx_list_size(mgr_focus_call_log), 1, unsigned int, "%u"); + for (bctbx_list_t *it = mgr_focus_call_log; it; it = bctbx_list_next(it)) { + LinphoneCallLog *call_log = (LinphoneCallLog *)it->data; + BC_ASSERT_TRUE(linphone_call_log_was_conference(call_log)); + } + bctbx_list_free_with_data(mgr_focus_call_log, (bctbx_list_free_func)linphone_call_log_unref); + } + } + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, laure, michelle}).waitUntil(chrono::seconds(2), [] { return false; }); + + linphone_address_unref(confAddr); + bctbx_list_free(coresList); + } +} + +static void change_active_speaker(void) { + Focus focus("chloe_rc"); + { // to make sure focus is destroyed after clients. + ClientConference marie("marie_rc", focus.getIdentity()); + ClientConference pauline("pauline_rc", focus.getIdentity()); // audio only + ClientConference laure("laure_tcp_rc", focus.getIdentity()); + + focus.registerAsParticipantDevice(marie); + setup_conference_info_cbs(marie.getCMgr()); + + bctbx_list_t *coresList = NULL; + std::list<LinphoneCoreManager *> invitesList{pauline.getCMgr(), laure.getCMgr()}; + std::list<LinphoneCoreManager *> participantsList{pauline.getCMgr(), laure.getCMgr(), marie.getCMgr()}; + std::list<LinphoneCoreManager *> cmgrsList{focus.getCMgr(), marie.getCMgr(), pauline.getCMgr(), + laure.getCMgr()}; + for (LinphoneCoreManager *mgr : cmgrsList) { + if (mgr != pauline.getCMgr()) { + LinphoneVideoActivationPolicy *pol = + linphone_factory_create_video_activation_policy(linphone_factory_get()); + linphone_video_activation_policy_set_automatically_accept(pol, TRUE); + linphone_video_activation_policy_set_automatically_initiate(pol, TRUE); + linphone_core_set_video_activation_policy(mgr->lc, pol); + linphone_video_activation_policy_unref(pol); + } + + linphone_core_set_video_device(mgr->lc, liblinphone_tester_mire_id); + linphone_core_enable_video_capture(mgr->lc, TRUE); + linphone_core_enable_video_display(mgr->lc, TRUE); + linphone_core_set_video_display_filter(mgr->lc, "MSAnalyseDisplay"); + + if (mgr != focus.getCMgr()) { + linphone_core_set_default_conference_layout(mgr->lc, LinphoneConferenceLayoutActiveSpeaker); + } + + enable_stun_in_core(mgr, TRUE, FALSE); + linphone_core_manager_wait_for_stun_resolution(mgr); + + coresList = bctbx_list_append(coresList, mgr->lc); + } + + linphone_core_set_file_transfer_server(marie.getLc(), file_transfer_url); + stats focus_stat = focus.getStats(); + + time_t start_time = ms_time(NULL); + int duration = -1; + time_t end_time = (duration <= 0) ? -1 : (start_time + duration * 60); + const char *initialSubject = "Test group"; + const char *description = "hello"; + std::map<LinphoneCoreManager *, LinphoneParticipantRole> participantList; + LinphoneParticipantRole role = LinphoneParticipantRoleSpeaker; + for (auto &p : invitesList) { + participantList.insert(std::make_pair(p, role)); + } + LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, + initialSubject, description, TRUE); + BC_ASSERT_PTR_NOT_NULL(confAddr); + + // Chat room creation to send ICS + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateCreated, 2, + liblinphone_tester_sip_timeout)); + + for (LinphoneCoreManager *mgr : participantsList) { + LinphoneCallParams *new_params = linphone_core_create_call_params(mgr->lc, nullptr); + linphone_call_params_set_video_direction(new_params, LinphoneMediaDirectionSendRecv); + if (mgr != pauline.getCMgr()) { + linphone_call_params_enable_video(new_params, TRUE); + } + linphone_core_invite_address_with_params_2(mgr->lc, confAddr, new_params, NULL, nullptr); + linphone_call_params_unref(new_params); + + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallOutgoingProgress, 1, + liblinphone_tester_sip_timeout)); + int nbStreamsRunning = 2; + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallUpdating, (nbStreamsRunning - 1), + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallStreamsRunning, nbStreamsRunning, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateCreated, + ((mgr == marie.getCMgr()) ? 3 : 2), liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionOutgoingProgress, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionActive, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(coresList, &mgr->stat.number_of_NotifyReceived, 1, liblinphone_tester_sip_timeout)); + } + + { + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallIncomingReceived, + focus_stat.number_of_LinphoneCallIncomingReceived + 3, + liblinphone_tester_sip_timeout)); + int focusNbStreamsRunning = 2; + BC_ASSERT_TRUE( + wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallUpdatedByRemote, + focus_stat.number_of_LinphoneCallUpdatedByRemote + 3 * (focusNbStreamsRunning - 1), + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallStreamsRunning, + focus_stat.number_of_LinphoneCallStreamsRunning + 3 * focusNbStreamsRunning, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateCreated, + focus_stat.number_of_LinphoneConferenceStateCreated + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneSubscriptionIncomingReceived, + focus_stat.number_of_LinphoneSubscriptionIncomingReceived + 3, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneSubscriptionActive, + focus_stat.number_of_LinphoneSubscriptionActive + 3, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participants_added, + focus_stat.number_of_participants_added + 3, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_added, + focus_stat.number_of_participant_devices_added + 3, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_joined, + focus_stat.number_of_participant_devices_joined + 3, + liblinphone_tester_sip_timeout)); + } + + LinphoneConference *fconference = linphone_core_search_conference_2(focus.getLc(), confAddr); + BC_ASSERT_PTR_NOT_NULL(fconference); + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, laure}).waitUntil(chrono::seconds(2), [] { return false; }); + + int nbStreamsAudio = 1; + int nbStreamsVideo = 0; + int nbStreamsText = 0; + + for (LinphoneCoreManager *mgr : cmgrsList) { + nbStreamsVideo = ((mgr != pauline.getCMgr()) ? 3 : 0); + LinphoneConference *pconference = linphone_core_search_conference_2(mgr->lc, confAddr); + BC_ASSERT_PTR_NOT_NULL(pconference); + if (pconference) { + const LinphoneConferenceParams *conference_params = linphone_conference_get_current_params(pconference); + int nbParticipants = 0; + if (start_time >= 0) { + BC_ASSERT_EQUAL((long long)linphone_conference_params_get_start_time(conference_params), + (long long)start_time, long long, "%lld"); + } + BC_ASSERT_EQUAL((long long)linphone_conference_params_get_end_time(conference_params), + (long long)end_time, long long, "%lld"); + if (mgr == focus.getCMgr()) { + nbParticipants = 3; + BC_ASSERT_FALSE(linphone_conference_is_in(pconference)); + } else { + nbParticipants = 2; + BC_ASSERT_TRUE(linphone_conference_is_in(pconference)); + LinphoneCall *current_call = linphone_core_get_current_call(mgr->lc); + BC_ASSERT_PTR_NOT_NULL(current_call); + if (current_call) { + BC_ASSERT_EQUAL((int)linphone_call_get_state(current_call), + (int)LinphoneCallStateStreamsRunning, int, "%0d"); + } + + LinphoneCall *pcall = linphone_core_get_call_by_remote_address2(mgr->lc, confAddr); + BC_ASSERT_PTR_NOT_NULL(pcall); + if (pcall) { + _linphone_call_check_nb_streams(pcall, nbStreamsAudio, nbStreamsVideo, nbStreamsText); + } + LinphoneCall *ccall = linphone_core_get_call_by_remote_address2(focus.getLc(), mgr->identity); + BC_ASSERT_PTR_NOT_NULL(ccall); + if (ccall) { + _linphone_call_check_nb_streams(ccall, nbStreamsAudio, nbStreamsVideo, nbStreamsText); + } + } + BC_ASSERT_EQUAL(linphone_conference_get_participant_count(pconference), nbParticipants, int, "%0d"); + bctbx_list_t *devices = linphone_conference_get_participant_device_list(pconference); + BC_ASSERT_EQUAL(bctbx_list_size(devices), 3, size_t, "%zu"); + if (devices) { + bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); + } + BC_ASSERT_STRING_EQUAL(linphone_conference_get_subject(pconference), initialSubject); + LinphoneParticipant *me = linphone_conference_get_me(pconference); + BC_ASSERT_TRUE(linphone_participant_is_admin(me) == + ((mgr == marie.getCMgr()) || (mgr == focus.getCMgr()))); + BC_ASSERT_TRUE(linphone_address_weak_equal(linphone_participant_get_address(me), mgr->identity)); + bctbx_list_t *participants = linphone_conference_get_participant_list(pconference); + for (bctbx_list_t *itp = participants; itp; itp = bctbx_list_next(itp)) { + LinphoneParticipant *p = (LinphoneParticipant *)bctbx_list_get_data(itp); + BC_ASSERT_TRUE( + linphone_participant_is_admin(p) == + linphone_address_weak_equal(linphone_participant_get_address(p), marie.getCMgr()->identity)); + } + bctbx_list_free_with_data(participants, (void (*)(void *))linphone_participant_unref); + + if (mgr != focus.getCMgr()) { + check_conference_ssrc(fconference, pconference); + } + } + } + + // need time to decode video + wait_for_list(coresList, NULL, 1, liblinphone_tester_sip_timeout); + _linphone_conference_video_change(coresList, marie.getCMgr(), pauline.getCMgr(), laure.getCMgr()); + + // end + for (LinphoneCoreManager *mgr : participantsList) { + LinphoneCall *call = linphone_core_get_call_by_remote_address2(mgr->lc, focus.getCMgr()->identity); + BC_ASSERT_PTR_NOT_NULL(call); + if (call) { + linphone_call_terminate(call); + BC_ASSERT_TRUE( + wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallEnd, 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallReleased, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionTerminated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateTerminationPending, + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateTerminated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateDeleted, 1, + liblinphone_tester_sip_timeout)); + } + } + + // Explicitely terminate conference as those on server are static by default + LinphoneConference *pconference = linphone_core_search_conference_2(focus.getLc(), confAddr); + BC_ASSERT_PTR_NOT_NULL(pconference); + if (pconference) { + linphone_conference_terminate(pconference); + } + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateTerminationPending, + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateTerminated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateDeleted, 1, + liblinphone_tester_sip_timeout)); + + for (auto mgr : cmgrsList) { + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionTerminated, 1, + liblinphone_tester_sip_timeout)); + + if (mgr && (mgr != focus.getCMgr())) { + LinphoneCall *participant_call = + linphone_core_get_call_by_remote_address2(mgr->lc, focus.getCMgr()->identity); + BC_ASSERT_PTR_NULL(participant_call); + LinphoneCall *conference_call = linphone_core_get_call_by_remote_address2(focus.getLc(), mgr->identity); + BC_ASSERT_PTR_NULL(conference_call); + + const bctbx_list_t *call_logs = linphone_core_get_call_logs(mgr->lc); + BC_ASSERT_EQUAL((unsigned int)bctbx_list_size(call_logs), 1, unsigned int, "%u"); + + bctbx_list_t *mgr_focus_call_log = + linphone_core_get_call_history_2(mgr->lc, focus.getCMgr()->identity, mgr->identity); + BC_ASSERT_PTR_NOT_NULL(mgr_focus_call_log); + if (mgr_focus_call_log) { + BC_ASSERT_EQUAL((unsigned int)bctbx_list_size(mgr_focus_call_log), 1, unsigned int, "%u"); + for (bctbx_list_t *it = mgr_focus_call_log; it; it = bctbx_list_next(it)) { + LinphoneCallLog *call_log = (LinphoneCallLog *)it->data; + BC_ASSERT_TRUE(linphone_call_log_was_conference(call_log)); + } + bctbx_list_free_with_data(mgr_focus_call_log, (bctbx_list_free_func)linphone_call_log_unref); + } + } + } + + linphone_address_unref(confAddr); + bctbx_list_free(coresList); + } +} + +static void create_one_participant_conference_toggle_video_base(LinphoneConferenceLayout layout, + bool_t enable_ice, + bool_t enable_stun) { + Focus focus("chloe_rc"); + { // to make sure focus is destroyed after clients. + ClientConference marie("marie_rc", focus.getIdentity()); + ClientConference pauline("pauline_rc", focus.getIdentity()); + ClientConference laure("laure_tcp_rc", focus.getIdentity()); + + focus.registerAsParticipantDevice(marie); + + setup_conference_info_cbs(marie.getCMgr()); + + bctbx_list_t *coresList = NULL; + + for (auto mgr : {focus.getCMgr(), marie.getCMgr(), pauline.getCMgr(), laure.getCMgr()}) { + LinphoneVideoActivationPolicy *pol = + linphone_factory_create_video_activation_policy(linphone_factory_get()); + linphone_video_activation_policy_set_automatically_accept(pol, TRUE); + linphone_video_activation_policy_set_automatically_initiate(pol, TRUE); + linphone_core_set_video_activation_policy(mgr->lc, pol); + linphone_video_activation_policy_unref(pol); + + linphone_core_set_video_device(mgr->lc, liblinphone_tester_mire_id); + linphone_core_enable_video_capture(mgr->lc, TRUE); + linphone_core_enable_video_display(mgr->lc, TRUE); + + if (mgr != focus.getCMgr()) { + linphone_core_set_default_conference_layout(mgr->lc, layout); + } + + enable_stun_in_core(mgr, enable_stun, enable_ice); + linphone_core_manager_wait_for_stun_resolution(mgr); + + coresList = bctbx_list_append(coresList, mgr->lc); + } + + linphone_core_set_file_transfer_server(marie.getLc(), file_transfer_url); + + stats focus_stat = focus.getStats(); + stats marie_stat = marie.getStats(); + + std::list<LinphoneCoreManager *> participants{pauline.getCMgr(), laure.getCMgr()}; + + time_t start_time = ms_time(NULL); + int duration = -1; + time_t end_time = (duration <= 0) ? -1 : (start_time + duration * 60); + const char *initialSubject = "Test characters: ^ :) ¤ çà @"; + const char *description = "- <F2><F3>\n\\çà "; + std::map<LinphoneCoreManager *, LinphoneParticipantRole> participantList; + LinphoneParticipantRole role = LinphoneParticipantRoleSpeaker; + for (auto &p : participants) { + participantList.insert(std::make_pair(p, role)); + role = (role == LinphoneParticipantRoleSpeaker) ? LinphoneParticipantRoleListener + : LinphoneParticipantRoleSpeaker; + } + LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, + initialSubject, description, TRUE); + BC_ASSERT_PTR_NOT_NULL(confAddr); + + // Chat room creation to send ICS + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateCreated, 2, + liblinphone_tester_sip_timeout)); + + for (auto mgr : {marie.getCMgr()}) { + LinphoneCallParams *new_params = linphone_core_create_call_params(mgr->lc, nullptr); + linphone_call_params_set_video_direction(new_params, LinphoneMediaDirectionSendRecv); + linphone_call_params_enable_video(new_params, TRUE); + linphone_core_invite_address_with_params_2(mgr->lc, confAddr, new_params, NULL, nullptr); + linphone_call_params_unref(new_params); + } + + for (auto mgr : {marie.getCMgr()}) { + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallOutgoingProgress, 1, + liblinphone_tester_sip_timeout)); + int no_streams_running = ((enable_ice) ? 3 : 2); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallUpdating, (no_streams_running - 1), + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallStreamsRunning, no_streams_running, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateCreated, + ((mgr == marie.getCMgr()) ? 3 : 2), liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionOutgoingProgress, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionActive, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(coresList, &mgr->stat.number_of_NotifyReceived, 1, liblinphone_tester_sip_timeout)); + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallIncomingReceived, + focus_stat.number_of_LinphoneCallIncomingReceived + 1, + liblinphone_tester_sip_timeout)); + int focus_no_streams_running = ((enable_ice) ? 3 : 2); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallUpdatedByRemote, + focus_stat.number_of_LinphoneCallUpdatedByRemote + (focus_no_streams_running - 1), + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallStreamsRunning, + focus_stat.number_of_LinphoneCallStreamsRunning + focus_no_streams_running, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateCreated, + focus_stat.number_of_LinphoneConferenceStateCreated + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneSubscriptionIncomingReceived, + focus_stat.number_of_LinphoneSubscriptionIncomingReceived + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneSubscriptionActive, + focus_stat.number_of_LinphoneSubscriptionActive + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participants_added, + focus_stat.number_of_participants_added + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_added, + focus_stat.number_of_participant_devices_added + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_joined, + focus_stat.number_of_participant_devices_joined + 1, + liblinphone_tester_sip_timeout)); + + LinphoneConference *fconference = linphone_core_search_conference_2(focus.getLc(), confAddr); + BC_ASSERT_PTR_NOT_NULL(fconference); + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, laure}).waitUntil(chrono::seconds(2), [] { return false; }); + + int no_streams_audio = 1; + int no_streams_video = 2; + int no_active_streams_video = (layout == LinphoneConferenceLayoutGrid) ? 0 : 1; + int no_streams_text = 0; + + for (auto mgr : {focus.getCMgr(), marie.getCMgr()}) { + LinphoneConference *pconference = linphone_core_search_conference_2(mgr->lc, confAddr); + BC_ASSERT_PTR_NOT_NULL(pconference); + if (pconference) { + const LinphoneConferenceParams *conference_params = linphone_conference_get_current_params(pconference); + int no_participants = 0; + if (start_time >= 0) { + BC_ASSERT_EQUAL((long long)linphone_conference_params_get_start_time(conference_params), + (long long)start_time, long long, "%lld"); + } + BC_ASSERT_EQUAL((long long)linphone_conference_params_get_end_time(conference_params), + (long long)end_time, long long, "%lld"); + if (mgr == focus.getCMgr()) { + no_participants = 1; + BC_ASSERT_FALSE(linphone_conference_is_in(pconference)); + } else { + no_participants = 0; + BC_ASSERT_TRUE(linphone_conference_is_in(pconference)); + LinphoneCall *current_call = linphone_core_get_current_call(mgr->lc); + BC_ASSERT_PTR_NOT_NULL(current_call); + if (current_call) { + BC_ASSERT_EQUAL((int)linphone_call_get_state(current_call), + (int)LinphoneCallStateStreamsRunning, int, "%0d"); + } + if (enable_ice) { + BC_ASSERT_TRUE(check_ice(mgr, focus.getCMgr(), LinphoneIceStateHostConnection)); + } + + LinphoneCall *pcall = linphone_core_get_call_by_remote_address2(mgr->lc, confAddr); + BC_ASSERT_PTR_NOT_NULL(pcall); + if (pcall) { + _linphone_call_check_nb_streams(pcall, no_streams_audio, no_streams_video, no_streams_text); + _linphone_call_check_nb_active_streams(pcall, no_streams_audio, no_streams_video, + no_streams_text); + const LinphoneCallParams *call_lparams = linphone_call_get_params(pcall); + BC_ASSERT_TRUE(linphone_call_params_video_enabled(call_lparams)); + const LinphoneCallParams *call_rparams = linphone_call_get_remote_params(pcall); + BC_ASSERT_TRUE(linphone_call_params_video_enabled(call_rparams)); + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(pcall); + BC_ASSERT_TRUE(linphone_call_params_video_enabled(call_cparams)); + } + LinphoneCall *ccall = linphone_core_get_call_by_remote_address2(focus.getLc(), mgr->identity); + BC_ASSERT_PTR_NOT_NULL(ccall); + if (ccall) { + _linphone_call_check_nb_streams(ccall, no_streams_audio, no_streams_video, no_streams_text); + _linphone_call_check_nb_active_streams(ccall, no_streams_audio, no_streams_video, + no_streams_text); + const LinphoneCallParams *call_lparams = linphone_call_get_params(ccall); + BC_ASSERT_TRUE(linphone_call_params_video_enabled(call_lparams)); + const LinphoneCallParams *call_rparams = linphone_call_get_remote_params(ccall); + BC_ASSERT_TRUE(linphone_call_params_video_enabled(call_rparams)); + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(ccall); + BC_ASSERT_TRUE(linphone_call_params_video_enabled(call_cparams)); + } + } + BC_ASSERT_EQUAL(linphone_conference_get_participant_count(pconference), no_participants, int, "%0d"); + bctbx_list_t *devices = linphone_conference_get_participant_device_list(pconference); + BC_ASSERT_EQUAL(bctbx_list_size(devices), 1, size_t, "%zu"); + if (devices) { + bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); + } + BC_ASSERT_STRING_EQUAL(linphone_conference_get_subject(pconference), initialSubject); + LinphoneParticipant *me = linphone_conference_get_me(pconference); + BC_ASSERT_TRUE(linphone_participant_is_admin(me) == + ((mgr == marie.getCMgr()) || (mgr == focus.getCMgr()))); + BC_ASSERT_TRUE(linphone_address_weak_equal(linphone_participant_get_address(me), mgr->identity)); + bctbx_list_t *participants = linphone_conference_get_participant_list(pconference); + for (bctbx_list_t *itp = participants; itp; itp = bctbx_list_next(itp)) { + LinphoneParticipant *p = (LinphoneParticipant *)bctbx_list_get_data(itp); + BC_ASSERT_TRUE( + linphone_participant_is_admin(p) == + linphone_address_weak_equal(linphone_participant_get_address(p), marie.getCMgr()->identity)); + } + bctbx_list_free_with_data(participants, (void (*)(void *))linphone_participant_unref); + + if (mgr != focus.getCMgr()) { + check_conference_ssrc(fconference, pconference); + } + } + } + + focus_stat = focus.getStats(); + marie_stat = marie.getStats(); + + LinphoneCall *marie_calls_focus = linphone_core_get_call_by_remote_address2(marie.getLc(), confAddr); + BC_ASSERT_PTR_NOT_NULL(marie_calls_focus); + LinphoneCall *focus_called_by_marie = + linphone_core_get_call_by_remote_address2(focus.getLc(), marie.getCMgr()->identity); + BC_ASSERT_PTR_NOT_NULL(focus_called_by_marie); + + LinphoneParticipant *marie_participant = + linphone_conference_find_participant(fconference, marie.getCMgr()->identity); + BC_ASSERT_PTR_NOT_NULL(marie_participant); + if (marie_participant) { + bctbx_list_t *devices = linphone_participant_get_devices(marie_participant); + for (bctbx_list_t *itd = devices; itd; itd = bctbx_list_next(itd)) { + LinphoneParticipantDevice *d = (LinphoneParticipantDevice *)bctbx_list_get_data(itd); + LinphoneMediaDirection video_direction = (layout == LinphoneConferenceLayoutGrid) + ? LinphoneMediaDirectionSendOnly + : LinphoneMediaDirectionSendRecv; + BC_ASSERT_TRUE(linphone_participant_device_get_stream_capability(d, LinphoneStreamTypeVideo) == + video_direction); + BC_ASSERT_TRUE(linphone_participant_device_get_stream_availability(d, LinphoneStreamTypeVideo)); + } + if (devices) { + bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); + } + } + + if (marie_calls_focus) { + LinphoneCallParams *new_params = linphone_core_create_call_params(marie.getLc(), marie_calls_focus); + linphone_call_params_enable_video(new_params, TRUE); + linphone_call_params_set_video_direction(new_params, LinphoneMediaDirectionRecvOnly); + linphone_call_update(marie_calls_focus, new_params); + linphone_call_params_unref(new_params); + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallUpdating, + marie_stat.number_of_LinphoneCallUpdating + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallStreamsRunning, + marie_stat.number_of_LinphoneCallStreamsRunning + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list( + coresList, &marie.getStats().number_of_participant_devices_media_capability_changed, + marie_stat.number_of_participant_devices_media_capability_changed + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallUpdatedByRemote, + focus_stat.number_of_LinphoneCallUpdatedByRemote + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallStreamsRunning, + focus_stat.number_of_LinphoneCallStreamsRunning + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list( + coresList, &focus.getStats().number_of_participant_devices_media_capability_changed, + focus_stat.number_of_participant_devices_media_capability_changed + 1, liblinphone_tester_sip_timeout)); + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, laure}).waitUntil(chrono::seconds(2), [] { return false; }); + + if (marie_calls_focus) { + _linphone_call_check_nb_streams(marie_calls_focus, no_streams_audio, no_streams_video, no_streams_text); + _linphone_call_check_nb_active_streams(marie_calls_focus, no_streams_audio, no_active_streams_video, + no_streams_text); + const LinphoneCallParams *call_lparams = linphone_call_get_params(marie_calls_focus); + BC_ASSERT_TRUE(linphone_call_params_video_enabled(call_lparams)); + const LinphoneCallParams *call_rparams = linphone_call_get_remote_params(marie_calls_focus); + BC_ASSERT_TRUE(linphone_call_params_video_enabled(call_rparams) != + (layout == LinphoneConferenceLayoutGrid)); + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(marie_calls_focus); + BC_ASSERT_TRUE(linphone_call_params_video_enabled(call_cparams) != + (layout == LinphoneConferenceLayoutGrid)); + } + if (focus_called_by_marie) { + _linphone_call_check_nb_streams(focus_called_by_marie, no_streams_audio, no_streams_video, no_streams_text); + _linphone_call_check_nb_active_streams(focus_called_by_marie, no_streams_audio, no_active_streams_video, + no_streams_text); + const LinphoneCallParams *call_lparams = linphone_call_get_params(focus_called_by_marie); + BC_ASSERT_TRUE(linphone_call_params_video_enabled(call_lparams) != + (layout == LinphoneConferenceLayoutGrid)); + const LinphoneCallParams *call_rparams = linphone_call_get_remote_params(focus_called_by_marie); + BC_ASSERT_TRUE(linphone_call_params_video_enabled(call_rparams) != + (layout == LinphoneConferenceLayoutGrid)); + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(focus_called_by_marie); + BC_ASSERT_TRUE(linphone_call_params_video_enabled(call_cparams) != + (layout == LinphoneConferenceLayoutGrid)); + } + + if (marie_participant) { + bctbx_list_t *devices = linphone_participant_get_devices(marie_participant); + for (bctbx_list_t *itd = devices; itd; itd = bctbx_list_next(itd)) { + LinphoneParticipantDevice *d = (LinphoneParticipantDevice *)bctbx_list_get_data(itd); + LinphoneMediaDirection video_direction = (layout == LinphoneConferenceLayoutGrid) + ? LinphoneMediaDirectionInactive + : LinphoneMediaDirectionRecvOnly; + BC_ASSERT_TRUE(linphone_participant_device_get_stream_capability(d, LinphoneStreamTypeVideo) == + video_direction); + BC_ASSERT_FALSE(linphone_participant_device_get_stream_availability(d, LinphoneStreamTypeVideo)); + } + if (devices) { + bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); + } + } + + focus_stat = focus.getStats(); + marie_stat = marie.getStats(); + + if (marie_calls_focus) { + LinphoneCallParams *new_params = linphone_core_create_call_params(marie.getLc(), marie_calls_focus); + linphone_call_params_enable_video(new_params, TRUE); + linphone_call_params_set_video_direction(new_params, LinphoneMediaDirectionSendRecv); + linphone_call_update(marie_calls_focus, new_params); + linphone_call_params_unref(new_params); + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallUpdating, + marie_stat.number_of_LinphoneCallUpdating + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallStreamsRunning, + marie_stat.number_of_LinphoneCallStreamsRunning + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list( + coresList, &marie.getStats().number_of_participant_devices_media_capability_changed, + marie_stat.number_of_participant_devices_media_capability_changed + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallUpdatedByRemote, + focus_stat.number_of_LinphoneCallUpdatedByRemote + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallStreamsRunning, + focus_stat.number_of_LinphoneCallStreamsRunning + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list( + coresList, &focus.getStats().number_of_participant_devices_media_capability_changed, + focus_stat.number_of_participant_devices_media_capability_changed + 1, liblinphone_tester_sip_timeout)); + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, laure}).waitUntil(chrono::seconds(2), [] { return false; }); + + if (marie_calls_focus) { + _linphone_call_check_nb_streams(marie_calls_focus, no_streams_audio, no_streams_video, no_streams_text); + _linphone_call_check_nb_active_streams(marie_calls_focus, no_streams_audio, no_streams_video, + no_streams_text); + const LinphoneCallParams *call_lparams = linphone_call_get_params(marie_calls_focus); + BC_ASSERT_TRUE(linphone_call_params_video_enabled(call_lparams)); + const LinphoneCallParams *call_rparams = linphone_call_get_remote_params(marie_calls_focus); + BC_ASSERT_TRUE(linphone_call_params_video_enabled(call_rparams)); + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(marie_calls_focus); + BC_ASSERT_TRUE(linphone_call_params_video_enabled(call_cparams)); + } + if (focus_called_by_marie) { + _linphone_call_check_nb_streams(focus_called_by_marie, no_streams_audio, no_streams_video, no_streams_text); + _linphone_call_check_nb_active_streams(focus_called_by_marie, no_streams_audio, no_streams_video, + no_streams_text); + const LinphoneCallParams *call_lparams = linphone_call_get_params(focus_called_by_marie); + BC_ASSERT_TRUE(linphone_call_params_video_enabled(call_lparams)); + const LinphoneCallParams *call_rparams = linphone_call_get_remote_params(focus_called_by_marie); + BC_ASSERT_TRUE(linphone_call_params_video_enabled(call_rparams)); + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(focus_called_by_marie); + BC_ASSERT_TRUE(linphone_call_params_video_enabled(call_cparams)); + } + + if (marie_participant) { + bctbx_list_t *devices = linphone_participant_get_devices(marie_participant); + for (bctbx_list_t *itd = devices; itd; itd = bctbx_list_next(itd)) { + LinphoneParticipantDevice *d = (LinphoneParticipantDevice *)bctbx_list_get_data(itd); + LinphoneMediaDirection video_direction = (layout == LinphoneConferenceLayoutGrid) + ? LinphoneMediaDirectionSendOnly + : LinphoneMediaDirectionSendRecv; + BC_ASSERT_TRUE(linphone_participant_device_get_stream_capability(d, LinphoneStreamTypeVideo) == + video_direction); + BC_ASSERT_TRUE(linphone_participant_device_get_stream_availability(d, LinphoneStreamTypeVideo)); + } + if (devices) { + bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); + } + } + + focus_stat = focus.getStats(); + marie_stat = marie.getStats(); + + if (marie_calls_focus) { + LinphoneCallParams *new_params = linphone_core_create_call_params(marie.getLc(), marie_calls_focus); + linphone_call_params_enable_video(new_params, FALSE); + linphone_call_params_set_video_direction(new_params, LinphoneMediaDirectionRecvOnly); + linphone_call_update(marie_calls_focus, new_params); + linphone_call_params_unref(new_params); + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallUpdating, + marie_stat.number_of_LinphoneCallUpdating + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallStreamsRunning, + marie_stat.number_of_LinphoneCallStreamsRunning + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list( + coresList, &marie.getStats().number_of_participant_devices_media_capability_changed, + marie_stat.number_of_participant_devices_media_capability_changed + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallUpdatedByRemote, + focus_stat.number_of_LinphoneCallUpdatedByRemote + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallStreamsRunning, + focus_stat.number_of_LinphoneCallStreamsRunning + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list( + coresList, &focus.getStats().number_of_participant_devices_media_capability_changed, + focus_stat.number_of_participant_devices_media_capability_changed + 1, liblinphone_tester_sip_timeout)); + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, laure}).waitUntil(chrono::seconds(2), [] { return false; }); + + if (marie_calls_focus) { + _linphone_call_check_nb_streams(marie_calls_focus, no_streams_audio, no_streams_video, no_streams_text); + _linphone_call_check_nb_active_streams(marie_calls_focus, no_streams_audio, 0, no_streams_text); + const LinphoneCallParams *call_lparams = linphone_call_get_params(marie_calls_focus); + BC_ASSERT_FALSE(linphone_call_params_video_enabled(call_lparams)); + const LinphoneCallParams *call_rparams = linphone_call_get_remote_params(marie_calls_focus); + BC_ASSERT_FALSE(linphone_call_params_video_enabled(call_rparams)); + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(marie_calls_focus); + BC_ASSERT_FALSE(linphone_call_params_video_enabled(call_cparams)); + } + if (focus_called_by_marie) { + _linphone_call_check_nb_streams(focus_called_by_marie, no_streams_audio, no_streams_video, no_streams_text); + _linphone_call_check_nb_active_streams(focus_called_by_marie, no_streams_audio, 0, no_streams_text); + const LinphoneCallParams *call_lparams = linphone_call_get_params(focus_called_by_marie); + BC_ASSERT_FALSE(linphone_call_params_video_enabled(call_lparams)); + const LinphoneCallParams *call_rparams = linphone_call_get_remote_params(focus_called_by_marie); + BC_ASSERT_FALSE(linphone_call_params_video_enabled(call_rparams)); + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(focus_called_by_marie); + BC_ASSERT_FALSE(linphone_call_params_video_enabled(call_cparams)); + } + + if (marie_participant) { + bctbx_list_t *devices = linphone_participant_get_devices(marie_participant); + for (bctbx_list_t *itd = devices; itd; itd = bctbx_list_next(itd)) { + LinphoneParticipantDevice *d = (LinphoneParticipantDevice *)bctbx_list_get_data(itd); + LinphoneMediaDirection video_direction = LinphoneMediaDirectionInactive; + BC_ASSERT_TRUE(linphone_participant_device_get_stream_capability(d, LinphoneStreamTypeVideo) == + video_direction); + BC_ASSERT_FALSE(linphone_participant_device_get_stream_availability(d, LinphoneStreamTypeVideo)); + } + if (devices) { + bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); + } + } + + focus_stat = focus.getStats(); + marie_stat = marie.getStats(); + + if (marie_calls_focus) { + LinphoneCallParams *new_params = linphone_core_create_call_params(marie.getLc(), marie_calls_focus); + linphone_call_params_enable_video(new_params, TRUE); + linphone_call_update(marie_calls_focus, new_params); + linphone_call_params_unref(new_params); + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallUpdating, + marie_stat.number_of_LinphoneCallUpdating + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallStreamsRunning, + marie_stat.number_of_LinphoneCallStreamsRunning + 1, + liblinphone_tester_sip_timeout)); + if (layout == LinphoneConferenceLayoutGrid) { + BC_ASSERT_FALSE(wait_for_list(coresList, + &marie.getStats().number_of_participant_devices_media_capability_changed, + marie_stat.number_of_participant_devices_media_capability_changed + 1, 2000)); + } else { + BC_ASSERT_TRUE(wait_for_list(coresList, + &marie.getStats().number_of_participant_devices_media_capability_changed, + marie_stat.number_of_participant_devices_media_capability_changed + 1, 2000)); + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallUpdatedByRemote, + focus_stat.number_of_LinphoneCallUpdatedByRemote + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallStreamsRunning, + focus_stat.number_of_LinphoneCallStreamsRunning + 1, + liblinphone_tester_sip_timeout)); + if (layout == LinphoneConferenceLayoutGrid) { + BC_ASSERT_FALSE(wait_for_list(coresList, + &focus.getStats().number_of_participant_devices_media_capability_changed, + focus_stat.number_of_participant_devices_media_capability_changed + 1, 2000)); + } else { + BC_ASSERT_TRUE(wait_for_list(coresList, + &focus.getStats().number_of_participant_devices_media_capability_changed, + focus_stat.number_of_participant_devices_media_capability_changed + 1, 2000)); + } + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, laure}).waitUntil(chrono::seconds(2), [] { return false; }); + + if (marie_calls_focus) { + _linphone_call_check_nb_streams(marie_calls_focus, no_streams_audio, no_streams_video, no_streams_text); + _linphone_call_check_nb_active_streams(marie_calls_focus, no_streams_audio, no_active_streams_video, + no_streams_text); + const LinphoneCallParams *call_lparams = linphone_call_get_params(marie_calls_focus); + BC_ASSERT_TRUE(linphone_call_params_video_enabled(call_lparams)); + const LinphoneCallParams *call_rparams = linphone_call_get_remote_params(marie_calls_focus); + BC_ASSERT_TRUE(linphone_call_params_video_enabled(call_rparams) != + (layout == LinphoneConferenceLayoutGrid)); + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(marie_calls_focus); + BC_ASSERT_TRUE(linphone_call_params_video_enabled(call_cparams) != + (layout == LinphoneConferenceLayoutGrid)); + } + if (focus_called_by_marie) { + _linphone_call_check_nb_streams(focus_called_by_marie, no_streams_audio, no_streams_video, no_streams_text); + _linphone_call_check_nb_active_streams(focus_called_by_marie, no_streams_audio, no_active_streams_video, + no_streams_text); + const LinphoneCallParams *call_lparams = linphone_call_get_params(focus_called_by_marie); + BC_ASSERT_TRUE(linphone_call_params_video_enabled(call_lparams) != + (layout == LinphoneConferenceLayoutGrid)); + const LinphoneCallParams *call_rparams = linphone_call_get_remote_params(focus_called_by_marie); + BC_ASSERT_TRUE(linphone_call_params_video_enabled(call_rparams) != + (layout == LinphoneConferenceLayoutGrid)); + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(focus_called_by_marie); + BC_ASSERT_TRUE(linphone_call_params_video_enabled(call_cparams) != + (layout == LinphoneConferenceLayoutGrid)); + } + + if (marie_participant) { + bctbx_list_t *devices = linphone_participant_get_devices(marie_participant); + for (bctbx_list_t *itd = devices; itd; itd = bctbx_list_next(itd)) { + LinphoneParticipantDevice *d = (LinphoneParticipantDevice *)bctbx_list_get_data(itd); + LinphoneMediaDirection video_direction = (layout == LinphoneConferenceLayoutGrid) + ? LinphoneMediaDirectionInactive + : LinphoneMediaDirectionRecvOnly; + BC_ASSERT_TRUE(linphone_participant_device_get_stream_capability(d, LinphoneStreamTypeVideo) == + video_direction); + BC_ASSERT_FALSE(linphone_participant_device_get_stream_availability(d, LinphoneStreamTypeVideo)); + } + if (devices) { + bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); + } + } + + focus_stat = focus.getStats(); + marie_stat = marie.getStats(); + + if (marie_calls_focus) { + LinphoneCallParams *new_params = linphone_core_create_call_params(marie.getLc(), marie_calls_focus); + linphone_call_params_set_video_direction(new_params, LinphoneMediaDirectionSendRecv); + linphone_call_update(marie_calls_focus, new_params); + linphone_call_params_unref(new_params); + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallUpdating, + marie_stat.number_of_LinphoneCallUpdating + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallStreamsRunning, + marie_stat.number_of_LinphoneCallStreamsRunning + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list( + coresList, &marie.getStats().number_of_participant_devices_media_capability_changed, + marie_stat.number_of_participant_devices_media_capability_changed + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallUpdatedByRemote, + focus_stat.number_of_LinphoneCallUpdatedByRemote + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallStreamsRunning, + focus_stat.number_of_LinphoneCallStreamsRunning + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list( + coresList, &focus.getStats().number_of_participant_devices_media_capability_changed, + focus_stat.number_of_participant_devices_media_capability_changed + 1, liblinphone_tester_sip_timeout)); + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, laure}).waitUntil(chrono::seconds(2), [] { return false; }); + + if (marie_calls_focus) { + _linphone_call_check_nb_streams(marie_calls_focus, no_streams_audio, no_streams_video, no_streams_text); + _linphone_call_check_nb_active_streams(marie_calls_focus, no_streams_audio, no_streams_video, + no_streams_text); + const LinphoneCallParams *call_lparams = linphone_call_get_params(marie_calls_focus); + BC_ASSERT_TRUE(linphone_call_params_video_enabled(call_lparams)); + const LinphoneCallParams *call_rparams = linphone_call_get_remote_params(marie_calls_focus); + BC_ASSERT_TRUE(linphone_call_params_video_enabled(call_rparams)); + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(marie_calls_focus); + BC_ASSERT_TRUE(linphone_call_params_video_enabled(call_cparams)); + } + if (focus_called_by_marie) { + _linphone_call_check_nb_streams(focus_called_by_marie, no_streams_audio, no_streams_video, no_streams_text); + _linphone_call_check_nb_active_streams(focus_called_by_marie, no_streams_audio, no_streams_video, + no_streams_text); + const LinphoneCallParams *call_lparams = linphone_call_get_params(focus_called_by_marie); + BC_ASSERT_TRUE(linphone_call_params_video_enabled(call_lparams)); + const LinphoneCallParams *call_rparams = linphone_call_get_remote_params(focus_called_by_marie); + BC_ASSERT_TRUE(linphone_call_params_video_enabled(call_rparams)); + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(focus_called_by_marie); + BC_ASSERT_TRUE(linphone_call_params_video_enabled(call_cparams)); + } + + if (marie_participant) { + bctbx_list_t *devices = linphone_participant_get_devices(marie_participant); + for (bctbx_list_t *itd = devices; itd; itd = bctbx_list_next(itd)) { + LinphoneParticipantDevice *d = (LinphoneParticipantDevice *)bctbx_list_get_data(itd); + LinphoneMediaDirection video_direction = (layout == LinphoneConferenceLayoutGrid) + ? LinphoneMediaDirectionSendOnly + : LinphoneMediaDirectionSendRecv; + BC_ASSERT_TRUE(linphone_participant_device_get_stream_capability(d, LinphoneStreamTypeVideo) == + video_direction); + BC_ASSERT_TRUE(linphone_participant_device_get_stream_availability(d, LinphoneStreamTypeVideo)); + } + if (devices) { + bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); + } + } + + LinphoneCall *call = linphone_core_get_call_by_remote_address2(marie.getLc(), focus.getCMgr()->identity); + BC_ASSERT_PTR_NOT_NULL(call); + if (call) { + linphone_call_terminate(call); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallEnd, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallReleased, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneSubscriptionTerminated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, + &marie.getStats().number_of_LinphoneConferenceStateTerminationPending, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateTerminated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateDeleted, 1, + liblinphone_tester_sip_timeout)); + + // Explicitely terminate conference as those on server are static by default + LinphoneConference *pconference = linphone_core_search_conference_2(focus.getLc(), confAddr); + BC_ASSERT_PTR_NOT_NULL(pconference); + if (pconference) { + linphone_conference_terminate(pconference); + } + BC_ASSERT_TRUE(wait_for_list(coresList, + &focus.getStats().number_of_LinphoneConferenceStateTerminationPending, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateTerminated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateDeleted, 1, + liblinphone_tester_sip_timeout)); + } + + for (auto mgr : {focus.getCMgr(), marie.getCMgr()}) { + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionTerminated, 1, + liblinphone_tester_sip_timeout)); + + if (mgr && (mgr != focus.getCMgr())) { + LinphoneCall *participant_call = + linphone_core_get_call_by_remote_address2(mgr->lc, focus.getCMgr()->identity); + BC_ASSERT_PTR_NULL(participant_call); + LinphoneCall *conference_call = linphone_core_get_call_by_remote_address2(focus.getLc(), mgr->identity); + BC_ASSERT_PTR_NULL(conference_call); + + const bctbx_list_t *call_logs = linphone_core_get_call_logs(mgr->lc); + BC_ASSERT_EQUAL((unsigned int)bctbx_list_size(call_logs), 1, unsigned int, "%u"); + + bctbx_list_t *mgr_focus_call_log = + linphone_core_get_call_history_2(mgr->lc, focus.getCMgr()->identity, mgr->identity); + BC_ASSERT_PTR_NOT_NULL(mgr_focus_call_log); + if (mgr_focus_call_log) { + BC_ASSERT_EQUAL((unsigned int)bctbx_list_size(mgr_focus_call_log), 1, unsigned int, "%u"); + for (bctbx_list_t *it = mgr_focus_call_log; it; it = bctbx_list_next(it)) { + LinphoneCallLog *call_log = (LinphoneCallLog *)it->data; + BC_ASSERT_TRUE(linphone_call_log_was_conference(call_log)); + } + bctbx_list_free_with_data(mgr_focus_call_log, (bctbx_list_free_func)linphone_call_log_unref); + } + } + } + + linphone_address_unref(confAddr); + bctbx_list_free(coresList); + } +} + +static void one_participant_conference_toggles_video_grid(void) { + create_one_participant_conference_toggle_video_base(LinphoneConferenceLayoutGrid, FALSE, FALSE); +} + +static void one_participant_conference_toggles_video_active_speaker(void) { + create_one_participant_conference_toggle_video_base(LinphoneConferenceLayoutActiveSpeaker, FALSE, FALSE); +} + +static void create_conference_with_active_call_base(bool_t dialout) { + Focus focus("chloe_rc"); + { // to make sure focus is destroyed after clients. + ClientConference marie("marie_rc", focus.getIdentity()); + ClientConference pauline("pauline_rc", focus.getIdentity()); + ClientConference laure("laure_tcp_rc", focus.getIdentity()); + ClientConference michelle("michelle_rc", focus.getIdentity()); + ClientConference berthe("berthe_rc", focus.getIdentity()); + + focus.registerAsParticipantDevice(marie); + focus.registerAsParticipantDevice(pauline); + focus.registerAsParticipantDevice(laure); + focus.registerAsParticipantDevice(michelle); + focus.registerAsParticipantDevice(berthe); + + setup_conference_info_cbs(marie.getCMgr()); + + bctbx_list_t *coresList = NULL; + + for (auto mgr : {focus.getCMgr(), marie.getCMgr(), pauline.getCMgr(), laure.getCMgr(), michelle.getCMgr(), + berthe.getCMgr()}) { + coresList = bctbx_list_append(coresList, mgr->lc); + } + + linphone_core_set_file_transfer_server(marie.getLc(), file_transfer_url); + + stats focus_stat = focus.getStats(); + stats marie_stat = marie.getStats(); + stats berthe_stat = berthe.getStats(); + + std::list<LinphoneCoreManager *> conferenceMgrs{focus.getCMgr(), marie.getCMgr(), pauline.getCMgr(), + laure.getCMgr(), michelle.getCMgr()}; + std::list<LinphoneCoreManager *> members{marie.getCMgr(), pauline.getCMgr(), laure.getCMgr(), + michelle.getCMgr()}; + std::list<LinphoneCoreManager *> participants{pauline.getCMgr(), laure.getCMgr(), michelle.getCMgr()}; + + const char *initialSubject = "Schedule of the trip towards the top of Europe"; + const char *description = "To the top of the Mont Blanc!!!! :-)"; + + time_t start_time = (dialout) ? -1 : (ms_time(NULL) + 10); + bool_t send_ics = TRUE; + std::map<LinphoneCoreManager *, LinphoneParticipantRole> participantList; + LinphoneParticipantRole role = LinphoneParticipantRoleSpeaker; + for (auto &p : participants) { + participantList.insert(std::make_pair(p, role)); + role = (role == LinphoneParticipantRoleSpeaker) ? LinphoneParticipantRoleListener + : LinphoneParticipantRoleSpeaker; + } + participantList.insert(std::make_pair(marie.getCMgr(), LinphoneParticipantRoleListener)); + LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, -1, + initialSubject, description, send_ics); + BC_ASSERT_PTR_NOT_NULL(confAddr); + + // the conference server calls Berthe - who knows why...... + LinphoneCallParams *new_params = linphone_core_create_call_params(berthe.getLc(), NULL); + + LinphoneContent *content = linphone_core_create_content(berthe.getLc()); + linphone_content_set_type(content, "application"); + linphone_content_set_subtype(content, "resource-lists+xml"); + + static const char *info_content = "<p1:resource-lists xmlns:p1=\"urn:ietf:params:xml:ns:resource-lists\">\n\ +<p1:list>\n\ + <p1:entry uri=\"sip:laure@sip.example.org\"/>\n\ + <p1:entry uri=\"sip:michelle@sip.example.org\"/>\n\ + <p1:entry uri=\"sip:pauline@sip.example.org\"/>\n\ +</p1:list>\n\ +</p1:resource-lists>"; + linphone_content_set_buffer(content, (const uint8_t *)info_content, strlen(info_content)); + linphone_call_params_add_custom_content(new_params, content); + + LinphoneCall *berthe_focus_call = + linphone_core_invite_address_with_params_2(berthe.getLc(), confAddr, new_params, NULL, nullptr); + BC_ASSERT_PTR_NOT_NULL(berthe_focus_call); + linphone_content_unref(content); + + BC_ASSERT_TRUE(wait_for_list(coresList, &berthe.getStats().number_of_LinphoneCallOutgoingInit, + berthe_stat.number_of_LinphoneCallOutgoingInit + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallIncomingReceived, + focus_stat.number_of_LinphoneCallIncomingReceived + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &berthe.getStats().number_of_LinphoneCallStreamsRunning, + berthe_stat.number_of_LinphoneCallStreamsRunning + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallStreamsRunning, + focus_stat.number_of_LinphoneCallStreamsRunning + 1, + liblinphone_tester_sip_timeout)); + + if (dialout) { + BC_ASSERT_PTR_NULL(linphone_core_get_current_call(focus.getLc())); + } else { + BC_ASSERT_PTR_NOT_NULL(linphone_core_get_current_call(focus.getLc())); + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneSubscriptionIncomingReceived, + focus_stat.number_of_LinphoneSubscriptionIncomingReceived + 1, 5000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneSubscriptionTerminated, + focus_stat.number_of_LinphoneSubscriptionTerminated + 1, 5000)); + BC_ASSERT_TRUE( + wait_for_list(coresList, &berthe.getStats().number_of_LinphoneSubscriptionOutgoingProgress, 1, 5000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &berthe.getStats().number_of_LinphoneSubscriptionError, 1, 5000)); + if (dialout) { + // Chat room creation to send ICS + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateCreated, + (send_ics ? 2 : 1), liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallOutgoingInit, + marie_stat.number_of_LinphoneCallOutgoingInit + 1, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallOutgoingInit, + focus_stat.number_of_LinphoneCallOutgoingInit + 3, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallIncomingReceived, + focus_stat.number_of_LinphoneCallIncomingReceived + 2, + liblinphone_tester_sip_timeout)); + + for (auto mgr : participants) { + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallIncomingReceived, 1, + liblinphone_tester_sip_timeout)); + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallStreamsRunning, + marie_stat.number_of_LinphoneCallStreamsRunning + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallStreamsRunning, + marie_stat.number_of_LinphoneCallStreamsRunning + 2, + liblinphone_tester_sip_timeout)); + + LinphoneConference *oconference = linphone_core_search_conference_2(marie.getLc(), confAddr); + if (BC_ASSERT_PTR_NOT_NULL(oconference)) { + BC_ASSERT_EQUAL(linphone_conference_get_participant_count(oconference), 3, int, "%0d"); + bctbx_list_t *devices = linphone_conference_get_participant_device_list(oconference); + BC_ASSERT_EQUAL(bctbx_list_size(devices), 4, size_t, "%zu"); + if (devices) { + bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); + } + } + + if (confAddr) { + for (auto mgr : participants) { + LinphoneConferenceInfo *info = + linphone_core_find_conference_information_from_uri(mgr->lc, confAddr); + if (BC_ASSERT_PTR_NOT_NULL(info)) { + BC_ASSERT_TRUE(linphone_address_weak_equal(marie.getCMgr()->identity, + linphone_conference_info_get_organizer(info))); + BC_ASSERT_TRUE(linphone_address_weak_equal(confAddr, linphone_conference_info_get_uri(info))); + + const bctbx_list_t *info_participants = linphone_conference_info_get_participants(info); + BC_ASSERT_EQUAL(bctbx_list_size(info_participants), 4, size_t, "%zu"); + + BC_ASSERT_NOT_EQUAL((long long)linphone_conference_info_get_date_time(info), 0, long long, + "%lld"); + const int duration_m = linphone_conference_info_get_duration(info); + BC_ASSERT_EQUAL(duration_m, 0, int, "%d"); + if (initialSubject) { + BC_ASSERT_STRING_EQUAL(linphone_conference_info_get_subject(info), initialSubject); + } else { + BC_ASSERT_PTR_NULL(linphone_conference_info_get_subject(info)); + } + if (send_ics) { + BC_ASSERT_STRING_EQUAL(linphone_conference_info_get_description(info), description); + } else { + BC_ASSERT_PTR_NULL(linphone_conference_info_get_description(info)); + } + linphone_conference_info_unref(info); + } + + LinphoneCall *pcall = linphone_core_get_call_by_remote_address2(mgr->lc, confAddr); + BC_ASSERT_PTR_NOT_NULL(pcall); + if (pcall) { + LinphoneCallLog *call_log = linphone_call_get_call_log(pcall); + BC_ASSERT_TRUE(linphone_call_log_was_conference(call_log)); + linphone_call_accept(pcall); + } + } + } + } else { + for (auto mgr : members) { + LinphoneCallParams *new_params = linphone_core_create_call_params(mgr->lc, nullptr); + linphone_core_invite_address_with_params_2(mgr->lc, confAddr, new_params, NULL, nullptr); + linphone_call_params_unref(new_params); + } + + for (auto mgr : members) { + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallOutgoingProgress, 1, + liblinphone_tester_sip_timeout)); + int no_streams_running = 2; + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallUpdating, + (no_streams_running - 1), liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallStreamsRunning, + no_streams_running, liblinphone_tester_sip_timeout)); + } + } + linphone_call_params_unref(new_params); + + for (auto mgr : participants) { + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallStreamsRunning, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallStreamsRunning, 2, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateCreated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionOutgoingProgress, 1, 5000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionActive, 1, 5000)); + BC_ASSERT_TRUE( + wait_for_list(coresList, &mgr->stat.number_of_NotifyReceived, 1, liblinphone_tester_sip_timeout)); + + LinphoneConferenceInfo *info = linphone_core_find_conference_information_from_uri(mgr->lc, confAddr); + if (BC_ASSERT_PTR_NOT_NULL(info)) { + BC_ASSERT_TRUE(linphone_address_weak_equal(marie.getCMgr()->identity, + linphone_conference_info_get_organizer(info))); + BC_ASSERT_TRUE(linphone_address_weak_equal(confAddr, linphone_conference_info_get_uri(info))); + + const bctbx_list_t *info_participants = linphone_conference_info_get_participants(info); + // Original participants + Marie who joined the conference + BC_ASSERT_EQUAL(bctbx_list_size(info_participants), 4, size_t, "%zu"); + + BC_ASSERT_GREATER_STRICT((long long)linphone_conference_info_get_date_time(info), 0, long long, "%lld"); + const int duration_m = linphone_conference_info_get_duration(info); + BC_ASSERT_EQUAL(duration_m, 0, int, "%d"); + if (initialSubject) { + BC_ASSERT_STRING_EQUAL(linphone_conference_info_get_subject(info), initialSubject); + } else { + BC_ASSERT_PTR_NULL(linphone_conference_info_get_subject(info)); + } + if (send_ics) { + BC_ASSERT_STRING_EQUAL(linphone_conference_info_get_description(info), description); + } else { + BC_ASSERT_PTR_NULL(linphone_conference_info_get_description(info)); + } + linphone_conference_info_unref(info); + } + + LinphoneCall *pcall = linphone_core_get_call_by_remote_address2(mgr->lc, confAddr); + BC_ASSERT_PTR_NOT_NULL(pcall); + if (pcall) { + LinphoneCallLog *call_log = linphone_call_get_call_log(pcall); + BC_ASSERT_TRUE(linphone_call_log_was_conference(call_log)); + LinphoneConferenceInfo *call_log_info = linphone_call_log_get_conference_info(call_log); + if (BC_ASSERT_PTR_NOT_NULL(call_log_info)) { + BC_ASSERT_TRUE(linphone_address_weak_equal(linphone_conference_info_get_organizer(call_log_info), + marie.getCMgr()->identity)); + BC_ASSERT_TRUE( + linphone_address_weak_equal(linphone_conference_info_get_uri(call_log_info), confAddr)); + + const bctbx_list_t *info_participants = linphone_conference_info_get_participants(call_log_info); + // Original participants + Marie who joined the conference + BC_ASSERT_EQUAL(bctbx_list_size(info_participants), 4, size_t, "%zu"); + BC_ASSERT_GREATER_STRICT((long long)linphone_conference_info_get_date_time(call_log_info), 0, + long long, "%lld"); + const int duration_m = linphone_conference_info_get_duration(call_log_info); + BC_ASSERT_EQUAL(duration_m, 0, int, "%d"); + if (initialSubject) { + BC_ASSERT_STRING_EQUAL(linphone_conference_info_get_subject(call_log_info), initialSubject); + } else { + BC_ASSERT_PTR_NULL(linphone_conference_info_get_subject(call_log_info)); + } + if (send_ics) { + BC_ASSERT_STRING_EQUAL(linphone_conference_info_get_description(call_log_info), description); + } else { + BC_ASSERT_PTR_NULL(linphone_conference_info_get_description(call_log_info)); + } + } + } + } + + BC_ASSERT_TRUE( + wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallStreamsRunning, + focus_stat.number_of_LinphoneCallStreamsRunning + static_cast<int>(participants.size() + 1), + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateCreated, + marie_stat.number_of_LinphoneConferenceStateCreated + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneSubscriptionOutgoingProgress, + marie_stat.number_of_LinphoneSubscriptionOutgoingProgress + 1, 5000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneSubscriptionActive, + marie_stat.number_of_LinphoneSubscriptionActive + 1, 5000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_NotifyReceived, + marie_stat.number_of_NotifyReceived + 1, liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateCreated, + focus_stat.number_of_LinphoneConferenceStateCreated + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneSubscriptionIncomingReceived, + focus_stat.number_of_LinphoneSubscriptionIncomingReceived + 5, 5000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneSubscriptionActive, + focus_stat.number_of_LinphoneSubscriptionActive + 4, 5000)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participants_added, + focus_stat.number_of_participants_added + 4, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_added, + focus_stat.number_of_participant_devices_added + 4, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_joined, + focus_stat.number_of_participant_devices_joined + 4, + liblinphone_tester_sip_timeout)); + + LinphoneConference *fconference = linphone_core_search_conference_2(focus.getLc(), confAddr); + BC_ASSERT_PTR_NOT_NULL(fconference); + + for (auto mgr : conferenceMgrs) { + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, laure, michelle, berthe}) + .waitUntil(chrono::seconds(50), [mgr, &focus, &members, confAddr, &participantList] { + size_t nb_audio_streams = 1; + size_t nb_video_streams = 0; + size_t nb_text_streams = 0; + std::list<LinphoneCall *> calls; + + if (mgr == focus.getCMgr()) { + for (auto m : members) { + LinphoneCall *call = linphone_core_get_call_by_remote_address2(mgr->lc, m->identity); + BC_ASSERT_PTR_NOT_NULL(call); + if (call) { + calls.push_back(call); + } else { + return false; + } + } + } else { + LinphoneCall *call = linphone_core_get_call_by_remote_address2(mgr->lc, confAddr); + BC_ASSERT_PTR_NOT_NULL(call); + if (call) { + calls.push_back(call); + } else { + return false; + } + } + + for (auto call : calls) { + if (call) { + const SalMediaDescription *call_result_desc = _linphone_call_get_result_desc(call); + if (!((call_result_desc->getNbStreams() == + nb_audio_streams + nb_video_streams + nb_text_streams) && + (call_result_desc->nbStreamsOfType(SalAudio) == nb_audio_streams) && + (call_result_desc->nbStreamsOfType(SalVideo) == nb_video_streams) && + (call_result_desc->nbStreamsOfType(SalText) == nb_text_streams) && + (linphone_call_get_state(call) == LinphoneCallStateStreamsRunning))) { + return false; + } + } + } + + bool video_check = true; + LinphoneConference *conference = linphone_core_search_conference_2(mgr->lc, confAddr); + BC_ASSERT_PTR_NOT_NULL(conference); + if (conference) { + bctbx_list_t *devices = linphone_conference_get_participant_device_list(conference); + for (bctbx_list_t *itd = devices; itd; itd = bctbx_list_next(itd)) { + LinphoneParticipantDevice *d = (LinphoneParticipantDevice *)bctbx_list_get_data(itd); + video_check &= + !linphone_participant_device_get_stream_availability(d, LinphoneStreamTypeVideo); + } + + if (devices) { + bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); + } + } + if (!video_check) { + return false; + } + + bool role_check = true; + for (auto m : participantList) { + LinphoneCoreManager *mMgr = m.first; + LinphoneParticipantRole role = m.second; + LinphoneParticipant *p = linphone_conference_is_me(conference, mMgr->identity) + ? linphone_conference_get_me(conference) + : linphone_conference_find_participant(conference, mMgr->identity); + role_check &= (p != nullptr); + if (p) { + role_check &= (linphone_participant_get_role(p) == role); + } + } + return role_check; + }); + } + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, laure, michelle, berthe}).waitUntil(chrono::seconds(15), [] { + return false; + }); + + for (auto mgr : conferenceMgrs) { + LinphoneAddress *uri = linphone_address_new(linphone_core_get_identity(mgr->lc)); + LinphoneConference *pconference = linphone_core_search_conference_2(mgr->lc, confAddr); + linphone_address_unref(uri); + BC_ASSERT_PTR_NOT_NULL(pconference); + if (pconference) { + int no_participants = 0; + if (mgr == focus.getCMgr()) { + no_participants = (dialout) ? 4 : 5; + BC_ASSERT_FALSE(linphone_conference_is_in(pconference)); + + int focus_no_streams_running = 8; + int focus_no_updating = focus_no_streams_running - 4; + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallUpdatedByRemote, + focus_stat.number_of_LinphoneCallUpdatedByRemote + focus_no_updating, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallStreamsRunning, + focus_stat.number_of_LinphoneCallStreamsRunning + focus_no_streams_running, + liblinphone_tester_sip_timeout)); + } else { + no_participants = (dialout) ? 3 : 4; + BC_ASSERT_TRUE(linphone_conference_is_in(pconference)); + LinphoneCall *current_call = linphone_core_get_current_call(mgr->lc); + BC_ASSERT_PTR_NOT_NULL(current_call); + if (current_call) { + BC_ASSERT_EQUAL((int)linphone_call_get_state(current_call), + (int)LinphoneCallStateStreamsRunning, int, "%0d"); + } + + bool_t enabled = FALSE; + int no_streams_audio = 1; + int no_streams_video = 0; + int no_active_streams_video = 0; + int no_streams_text = 0; + + LinphoneCall *pcall = linphone_core_get_call_by_remote_address2(mgr->lc, confAddr); + BC_ASSERT_PTR_NOT_NULL(pcall); + if (pcall) { + _linphone_call_check_nb_streams(pcall, no_streams_audio, no_streams_video, no_streams_text); + _linphone_call_check_nb_active_streams(pcall, no_streams_audio, no_active_streams_video, + no_streams_text); + const LinphoneCallParams *call_lparams = linphone_call_get_params(pcall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_lparams), enabled, int, "%0d"); + const LinphoneCallParams *call_rparams = linphone_call_get_remote_params(pcall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_rparams), enabled, int, "%0d"); + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(pcall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_cparams), enabled, int, "%0d"); + } + + LinphoneCall *ccall = linphone_core_get_call_by_remote_address2(focus.getLc(), mgr->identity); + BC_ASSERT_PTR_NOT_NULL(ccall); + if (ccall) { + _linphone_call_check_nb_streams(ccall, no_streams_audio, no_streams_video, no_streams_text); + _linphone_call_check_nb_active_streams(ccall, no_streams_audio, no_active_streams_video, + no_streams_text); + const LinphoneCallParams *call_lparams = linphone_call_get_params(ccall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_lparams), enabled, int, "%0d"); + const LinphoneCallParams *call_rparams = linphone_call_get_remote_params(ccall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_rparams), enabled, int, "%0d"); + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(ccall); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_cparams), enabled, int, "%0d"); + } + } + BC_ASSERT_EQUAL(linphone_conference_get_participant_count(pconference), no_participants, int, "%0d"); + bctbx_list_t *devices = linphone_conference_get_participant_device_list(pconference); + BC_ASSERT_EQUAL(bctbx_list_size(devices), (dialout) ? 4 : 5, size_t, "%zu"); + if (devices) { + bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); + } + BC_ASSERT_STRING_EQUAL(linphone_conference_get_subject(pconference), initialSubject); + LinphoneParticipant *me = linphone_conference_get_me(pconference); + BC_ASSERT_TRUE(linphone_participant_is_admin(me) == + ((mgr == marie.getCMgr()) || (mgr == focus.getCMgr()))); + BC_ASSERT_TRUE(linphone_address_weak_equal(linphone_participant_get_address(me), mgr->identity)); + bctbx_list_t *participants = linphone_conference_get_participant_list(pconference); + for (bctbx_list_t *itp = participants; itp; itp = bctbx_list_next(itp)) { + LinphoneParticipant *p = (LinphoneParticipant *)bctbx_list_get_data(itp); + BC_ASSERT_TRUE( + linphone_participant_is_admin(p) == + linphone_address_weak_equal(linphone_participant_get_address(p), marie.getCMgr()->identity)); + } + bctbx_list_free_with_data(participants, (void (*)(void *))linphone_participant_unref); + + if (mgr != focus.getCMgr()) { + check_conference_ssrc(fconference, pconference); + } + + LinphoneConference *conference = linphone_core_search_conference_2(mgr->lc, confAddr); + BC_ASSERT_PTR_NOT_NULL(conference); + if (conference) { + bctbx_list_t *devices = linphone_conference_get_participant_device_list(conference); + for (bctbx_list_t *itd = devices; itd; itd = bctbx_list_next(itd)) { + LinphoneParticipantDevice *d = (LinphoneParticipantDevice *)bctbx_list_get_data(itd); + BC_ASSERT_FALSE( + linphone_participant_device_get_stream_availability(d, LinphoneStreamTypeVideo)); + } + + if (devices) { + bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); + } + } + } + } + + focus_stat = focus.getStats(); + for (auto mgr : members) { + LinphoneCall *call = linphone_core_get_call_by_remote_address2(mgr->lc, focus.getCMgr()->identity); + BC_ASSERT_PTR_NOT_NULL(call); + if (call) { + ms_message("%s is terminating call with %s", linphone_core_get_identity(mgr->lc), + linphone_core_get_identity(focus.getLc())); + linphone_call_terminate(call); + BC_ASSERT_TRUE( + wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallEnd, 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallReleased, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionTerminated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateTerminationPending, + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateTerminated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateDeleted, 1, + liblinphone_tester_sip_timeout)); + + LinphoneAddress *uri = linphone_address_new(linphone_core_get_identity(mgr->lc)); + LinphoneConference *pconference = linphone_core_search_conference(mgr->lc, NULL, uri, confAddr, NULL); + BC_ASSERT_PTR_NULL(pconference); + linphone_address_unref(uri); + } + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallEnd, + focus_stat.number_of_LinphoneCallEnd + 4, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallReleased, + focus_stat.number_of_LinphoneCallReleased + 4, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneSubscriptionTerminated, + focus_stat.number_of_LinphoneSubscriptionTerminated + 4, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participants_removed, + focus_stat.number_of_participants_removed + 4, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_removed, + focus_stat.number_of_participant_devices_removed + 4, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_EQUAL(focus.getStats().number_of_LinphoneConferenceStateTerminationPending, + focus_stat.number_of_LinphoneConferenceStateTerminationPending, int, "%d"); + BC_ASSERT_EQUAL(focus.getStats().number_of_LinphoneConferenceStateTerminated, + focus_stat.number_of_LinphoneConferenceStateTerminated, int, "%d"); + BC_ASSERT_EQUAL(focus.getStats().number_of_LinphoneConferenceStateDeleted, + focus_stat.number_of_LinphoneConferenceStateDeleted, int, "%d"); + + for (auto mgr : {focus.getCMgr()}) { + LinphoneConference *pconference = linphone_core_search_conference_2(mgr->lc, confAddr); + BC_ASSERT_PTR_NOT_NULL(pconference); + if (pconference) { + BC_ASSERT_EQUAL(linphone_conference_get_participant_count(pconference), (dialout) ? 0 : 1, int, "%0d"); + bctbx_list_t *devices = linphone_conference_get_participant_device_list(pconference); + BC_ASSERT_EQUAL(bctbx_list_size(devices), (dialout) ? 0 : 1, size_t, "%zu"); + if (devices) { + bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); + } + BC_ASSERT_STRING_EQUAL(linphone_conference_get_subject(pconference), initialSubject); + } + } + + const bctbx_list_t *calls = linphone_core_get_calls(focus.getLc()); + BC_ASSERT_EQUAL(bctbx_list_size(calls), 1, size_t, "%zu"); + + LinphoneCall *focus_berthe_call = + linphone_core_get_call_by_remote_address2(focus.getLc(), berthe.getCMgr()->identity); + BC_ASSERT_PTR_NOT_NULL(focus_berthe_call); + if (focus_berthe_call) { + linphone_call_terminate(focus_berthe_call); + } + + if (!dialout) { + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participants_removed, + focus_stat.number_of_participants_removed + 5, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_removed, + focus_stat.number_of_participant_devices_removed + 5, + liblinphone_tester_sip_timeout)); + } + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallEnd, + focus_stat.number_of_LinphoneCallEnd + 5, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallReleased, + focus_stat.number_of_LinphoneCallReleased + 5, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(coresList, &berthe.getStats().number_of_LinphoneCallEnd, 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &berthe.getStats().number_of_LinphoneCallReleased, 1, + liblinphone_tester_sip_timeout)); + + // Explicitely terminate conference as those on server are static by default + if (fconference) { + linphone_conference_terminate(fconference); + } + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateTerminationPending, + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateTerminated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateDeleted, 1, + liblinphone_tester_sip_timeout)); + + for (auto mgr : members) { + const bctbx_list_t *call_logs = linphone_core_get_call_logs(mgr->lc); + BC_ASSERT_EQUAL((unsigned int)bctbx_list_size(call_logs), 1, unsigned int, "%u"); + + bctbx_list_t *mgr_focus_call_log = + linphone_core_get_call_history_2(mgr->lc, focus.getCMgr()->identity, mgr->identity); + BC_ASSERT_PTR_NOT_NULL(mgr_focus_call_log); + if (mgr_focus_call_log) { + BC_ASSERT_EQUAL((unsigned int)bctbx_list_size(mgr_focus_call_log), 1, unsigned int, "%u"); + for (bctbx_list_t *it = mgr_focus_call_log; it; it = bctbx_list_next(it)) { + LinphoneCallLog *call_log = (LinphoneCallLog *)it->data; + BC_ASSERT_TRUE(linphone_call_log_was_conference(call_log)); + } + bctbx_list_free_with_data(mgr_focus_call_log, (bctbx_list_free_func)linphone_call_log_unref); + } + + LinphoneConferenceInfo *info = linphone_core_find_conference_information_from_uri(mgr->lc, confAddr); + if (BC_ASSERT_PTR_NOT_NULL(info)) { + BC_ASSERT_TRUE(linphone_address_weak_equal(marie.getCMgr()->identity, + linphone_conference_info_get_organizer(info))); + BC_ASSERT_TRUE(linphone_address_weak_equal(confAddr, linphone_conference_info_get_uri(info))); + + const bctbx_list_t *info_participants = linphone_conference_info_get_participants(info); + // Original participants + Marie who joined the conference + BC_ASSERT_EQUAL(bctbx_list_size(info_participants), (dialout) ? 4 : 5, size_t, "%zu"); + + BC_ASSERT_GREATER_STRICT((long long)linphone_conference_info_get_date_time(info), 0, long long, "%lld"); + const int duration_m = linphone_conference_info_get_duration(info); + BC_ASSERT_EQUAL(duration_m, 0, int, "%d"); + if (initialSubject) { + BC_ASSERT_STRING_EQUAL(linphone_conference_info_get_subject(info), initialSubject); + } else { + BC_ASSERT_PTR_NULL(linphone_conference_info_get_subject(info)); + } + if (send_ics || (mgr == marie.getCMgr())) { + BC_ASSERT_STRING_EQUAL(linphone_conference_info_get_description(info), description); + } else { + BC_ASSERT_PTR_NULL(linphone_conference_info_get_description(info)); + } + linphone_conference_info_unref(info); + } + } + + // wait bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, laure, michelle, berthe}).waitUntil(chrono::seconds(2), [] { + return false; + }); + + linphone_address_unref(confAddr); + bctbx_list_free(coresList); + } +} + +static void create_dial_out_conference_with_active_call(void) { + create_conference_with_active_call_base(TRUE); +} + +static void create_scheduled_conference_with_active_call(void) { + create_conference_with_active_call_base(FALSE); +} + +} // namespace LinphoneTest + +static test_t local_conference_chat_tests[] = { + TEST_ONE_TAG("Group chat room creation local server", + LinphoneTest::group_chat_room_creation_server, + "LeaksMemory"), /* beacause of coreMgr restart*/ + TEST_NO_TAG("Group chat Server chat room deletion", LinphoneTest::group_chat_room_server_deletion), + TEST_ONE_TAG("Group chat with client removed added", + LinphoneTest::group_chat_room_with_client_removed_added, + "LeaksMemory"), /* beacause of coreMgr restart*/ + TEST_ONE_TAG("Group chat with client restart", + LinphoneTest::group_chat_room_with_client_restart, + "LeaksMemory"), /* beacause of coreMgr restart*/ + TEST_NO_TAG("Group chat room bulk notify to participant", + LinphoneTest::group_chat_room_bulk_notify_to_participant), /* because of network up and down*/ + TEST_ONE_TAG("One to one chatroom exhumed while participant is offline", + LinphoneTest::one_to_one_chatroom_exhumed_while_offline, + "LeaksMemory"), /* because of network up and down*/ + TEST_ONE_TAG("Group chat Server chat room deletion with remote list event handler", + LinphoneTest::group_chat_room_server_deletion_with_rmt_lst_event_handler, + "LeaksMemory"), /* because of coreMgr restart*/ + TEST_ONE_TAG("One to one group chat deletion initiated by server and client", + LinphoneTest::one_to_one_group_chat_room_deletion_by_server_client, + "LeaksMemory"), /* because of network up and down */ + TEST_ONE_TAG("Group chat room deletes chatroom after restart", + LinphoneTest::group_chat_room_with_client_deletes_chatroom_after_restart, + "LeaksMemory"), /* because of network up and down */ + TEST_ONE_TAG("Multi domain chatroom", + LinphoneTest::multidomain_group_chat_room, + "LeaksMemory") /* because of coreMgr restart*/ +}; + +static test_t local_conference_chat_error_tests[] = { + TEST_ONE_TAG("Group chat with INVITE session error", + LinphoneTest::group_chat_room_with_invite_error, + "LeaksMemory"), /* because of network up and down */ + TEST_ONE_TAG("Group chat with SUBSCRIBE session error", + LinphoneTest::group_chat_room_with_subscribe_error, + "LeaksMemory"), /* because of network up and down */ + TEST_NO_TAG("Group chat Add participant with invalid address", + LinphoneTest::group_chat_room_add_participant_with_invalid_address), + TEST_NO_TAG("Group chat Only participant with invalid address", + LinphoneTest::group_chat_room_with_only_participant_with_invalid_address), + +}; + +static test_t local_conference_chat_imdn_tests[] = { + TEST_ONE_TAG("Group chat with client IMDN after restart", + LinphoneTest::group_chat_room_with_client_idmn_after_restart, + "LeaksMemory"), /* because of network up and down */ + TEST_NO_TAG("Group chat Lime Server chat room send imdn error", + LinphoneTest::group_chat_room_lime_session_corrupted), + TEST_ONE_TAG("Secure one to one group chat deletion initiated by server and client", + LinphoneTest::secure_one_to_one_group_chat_room_deletion_by_server_client, + "LeaksMemory"), /* because of network up and down */ + TEST_ONE_TAG("Secure group chat with client IMDN sent after restart", + LinphoneTest::secure_group_chat_room_with_client_idmn_sent_after_restart, + "LeaksMemory"), /* because of network up and down */ + TEST_ONE_TAG("Secure group chat with client IMDN sent after restart and participant added", + LinphoneTest::secure_group_chat_room_with_client_idmn_sent_after_restart_and_participant_added, + "LeaksMemory"), /* because of network up and down */ + TEST_ONE_TAG( + "Secure group chat with client IMDN sent after restart and participant added and core stopped before sending " + "IMDN", + LinphoneTest::secure_group_chat_room_with_client_idmn_sent_after_restart_and_participant_added_and_core_stopped, + "LeaksMemory"), /* because of network up and down */ + TEST_NO_TAG("Group chat Lime Server chat room clear message", + LinphoneTest::group_chat_room_lime_server_clear_message)}; + +static test_t local_conference_ephemeral_chat_tests[] = { + TEST_NO_TAG("Unencrypted group chat server chat room with admin managed ephemeral messages", + LinphoneTest::group_chat_room_server_admin_managed_messages_unencrypted), + TEST_ONE_TAG("Group chat Server chat room with admin managed ephemeral messages disabled after creation", + LinphoneTest::group_chat_room_server_admin_managed_messages_ephemeral_disabled_after_creation, + "LeaksMemory"), /* because of coreMgr restart*/ + TEST_ONE_TAG("Group chat Server chat room with admin managed ephemeral messages enabled after creation", + LinphoneTest::group_chat_room_server_admin_managed_messages_ephemeral_enabled_after_creation, + "LeaksMemory"), /* because of coreMgr restart*/ + TEST_ONE_TAG("Group chat Server chat room with admin managed ephemeral messages with lifetime update", + LinphoneTest::group_chat_room_server_admin_managed_messages_ephemeral_lifetime_update, + "LeaksMemory"), /* because of coreMgr restart*/ + TEST_NO_TAG( + "Group chat Server chat room with admin managed ephemeral messages with lifetime toggle", + LinphoneTest::group_chat_room_server_admin_managed_messages_ephemeral_lifetime_toggle_using_different_methods), + TEST_NO_TAG("Group chat Server chat room with ephemeral message mode changed", + LinphoneTest::group_chat_room_server_ephemeral_mode_changed)}; + +static test_t local_conference_secure_chat_tests[] = { + TEST_ONE_TAG("Secure Group chat with client restart", + LinphoneTest::secure_group_chat_room_with_client_restart, + "LeaksMemory"), /* beacause of coreMgr restart*/ + TEST_ONE_TAG("Secure group chat with INVITE session error", + LinphoneTest::secure_group_chat_room_with_invite_error, + "LeaksMemory"), /* because of network up and down */ + TEST_ONE_TAG("Secure group chat with SUBSCRIBE session error", + LinphoneTest::secure_group_chat_room_with_subscribe_error, + "LeaksMemory"), /* because of network up and down */ + TEST_ONE_TAG("Secure group chat with chat room deleted before server restart", + LinphoneTest::secure_group_chat_room_with_chat_room_deleted_before_server_restart, + "LeaksMemory"), /* because of network up and down */ + TEST_NO_TAG("Group chat Lime Server chat room encrypted message", + LinphoneTest::group_chat_room_lime_server_encrypted_message)}; + +static test_t local_conference_scheduled_conference_basic_tests[] = { + + TEST_NO_TAG("Call to inexisting conference address", LinphoneTest::call_to_inexisting_conference_address), + TEST_NO_TAG("Create simple conference", LinphoneTest::create_simple_conference), + TEST_NO_TAG("Create conference with uninvited participant", + LinphoneTest::create_conference_with_uninvited_participant), + TEST_NO_TAG("Create simple conference with server restart", + LinphoneTest::create_simple_conference_with_server_restart), + TEST_NO_TAG("Create simple conference with client restart", + LinphoneTest::create_simple_conference_with_client_restart), + TEST_NO_TAG("Create simple conference with audio only participant", + LinphoneTest::create_simple_conference_with_audio_only_participant), + TEST_NO_TAG("Create conference with late participant addition", + LinphoneTest::create_conference_with_late_participant_addition), + TEST_NO_TAG("Organizer schedules 2 conferences", LinphoneTest::organizer_schedule_two_conferences), + TEST_NO_TAG("Create conference starting immediately", LinphoneTest::create_conference_starting_immediately), + TEST_NO_TAG("Create conference starting in the past", LinphoneTest::create_conference_starting_in_the_past), + TEST_NO_TAG("Create conference with participant codec mismatch", + LinphoneTest::create_conference_with_participant_codec_mismatch), + TEST_NO_TAG("Create conference with organizer codec mismatch", + LinphoneTest::create_conference_with_organizer_codec_mismatch)}; + +static test_t local_conference_scheduled_conference_advanced_tests[] = { + TEST_NO_TAG("Create simple SRTP conference", LinphoneTest::create_simple_srtp_conference), + TEST_NO_TAG("Create simple ZRTP conference", LinphoneTest::create_simple_zrtp_conference), + TEST_NO_TAG("Create simple DTLS conference", LinphoneTest::create_simple_dtls_conference), + TEST_NO_TAG("Create conference with server restart (participant first)", + LinphoneTest::create_conference_with_server_restart_participant_first), + TEST_NO_TAG("Create conference with server restart (organizer first)", + LinphoneTest::create_conference_with_server_restart_organizer_first), + TEST_NO_TAG("Create simple conference with update deferred", + LinphoneTest::create_simple_conference_with_update_deferred), + TEST_NO_TAG("Create conference with uninvited participant not allowed", + LinphoneTest::create_conference_with_uninvited_participant_not_allowed), + TEST_NO_TAG("Create conference with late participant addition declined", + LinphoneTest::create_conference_with_late_participant_addition_declined), +#if 0 + TEST_NO_TAG("Conference with participants added after conference end", LinphoneTest::conference_with_participants_added_after_end), + TEST_NO_TAG("Conference with participants added before conference start", LinphoneTest::conference_with_participants_added_before_start), +#endif + TEST_NO_TAG("Create conference with audio only and uninvited participant", + LinphoneTest::create_conference_with_audio_only_and_uninvited_participant), + TEST_NO_TAG("Create simple conference with audio only participant enabling video", + LinphoneTest::create_simple_conference_with_audio_only_participant_enabling_video), + TEST_NO_TAG("Create one participant conference toggles video in grid layout", + LinphoneTest::one_participant_conference_toggles_video_grid), + TEST_NO_TAG("Create one participant conference toggles video in active speaker layout", + LinphoneTest::one_participant_conference_toggles_video_active_speaker), + TEST_NO_TAG("2 overlapping conferences from different organizers", + LinphoneTest::two_overlapping_scheduled_conferences_from_different_organizers), + TEST_NO_TAG("Create scheduled conference with active call", + LinphoneTest::create_scheduled_conference_with_active_call), + TEST_NO_TAG("Change active speaker", LinphoneTest::change_active_speaker)}; + +static test_t local_conference_conference_edition_tests[] = { + TEST_NO_TAG("Organizer edits simple conference", LinphoneTest::organizer_edits_simple_conference), + TEST_NO_TAG("Organizer edits simple conference using different account", + LinphoneTest::organizer_edits_simple_conference_using_different_account), + TEST_NO_TAG("Organizer edits simple conference while it is active", + LinphoneTest::organizer_edits_simple_conference_while_active), + TEST_NO_TAG("Organizer edits simple conference with server restart", + LinphoneTest::organizer_edits_simple_conference_with_server_restart), + TEST_NO_TAG("Participant edits simple conference", LinphoneTest::participant_edits_simple_conference), + TEST_NO_TAG("Participant edits simple conference using different account", + LinphoneTest::participant_edits_simple_conference_using_different_account), + TEST_NO_TAG("Conference cancelled through edit", LinphoneTest::conference_cancelled_through_edit), + TEST_NO_TAG("Conference edition with simultanoues participant added removed", + LinphoneTest::conference_edition_with_simultaneous_participant_add_remove), + TEST_NO_TAG("Conference edition with participant role changed", + LinphoneTest::conference_edition_with_participant_role_changed), + TEST_NO_TAG("Conference edition with organizer codec mismatch", + LinphoneTest::conference_edition_with_organizer_codec_mismatch), + TEST_NO_TAG("Create conference with server restart (conference cancelled)", + LinphoneTest::create_conference_with_server_restart_conference_cancelled)}; + +static test_t local_conference_scheduled_ice_conference_tests[] = { + TEST_NO_TAG("Create simple ICE conference", LinphoneTest::create_simple_ice_conference), + TEST_NO_TAG("Create simple STUN+ICE conference", LinphoneTest::create_simple_stun_ice_conference), + TEST_NO_TAG("Create simple ICE SRTP conference", LinphoneTest::create_simple_ice_srtp_conference), + TEST_NO_TAG("Create simple ICE DTLS conference", LinphoneTest::create_simple_ice_dtls_conference), + TEST_NO_TAG("Create simple STUN+ICE SRTP conference", LinphoneTest::create_simple_stun_ice_srtp_conference), + TEST_NO_TAG("Create simple ICE conference with audio only participant", + LinphoneTest::create_simple_ice_conference_with_audio_only_participant), + TEST_NO_TAG("Create simple STUN+ICE conference with audio only participant", + LinphoneTest::create_simple_stun_ice_conference_with_audio_only_participant), + TEST_NO_TAG("Create simple STUN+ICE SRTP conference with audio only participant", + LinphoneTest::create_simple_stun_ice_srtp_conference_with_audio_only_participant), + TEST_ONE_TAG("Abort call to ICE conference", + LinphoneTest::abort_call_to_ice_conference, + "LeaksMemory") /* because of aborted calls*/ +}; + +static test_t local_conference_inpromptu_conference_tests[] = { + TEST_NO_TAG("Create simple dial out conference", LinphoneTest::create_simple_conference_dial_out), + TEST_NO_TAG("Create simple dial out conference and ICS sent", + LinphoneTest::create_simple_conference_dial_out_and_ics), + TEST_NO_TAG("Create simple dial out conference with late participant addition", + LinphoneTest::create_simple_conference_dial_out_with_late_participant_addition), + TEST_NO_TAG("Create simple dial out conference with many late participant additions", + LinphoneTest::create_simple_conference_dial_out_with_many_late_participant_additions), + TEST_NO_TAG("Create simple conference by merging calls", LinphoneTest::create_simple_conference_merging_calls), + TEST_ONE_TAG("Create simple conference by merging calls with video toggling", + LinphoneTest::create_simple_conference_merging_calls_with_video_toggling, + "LeaksMemory"), /* because of aborted calls*/ + TEST_ONE_TAG("Create simple ICE conference by merging calls", + LinphoneTest::create_simple_ice_conference_merging_calls, + "LeaksMemory"), /* because of aborted calls*/ + TEST_NO_TAG("Create dial out conference with active call", + LinphoneTest::create_dial_out_conference_with_active_call), + TEST_NO_TAG("Organizer creates 2 dialout conferences", LinphoneTest::organizer_creates_two_dialout_conferences), + TEST_NO_TAG("2 overlapping dialout conferences from different organizers", + LinphoneTest::two_overlapping_dialout_conferences_from_different_organizers)}; + +static test_t local_conference_inpromptu_mismatch_conference_tests[] = { + TEST_NO_TAG("Create simple dial out conference with calls declined", + LinphoneTest::create_simple_conference_dial_out_with_calls_declined), + TEST_NO_TAG("Create simple dial out conference with some calls declined", + LinphoneTest::create_simple_conference_dial_out_with_some_calls_declined), + TEST_NO_TAG("Create simple dial out conference with some calls busy", + LinphoneTest::create_simple_conference_dial_out_with_some_calls_busy), + TEST_NO_TAG("Create simple dial out conference with participant codec mismatch", + LinphoneTest::create_simple_conference_dial_out_participant_codec_mismatch), + TEST_NO_TAG("Create simple dial out conference with organizer codec mismatch", + LinphoneTest::create_simple_conference_dial_out_organizer_codec_mismatch), + TEST_NO_TAG("Create simple dial out conference with video not initiated", + LinphoneTest::create_simple_conference_dial_out_with_video_not_initiated), + TEST_NO_TAG("Create simple dial out conference with video not accepted", + LinphoneTest::create_simple_conference_dial_out_with_video_not_accepted), + TEST_NO_TAG("Simple dial out conference with no payloads", + LinphoneTest::simple_dial_out_conference_with_no_payloads)}; + +test_suite_t local_conference_test_suite_chat = {"Local conference tester (Chat)", + NULL, + NULL, + liblinphone_tester_before_each, + liblinphone_tester_after_each, + sizeof(local_conference_chat_tests) / + sizeof(local_conference_chat_tests[0]), + local_conference_chat_tests}; + +test_suite_t local_conference_test_suite_chat_error = {"Local conference tester (Chat error)", + NULL, + NULL, + liblinphone_tester_before_each, + liblinphone_tester_after_each, + sizeof(local_conference_chat_error_tests) / + sizeof(local_conference_chat_error_tests[0]), + local_conference_chat_error_tests}; + +test_suite_t local_conference_test_suite_chat_imdn = {"Local conference tester (Chat IMDN)", + NULL, + NULL, + liblinphone_tester_before_each, + liblinphone_tester_after_each, + sizeof(local_conference_chat_imdn_tests) / + sizeof(local_conference_chat_imdn_tests[0]), + local_conference_chat_imdn_tests}; + +test_suite_t local_conference_test_suite_ephemeral_chat = {"Local conference tester (Ephemeral Chat)", + NULL, + NULL, + liblinphone_tester_before_each, + liblinphone_tester_after_each, + sizeof(local_conference_ephemeral_chat_tests) / + sizeof(local_conference_ephemeral_chat_tests[0]), + local_conference_ephemeral_chat_tests}; + +test_suite_t local_conference_test_suite_secure_chat = {"Local conference tester (Secure Chat)", + NULL, + NULL, + liblinphone_tester_before_each, + liblinphone_tester_after_each, + sizeof(local_conference_secure_chat_tests) / + sizeof(local_conference_secure_chat_tests[0]), + local_conference_secure_chat_tests}; + +test_suite_t local_conference_test_suite_conference_edition = {"Local conference tester (Conference edition)", + NULL, + NULL, + liblinphone_tester_before_each, + liblinphone_tester_after_each, + sizeof(local_conference_conference_edition_tests) / + sizeof(local_conference_conference_edition_tests[0]), + local_conference_conference_edition_tests}; + +test_suite_t local_conference_test_suite_scheduled_conference_basic = { + "Local conference tester (Scheduled Conference Basic)", + NULL, + NULL, + liblinphone_tester_before_each, + liblinphone_tester_after_each, + sizeof(local_conference_scheduled_conference_basic_tests) / + sizeof(local_conference_scheduled_conference_basic_tests[0]), + local_conference_scheduled_conference_basic_tests}; + +test_suite_t local_conference_test_suite_scheduled_conference_advanced = { + "Local conference tester (Scheduled Conference Advanced)", + NULL, + NULL, + liblinphone_tester_before_each, + liblinphone_tester_after_each, + sizeof(local_conference_scheduled_conference_advanced_tests) / + sizeof(local_conference_scheduled_conference_advanced_tests[0]), + local_conference_scheduled_conference_advanced_tests}; + +test_suite_t local_conference_test_suite_scheduled_ice_conference = { + "Local conference tester (Scheduled ICE Conference)", + NULL, + NULL, + liblinphone_tester_before_each, + liblinphone_tester_after_each, + sizeof(local_conference_scheduled_ice_conference_tests) / + sizeof(local_conference_scheduled_ice_conference_tests[0]), + local_conference_scheduled_ice_conference_tests}; + +test_suite_t local_conference_test_suite_inpromptu_conference = { + "Local conference tester (Inpromptu Conference)", + NULL, + NULL, + liblinphone_tester_before_each, + liblinphone_tester_after_each, + sizeof(local_conference_inpromptu_conference_tests) / sizeof(local_conference_inpromptu_conference_tests[0]), + local_conference_inpromptu_conference_tests}; + +test_suite_t local_conference_test_suite_inpromptu_mismatch_conference = { + "Local conference tester (Inpromptu Conference with mismatch)", + NULL, + NULL, + liblinphone_tester_before_each, + liblinphone_tester_after_each, + sizeof(local_conference_inpromptu_mismatch_conference_tests) / + sizeof(local_conference_inpromptu_mismatch_conference_tests[0]), + local_conference_inpromptu_mismatch_conference_tests}; + +#if __clang__ || ((__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4) +#pragma GCC diagnostic pop +#endif diff --git a/tester/local_conference_tester_functions.cpp b/tester/local_conference_tester_functions.cpp index 25cfe44cffb06800c4e6f7c1df3126c6d3cc7bbc..1148da05d017c9a66f8e5486d7c7c77b07d31c7c 100644 --- a/tester/local_conference_tester_functions.cpp +++ b/tester/local_conference_tester_functions.cpp @@ -23,9 +23,6 @@ #include "conference/participant.h" #include "shared_tester_functions.h" -#if __clang__ || ((__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4) -#pragma GCC diagnostic push -#endif #ifdef _MSC_VER #pragma warning(disable : 4996) #endif @@ -1592,9 +1589,8 @@ void check_conference_info(LinphoneCoreManager *mgr, BC_ASSERT_TRUE(linphone_address_weak_equal(organizer->identity, linphone_conference_info_get_organizer(info))); BC_ASSERT_TRUE(linphone_address_weak_equal(confAddr, linphone_conference_info_get_uri(info))); - bctbx_list_t *info_participants = linphone_conference_info_get_participants(info); + const bctbx_list_t *info_participants = linphone_conference_info_get_participants(info); BC_ASSERT_EQUAL(bctbx_list_size(info_participants), no_members, size_t, "%zu"); - bctbx_list_free(info_participants); BC_ASSERT_EQUAL((int)linphone_conference_info_get_security_level(info), (int)security_level, int, "%0d"); if (start_time > 0) { @@ -1638,20 +1634,21 @@ static bool have_common_audio_payload(LinphoneCoreManager *mgr1, LinphoneCoreMan return found; } -LinphoneAddress *create_conference_on_server(Focus &focus, - ClientConference &organizer, - std::list<LinphoneCoreManager *> requested_participants, - time_t start_time, - time_t end_time, - const char *subject, - const char *description, - bool_t send_ics, - LinphoneConferenceSecurityLevel security_level) { +LinphoneAddress * +create_conference_on_server(Focus &focus, + ClientConference &organizer, + std::map<LinphoneCoreManager *, LinphoneParticipantRole> requested_participants, + time_t start_time, + time_t end_time, + const char *subject, + const char *description, + bool_t send_ics, + LinphoneConferenceSecurityLevel security_level) { bctbx_list_t *coresList = bctbx_list_append(NULL, focus.getLc()); coresList = bctbx_list_append(coresList, organizer.getLc()); std::vector<stats> participant_stats; std::map<LinphoneCoreManager *, LinphoneCall *> previous_calls; - bctbx_list_t *participant_addresses = NULL; + bctbx_list_t *participant_infos = NULL; std::list<LinphoneCoreManager *> participants; const LinphoneConferenceInfo *updated_conf_info = NULL; bool focus_organizer_common_payload = have_common_audio_payload(organizer.getCMgr(), focus.getCMgr()); @@ -1662,14 +1659,16 @@ LinphoneAddress *create_conference_on_server(Focus &focus, size_t participant_expected_participant_number = 0; char *uid = NULL; LinphoneConferenceInfo *info = NULL; - for (auto &p : requested_participants) { - participant_addresses = bctbx_list_append(participant_addresses, p->identity); - if (p == organizer.getCMgr()) { + for (const auto &[mgr, role] : requested_participants) { + LinphoneParticipantInfo *participant_info = linphone_participant_info_new(mgr->identity); + linphone_participant_info_set_role(participant_info, role); + participant_infos = bctbx_list_append(participant_infos, participant_info); + if (mgr == organizer.getCMgr()) { found_me = true; } else { - coresList = bctbx_list_append(coresList, p->lc); - participant_stats.push_back(p->stat); - participants.push_back(p); + coresList = bctbx_list_append(coresList, mgr->lc); + participant_stats.push_back(mgr->stat); + participants.push_back(mgr); } } @@ -1696,7 +1695,7 @@ LinphoneAddress *create_conference_on_server(Focus &focus, linphone_account_get_params(default_account))) : linphone_address_new(linphone_core_get_identity(organizer.getLc())); linphone_conference_info_set_organizer(conf_info, organizer_address); - linphone_conference_info_set_participants(conf_info, participant_addresses); + linphone_conference_info_set_participants_2(conf_info, participant_infos); if ((end_time >= 0) && (start_time >= 0) && (end_time > start_time)) { linphone_conference_info_set_duration( conf_info, (int)((end_time - start_time) / 60)); // duration is expected to be set in minutes @@ -1882,17 +1881,15 @@ LinphoneAddress *create_conference_on_server(Focus &focus, linphone_conference_info_get_uri(conf_info_in_db), linphone_conference_info_get_uri(conf_info_from_original_content))); - bctbx_list_t *ics_participants = + const bctbx_list_t *ics_participants = linphone_conference_info_get_participants(conf_info_from_original_content); BC_ASSERT_EQUAL(bctbx_list_size(ics_participants), participant_expected_participant_number, size_t, "%zu"); - bctbx_list_free(ics_participants); - bctbx_list_t *ics_participants_db = + const bctbx_list_t *ics_participants_db = linphone_conference_info_get_participants(conf_info_in_db); BC_ASSERT_EQUAL(bctbx_list_size(ics_participants_db), participant_expected_participant_number, size_t, "%zu"); - bctbx_list_free(ics_participants_db); if (start_time > 0) { BC_ASSERT_EQUAL( @@ -1963,7 +1960,7 @@ LinphoneAddress *create_conference_on_server(Focus &focus, if (!BC_ASSERT_PTR_NOT_NULL(conf_info_in_db)) { goto end; } - // Encryption is None because we haven't received yet the NOTIFY full stae with this information + // Encryption is None because we haven't received yet the NOTIFY full state with this information check_conference_info(mgr, conference_address, organizer.getCMgr(), participant_expected_participant_number, start_time, ((start_time > 0) && (end_time > 0)) ? (int)(end_time - start_time) / 60 : 0, @@ -2015,7 +2012,7 @@ end: if (organizer_address) linphone_address_unref(organizer_address); linphone_conference_scheduler_unref(conference_scheduler); bctbx_list_free(coresList); - bctbx_list_free(participant_addresses); + bctbx_list_free_with_data(participant_infos, (bctbx_list_free_func)linphone_participant_info_unref); return conference_address; } @@ -2064,24 +2061,44 @@ size_t compute_no_video_streams(bool_t enable_video, LinphoneCall *call, Linphon return nb_video_streams; } -size_t compute_no_audio_streams(LinphoneConference *conference) { +size_t compute_no_audio_streams(LinphoneCall *call, LinphoneConference *conference) { size_t nb_audio_streams = 0; const LinphoneConferenceParams *conference_params = linphone_conference_get_current_params(conference); if (linphone_conference_params_get_security_level(conference_params) == LinphoneConferenceSecurityLevelEndToEnd) { - bctbx_list_t *devices = linphone_conference_get_participant_device_list(conference); - nb_audio_streams = bctbx_list_size(devices); - bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); + const LinphoneAddress *remote_address = linphone_call_get_remote_address(call); + bctbx_list_t *participants = linphone_conference_get_participant_list(conference); + for (bctbx_list_t *itp = participants; itp; itp = bctbx_list_next(itp)) { + LinphoneParticipant *participant = (LinphoneParticipant *)bctbx_list_get_data(itp); + LinphoneParticipantRole role = linphone_participant_get_role(participant); + if (role == LinphoneParticipantRoleSpeaker) { + bctbx_list_t *devices = linphone_participant_get_devices(participant); + for (bctbx_list_t *itd = devices; itd; itd = bctbx_list_next(itd)) { + LinphoneParticipantDevice *device = (LinphoneParticipantDevice *)bctbx_list_get_data(itd); + if (linphone_participant_device_get_stream_availability(device, LinphoneStreamTypeAudio)) { + nb_audio_streams++; + } + } + bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); + } else if (linphone_address_weak_equal(linphone_participant_get_address(participant), remote_address)) { + // Add an audio stream anyway if the participant holding the call is a listener + nb_audio_streams++; + } + } + bctbx_list_free_with_data(participants, (void (*)(void *))linphone_participant_unref); + if (!linphone_core_conference_server_enabled(linphone_conference_get_core(conference))) { + // Add own audio stream if the core holding the conference is not a server + nb_audio_streams++; + } } else { nb_audio_streams = 1; } - return nb_audio_streams; } void wait_for_conference_streams(std::initializer_list<std::reference_wrapper<CoreManager>> coreMgrs, std::list<LinphoneCoreManager *> conferenceMgrs, LinphoneCoreManager *focus, - std::list<LinphoneCoreManager *> members, + std::map<LinphoneCoreManager *, LinphoneParticipantRole> members, const LinphoneAddress *confAddr, bool_t enable_video) { for (auto mgr : conferenceMgrs) { @@ -2092,14 +2109,17 @@ void wait_for_conference_streams(std::initializer_list<std::reference_wrapper<Co std::list<LinphoneCall *> calls; bool video_check = false; + bool participant_check = false; bool device_present = false; bool call_ok = true; + bool audio_direction_ok = true; if (mgr == focus) { - for (auto m : members) { - LinphoneCall *pcall = linphone_core_get_call_by_remote_address2(m->lc, confAddr); + for (const auto &m : members) { + LinphoneCoreManager *mMgr = m.first; + LinphoneCall *pcall = linphone_core_get_call_by_remote_address2(mMgr->lc, confAddr); call_ok &= (pcall != nullptr); - LinphoneCall *call = linphone_core_get_call_by_remote_address2(mgr->lc, m->identity); + LinphoneCall *call = linphone_core_get_call_by_remote_address2(mgr->lc, mMgr->identity); call_ok &= (call != nullptr); if (call) { calls.push_back(call); @@ -2120,7 +2140,7 @@ void wait_for_conference_streams(std::initializer_list<std::reference_wrapper<Co LinphoneConference *conference = linphone_core_search_conference_2(mgr->lc, confAddr); for (auto call : calls) { if (call) { - size_t nb_audio_streams = compute_no_audio_streams(conference); + size_t nb_audio_streams = compute_no_audio_streams(call, conference); size_t nb_video_streams = compute_no_video_streams(enable_video, call, conference); const SalMediaDescription *call_result_desc = _linphone_call_get_result_desc(call); call_ok &= ((call_result_desc->nbActiveStreamsOfType(SalAudio) == nb_audio_streams) && @@ -2133,24 +2153,56 @@ void wait_for_conference_streams(std::initializer_list<std::reference_wrapper<Co if (conference) { bctbx_list_t *devices = linphone_conference_get_participant_device_list(conference); video_check = (bctbx_list_size(devices) > 0); + participant_check = (bctbx_list_size(devices) > 0); device_present = (bctbx_list_size(devices) > 0); LinphoneCall *call = NULL; for (bctbx_list_t *itd = devices; itd; itd = bctbx_list_next(itd)) { LinphoneParticipantDevice *d = (LinphoneParticipantDevice *)bctbx_list_get_data(itd); bool_t found = FALSE; - for (const auto &p : members) { - found |= linphone_address_weak_equal(p->identity, linphone_participant_device_get_address(d)); + const LinphoneAddress *device_address = linphone_participant_device_get_address(d); + for (const auto &m : members) { + LinphoneCoreManager *mMgr = m.first; + found |= linphone_address_weak_equal(mMgr->identity, device_address); } if (mgr == focus) { - const LinphoneAddress *address = linphone_participant_device_get_address(d); - call = linphone_core_get_call_by_remote_address2(mgr->lc, address); + call = linphone_core_get_call_by_remote_address2(mgr->lc, device_address); } else { if (calls.front()) { call = calls.front(); } } + bool_t is_me = linphone_conference_is_me(conference, device_address); + LinphoneParticipant *p = is_me ? linphone_conference_get_me(conference) + : linphone_conference_find_participant(conference, device_address); + participant_check &= (p != nullptr); + LinphoneMediaDirection expected_audio_direction = LinphoneMediaDirectionInactive; + if (p) { + LinphoneParticipantRole role = linphone_participant_get_role(p); + expected_audio_direction = + ((role == LinphoneParticipantRoleSpeaker) ? LinphoneMediaDirectionSendRecv + : LinphoneMediaDirectionRecvOnly); + LinphoneMediaDirection audio_dir = + linphone_participant_device_get_stream_capability(d, LinphoneStreamTypeAudio); + audio_direction_ok &= (audio_dir == expected_audio_direction); + } else { + audio_direction_ok = false; + } + LinphoneParticipantDeviceState expected_state = LinphoneParticipantDeviceStateJoining; + if (found) { + expected_state = LinphoneParticipantDeviceStatePresent; + } else { + expected_state = LinphoneParticipantDeviceStateLeft; + } + device_present &= (linphone_participant_device_get_state(d) == expected_state); if (call) { + if (is_me) { + const LinphoneCallParams *call_current_params = linphone_call_get_current_params(call); + LinphoneMediaDirection call_audio_direction = + linphone_call_params_get_audio_direction(call_current_params); + audio_direction_ok &= (call_audio_direction == expected_audio_direction); + } + const LinphoneCallParams *call_params = linphone_call_get_params(call); bool_t call_video_enabled = linphone_call_params_video_enabled(call_params); if (enable_video && ((mgr == focus) || call_video_enabled)) { @@ -2165,20 +2217,23 @@ void wait_for_conference_streams(std::initializer_list<std::reference_wrapper<Co video_check &= !linphone_participant_device_get_stream_availability(d, LinphoneStreamTypeVideo); } - LinphoneParticipantDeviceState expected_state = LinphoneParticipantDeviceStateJoining; - if (found) { - expected_state = LinphoneParticipantDeviceStatePresent; - } else { - expected_state = LinphoneParticipantDeviceStateLeft; - } - device_present &= (linphone_participant_device_get_state(d) == expected_state); } } if (devices) { bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); } + + for (const auto &[mMgr, role] : members) { + LinphoneParticipant *p = linphone_conference_is_me(conference, mMgr->identity) + ? linphone_conference_get_me(conference) + : linphone_conference_find_participant(conference, mMgr->identity); + participant_check &= (p != nullptr); + if (p) { + participant_check &= (linphone_participant_get_role(p) == role); + } + } } - return video_check && device_present && call_ok; + return audio_direction_ok && video_check && device_present && call_ok && participant_check; })); } } @@ -2565,7 +2620,14 @@ void create_conference_base(time_t start_time, const char *initialSubject = "Test characters: ^ :) ¤ çà @"; const char *description = "Paris Baker"; - LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participants, start_time, end_time, + std::map<LinphoneCoreManager *, LinphoneParticipantRole> participantList; + LinphoneParticipantRole role = LinphoneParticipantRoleListener; + for (auto &p : participants) { + participantList.insert(std::make_pair(p, role)); + role = (role == LinphoneParticipantRoleSpeaker) ? LinphoneParticipantRoleListener + : LinphoneParticipantRoleSpeaker; + } + LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, initialSubject, description, TRUE, security_level); BC_ASSERT_PTR_NOT_NULL(confAddr); char *conference_address_str = (confAddr) ? linphone_address_as_string(confAddr) : ms_strdup("<unknown>"); @@ -2682,8 +2744,23 @@ void create_conference_base(time_t start_time, focus_stat.number_of_participant_devices_joined + 3, liblinphone_tester_sip_timeout)); + std::map<LinphoneCoreManager *, LinphoneParticipantRole> memberList; + for (const auto &member : members) { + try { + const auto &participantInfo = participantList.at(member); + memberList.insert(std::make_pair(member, participantInfo)); + } catch (std::out_of_range &) { + if (member == marie.getCMgr()) { + memberList.insert(std::make_pair(marie.getCMgr(), LinphoneParticipantRoleSpeaker)); + } else { + ms_fatal("Unable to find active participant %s in the participant list", + linphone_core_get_identity(member->lc)); + } + } + } + wait_for_conference_streams({focus, marie, pauline, laure, michelle, berthe}, conferenceMgrs, focus.getCMgr(), - members, confAddr, enable_video); + memberList, confAddr, enable_video); LinphoneConference *fconference = linphone_core_search_conference_2(focus.getLc(), confAddr); BC_ASSERT_PTR_NOT_NULL(fconference); @@ -2811,9 +2888,8 @@ void create_conference_base(time_t start_time, LinphoneCall *pcall = linphone_core_get_call_by_remote_address2(mgr->lc, confAddr); BC_ASSERT_PTR_NOT_NULL(pcall); if (pcall) { - no_streams_audio = static_cast<int>(compute_no_audio_streams(pconference)); - no_active_streams_video = - static_cast<int>(compute_no_video_streams(enabled, pcall, pconference)); + no_streams_audio = compute_no_audio_streams(pcall, pconference); + no_active_streams_video = compute_no_video_streams(enabled, pcall, pconference); video_negotiated = enabled && (no_active_streams_video > 0); if (!enable_ice) { _linphone_call_check_max_nb_streams(pcall, no_streams_audio, no_streams_video, @@ -3076,9 +3152,8 @@ void create_conference_base(time_t start_time, LinphoneCall *pcall = linphone_core_get_call_by_remote_address2(mgr->lc, confAddr); BC_ASSERT_PTR_NOT_NULL(pcall); if (pcall) { - no_streams_audio = static_cast<int>(compute_no_audio_streams(pconference)); - no_active_streams_video = - static_cast<int>(compute_no_video_streams(enabled, pcall, pconference)); + no_streams_audio = compute_no_audio_streams(pcall, pconference); + no_active_streams_video = compute_no_video_streams(enabled, pcall, pconference); _linphone_call_check_max_nb_streams(pcall, no_streams_audio, no_streams_video, no_streams_text); _linphone_call_check_nb_active_streams(pcall, no_streams_audio, no_active_streams_video, @@ -3707,8 +3782,25 @@ void create_conference_base(time_t start_time, liblinphone_tester_sip_timeout)); } + std::map<LinphoneCoreManager *, LinphoneParticipantRole> memberList; + for (const auto &member : members) { + try { + const auto &participantInfo = participantList.at(member); + memberList.insert(std::make_pair(member, participantInfo)); + } catch (std::out_of_range &) { + if (member == marie.getCMgr()) { + memberList.insert(std::make_pair(member, LinphoneParticipantRoleSpeaker)); + } else if (member == michelle.getCMgr()) { + memberList.insert(std::make_pair(member, LinphoneParticipantRoleListener)); + } else { + ms_fatal("Unable to find active participant %s in the participant list", + linphone_core_get_identity(member->lc)); + } + } + } wait_for_conference_streams({focus, marie, pauline, laure, michelle, berthe}, conferenceMgrs, - focus.getCMgr(), members, confAddr, enable_video); + focus.getCMgr(), memberList, confAddr, enable_video); + } else if (participant_list_type == LinphoneConferenceParticipantListTypeClosed) { extra_participants = 0; @@ -3776,25 +3868,23 @@ void create_conference_base(time_t start_time, no_participants = no_local_participants + extra_participants; BC_ASSERT_FALSE(linphone_conference_is_in(pconference)); } else { - // Substracting one because we conference server is not in the conference + // Substracting one because we conference server is not in the + // conference no_participants = (no_local_participants - 1) + extra_participants; BC_ASSERT_TRUE(linphone_conference_is_in(pconference)); LinphoneCall *pcall = linphone_core_get_call_by_remote_address2(mgr->lc, confAddr); BC_ASSERT_PTR_NOT_NULL(pcall); - size_t no_streams_audio = - (security_level == LinphoneConferenceSecurityLevelEndToEnd) - ? ((participant_list_type == LinphoneConferenceParticipantListTypeOpen) ? 4 : 3) - : 1; + size_t no_streams_audio = 0; size_t no_streams_video = 0; size_t no_active_streams_video = 0; size_t no_streams_text = 0; if (pcall) { const LinphoneCallParams *call_cparams = linphone_call_get_current_params(pcall); const bool_t enabled = linphone_call_params_video_enabled(call_cparams); - no_active_streams_video = - static_cast<int>(compute_no_video_streams(enabled, pcall, pconference)); + no_streams_audio = compute_no_audio_streams(pcall, pconference); + no_active_streams_video = compute_no_video_streams(enabled, pcall, pconference); no_streams_video = ((security_level == LinphoneConferenceSecurityLevelEndToEnd) && (layout == LinphoneConferenceLayoutActiveSpeaker)) ? 6 @@ -4124,24 +4214,24 @@ void create_conference_base(time_t start_time, LinphoneCall *pcall = linphone_core_get_call_by_remote_address2(mgr->lc, confAddr); BC_ASSERT_PTR_NOT_NULL(pcall); - size_t no_streams_audio = - (security_level == LinphoneConferenceSecurityLevelEndToEnd) - ? ((participant_list_type == LinphoneConferenceParticipantListTypeOpen) ? 4 : 3) - : 1; + size_t no_streams_audio = 0; + size_t no_max_streams_audio = 0; size_t no_streams_video = 0; size_t no_active_streams_video = 0; size_t no_streams_text = 0; if (pcall) { const LinphoneCallParams *call_cparams = linphone_call_get_current_params(pcall); const bool_t enabled = linphone_call_params_video_enabled(call_cparams); - no_active_streams_video = - static_cast<int>(compute_no_video_streams(enabled, pcall, pconference)); + no_streams_audio = compute_no_audio_streams(pcall, pconference); + no_active_streams_video = compute_no_video_streams(enabled, pcall, pconference); no_streams_video = ((security_level == LinphoneConferenceSecurityLevelEndToEnd) && (layout == LinphoneConferenceLayoutActiveSpeaker)) ? 6 : 4; + no_max_streams_audio = + (security_level == LinphoneConferenceSecurityLevelEndToEnd) ? 3 : 1; - _linphone_call_check_max_nb_streams(pcall, no_streams_audio, no_streams_video, + _linphone_call_check_max_nb_streams(pcall, no_max_streams_audio, no_streams_video, no_streams_text); _linphone_call_check_nb_active_streams(pcall, no_streams_audio, no_active_streams_video, no_streams_text); @@ -4151,13 +4241,14 @@ void create_conference_base(time_t start_time, linphone_core_get_call_by_remote_address2(focus.getLc(), mgr->identity); BC_ASSERT_PTR_NOT_NULL(fcall); if (fcall) { - _linphone_call_check_max_nb_streams(fcall, no_streams_audio, no_streams_video, + _linphone_call_check_max_nb_streams(fcall, no_max_streams_audio, no_streams_video, no_streams_text); _linphone_call_check_nb_active_streams(fcall, no_streams_audio, no_active_streams_video, no_streams_text); } - // Substracting one because we conference server is not in the conference + // Substracting one because we conference server is not in the + // conference no_participants = (no_local_participants - 1) + extra_participants; BC_ASSERT_TRUE(linphone_conference_is_in(pconference)); } @@ -4660,7 +4751,14 @@ void create_conference_with_late_participant_addition_base(time_t start_time, const char *initialSubject = "Weekly recap"; const char *description = "What happened in the past week"; - LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participants, start_time, end_time, + std::map<LinphoneCoreManager *, LinphoneParticipantRole> participantList; + LinphoneParticipantRole role = LinphoneParticipantRoleSpeaker; + for (auto &p : participants) { + participantList.insert(std::make_pair(p, role)); + role = (role == LinphoneParticipantRoleSpeaker) ? LinphoneParticipantRoleListener + : LinphoneParticipantRoleSpeaker; + } + LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, initialSubject, description, TRUE, security_level); BC_ASSERT_PTR_NOT_NULL(confAddr); @@ -4770,11 +4868,10 @@ void create_conference_with_late_participant_addition_base(time_t start_time, BC_ASSERT_TRUE( linphone_address_weak_equal(linphone_conference_info_get_uri(call_log_info), confAddr)); - bctbx_list_t *info_participants = linphone_conference_info_get_participants(call_log_info); + const bctbx_list_t *info_participants = linphone_conference_info_get_participants(call_log_info); // Original participants + Marie who joined the conference BC_ASSERT_EQUAL(bctbx_list_size(info_participants), static_cast<int>(members.size()), size_t, "%zu"); - bctbx_list_free(info_participants); if (start_time > 0) { BC_ASSERT_EQUAL((long long)linphone_conference_info_get_date_time(call_log_info), start_time, @@ -4835,8 +4932,22 @@ void create_conference_with_late_participant_addition_base(time_t start_time, LinphoneConference *fconference = linphone_core_search_conference_2(focus.getLc(), confAddr); BC_ASSERT_PTR_NOT_NULL(fconference); + std::map<LinphoneCoreManager *, LinphoneParticipantRole> memberList; + for (const auto &member : members) { + try { + const auto &participantInfo = participantList.at(member); + memberList.insert(std::make_pair(member, participantInfo)); + } catch (std::out_of_range &) { + if (member == marie.getCMgr()) { + memberList.insert(std::make_pair(marie.getCMgr(), LinphoneParticipantRoleSpeaker)); + } else { + ms_fatal("Unable to find active participant %s in the participant list", + linphone_core_get_identity(member->lc)); + } + } + } wait_for_conference_streams({focus, marie, pauline, laure, michelle, berthe}, conferenceMgrs, focus.getCMgr(), - members, confAddr, TRUE); + memberList, confAddr, TRUE); // wait bit more to detect side effect if any CoreManagerAssert({focus, marie, pauline, laure, michelle, berthe}).waitUntil(chrono::seconds(15), [] { @@ -5003,6 +5114,7 @@ void create_conference_with_late_participant_addition_base(time_t start_time, conferenceMgrs.push_back(berthe.getCMgr()); members.push_back(berthe.getCMgr()); + memberList.insert(std::make_pair(berthe.getCMgr(), LinphoneParticipantRoleListener)); BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallStreamsRunning, focus_stat.number_of_LinphoneCallStreamsRunning + 2, @@ -5031,6 +5143,7 @@ void create_conference_with_late_participant_addition_base(time_t start_time, conferenceMgrs.push_back(michelle.getCMgr()); members.push_back(michelle.getCMgr()); + memberList.insert(std::make_pair(michelle.getCMgr(), LinphoneParticipantRoleListener)); BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallStreamsRunning, focus_stat.number_of_LinphoneCallStreamsRunning + 4, @@ -5159,7 +5272,7 @@ void create_conference_with_late_participant_addition_base(time_t start_time, } wait_for_conference_streams({focus, marie, pauline, laure, michelle, berthe}, conferenceMgrs, focus.getCMgr(), - members, confAddr, TRUE); + memberList, confAddr, TRUE); LinphoneCall *pauline_call = linphone_core_get_call_by_remote_address2(pauline.getLc(), confAddr); BC_ASSERT_PTR_NOT_NULL(pauline_call); @@ -5383,7 +5496,14 @@ void two_overlapping_conferences_base(bool_t same_organizer, bool_t dialout) { time_t end_time1 = (start_time1 + 600); const char *subject1 = "Colleagues"; const char *description1 = NULL; - LinphoneAddress *confAddr1 = create_conference_on_server(focus, marie, participants1, start_time1, end_time1, + std::map<LinphoneCoreManager *, LinphoneParticipantRole> participantList1; + LinphoneParticipantRole role = LinphoneParticipantRoleSpeaker; + for (auto &p : participants1) { + participantList1.insert(std::make_pair(p, role)); + role = (role == LinphoneParticipantRoleSpeaker) ? LinphoneParticipantRoleListener + : LinphoneParticipantRoleSpeaker; + } + LinphoneAddress *confAddr1 = create_conference_on_server(focus, marie, participantList1, start_time1, end_time1, subject1, description1, TRUE, security_level); BC_ASSERT_PTR_NOT_NULL(confAddr1); char *conference1_address_str = (confAddr1) ? linphone_address_as_string(confAddr1) : ms_strdup("<unknown>"); @@ -5520,35 +5640,41 @@ void two_overlapping_conferences_base(bool_t same_organizer, bool_t dialout) { time_t end_time2 = (dialout) ? -1 : (start_time2 + 600); const char *subject2 = "All Hands Q3 FY2021 - Attendance Mandatory"; const char *description2 = "Financial result - Internal only - Strictly confidential"; - LinphoneAddress *confAddr2 = NULL; std::list<LinphoneCoreManager *> participants2{pauline.getCMgr()}; std::list<LinphoneCoreManager *> mgr_having_two_confs{}; std::list<LinphoneCoreManager *> mgr_in_conf2{focus.getCMgr(), michelle.getCMgr()}; + ClientConference &confCreator2 = (same_organizer) ? marie : michelle; if (same_organizer) { participants2.push_back(michelle.getCMgr()); mgr_having_two_confs.push_back(marie.getCMgr()); - confAddr2 = create_conference_on_server(focus, marie, participants2, start_time2, end_time2, subject2, - description2, TRUE, security_level); - - // Chat room creation to send ICS - BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateCreated, 3, - liblinphone_tester_sip_timeout)); - } else { participants2.push_back(marie.getCMgr()); if (!dialout) { mgr_having_two_confs.push_back(marie.getCMgr()); mgr_in_conf2.push_back(marie.getCMgr()); } - confAddr2 = create_conference_on_server(focus, michelle, participants2, start_time2, end_time2, subject2, - description2, TRUE, security_level); + } + std::map<LinphoneCoreManager *, LinphoneParticipantRole> participantList2; + for (auto &p : participants2) { + role = (role == LinphoneParticipantRoleSpeaker) ? LinphoneParticipantRoleListener + : LinphoneParticipantRoleSpeaker; + participantList2.insert(std::make_pair(p, role)); + } - // Chat room creation to send ICS + LinphoneAddress *confAddr2 = + create_conference_on_server(focus, confCreator2, participantList2, start_time2, end_time2, subject2, + description2, TRUE, security_level); + BC_ASSERT_PTR_NOT_NULL(confAddr2); + char *conference2_address_str = (confAddr2) ? linphone_address_as_string(confAddr2) : ms_strdup("<unknown>"); + + // Chat room creation to send ICS + if (same_organizer) { + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateCreated, 3, + liblinphone_tester_sip_timeout)); + } else { BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_LinphoneConferenceStateCreated, 2, liblinphone_tester_sip_timeout)); } - BC_ASSERT_PTR_NOT_NULL(confAddr2); - char *conference2_address_str = (confAddr2) ? linphone_address_as_string(confAddr2) : ms_strdup("<unknown>"); if (confAddr2) { if (dialout) { @@ -6264,7 +6390,14 @@ void create_one_participant_conference_toggle_video_base(LinphoneConferenceLayou const char *description = "- <F2><F3>\n\\çà "; LinphoneConferenceSecurityLevel security_level = LinphoneConferenceSecurityLevelNone; - LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participants, start_time, end_time, + std::map<LinphoneCoreManager *, LinphoneParticipantRole> participantList; + LinphoneParticipantRole role = LinphoneParticipantRoleSpeaker; + for (auto &p : participants) { + participantList.insert(std::make_pair(p, role)); + role = (role == LinphoneParticipantRoleSpeaker) ? LinphoneParticipantRoleListener + : LinphoneParticipantRoleSpeaker; + } + LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, initialSubject, description, TRUE, security_level); BC_ASSERT_PTR_NOT_NULL(confAddr); @@ -6932,7 +7065,15 @@ void create_conference_with_active_call_base(bool_t dialout) { time_t start_time = (dialout) ? -1 : (ms_time(NULL) + 10); bool_t send_ics = TRUE; - LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participants, start_time, -1, + std::map<LinphoneCoreManager *, LinphoneParticipantRole> participantList; + LinphoneParticipantRole role = LinphoneParticipantRoleSpeaker; + for (auto &p : participants) { + participantList.insert(std::make_pair(p, role)); + role = (role == LinphoneParticipantRoleSpeaker) ? LinphoneParticipantRoleListener + : LinphoneParticipantRoleSpeaker; + } + participantList.insert(std::make_pair(marie.getCMgr(), LinphoneParticipantRoleListener)); + LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, -1, initialSubject, description, send_ics, security_level); BC_ASSERT_PTR_NOT_NULL(confAddr); @@ -7032,9 +7173,8 @@ void create_conference_with_active_call_base(bool_t dialout) { linphone_conference_info_get_organizer(info))); BC_ASSERT_TRUE(linphone_address_weak_equal(confAddr, linphone_conference_info_get_uri(info))); - bctbx_list_t *info_participants = linphone_conference_info_get_participants(info); + const bctbx_list_t *info_participants = linphone_conference_info_get_participants(info); BC_ASSERT_EQUAL(bctbx_list_size(info_participants), 4, size_t, "%zu"); - bctbx_list_free(info_participants); BC_ASSERT_NOT_EQUAL((long long)linphone_conference_info_get_date_time(info), 0, long long, "%lld"); @@ -7101,10 +7241,9 @@ void create_conference_with_active_call_base(bool_t dialout) { linphone_conference_info_get_organizer(info))); BC_ASSERT_TRUE(linphone_address_weak_equal(confAddr, linphone_conference_info_get_uri(info))); - bctbx_list_t *info_participants = linphone_conference_info_get_participants(info); + const bctbx_list_t *info_participants = linphone_conference_info_get_participants(info); // Original participants + Marie who joined the conference BC_ASSERT_EQUAL(bctbx_list_size(info_participants), 4, size_t, "%zu"); - bctbx_list_free(info_participants); BC_ASSERT_GREATER_STRICT((long long)linphone_conference_info_get_date_time(info), 0, long long, "%lld"); const int duration_m = linphone_conference_info_get_duration(info); @@ -7134,10 +7273,9 @@ void create_conference_with_active_call_base(bool_t dialout) { BC_ASSERT_TRUE( linphone_address_weak_equal(linphone_conference_info_get_uri(call_log_info), confAddr)); - bctbx_list_t *info_participants = linphone_conference_info_get_participants(call_log_info); + const bctbx_list_t *info_participants = linphone_conference_info_get_participants(call_log_info); // Original participants + Marie who joined the conference BC_ASSERT_EQUAL(bctbx_list_size(info_participants), 4, size_t, "%zu"); - bctbx_list_free(info_participants); BC_ASSERT_GREATER_STRICT((long long)linphone_conference_info_get_date_time(call_log_info), 0, long long, "%lld"); const int duration_m = linphone_conference_info_get_duration(call_log_info); @@ -7196,7 +7334,7 @@ void create_conference_with_active_call_base(bool_t dialout) { for (auto mgr : conferenceMgrs) { // wait bit more to detect side effect if any CoreManagerAssert({focus, marie, pauline, laure, michelle, berthe}) - .waitUntil(chrono::seconds(50), [mgr, &focus, &members, confAddr] { + .waitUntil(chrono::seconds(50), [mgr, &focus, &members, confAddr, &participantList] { size_t nb_audio_streams = 1; size_t nb_video_streams = 0; size_t nb_text_streams = 0; @@ -7225,29 +7363,49 @@ void create_conference_with_active_call_base(bool_t dialout) { for (auto call : calls) { if (call) { const SalMediaDescription *call_result_desc = _linphone_call_get_result_desc(call); - return (call_result_desc->getNbStreams() == - nb_audio_streams + nb_video_streams + nb_text_streams) && - (call_result_desc->nbStreamsOfType(SalAudio) == nb_audio_streams) && - (call_result_desc->nbStreamsOfType(SalVideo) == nb_video_streams) && - (call_result_desc->nbStreamsOfType(SalText) == nb_text_streams) && - (linphone_call_get_state(call) == LinphoneCallStateStreamsRunning); + if (!((call_result_desc->getNbStreams() == + nb_audio_streams + nb_video_streams + nb_text_streams) && + (call_result_desc->nbStreamsOfType(SalAudio) == nb_audio_streams) && + (call_result_desc->nbStreamsOfType(SalVideo) == nb_video_streams) && + (call_result_desc->nbStreamsOfType(SalText) == nb_text_streams) && + (linphone_call_get_state(call) == LinphoneCallStateStreamsRunning))) { + return false; + } } } + bool video_check = true; LinphoneConference *conference = linphone_core_search_conference_2(mgr->lc, confAddr); BC_ASSERT_PTR_NOT_NULL(conference); if (conference) { bctbx_list_t *devices = linphone_conference_get_participant_device_list(conference); for (bctbx_list_t *itd = devices; itd; itd = bctbx_list_next(itd)) { LinphoneParticipantDevice *d = (LinphoneParticipantDevice *)bctbx_list_get_data(itd); - return !linphone_participant_device_get_stream_availability(d, LinphoneStreamTypeVideo); + video_check &= + !linphone_participant_device_get_stream_availability(d, LinphoneStreamTypeVideo); } if (devices) { bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); } } - return false; + if (!video_check) { + return false; + } + + bool role_check = true; + for (auto m : participantList) { + LinphoneCoreManager *mMgr = m.first; + LinphoneParticipantRole role = m.second; + LinphoneParticipant *p = linphone_conference_is_me(conference, mMgr->identity) + ? linphone_conference_get_me(conference) + : linphone_conference_find_participant(conference, mMgr->identity); + role_check &= (p != nullptr); + if (p) { + role_check &= (linphone_participant_get_role(p) == role); + } + } + return role_check; }); } @@ -7484,10 +7642,9 @@ void create_conference_with_active_call_base(bool_t dialout) { linphone_conference_info_get_organizer(info))); BC_ASSERT_TRUE(linphone_address_weak_equal(confAddr, linphone_conference_info_get_uri(info))); - bctbx_list_t *info_participants = linphone_conference_info_get_participants(info); + const bctbx_list_t *info_participants = linphone_conference_info_get_participants(info); // Original participants + Marie who joined the conference BC_ASSERT_EQUAL(bctbx_list_size(info_participants), (dialout) ? 4 : 5, size_t, "%zu"); - bctbx_list_free(info_participants); BC_ASSERT_GREATER_STRICT((long long)linphone_conference_info_get_date_time(info), 0, long long, "%lld"); const int duration_m = linphone_conference_info_get_duration(info); @@ -7699,8 +7856,8 @@ void create_simple_conference_merging_calls_base(bool_t enable_ice, if (participant_call) { BC_ASSERT_PTR_NOT_NULL(linphone_call_get_conference(participant_call)); BC_ASSERT_FALSE(linphone_call_is_in_conference(participant_call)); - // BC_ASSERT_TRUE(linphone_call_get_microphone_muted(participant_call) == (mgr == - // pauline.getCMgr())); + // BC_ASSERT_TRUE(linphone_call_get_microphone_muted(participant_call) + // == (mgr == pauline.getCMgr())); _linphone_call_check_nb_active_streams(participant_call, 1, (toggle_video) ? 4 : 0, 0); } } @@ -7907,8 +8064,8 @@ void create_simple_conference_merging_calls_base(bool_t enable_ice, if (participant_call) { BC_ASSERT_PTR_NOT_NULL(linphone_call_get_conference(participant_call)); BC_ASSERT_FALSE(linphone_call_is_in_conference(participant_call)); - // BC_ASSERT_TRUE(linphone_call_get_microphone_muted(participant_call) == (mgr == - // pauline.getCMgr())); + // BC_ASSERT_TRUE(linphone_call_get_microphone_muted(participant_call) + // == (mgr == pauline.getCMgr())); } } @@ -8155,7 +8312,3 @@ void create_simple_conference_merging_calls_base(bool_t enable_ice, } } // namespace LinphoneTest - -#if __clang__ || ((__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4) -#pragma GCC diagnostic pop -#endif diff --git a/tester/local_conference_tester_functions.h b/tester/local_conference_tester_functions.h index c4fb85cce11c2481c430b54ccab8c49dcb79822e..bdce9c14cdd7a20db342050620c82f1b38c9b9c2 100644 --- a/tester/local_conference_tester_functions.h +++ b/tester/local_conference_tester_functions.h @@ -353,6 +353,7 @@ void sendEphemeralMessageInAdminMode(Focus &focus, LinphoneChatRoom *recipientCr, const std::string basicText, const int noMsg); + bool checkChatroom(Focus &focus, const ConfCoreManager &core, const time_t baseJoiningTime); // Conference @@ -362,6 +363,7 @@ void create_simple_conference_merging_calls_base(bool_t enable_ice, bool_t toggle_all_mananger_video, bool_t change_layout, LinphoneConferenceSecurityLevel security_level); + void create_conference_base(time_t start_time, int duration, bool_t uninvited_participant_dials, @@ -379,13 +381,16 @@ void create_conference_base(time_t start_time, LinphoneMediaDirection video_direction, bool_t network_restart, LinphoneConferenceSecurityLevel security_level); + void wait_for_conference_streams(std::initializer_list<std::reference_wrapper<CoreManager>> coreMgrs, std::list<LinphoneCoreManager *> conferenceMgrs, LinphoneCoreManager *focus, - std::list<LinphoneCoreManager *> members, + std::map<LinphoneCoreManager *, LinphoneParticipantRole> members, const LinphoneAddress *confAddr, bool_t enable_video); + void two_overlapping_conferences_base(bool_t same_organizer, bool_t dialout); + void create_conference_with_late_participant_addition_base(time_t start_time, int duration, LinphoneConferenceLayout layout, @@ -393,19 +398,24 @@ void create_conference_with_late_participant_addition_base(time_t start_time, bool_t accept, bool_t one_addition, LinphoneConferenceSecurityLevel security_level); + void create_one_participant_conference_toggle_video_base(LinphoneConferenceLayout layout, bool_t enable_ice, bool_t enable_stun); + void create_conference_with_active_call_base(bool_t dialout); -LinphoneAddress *create_conference_on_server(Focus &focus, - ClientConference &organizer, - std::list<LinphoneCoreManager *> requested_participants, - time_t start_time, - time_t end_time, - const char *subject, - const char *description, - bool_t send_ics, - LinphoneConferenceSecurityLevel security_level); + +LinphoneAddress * +create_conference_on_server(Focus &focus, + ClientConference &organizer, + std::map<LinphoneCoreManager *, LinphoneParticipantRole> requested_participants, + time_t start_time, + time_t end_time, + const char *subject, + const char *description, + bool_t send_ics, + LinphoneConferenceSecurityLevel security_level); + void set_video_settings_in_conference(LinphoneCoreManager *focus, LinphoneCoreManager *participant, std::list<LinphoneCoreManager *> members, @@ -414,7 +424,9 @@ void set_video_settings_in_conference(LinphoneCoreManager *focus, LinphoneMediaDirection video_direction, bool_t answer_enable_video, LinphoneMediaDirection answer_video_direction); + size_t compute_no_video_streams(bool_t enable_video, LinphoneCall *call, LinphoneConference *conference); + void check_conference_info(LinphoneCoreManager *mgr, LinphoneAddress *confAddr, LinphoneCoreManager *organizer, @@ -426,9 +438,11 @@ void check_conference_info(LinphoneCoreManager *mgr, unsigned int sequence, LinphoneConferenceInfoState state, LinphoneConferenceSecurityLevel security_level); -size_t compute_no_audio_streams(LinphoneConference *conference); + +size_t compute_no_audio_streams(LinphoneCall *call, LinphoneConference *conference); void conference_scheduler_state_changed(LinphoneConferenceScheduler *scheduler, LinphoneConferenceSchedulerState state); + void conference_scheduler_invitations_sent(LinphoneConferenceScheduler *scheduler, const bctbx_list_t *failed_addresses); diff --git a/tester/local_ice_conference_tester.cpp b/tester/local_ice_conference_tester.cpp index d724542116852e8dd351f29482be49f4f0d61f78..b1a1c0f23d912b384d38d1fbb1728672baffca5c 100644 --- a/tester/local_ice_conference_tester.cpp +++ b/tester/local_ice_conference_tester.cpp @@ -132,8 +132,16 @@ static void abort_call_to_ice_conference(void) { stats focus_stat = focus.getStats(); - LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participants, start_time, end_time, + std::map<LinphoneCoreManager *, LinphoneParticipantRole> participantList; + LinphoneParticipantRole role = LinphoneParticipantRoleSpeaker; + for (auto &p : participants) { + participantList.insert(std::make_pair(p, role)); + role = (role == LinphoneParticipantRoleSpeaker) ? LinphoneParticipantRoleListener + : LinphoneParticipantRoleSpeaker; + } + LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, initialSubject, description, TRUE, security_level); + BC_ASSERT_PTR_NOT_NULL(confAddr); // Chat room creation to send ICS diff --git a/tester/local_inpromptu_conference_tester.cpp b/tester/local_inpromptu_conference_tester.cpp index a9d8c706737a5f289be0d0bfa38770cb8c09a8f1..48cf2b81e67c38823c6f72dfc72c2af2b7c98a31 100644 --- a/tester/local_inpromptu_conference_tester.cpp +++ b/tester/local_inpromptu_conference_tester.cpp @@ -99,7 +99,14 @@ static void create_conference_dial_out_base(bool_t send_ics, const char *initialSubject = "Schedule of the trip towards the top of Europe"; const char *description = "To the top of the Mont Blanc!!!! :-)"; - LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participants, -1, -1, initialSubject, + std::map<LinphoneCoreManager *, LinphoneParticipantRole> participantList; + LinphoneParticipantRole role = LinphoneParticipantRoleUnknown; + for (auto &p : participants) { + participantList.insert(std::make_pair(p, role)); + role = (role == LinphoneParticipantRoleSpeaker) ? LinphoneParticipantRoleListener + : LinphoneParticipantRoleSpeaker; + } + LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, -1, -1, initialSubject, description, send_ics, security_level); BC_ASSERT_PTR_NOT_NULL(confAddr); @@ -251,11 +258,11 @@ static void create_conference_dial_out_base(bool_t send_ics, linphone_conference_info_get_organizer(call_log_info), marie.getCMgr()->identity)); BC_ASSERT_TRUE( linphone_address_weak_equal(linphone_conference_info_get_uri(call_log_info), confAddr)); - bctbx_list_t *info_participants = linphone_conference_info_get_participants(call_log_info); + const bctbx_list_t *info_participants = + linphone_conference_info_get_participants(call_log_info); // Original participants + Marie who joined the conference BC_ASSERT_EQUAL(bctbx_list_size(info_participants), participant_conference_info_participants, size_t, "%zu"); - bctbx_list_free(info_participants); BC_ASSERT_GREATER_STRICT((long long)linphone_conference_info_get_date_time(call_log_info), 0, long long, "%lld"); const int duration_m = linphone_conference_info_get_duration(call_log_info); @@ -299,8 +306,6 @@ static void create_conference_dial_out_base(bool_t send_ics, marie_stat.number_of_NotifyFullStateReceived + 1, liblinphone_tester_sip_timeout)); - // Update to add to conference. - // If ICE is enabled, the addition to a conference may go through a resume of the call BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateCreated, focus_stat.number_of_LinphoneConferenceStateCreated + 1, liblinphone_tester_sip_timeout)); @@ -324,8 +329,26 @@ static void create_conference_dial_out_base(bool_t send_ics, LinphoneConference *fconference = linphone_core_search_conference_2(focus.getLc(), confAddr); BC_ASSERT_PTR_NOT_NULL(fconference); + std::map<LinphoneCoreManager *, LinphoneParticipantRole> memberList; + for (const auto &member : members) { + try { + const auto &participantInfo = participantList.at(member); + auto role = participantInfo; + if (role == LinphoneParticipantRoleUnknown) { + role = LinphoneParticipantRoleSpeaker; + } + memberList.insert(std::make_pair(member, role)); + } catch (std::out_of_range &) { + if (member == marie.getCMgr()) { + memberList.insert(std::make_pair(marie.getCMgr(), LinphoneParticipantRoleSpeaker)); + } else { + ms_fatal("Unable to find active participant %s in the participant list", + linphone_core_get_identity(member->lc)); + } + } + } wait_for_conference_streams({focus, marie, pauline, laure, michelle, berthe}, conferenceMgrs, - focus.getCMgr(), members, confAddr, enable_video); + focus.getCMgr(), memberList, confAddr, enable_video); // wait bit more to detect side effect if any CoreManagerAssert({focus, marie, pauline, laure, michelle, berthe}).waitUntil(chrono::seconds(15), [] { @@ -375,6 +398,10 @@ static void create_conference_dial_out_base(bool_t send_ics, linphone_video_activation_policy_unref(pol); size_t no_streams_audio = 0; + size_t no_max_streams_audio = + (security_level == LinphoneConferenceSecurityLevelEndToEnd) + ? static_cast<size_t>(participant_conference_info_participants) + : 1; size_t no_streams_video = 0; size_t no_max_streams_video = 0; size_t no_streams_text = 0; @@ -382,13 +409,12 @@ static void create_conference_dial_out_base(bool_t send_ics, LinphoneCall *pcall = linphone_core_get_call_by_remote_address2(mgr->lc, confAddr); BC_ASSERT_PTR_NOT_NULL(pcall); if (pcall) { - no_streams_audio = static_cast<int>(compute_no_audio_streams(pconference)); + no_streams_audio = compute_no_audio_streams(pcall, pconference); // Even if video is not enabled, the server will offer it and clients reject the video // stream if they do not want to send or receive it. - no_streams_video = - static_cast<int>(compute_no_video_streams(enable_video, pcall, pconference)); + no_streams_video = compute_no_video_streams(enable_video, pcall, pconference); no_max_streams_video = (enabled || (mgr == marie.getCMgr())) ? no_streams_video : 1; - _linphone_call_check_max_nb_streams(pcall, no_streams_audio, no_max_streams_video, + _linphone_call_check_max_nb_streams(pcall, no_max_streams_audio, no_max_streams_video, no_streams_text); _linphone_call_check_nb_active_streams(pcall, no_streams_audio, no_streams_video, no_streams_text); @@ -410,12 +436,11 @@ static void create_conference_dial_out_base(bool_t send_ics, BC_ASSERT_TRUE(linphone_address_weak_equal( linphone_conference_info_get_uri(call_log_info), confAddr)); - bctbx_list_t *info_participants = + const bctbx_list_t *info_participants = linphone_conference_info_get_participants(call_log_info); // Original participants + Marie who joined the conference BC_ASSERT_EQUAL(bctbx_list_size(info_participants), participant_conference_info_participants, size_t, "%zu"); - bctbx_list_free(info_participants); BC_ASSERT_GREATER_STRICT( (long long)linphone_conference_info_get_date_time(call_log_info), 0, long long, @@ -442,7 +467,7 @@ static void create_conference_dial_out_base(bool_t send_ics, LinphoneCall *ccall = linphone_core_get_call_by_remote_address2(focus.getLc(), mgr->identity); BC_ASSERT_PTR_NOT_NULL(ccall); if (ccall) { - _linphone_call_check_max_nb_streams(ccall, no_streams_audio, no_max_streams_video, + _linphone_call_check_max_nb_streams(ccall, no_max_streams_audio, no_max_streams_video, no_streams_text); _linphone_call_check_nb_active_streams(ccall, no_streams_audio, no_streams_video, no_streams_text); @@ -897,9 +922,16 @@ static void create_simple_conference_dial_out_organizer_codec_mismatch(void) { const char *initialSubject = "Schedule of the trip towards the top of Europe"; const char *description = "To the Goutier mountain hut!!!! :-)"; - LinphoneConferenceSecurityLevel security_level = LinphoneConferenceSecurityLevelNone; - LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participants, -1, -1, initialSubject, + + std::map<LinphoneCoreManager *, LinphoneParticipantRole> participantList; + LinphoneParticipantRole role = LinphoneParticipantRoleSpeaker; + for (auto &p : participants) { + participantList.insert(std::make_pair(p, role)); + role = (role == LinphoneParticipantRoleSpeaker) ? LinphoneParticipantRoleListener + : LinphoneParticipantRoleSpeaker; + } + LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, -1, -1, initialSubject, description, TRUE, security_level); BC_ASSERT_PTR_NULL(confAddr); @@ -985,7 +1017,14 @@ static void create_simple_conference_dial_out_with_some_calls_declined_base(Linp const char *description = "Having fun!!!! :-)"; LinphoneConferenceSecurityLevel security_level = LinphoneConferenceSecurityLevelNone; - LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participants, -1, -1, initialSubject, + std::map<LinphoneCoreManager *, LinphoneParticipantRole> participantList; + LinphoneParticipantRole role = LinphoneParticipantRoleSpeaker; + for (auto &p : participants) { + participantList.insert(std::make_pair(p, role)); + role = (role == LinphoneParticipantRoleSpeaker) ? LinphoneParticipantRoleListener + : LinphoneParticipantRoleSpeaker; + } + LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, -1, -1, initialSubject, description, FALSE, security_level); BC_ASSERT_PTR_NOT_NULL(confAddr); @@ -1094,10 +1133,9 @@ static void create_simple_conference_dial_out_with_some_calls_declined_base(Linp BC_ASSERT_TRUE( linphone_address_weak_equal(linphone_conference_info_get_uri(call_log_info), confAddr)); - bctbx_list_t *info_participants = linphone_conference_info_get_participants(call_log_info); + const bctbx_list_t *info_participants = linphone_conference_info_get_participants(call_log_info); // Original participants + Marie who joined the conference BC_ASSERT_EQUAL(bctbx_list_size(info_participants), 5, size_t, "%zu"); - bctbx_list_free(info_participants); BC_ASSERT_GREATER_STRICT((long long)linphone_conference_info_get_date_time(call_log_info), 0, long long, "%lld"); @@ -1192,8 +1230,22 @@ static void create_simple_conference_dial_out_with_some_calls_declined_base(Linp LinphoneConference *fconference = linphone_core_search_conference_2(focus.getLc(), confAddr); BC_ASSERT_PTR_NOT_NULL(fconference); + std::map<LinphoneCoreManager *, LinphoneParticipantRole> memberList; + for (const auto &member : all_active_participants) { + try { + const auto &participantInfo = participantList.at(member); + memberList.insert(std::make_pair(member, participantInfo)); + } catch (std::out_of_range &) { + if (member == marie.getCMgr()) { + memberList.insert(std::make_pair(marie.getCMgr(), LinphoneParticipantRoleSpeaker)); + } else { + ms_fatal("Unable to find active participant %s in the participant list", + linphone_core_get_identity(member->lc)); + } + } + } wait_for_conference_streams({focus, marie, pauline, laure, michelle, berthe}, conference_members, - focus.getCMgr(), all_active_participants, confAddr, TRUE); + focus.getCMgr(), memberList, confAddr, TRUE); // wait bit more to detect side effect if any CoreManagerAssert({focus, marie, pauline, laure, michelle, berthe}).waitUntil(chrono::seconds(15), [] { @@ -1225,13 +1277,14 @@ static void create_simple_conference_dial_out_with_some_calls_declined_base(Linp bool_t enabled = !!linphone_video_activation_policy_get_automatically_initiate(pol); linphone_video_activation_policy_unref(pol); - size_t no_streams_audio = 1; + size_t no_streams_audio = 0; size_t no_streams_video = (enabled) ? (static_cast<int>(all_active_participants.size()) + 1) : 0; size_t no_streams_text = 0; LinphoneCall *pcall = linphone_core_get_call_by_remote_address2(mgr->lc, confAddr); BC_ASSERT_PTR_NOT_NULL(pcall); if (pcall) { + no_streams_audio = compute_no_audio_streams(pcall, pconference); _linphone_call_check_max_nb_streams(pcall, no_streams_audio, no_streams_video, no_streams_text); _linphone_call_check_nb_active_streams(pcall, no_streams_audio, no_streams_video, no_streams_text); @@ -1556,7 +1609,14 @@ static void simple_dial_out_conference_with_no_payloads(void) { const char *description = "To the top of the Mont Blanc!!!! :-)"; LinphoneConferenceSecurityLevel security_level = LinphoneConferenceSecurityLevelNone; - LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participants, -1, -1, initialSubject, + std::map<LinphoneCoreManager *, LinphoneParticipantRole> participantList; + LinphoneParticipantRole role = LinphoneParticipantRoleSpeaker; + for (auto &p : participants) { + participantList.insert(std::make_pair(p, role)); + role = (role == LinphoneParticipantRoleSpeaker) ? LinphoneParticipantRoleListener + : LinphoneParticipantRoleSpeaker; + } + LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, -1, -1, initialSubject, description, FALSE, security_level); BC_ASSERT_PTR_NOT_NULL(confAddr); @@ -1608,7 +1668,6 @@ static void simple_dial_out_conference_with_no_payloads(void) { BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallStreamsRunning, 2, liblinphone_tester_sip_timeout)); - // If ICE is enabled, the addition to a conference may go through a resume of the call BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateCreated, 1, liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE( diff --git a/tester/local_scheduled_conference_tester.cpp b/tester/local_scheduled_conference_tester.cpp index 35dc5391f021e42b961acd3086d082f8e836e0b4..ea049d0e4de8cc3fbeb3b3d2632ad93e1eccfe80 100644 --- a/tester/local_scheduled_conference_tester.cpp +++ b/tester/local_scheduled_conference_tester.cpp @@ -218,7 +218,15 @@ static void create_conference_with_audio_only_participants_base(LinphoneConferen time_t end_time = (duration <= 0) ? -1 : (start_time + duration * 60); const char *initialSubject = "Test characters: ^ :) ¤ çà @"; const char *description = "Chamrousse Pub"; - LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participants, start_time, end_time, + + std::map<LinphoneCoreManager *, LinphoneParticipantRole> participantList; + LinphoneParticipantRole role = LinphoneParticipantRoleUnknown; + for (auto &p : participants) { + participantList.insert(std::make_pair(p, role)); + role = (role == LinphoneParticipantRoleSpeaker) ? LinphoneParticipantRoleListener + : LinphoneParticipantRoleSpeaker; + } + LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, initialSubject, description, TRUE, security_level); BC_ASSERT_PTR_NOT_NULL(confAddr); @@ -273,8 +281,8 @@ static void create_conference_with_audio_only_participants_base(LinphoneConferen BC_ASSERT_TRUE( wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionOutgoingProgress, 1, 5000)); BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionActive, 1, 5000)); - BC_ASSERT_TRUE( - wait_for_list(coresList, &mgr->stat.number_of_NotifyReceived, 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_NotifyFullStateReceived, 1, + liblinphone_tester_sip_timeout)); } BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallIncomingReceived, @@ -307,8 +315,26 @@ static void create_conference_with_audio_only_participants_base(LinphoneConferen focus_stat.number_of_participant_devices_joined + 3, liblinphone_tester_sip_timeout)); - wait_for_conference_streams({focus, marie, pauline, laure}, conferenceMgrs, focus.getCMgr(), members, confAddr, - TRUE); + std::map<LinphoneCoreManager *, LinphoneParticipantRole> memberList; + for (const auto &member : members) { + try { + const auto &participantInfo = participantList.at(member); + auto role = participantInfo; + if (role == LinphoneParticipantRoleUnknown) { + role = LinphoneParticipantRoleSpeaker; + } + memberList.insert(std::make_pair(member, role)); + } catch (std::out_of_range &) { + if (member == marie.getCMgr()) { + memberList.insert(std::make_pair(marie.getCMgr(), LinphoneParticipantRoleSpeaker)); + } else { + ms_fatal("Unable to find active participant %s in the participant list", + linphone_core_get_identity(member->lc)); + } + } + } + wait_for_conference_streams({focus, marie, pauline, laure}, conferenceMgrs, focus.getCMgr(), memberList, + confAddr, TRUE); LinphoneConference *fconference = linphone_core_search_conference_2(focus.getLc(), confAddr); BC_ASSERT_PTR_NOT_NULL(fconference); @@ -345,7 +371,7 @@ static void create_conference_with_audio_only_participants_base(LinphoneConferen (int)LinphoneCallStateStreamsRunning, int, "%0d"); } - size_t no_streams_audio = (security_level == LinphoneConferenceSecurityLevelEndToEnd) ? 3 : 1; + size_t no_streams_audio = 0; size_t no_streams_video = 0; size_t no_active_streams_video = 0; size_t no_streams_text = 0; @@ -353,6 +379,7 @@ static void create_conference_with_audio_only_participants_base(LinphoneConferen LinphoneCall *pcall = linphone_core_get_call_by_remote_address2(mgr->lc, confAddr); BC_ASSERT_PTR_NOT_NULL(pcall); if (pcall) { + no_streams_audio = compute_no_audio_streams(pcall, pconference); _linphone_call_check_nb_streams(pcall, no_streams_audio, no_streams_video, no_streams_text); _linphone_call_check_nb_active_streams(pcall, no_streams_audio, no_active_streams_video, no_streams_text); @@ -429,14 +456,14 @@ static void create_conference_with_audio_only_participants_base(LinphoneConferen linphone_address_unref(uri); BC_ASSERT_PTR_NOT_NULL(pconference); if (pconference) { - size_t no_streams_audio = - (security_level == LinphoneConferenceSecurityLevelEndToEnd) ? 3 : 1; + size_t no_streams_audio = 0; size_t no_active_streams_video = 0; size_t no_streams_text = 0; LinphoneCall *pcall = linphone_core_get_call_by_remote_address2(mgr->lc, confAddr); BC_ASSERT_PTR_NOT_NULL(pcall); if (pcall) { + no_streams_audio = compute_no_audio_streams(pcall, pconference); no_active_streams_video = static_cast<int>(compute_no_video_streams(enable, pcall, pconference)); _linphone_call_check_nb_active_streams(pcall, no_streams_audio, no_active_streams_video, @@ -608,7 +635,15 @@ static void create_conference_with_codec_mismatch_base(bool_t organizer_codec_mi const char *description = "Paris Baker"; LinphoneConferenceSecurityLevel security_level = LinphoneConferenceSecurityLevelNone; - LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participants, start_time, end_time, + std::map<LinphoneCoreManager *, LinphoneParticipantRole> participantList; + LinphoneParticipantRole role = LinphoneParticipantRoleSpeaker; + for (auto &p : participants) { + participantList.insert(std::make_pair(p, role)); + role = (role == LinphoneParticipantRoleSpeaker) ? LinphoneParticipantRoleListener + : LinphoneParticipantRoleSpeaker; + } + participantList.insert(std::make_pair(marie.getCMgr(), LinphoneParticipantRoleListener)); + LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, initialSubject, description, TRUE, security_level); BC_ASSERT_PTR_NOT_NULL(confAddr); char *conference_address_str = (confAddr) ? linphone_address_as_string(confAddr) : ms_strdup("<unknown>"); @@ -721,8 +756,22 @@ static void create_conference_with_codec_mismatch_base(bool_t organizer_codec_mi liblinphone_tester_sip_timeout)); } + std::map<LinphoneCoreManager *, LinphoneParticipantRole> memberList; + for (const auto &member : members) { + try { + const auto &participantInfo = participantList.at(member); + memberList.insert(std::make_pair(member, participantInfo)); + } catch (std::out_of_range &) { + if (member == marie.getCMgr()) { + memberList.insert(std::make_pair(marie.getCMgr(), LinphoneParticipantRoleSpeaker)); + } else { + ms_fatal("Unable to find active participant %s in the participant list", + linphone_core_get_identity(member->lc)); + } + } + } wait_for_conference_streams({focus, marie, pauline, laure, michelle, berthe}, conferenceMgrs, focus.getCMgr(), - members, confAddr, TRUE); + memberList, confAddr, TRUE); LinphoneConference *fconference = linphone_core_search_conference_2(focus.getLc(), confAddr); BC_ASSERT_PTR_NOT_NULL(fconference); @@ -958,7 +1007,16 @@ static void create_conference_with_server_restart_base(bool_t organizer_first) { const char *initialSubject = "Test characters: ^ :) ¤ çà @"; const char *description = "London Pub"; LinphoneConferenceSecurityLevel security_level = LinphoneConferenceSecurityLevelNone; - LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participants, start_time, end_time, + + std::map<LinphoneCoreManager *, LinphoneParticipantRole> participantList; + LinphoneParticipantRole role = LinphoneParticipantRoleSpeaker; + for (auto &p : participants) { + participantList.insert(std::make_pair(p, role)); + role = (role == LinphoneParticipantRoleSpeaker) ? LinphoneParticipantRoleListener + : LinphoneParticipantRoleSpeaker; + } + participantList.insert(std::make_pair(marie.getCMgr(), LinphoneParticipantRoleSpeaker)); + LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, initialSubject, description, TRUE, security_level); BC_ASSERT_PTR_NOT_NULL(confAddr); @@ -1060,8 +1118,22 @@ static void create_conference_with_server_restart_base(bool_t organizer_first) { focus_stat.number_of_participant_devices_joined + 3, liblinphone_tester_sip_timeout)); - wait_for_conference_streams({focus, marie, pauline, laure}, conferenceMgrs, focus.getCMgr(), members, confAddr, - TRUE); + std::map<LinphoneCoreManager *, LinphoneParticipantRole> memberList; + for (const auto &member : members) { + try { + const auto &participantInfo = participantList.at(member); + memberList.insert(std::make_pair(member, participantInfo)); + } catch (std::out_of_range &) { + if (member == marie.getCMgr()) { + memberList.insert(std::make_pair(marie.getCMgr(), LinphoneParticipantRoleSpeaker)); + } else { + ms_fatal("Unable to find active participant %s in the participant list", + linphone_core_get_identity(member->lc)); + } + } + } + wait_for_conference_streams({focus, marie, pauline, laure}, conferenceMgrs, focus.getCMgr(), memberList, + confAddr, TRUE); LinphoneConference *fconference = linphone_core_search_conference_2(focus.getLc(), confAddr); BC_ASSERT_PTR_NOT_NULL(fconference); @@ -1291,7 +1363,14 @@ static void create_simple_conference_with_update_deferred(void) { const char *description = "Paris Baker"; LinphoneConferenceSecurityLevel security_level = LinphoneConferenceSecurityLevelNone; - LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participants, start_time, end_time, + std::map<LinphoneCoreManager *, LinphoneParticipantRole> participantList; + LinphoneParticipantRole role = LinphoneParticipantRoleSpeaker; + for (auto &p : participants) { + participantList.insert(std::make_pair(p, role)); + role = (role == LinphoneParticipantRoleSpeaker) ? LinphoneParticipantRoleListener + : LinphoneParticipantRoleSpeaker; + } + LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, initialSubject, description, TRUE, security_level); BC_ASSERT_PTR_NOT_NULL(confAddr); @@ -1751,7 +1830,12 @@ static void change_active_speaker(void) { const char *description = "hello"; LinphoneConferenceSecurityLevel security_level = LinphoneConferenceSecurityLevelNone; - LinphoneAddress *confAddr = create_conference_on_server(focus, marie, invitesList, start_time, end_time, + std::map<LinphoneCoreManager *, LinphoneParticipantRole> participantList; + LinphoneParticipantRole role = LinphoneParticipantRoleSpeaker; + for (auto &p : invitesList) { + participantList.insert(std::make_pair(p, role)); + } + LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, initialSubject, description, TRUE, security_level); BC_ASSERT_PTR_NOT_NULL(confAddr); @@ -2010,7 +2094,13 @@ static void conference_with_participant_added_outside_valid_time_slot (bool_t be const char *initialSubject = "Colleagues"; const char *description = "Tom Black"; - LinphoneAddress* confAddr = create_conference_on_server(focus, marie, participants, start_time, end_time, initialSubject, description, TRUE, security_level); + std::map<LinphoneCoreManager *, LinphoneParticipantRole> participantList; + LinphoneParticipantRole role = LinphoneParticipantRoleSpeaker; + for (auto &p : participants) { + participantList.insert(std::make_pair(p, role)); + role = (role == LinphoneParticipantRoleSpeaker) ? LinphoneParticipantRoleListener : LinphoneParticipantRoleSpeaker; + } + LinphoneAddress* confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, initialSubject, description, TRUE, security_level); BC_ASSERT_PTR_NOT_NULL(confAddr); // Chat room creation to send ICS BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateCreated, 2, liblinphone_tester_sip_timeout)); diff --git a/tester/shared_tester_functions.cpp b/tester/shared_tester_functions.cpp index 234db23ae47faabbe78f069e167523746d73a04d..c6a8f2948b842712ed6545e0a2660a8161f37d54 100644 --- a/tester/shared_tester_functions.cpp +++ b/tester/shared_tester_functions.cpp @@ -25,6 +25,7 @@ #include "conference/conference-info.h" #include "conference/params/media-session-params.h" #include "conference/participant-device.h" +#include "conference/participant-info.h" #include "conference/session/media-session.h" #include "liblinphone_tester.h" #include "mediastreamer2/msmire.h" @@ -360,21 +361,6 @@ void _linphone_call_check_max_nb_streams(const LinphoneCall *call, } } -void _linphone_call_check_nb_streams(const LinphoneCall *call, - const int nb_audio_streams, - const int nb_video_streams, - const int nb_text_streams) { - const SalMediaDescription *call_result_desc = _linphone_call_get_result_desc(call); - BC_ASSERT_PTR_NOT_NULL(call_result_desc); - if (call_result_desc) { - BC_ASSERT_LOWER(call_result_desc->getNbStreams(), nb_audio_streams + nb_video_streams + nb_text_streams, size_t, - "%zu"); - BC_ASSERT_LOWER(call_result_desc->nbStreamsOfType(SalAudio), nb_audio_streams, size_t, "%zu"); - BC_ASSERT_LOWER(call_result_desc->nbStreamsOfType(SalVideo), nb_video_streams, size_t, "%zu"); - BC_ASSERT_LOWER(call_result_desc->nbStreamsOfType(SalText), nb_text_streams, size_t, "%zu"); - } -} - void _linphone_call_check_nb_streams(const LinphoneCall *call, const size_t nb_audio_streams, const size_t nb_video_streams, @@ -753,23 +739,22 @@ bool_t linphone_conference_type_is_full_state(const char *text) { : FALSE; } +void linphone_conference_info_check_participant_info(const std::shared_ptr<ParticipantInfo> &info, + int sequence_number) { + const auto &sequence = info->getSequenceNumber(); + BC_ASSERT_GREATER(sequence, 0, int, "%0d"); + BC_ASSERT_EQUAL(sequence, sequence_number, int, "%d"); +} + void linphone_conference_info_check_participant(const LinphoneConferenceInfo *conference_info, LinphoneAddress *address, int sequence_number) { - const auto &sequence = LinphonePrivate::ConferenceInfo::toCpp(conference_info) - ->getParticipantParam(Address::toCpp(address)->getSharedFromThis(), "X-SEQ"); - BC_ASSERT_TRUE(!sequence.empty()); - if (!sequence.empty()) { - const int sequenceNumber = std::atoi(sequence.c_str()); - BC_ASSERT_EQUAL(sequenceNumber, sequence_number, int, "%d"); - } + const auto &participantInfo = LinphonePrivate::ConferenceInfo::toCpp(conference_info) + ->findParticipant(Address::toCpp(address)->getSharedFromThis()); + linphone_conference_info_check_participant_info(participantInfo, sequence_number); } void linphone_conference_info_check_organizer(const LinphoneConferenceInfo *conference_info, int sequence_number) { - const auto &sequence = LinphonePrivate::ConferenceInfo::toCpp(conference_info)->getOrganizerParam("X-SEQ"); - BC_ASSERT_TRUE(!sequence.empty()); - if (!sequence.empty()) { - const int sequenceNumber = std::atoi(sequence.c_str()); - BC_ASSERT_EQUAL(sequenceNumber, sequence_number, int, "%d"); - } + const auto &organizer = LinphonePrivate::ConferenceInfo::toCpp(conference_info)->getOrganizer(); + linphone_conference_info_check_participant_info(organizer, sequence_number); } diff --git a/tester/utils-tester.cpp b/tester/utils-tester.cpp index 94ca8bd8cf3a279c75312d091fb8e9e32ab5cb67..84baa33c33f1e3623f71416c482df4bead68e4d2 100644 --- a/tester/utils-tester.cpp +++ b/tester/utils-tester.cpp @@ -21,6 +21,7 @@ #include "bctoolbox/utils.hh" #include "address/address.h" +#include "conference/conference-id.h" #include "liblinphone_tester.h" #include "linphone/utils/utils.h" #include "tester_utils.h"