diff --git a/CHANGELOG.md b/CHANGELOG.md index e96fb4d793ec3e06f53015d9712a78e3b717c2f3..ab173a2c3b924aaec66a35c154e26bf666ef46f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,15 +10,13 @@ This changelog file was started on October 2019. Previous changes were more or l ## Unreleased -## [5.4.0] 2025-01-14 +## [5.4.0] 2025-03-11 ### Added - Forward Error Correction to video stream, based on FlexFEC RFC8627 - Java wrapper for Linux and Windows (see *java-sdk-linux* and *java-sdk-windows* linphone-sdk cmake presets) - Python wrapper for Linux (use -DENABLE_PYTHON_WRAPPER=ON) - Screensharing. -- Support for SFU-based audio and video conferencing -- End to end encryption for audio/video conferences, using double SRTP encryption RFC8723 and RFC8870 (EKT) - Creation of conferences through CCMP protocol (RFC6503) via ConferenceScheduler object. - VCARD version 3 compatibility (in addition to version 4). - Bearer authentication for SIP and http utilities (remote provisioning, file transfer...). @@ -33,6 +31,9 @@ This changelog file was started on October 2019. Previous changes were more or l - new linphone_video_activation_policy_set_automatically_accept_direction() to help apps handle asymmetric video activation during calls. - LinphoneFriendDevice object, to present user's device and associated security levels. - Automatic retry of pending outgoing IMs in case of timeout or network disconnection. +- BETA features: + - SFU-based audio and video conferencing + - End to end encryption for audio/video conferences, using double SRTP encryption RFC8723 and RFC8870 (EKT) ### Changed - Unifies ChatRoom and Conferences internal classes @@ -53,6 +54,7 @@ This changelog file was started on October 2019. Previous changes were more or l - internal refactoring of C code into C++. - AccountCreator object is deprecated (use LinphoneAccountManagerServices as a replacement) - Allow LinphoneCoreCbs' s authentication_requested() callack to be invoked during remote provisioning. +- Disable peer to peer presence by default (linphone_friend_subscribes_enabled() is FALSE, and linphone_friend_get_inc_subscribe_policy() returns LinphoneSPDeny). ### Fixed - Added _utf8() getter and setters for subject of conference and chatrooms. @@ -63,6 +65,7 @@ This changelog file was started on October 2019. Previous changes were more or l ### Removed - Removed old & deprecated VideoPolicy object. +- ISAC and iLBC audio codecs are deprecated and will be removed in a future release. ## [5.3.55] 2024-05-31 diff --git a/CMakeLists.txt b/CMakeLists.txt index 50dbd0632bbcbc82fb3b1e40aaa85bdb65287875..f8767521ce3662e09205d7ec7bfdb2b915b0efd4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -122,6 +122,24 @@ find_package(BCToolbox 5.3.0 REQUIRED OPTIONAL_COMPONENTS tester) bc_init_compilation_flags(STRICT_OPTIONS_CPP STRICT_OPTIONS_C STRICT_OPTIONS_CXX ENABLE_STRICT) find_package(Belr 5.3.0 REQUIRED) +if(ANDROID_BUNDLED) + find_package(MsAaudio) + find_package(MsAndroidCamera2) + find_package(MsWebRtc) +endif() +if (IOS) + find_package(MsWebRtc) +endif() +if(MsAaudio_FOUND) + set(HAVE_STATIC_MSAAUDIO 1) +endif() +if(MsAndroidCamera2_FOUND) + set(HAVE_STATIC_MSANDROIDCAMERA2 1) +endif() +if(MsWebRtc_FOUND) + set(HAVE_STATIC_MSWEBRTC 1) +endif() + if(ENABLE_XERCESC) bc_find_package(XercesC XercesC::XercesC xerces-c REQUIRED) set(LIBXSD_INCLUDE_DIRS "${PROJECT_SOURCE_DIR}/libxsd") diff --git a/cmake/FindMsAaudio.cmake b/cmake/FindMsAaudio.cmake new file mode 100644 index 0000000000000000000000000000000000000000..b81306e743daef5882ed737d99fb4a6c5daea96d --- /dev/null +++ b/cmake/FindMsAaudio.cmake @@ -0,0 +1,56 @@ +############################################################################ +# FindMsAaudio.cmake +# Copyright (C) 2010-2025 Belledonne Communications, Grenoble France +# +############################################################################ +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +############################################################################ +# +# Find the msaaudio plugin. +# +# Targets +# ^^^^^^^ +# +# The following targets may be defined: +# +# msaaudio - If the msaaudio plugin has been found +# +# +# Result variables +# ^^^^^^^^^^^^^^^^ +# +# This module will set the following variables in your project: +# +# MsAaudio_FOUND - The msaaudio plugin has been found +# MsAaudio_TARGET - The name of the CMake target for the msaaudio plugin + + +include(FindPackageHandleStandardArgs) + +set(_MsAaudio_REQUIRED_VARS MsAaudio_TARGET) +set(_MsAaudio_CACHE_VARS ${_MsAaudio_REQUIRED_VARS}) + +if(TARGET msaaudio) + + set(MsAaudio_TARGET msaaudio) + +endif() + +find_package_handle_standard_args(MsAaudio + REQUIRED_VARS ${_MsAaudio_REQUIRED_VARS} +) +mark_as_advanced(${_MsAaudio_CACHE_VARS}) diff --git a/cmake/FindMsAndroidCamera2.cmake b/cmake/FindMsAndroidCamera2.cmake new file mode 100644 index 0000000000000000000000000000000000000000..1dc03759ef642961549688a9f4a65901777a6897 --- /dev/null +++ b/cmake/FindMsAndroidCamera2.cmake @@ -0,0 +1,56 @@ +############################################################################ +# FindMsAndroidCamera2.cmake +# Copyright (C) 2010-2025 Belledonne Communications, Grenoble France +# +############################################################################ +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +############################################################################ +# +# Find the msandroidcamera2 plugin. +# +# Targets +# ^^^^^^^ +# +# The following targets may be defined: +# +# msandroidcamera2 - If the msandroidcamera2 plugin has been found +# +# +# Result variables +# ^^^^^^^^^^^^^^^^ +# +# This module will set the following variables in your project: +# +# MsAndroidCamera2_FOUND - The msandroidcamera2 plugin has been found +# MsAndroidCamera2_TARGET - The name of the CMake target for the msandroidcamera2 plugin + + +include(FindPackageHandleStandardArgs) + +set(_MsAndroidCamera2_REQUIRED_VARS MsAndroidCamera2_TARGET) +set(_MsAndroidCamera2_CACHE_VARS ${_MsAndroidCamera2_REQUIRED_VARS}) + +if(TARGET msandroidcamera2) + + set(MsAndroidCamera2_TARGET msandroidcamera2) + +endif() + +find_package_handle_standard_args(MsAndroidCamera2 + REQUIRED_VARS ${_MsAndroidCamera2_REQUIRED_VARS} +) +mark_as_advanced(${_MsAndroidCamera2_CACHE_VARS}) diff --git a/cmake/FindMsWebRtc.cmake b/cmake/FindMsWebRtc.cmake new file mode 100644 index 0000000000000000000000000000000000000000..1410d971d65d1292db97417ce3cc116a23213e2b --- /dev/null +++ b/cmake/FindMsWebRtc.cmake @@ -0,0 +1,56 @@ +############################################################################ +# FindMsWebRtc.cmake +# Copyright (C) 2010-2025 Belledonne Communications, Grenoble France +# +############################################################################ +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +############################################################################ +# +# Find the mswebrtc plugin. +# +# Targets +# ^^^^^^^ +# +# The following targets may be defined: +# +# mswebrtc - If the mswebrtc plugin has been found +# +# +# Result variables +# ^^^^^^^^^^^^^^^^ +# +# This module will set the following variables in your project: +# +# MsWebRtc_FOUND - The mswebrtc plugin has been found +# MsWebRtc_TARGET - The name of the CMake target for the mswebrtc plugin + + +include(FindPackageHandleStandardArgs) + +set(_MsWebRtc_REQUIRED_VARS MsWebRtc_TARGET) +set(_MsWebRtc_CACHE_VARS ${_MsWebRtc_REQUIRED_VARS}) + +if(TARGET mswebrtc) + + set(MsWebRtc_TARGET mswebrtc) + +endif() + +find_package_handle_standard_args(MsWebRtc + REQUIRED_VARS ${_MsWebRtc_REQUIRED_VARS} +) +mark_as_advanced(${_MsWebRtc_CACHE_VARS}) diff --git a/config.h.cmake b/config.h.cmake index 7df482ffd16499a5516be38b6726f2dc6465772d..ef56c78d56c1ac9be06b814a321e6c6591cb76d7 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -69,3 +69,6 @@ #cmakedefine ENABLE_SANITIZER #cmakedefine HAVE_MANUAL_SOCI_BACKEND_REGISTRATION #cmakedefine HAVE_BAUDOT +#cmakedefine HAVE_STATIC_MSAAUDIO +#cmakedefine HAVE_STATIC_MSANDROIDCAMERA2 +#cmakedefine HAVE_STATIC_MSWEBRTC diff --git a/console/commands.c b/console/commands.c index de5678c8bb576d45a55315af223127eaaa58fbf4..0ddd2b3211eb8643b17914ee98ab745a401e9325 100644 --- a/console/commands.c +++ b/console/commands.c @@ -1368,7 +1368,7 @@ static void linphonec_proxy_add(LinphoneCore *lc) { continue; } - params = linphone_account_params_new(lc); + params = linphone_account_params_new(lc, TRUE); /*create account parameters*/ if (linphone_account_params_set_server_addr(params, clean) < 0) { linphonec_out("Invalid sip address (sip:sip.domain.tld).\n"); diff --git a/coreapi/authentication.c b/coreapi/authentication.c index 37a81ce72423459cbcf1a0ca6d40d2648e6ed9df..aa58f0cd0edffe9e39ea561771c35d6736ae8786 100644 --- a/coreapi/authentication.c +++ b/coreapi/authentication.c @@ -78,11 +78,11 @@ static bool_t check_algorithm_compatibility(const LinphoneAuthInfo *ai, const ch /* We have the clear text password, so if the user didn't requested a specific algorithm, we can satisfy all * algorithms.*/ if (ai_algorithm == NULL) return TRUE; - } else { + } else if (linphone_auth_info_get_ha1(ai)) { /* If we don't have the clear text password but the ha1, and if algorithm is empty in LinphoneAuthInfo * for backward compatibility, we assume it is MD5. */ if (ai_algorithm == NULL && strcasecmp(algorithm, "MD5") == 0) return TRUE; - } + } /*else*/ /* In all other cases, algorithm must match. */ if (ai_algorithm && strcasecmp(algorithm, ai_algorithm) == 0) return TRUE; /* algorithm do match */ return FALSE; @@ -215,7 +215,8 @@ const LinphoneAuthInfo *linphone_core_find_auth_info_to_be_replaced(LinphoneCore if (cppOther->getUsername() != pinfo->getUsername()) continue; if (cppOther->getRealm() != pinfo->getRealm()) continue; if (cppOther->getDomain() != pinfo->getDomain()) continue; - if (cppOther->getAccessToken() && !pinfo->getAccessToken()) continue; /* not of the same type */ + if ((cppOther->getAccessToken() != nullptr) ^ (pinfo->getAccessToken() != nullptr)) + continue; /* not of the same type */ return pinfo->toC(); } return nullptr; @@ -548,6 +549,8 @@ AuthStatus linphone_core_fill_belle_sip_auth_event(LinphoneCore *lc, // - call again _linphone_core_find_indexed_tls_auth_info to retrieve the auth_info set by the // callback. Not done as we assume that authentication on flexisip server was performed before so the // application layer already got a chance to set the correct auth_info in the core + // THIS IS NOT TRUE ANYMORE: Flexisip auth is performed after the access to the lime server as the + // register is performed after the lime user creation. } status = AuthStatus::Done; // since we can't know if server requested a client certificate, assume all is good. diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c index 0dff8ed341c1bf1da0834a5657154358cec65465..dd4cd5470ff7837ba39be109d06682a580bfc896 100644 --- a/coreapi/callbacks.c +++ b/coreapi/callbacks.c @@ -35,6 +35,7 @@ #include "call/call.h" #include "chat/chat-message/chat-message-p.h" #include "chat/chat-room/chat-room.h" +#include "conference/conference-id-params.h" #include "conference/conference.h" #include "conference/server-conference.h" #include "linphone/api/c-account-params.h" @@ -180,13 +181,17 @@ static void call_received(SalCallOp *h) { #endif auto params = ConferenceParams::create(L_GET_CPP_PTR_FROM_C_OBJECT(lc)); + LinphoneAccount *account = linphone_core_lookup_account_by_identity(lc, to->toC()); + if (account) { + params->setAccount(Account::toCpp(account)->getSharedFromThis()); + } + params->setUtf8Subject(h->getSubject()); if (hasStreams) { params->setAudioVideoDefaults(); } if (chatCapabilities) { #ifdef HAVE_ADVANCED_IM params->setChatDefaults(); - params->setUtf8Subject(h->getSubject()); params->setSecurityLevel(encrypted ? ConferenceParams::SecurityLevel::EndToEnd : ConferenceParams::SecurityLevel::None); params->setGroup(!isOneToOne); @@ -226,7 +231,11 @@ static void call_received(SalCallOp *h) { } } auto remoteContact = Address::create(h->getRemoteContact()); - shared_ptr<AbstractChatRoom> chatRoom = core->findChatRoom(ConferenceId(remoteContact, to)); + auto conferenceIdParams = core->createConferenceIdParams(); + conferenceIdParams.setKeepGruu(false); + conferenceIdParams.enableExtractUri(true); + ConferenceId conferenceId(remoteContact, to, conferenceIdParams); + shared_ptr<AbstractChatRoom> chatRoom = core->findChatRoom(conferenceId, false); if (chatRoom && chatRoom->getCapabilities() & ChatRoom::Capabilities::Basic) { lError() << "Invalid basic chat room found. It should have been a ClientChatRoom... Recreating it..."; chatRoom->deleteFromDb(); @@ -235,8 +244,7 @@ static void call_received(SalCallOp *h) { if (!chatRoom) { const auto localAddressWithoutGruu = Address::create(to->getUriWithoutGruu()); const auto peerAddressWithoutGruu = Address::create(remoteContact->getUriWithoutGruu()); - chatRoom = L_GET_PRIVATE_FROM_C_OBJECT(lc)->createClientChatRoom( - to, ConferenceId(peerAddressWithoutGruu, localAddressWithoutGruu), h, params); + chatRoom = L_GET_PRIVATE_FROM_C_OBJECT(lc)->createClientChatRoom(to, conferenceId, h, params); } conference = chatRoom->getConference(); @@ -254,9 +262,10 @@ static void call_received(SalCallOp *h) { sal_address_has_uri_param(h->getToAddress(), Conference::ConfIdParameter.c_str()) || ((sal_address_has_param(remoteContactAddress, "admin") && (strcmp(sal_address_get_param(remoteContactAddress, "admin"), "1") == 0)))) { + const auto conferenceIdParams = core->createConferenceIdParams(); // Create a conference if remote is trying to schedule one or it is calling a conference focus if (isServer) { - conference = core->findConference(ConferenceId(to, to)); + conference = core->findConference(ConferenceId(to, to, conferenceIdParams), false); if (conference) { auto serverConference = dynamic_pointer_cast<ServerConference>(conference); if (serverConference) { @@ -317,7 +326,7 @@ static void call_received(SalCallOp *h) { // The duration is stored in minutes whereas the start time in seconds params->setEndTime(startTime + duration * 60); } - conference = (new ServerConference(core, to, nullptr, params))->toSharedPtr(); + conference = (new ServerConference(core, nullptr, params))->toSharedPtr(); conference->init(h); } else #endif // HAVE_DB_STORAGE @@ -333,7 +342,7 @@ static void call_received(SalCallOp *h) { h->release(); return; } else { - auto serverConference = (new ServerConference(core, to, nullptr, params))->toSharedPtr(); + auto serverConference = (new ServerConference(core, nullptr, params))->toSharedPtr(); serverConference->init(h); static_pointer_cast<ServerConference>(serverConference)->confirmCreation(); return; @@ -371,13 +380,15 @@ static void call_received(SalCallOp *h) { fromOp, participant, encrypted); if (confAddr && confAddr->isValid()) { shared_ptr<AbstractChatRoom> chatRoom = - core->findChatRoom(ConferenceId(confAddr, confAddr)); + core->findChatRoom(ConferenceId(confAddr, confAddr, conferenceIdParams)); static_pointer_cast<ServerChatRoom>(chatRoom)->confirmRecreation(h); return; } } auto chatRoom = L_GET_PRIVATE_FROM_C_OBJECT(lc)->createServerChatRoom(to, h, params); - static_pointer_cast<ServerChatRoom>(chatRoom)->confirmCreation(); + if (chatRoom) { + static_pointer_cast<ServerChatRoom>(chatRoom)->confirmCreation(); + } } else { // invite is for an unknown chatroom h->decline(SalReasonNotFound); @@ -430,14 +441,15 @@ static void call_received(SalCallOp *h) { if (!L_GET_PRIVATE_FROM_C_OBJECT(lc)->canWeAddCall()) { /* Busy */ h->decline(SalReasonBusy); LinphoneErrorInfo *ei = linphone_error_info_new(); - linphone_error_info_set(ei, nullptr, LinphoneReasonBusy, 486, "Busy - too many calls", nullptr); + linphone_error_info_set(ei, nullptr, LinphoneReasonBusy, 486, "Busy - too many calls", "Busy - too many calls"); core->reportEarlyCallFailed(LinphoneCallIncoming, from, to, ei, h->getCallId()); h->release(); linphone_error_info_unref(ei); return; } - if (linphone_config_get_int(linphone_core_get_config(lc), "sip", "reject_duplicated_calls", 1)) { + if (linphone_config_get_int(linphone_core_get_config(lc), "sip", "reject_duplicated_calls", 1) && + !sal_address_has_param(remoteContactAddress, Conference::IsFocusParameter.c_str())) { /* Check if I'm the caller */ std::shared_ptr<Address> fromAddressToSearchIfMe = nullptr; if (h->getPrivacy() == SalPrivacyNone) fromAddressToSearchIfMe = from->clone()->toSharedPtr(); @@ -450,7 +462,8 @@ static void call_received(SalCallOp *h) { << "] is initiated, refusing this one with busy message"; h->decline(SalReasonBusy); LinphoneErrorInfo *ei = linphone_error_info_new(); - linphone_error_info_set(ei, nullptr, LinphoneReasonBusy, 486, "Busy - duplicated call", nullptr); + linphone_error_info_set(ei, nullptr, LinphoneReasonBusy, 486, "Busy - duplicated call", + "Busy - duplicated call"); core->reportEarlyCallFailed(LinphoneCallIncoming, from, to, ei, h->getCallId()); h->release(); linphone_error_info_unref(ei); @@ -976,10 +989,14 @@ static void message_delivery_update(SalOp *op, SalMessageDeliveryStatus status) auto chatRoom = msg->getChatRoom(); // Check that the message does not belong to an already destroyed chat room - if so, do not invoke callbacks if (chatRoom) { - L_GET_PRIVATE(msg)->setParticipantState( - chatRoom->getMe()->getAddress(), - (LinphonePrivate::ChatMessage::State)chatStatusSal2Linphone(lc, status, op->getErrorInfo()), - ::ms_time(NULL)); + const auto &me = chatRoom->getMe(); + if (me) { + auto errorInfo = op->getErrorInfo(); + auto reason = errorInfo ? linphone_reason_from_sal(errorInfo->reason) : LinphoneReasonNone; + L_GET_PRIVATE(msg)->setParticipantState( + me->getAddress(), (LinphonePrivate::ChatMessage::State)chatStatusSal2Linphone(lc, status, errorInfo), + ::ms_time(NULL), reason); + } } } @@ -1065,10 +1082,6 @@ static void subscribe_received(SalSubscribeOp *op, const char *eventname, const if (strcmp(linphone_event_get_name(lev), "conference") == 0) linphone_event_set_internal(lev, TRUE); linphone_event_set_state(lev, LinphoneSubscriptionIncomingReceived); LinphoneContent *ct = linphone_content_from_sal_body_handler(body_handler); - LinphoneAccount *account = linphone_core_lookup_known_account(lc, Address(op->getTo()).toC()); - if (account && linphone_account_params_get_realm(linphone_account_get_params(account))) { - op->setRealm(linphone_account_params_get_realm(linphone_account_get_params(account))); - } auto nbRefBeforeCbs = cppLev->getRefCount(); linphone_core_notify_subscribe_received(lc, lev, eventname, ct); auto nbRefAfterCbs = cppLev->getRefCount(); @@ -1161,13 +1174,11 @@ static void on_expire(SalOp *op) { linphone_event_set_state(lev, LinphoneSubscriptionExpiring); } + /* FIXME: the Account should simply use the EventCbs to get notified of Expiring state */ void *user_data = linphone_event_get_user_data(lev); const char *event_name = linphone_event_get_name(lev); if (user_data && event_name && linphone_event_is_internal(lev) && strcmp(event_name, "presence") == 0) { - LinphoneCore *lc = (LinphoneCore *)op->getSal()->getUserPointer(); - LinphoneAddress *identity_address = (LinphoneAddress *)user_data; - auto account = L_GET_CPP_PTR_FROM_C_OBJECT(lc)->findAccountByIdentityAddress( - Address::toCpp(identity_address)->getSharedFromThis()); + Account *account = (Account *)user_data; if (account) { lInfo() << "Presence publish about to expire, manually refreshing it for account [" << account << "]"; account->sendPublish(); @@ -1218,12 +1229,14 @@ static void refer_received(SalOp *op, method = referToAddr->getUriParamValue("method"); } std::shared_ptr<LinphonePrivate::Address> to = LinphonePrivate::Address::create(op->getTo()); - shared_ptr<Conference> conference; + std::shared_ptr<Conference> conference; + std::shared_ptr<Core> core = L_GET_CPP_PTR_FROM_C_OBJECT(lc); + const auto conferenceIdParams = core->createConferenceIdParams(); if (linphone_core_conference_server_enabled(lc)) { // Removal of a participant at the server side - conference = L_GET_CPP_PTR_FROM_C_OBJECT(lc)->findConference(ConferenceId(to, to)); + conference = core->findConference(ConferenceId(to, to, conferenceIdParams), false); } else { - conference = L_GET_CPP_PTR_FROM_C_OBJECT(lc)->findConference(ConferenceId(referToAddr, to)); + conference = core->findConference(ConferenceId(referToAddr, to, conferenceIdParams), false); } SalReferOp *referOp = dynamic_cast<SalReferOp *>(op); if (conference && referOp) { diff --git a/coreapi/chat.c b/coreapi/chat.c index 52efa947a9044e5440333fd442903475913ea1f0..3efb2eb007bb324d96085736deed9ee81c320d18 100644 --- a/coreapi/chat.c +++ b/coreapi/chat.c @@ -61,7 +61,7 @@ LinphoneChatRoom *linphone_core_create_client_group_chat_room(LinphoneCore *lc, return linphone_core_create_client_group_chat_room_2(lc, subject, fallback, FALSE); } -// Deprecated see linphone_core_create_chat_room_6 +// Deprecated see linphone_core_create_chat_room_7 LinphoneChatRoom *linphone_core_create_client_group_chat_room_2(LinphoneCore *lc, const char *subject, bool_t fallback, @@ -71,7 +71,7 @@ LinphoneChatRoom *linphone_core_create_client_group_chat_room_2(LinphoneCore *lc ->toC(); } -// Deprecated see linphone_core_create_chat_room_6 +// Deprecated see linphone_core_create_chat_room_7 LinphoneChatRoom *linphone_core_create_chat_room(LinphoneCore *lc, const LinphoneChatRoomParams *params, const LinphoneAddress *localAddr, @@ -84,7 +84,7 @@ LinphoneChatRoom *linphone_core_create_chat_room(LinphoneCore *lc, return result; } -// Deprecated see linphone_core_create_chat_room_6 +// Deprecated see linphone_core_create_chat_room_7 LinphoneChatRoom *linphone_core_create_chat_room_2(LinphoneCore *lc, const LinphoneChatRoomParams *params, const char *subject, @@ -96,7 +96,7 @@ LinphoneChatRoom *linphone_core_create_chat_room_2(LinphoneCore *lc, return result; } -// Deprecated see linphone_core_create_chat_room_6 +// Deprecated see linphone_core_create_chat_room_7 LinphoneChatRoom * linphone_core_create_chat_room_3(LinphoneCore *lc, const char *subject, const bctbx_list_t *participants) { LinphoneChatRoomParams *params = linphone_core_create_default_chat_room_params(lc); @@ -106,7 +106,7 @@ linphone_core_create_chat_room_3(LinphoneCore *lc, const char *subject, const bc return result; } -// Deprecated see linphone_core_create_chat_room_6 +// Deprecated see linphone_core_create_chat_room_7 LinphoneChatRoom *linphone_core_create_chat_room_4(LinphoneCore *lc, const LinphoneChatRoomParams *params, const LinphoneAddress *localAddr, @@ -117,7 +117,7 @@ LinphoneChatRoom *linphone_core_create_chat_room_4(LinphoneCore *lc, return result; } -// Deprecated see linphone_core_create_chat_room_6 +// Deprecated see linphone_core_create_chat_room_7 LinphoneChatRoom *linphone_core_create_chat_room_5(LinphoneCore *lc, const LinphoneAddress *participant) { bctbx_list_t *paricipants = bctbx_list_prepend(NULL, (LinphoneAddress *)participant); LinphoneChatRoomParams *params = linphone_core_create_default_chat_room_params(lc); @@ -127,33 +127,37 @@ LinphoneChatRoom *linphone_core_create_chat_room_5(LinphoneCore *lc, const Linph return result; } +// Deprecated see linphone_core_create_chat_room_7 LinphoneChatRoom *linphone_core_create_chat_room_6(LinphoneCore *lc, const LinphoneChatRoomParams *params, const LinphoneAddress *localAddr, const bctbx_list_t *participants) { - return linphone_core_create_chat_room_7(lc, params, localAddr, participants); + LinphoneConferenceParams *cloned_params = NULL; + if (params) { + cloned_params = linphone_conference_params_clone(params); + } else { + cloned_params = linphone_core_create_conference_params_2(lc, NULL); + } + if (!linphone_conference_params_get_account(cloned_params) && localAddr) { + linphone_conference_params_set_account(cloned_params, linphone_core_lookup_account_by_identity(lc, localAddr)); + } + LinphoneChatRoom *chat_room = linphone_core_create_chat_room_7(lc, cloned_params, participants); + if (cloned_params) { + linphone_conference_params_unref(cloned_params); + } + return chat_room; } LinphoneChatRoom *linphone_core_create_chat_room_7(LinphoneCore *lc, const LinphoneConferenceParams *params, - const LinphoneAddress *localAddr, const bctbx_list_t *participants) { CoreLogContextualizer logContextualizer(lc); shared_ptr<LinphonePrivate::ConferenceParams> conferenceParams = params ? LinphonePrivate::ConferenceParams::toCpp(params)->clone()->toSharedPtr() : nullptr; - // If a participant has an invalid address, the pointer to its address is NULL. - // For the purpose of building an std::list from a bctbx_list_t, replace it by an empty Address (that is invalid) - std::list<std::shared_ptr<const LinphonePrivate::Address>> participantsList; - for (const bctbx_list_t *elem = participants; elem != NULL; elem = elem->next) { - const LinphoneAddress *data = static_cast<const LinphoneAddress *>(bctbx_list_get_data(elem)); - participantsList.push_back(LinphonePrivate::Address::toCpp(data)->getSharedFromThis()); - } - - shared_ptr<const LinphonePrivate::Address> identityAddress = - localAddr ? LinphonePrivate::Address::toCpp(localAddr)->getSharedFromThis() - : L_GET_PRIVATE_FROM_C_OBJECT(lc)->getDefaultLocalAddress(nullptr, false); + const list<std::shared_ptr<LinphonePrivate::Address>> participantsList = + LinphonePrivate::Utils::bctbxListToCppSharedPtrList<LinphoneAddress, Address>(participants); shared_ptr<LinphonePrivate::AbstractChatRoom> room = - L_GET_PRIVATE_FROM_C_OBJECT(lc)->createChatRoom(conferenceParams, identityAddress, participantsList); + L_GET_PRIVATE_FROM_C_OBJECT(lc)->createChatRoom(conferenceParams, participantsList); if (room) { auto cRoom = room->toC(); linphone_chat_room_ref(cRoom); @@ -180,14 +184,20 @@ LinphoneChatRoom *linphone_core_search_chat_room_2(const LinphoneCore *lc, shared_ptr<LinphonePrivate::ConferenceParams> conferenceParams = params ? LinphonePrivate::ConferenceParams::toCpp(params)->clone()->toSharedPtr() : nullptr; const list<std::shared_ptr<LinphonePrivate::Address>> participantsList = - LinphonePrivate::Address::getCppListFromCList(participants); - shared_ptr<const LinphonePrivate::Address> identityAddress = - localAddr ? LinphonePrivate::Address::toCpp(localAddr)->getSharedFromThis() - : L_GET_PRIVATE_FROM_C_OBJECT(lc)->getDefaultLocalAddress(nullptr, false); + LinphonePrivate::Utils::bctbxListToCppSharedPtrList<LinphoneAddress, Address>(participants); + shared_ptr<const LinphonePrivate::Address> localAddress = + localAddr ? LinphonePrivate::Address::toCpp(localAddr)->getSharedFromThis() : nullptr; shared_ptr<const LinphonePrivate::Address> remoteAddress = remoteAddr ? LinphonePrivate::Address::toCpp(remoteAddr)->getSharedFromThis() : nullptr; shared_ptr<LinphonePrivate::AbstractChatRoom> room = L_GET_PRIVATE_FROM_C_OBJECT(lc)->searchChatRoom( - conferenceParams, identityAddress, remoteAddress, participantsList); + conferenceParams, localAddress, remoteAddress, participantsList); + if (room) return room->toC(); + return NULL; +} + +LinphoneChatRoom *linphone_core_search_chat_room_by_identifier(const LinphoneCore *lc, const char *identifier) { + shared_ptr<LinphonePrivate::AbstractChatRoom> room = + L_GET_PRIVATE_FROM_C_OBJECT(lc)->searchChatRoom(L_C_TO_STRING(identifier)); if (room) return room->toC(); return NULL; } @@ -207,15 +217,15 @@ void linphone_core_delete_chat_room(LinphoneCore *lc, LinphoneChatRoom *cr) { LinphonePrivate::AbstractChatRoom::toCpp(cr)->deleteFromDb(); } -// Deprecated see linphone_core_search_chat_room +// Deprecated see linphone_core_search_chat_room_2 LinphoneChatRoom *linphone_core_get_chat_room(LinphoneCore *lc, const LinphoneAddress *peerAddr) { return linphone_core_get_chat_room_2(lc, peerAddr, NULL); } -// Deprecated see linphone_core_search_chat_room +// Deprecated see linphone_core_search_chat_room_2 LinphoneChatRoom * linphone_core_get_chat_room_2(LinphoneCore *lc, const LinphoneAddress *peer_addr, const LinphoneAddress *local_addr) { - LinphoneChatRoom *result = linphone_core_search_chat_room(lc, NULL, local_addr, peer_addr, NULL); + LinphoneChatRoom *result = linphone_core_search_chat_room_2(lc, NULL, local_addr, peer_addr, NULL); if (result == NULL) { bctbx_list_t *paricipants = bctbx_list_prepend(NULL, (LinphoneAddress *)peer_addr); LinphoneChatRoomParams *params = linphone_core_create_default_chat_room_params(lc); @@ -229,7 +239,7 @@ linphone_core_get_chat_room_2(LinphoneCore *lc, const LinphoneAddress *peer_addr return result; } -// Deprecated see linphone_core_search_chat_room +// Deprecated see linphone_core_search_chat_room_2 LinphoneChatRoom *linphone_core_get_chat_room_from_uri(LinphoneCore *lc, const char *to) { LinphoneAddress *addr = linphone_core_interpret_url(lc, to); LinphoneChatRoom *room = linphone_core_get_chat_room(lc, addr); @@ -237,15 +247,15 @@ LinphoneChatRoom *linphone_core_get_chat_room_from_uri(LinphoneCore *lc, const c return room; } -// Deprecated see linphone_core_search_chat_room +// Deprecated see linphone_core_search_chat_room_2 LinphoneChatRoom *linphone_core_find_chat_room(const LinphoneCore *lc, const LinphoneAddress *peer_addr, const LinphoneAddress *local_addr) { - LinphoneChatRoom *result = linphone_core_search_chat_room(lc, NULL, local_addr, peer_addr, NULL); + LinphoneChatRoom *result = linphone_core_search_chat_room_2(lc, NULL, local_addr, peer_addr, NULL); return result; } -// Deprecated see linphone_core_search_chat_room +// Deprecated see linphone_core_search_chat_room_2 LinphoneChatRoom *linphone_core_find_one_to_one_chat_room(const LinphoneCore *lc, const LinphoneAddress *local_addr, const LinphoneAddress *participant_addr) { @@ -263,7 +273,7 @@ LinphoneChatRoom *linphone_core_find_one_to_one_chat_room(const LinphoneCore *lc return result; } -// Deprecated see linphone_core_search_chat_room +// Deprecated see linphone_core_search_chat_room_2 LinphoneChatRoom *linphone_core_find_one_to_one_chat_room_2(const LinphoneCore *lc, const LinphoneAddress *local_addr, const LinphoneAddress *participant_addr, diff --git a/coreapi/help/examples/C/buddy_status.c b/coreapi/help/examples/C/buddy_status.c index bf775c43d7ca2f9133be932252a2938283aba293..4af11f0f7947a7f74cfdf5c8f29f525e4a516e9f 100644 --- a/coreapi/help/examples/C/buddy_status.c +++ b/coreapi/help/examples/C/buddy_status.c @@ -129,7 +129,7 @@ int main(int argc, char *argv[]) { /*sip proxy might be requested*/ if (identity != NULL) { /*create account parameters*/ - LinphoneAccountParams *params = linphone_account_params_new(NULL); + LinphoneAccountParams *params = linphone_account_params_new(nullptr, FALSE); /*parse identity*/ LinphoneAddress *from = linphone_address_new(identity); LinphoneAuthInfo *info; diff --git a/coreapi/help/examples/C/notify.c b/coreapi/help/examples/C/notify.c index 6aba31aaaab1b8ffa6da1875e2120773d13a31f5..7cee6d9927ab6e95bc860673e446156de983938e 100644 --- a/coreapi/help/examples/C/notify.c +++ b/coreapi/help/examples/C/notify.c @@ -122,7 +122,7 @@ int main(int argc, char *argv[]) { lc = linphone_core_new(&vtable, NULL, NULL, data); /*create account parameters*/ - LinphoneAccountParams *params = linphone_account_params_new(NULL); + LinphoneAccountParams *params = linphone_account_params_new(nullptr, FALSE); /*parse identity*/ LinphoneAddress *from = linphone_address_new(identity); LinphoneAuthInfo *info; diff --git a/coreapi/help/examples/C/registration.c b/coreapi/help/examples/C/registration.c index c6a71da359a98b48e78df0fb0eecd6d66df3d804..0f64b31b3b8ca46968136f49b4286fcc91e15c1a 100644 --- a/coreapi/help/examples/C/registration.c +++ b/coreapi/help/examples/C/registration.c @@ -95,7 +95,7 @@ int main(int argc, char *argv[]) { lc = linphone_core_new(&vtable, NULL, NULL, NULL); /*create account parameters*/ - LinphoneAccountParams *params = linphone_account_params_new(NULL); + LinphoneAccountParams *params = linphone_account_params_new(nullptr, FALSE); /*parse identity*/ LinphoneAddress *from = linphone_address_new(identity); LinphoneAuthInfo *info; diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 722c2aa082cf2ff938085251a73c0aac86190169..decbdedfdcb650d42d9b78a322d42e010e31acb6 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2022 Belledonne Communications SARL. + * Copyright (c) 2010-2025 Belledonne Communications SARL. * * This file is part of Liblinphone * (see https://gitlab.linphone.org/BC/public/liblinphone). @@ -162,6 +162,16 @@ static const char *liblinphone_version = #endif ; +#ifdef HAVE_STATIC_MSAAUDIO +extern "C" void libmsaaudio_init(MSFactory *factory); +#endif +#ifdef HAVE_STATIC_MSANDROIDCAMERA2 +extern "C" void libmsandroidcamera2_init(MSFactory *factory); +#endif +#ifdef HAVE_STATIC_MSWEBRTC +extern "C" void libmswebrtc_init(MSFactory *factory); +#endif + inline OrtpLogLevel operator|=(OrtpLogLevel a, OrtpLogLevel b) { int ia = static_cast<int>(a); int ib = static_cast<int>(b); @@ -2905,6 +2915,7 @@ static void linphone_core_internal_notify_received(LinphoneCore *lc, LinphoneEvent *lev, const char *notified_event, const LinphoneContent *body) { + std::shared_ptr<Core> core = L_GET_CPP_PTR_FROM_C_OBJECT(lc); if (strcmp(notified_event, "Presence") == 0) { for (const bctbx_list_t *it = linphone_core_get_friends_lists(lc); it; it = bctbx_list_next(it)) { LinphoneFriendList *list = reinterpret_cast<LinphoneFriendList *>(bctbx_list_get_data(it)); @@ -2931,10 +2942,9 @@ static void linphone_core_internal_notify_received(LinphoneCore *lc, } const auto fromAddr = ev->getFrom(); - LinphonePrivate::ConferenceId conferenceId = LinphonePrivate::ConferenceId(resourceAddr, fromAddr); - shared_ptr<AbstractChatRoom> chatRoom = L_GET_CPP_PTR_FROM_C_OBJECT(lc)->findChatRoom(conferenceId); - shared_ptr<Conference> conference = - (chatRoom) ? chatRoom->getConference() : L_GET_CPP_PTR_FROM_C_OBJECT(lc)->findConference(conferenceId); + LinphonePrivate::ConferenceId conferenceId = + LinphonePrivate::ConferenceId(resourceAddr, fromAddr, core->createConferenceIdParams()); + shared_ptr<Conference> conference = core->findConference(conferenceId, false); Content content = body ? *Content::toCpp(body) : Content(); if (conference) { shared_ptr<ClientConference> clientConference = dynamic_pointer_cast<ClientConference>(conference); @@ -2943,13 +2953,16 @@ static void linphone_core_internal_notify_received(LinphoneCore *lc, } else { clientConference->notifyReceived(ev, content); } + } else { + lError() << "Unable to handle NOTIFY message because no conference with id " << conferenceId + << " has been found"; } #else - ms_message("Advanced IM such as group chat is disabled!"); + lWarning() << "Unable to handle NOTIFY because advanced IM such as group chat is disabled!"; #endif } else if (Utils::iequals(notified_event, "message-summary")) { - L_GET_CPP_PTR_FROM_C_OBJECT(lc)->handleIncomingMessageWaitingIndication(Event::getSharedFromThis(lev), - body ? Content::toCpp(body) : nullptr); + core->handleIncomingMessageWaitingIndication(Event::getSharedFromThis(lev), + body ? Content::toCpp(body) : nullptr); } } @@ -2970,15 +2983,18 @@ _linphone_core_conference_subscribe_received(LinphoneCore *lc, LinphoneEvent *le auto evSub = dynamic_pointer_cast<EventSubscribe>(Event::toCpp(lev)->getSharedFromThis()); const std::shared_ptr<Address> conferenceAddress = evSub->getResource(); - LinphonePrivate::ConferenceId conferenceId = LinphonePrivate::ConferenceId(conferenceAddress, conferenceAddress); - shared_ptr<AbstractChatRoom> chatRoom = L_GET_CPP_PTR_FROM_C_OBJECT(lc)->findChatRoom(conferenceId); - shared_ptr<Conference> conference = - (chatRoom) ? chatRoom->getConference() : L_GET_CPP_PTR_FROM_C_OBJECT(lc)->findConference(conferenceId); + std::shared_ptr<Core> core = L_GET_CPP_PTR_FROM_C_OBJECT(lc); + LinphonePrivate::ConferenceId conferenceId = + LinphonePrivate::ConferenceId(conferenceAddress, conferenceAddress, core->createConferenceIdParams()); + shared_ptr<Conference> conference = core->findConference(conferenceId, false); if (conference) static_pointer_cast<ServerConference>(conference)->subscribeReceived(evSub); - else linphone_event_deny_subscription(lev, LinphoneReasonDeclined); + else { + lError() << "Denying subscription because no conference with id " << conferenceId << " has been found"; + linphone_event_deny_subscription(lev, LinphoneReasonDeclined); + } #else // !HAVE_ADVANCED_IM linphone_event_deny_subscription(lev, LinphoneReasonNotAcceptable); - ms_warning("Advanced IM such as group chat is disabled!"); + lWarning() << "Denying subscription because advanced IM such as group chat is disabled!"; #endif // HAVE_ADVANCED_IM } #ifndef _MSC_VER @@ -3019,15 +3035,20 @@ static void _linphone_core_conference_subscription_state_changed(LinphoneCore *l } else { /* This has to be done only when running as server */ const auto &conferenceAddress = evSub->getResource(); + std::shared_ptr<Core> core = L_GET_CPP_PTR_FROM_C_OBJECT(lc); LinphonePrivate::ConferenceId conferenceId = - LinphonePrivate::ConferenceId(conferenceAddress, conferenceAddress); - shared_ptr<AbstractChatRoom> chatRoom = L_GET_CPP_PTR_FROM_C_OBJECT(lc)->findChatRoom(conferenceId); - shared_ptr<Conference> conference = L_GET_CPP_PTR_FROM_C_OBJECT(lc)->findConference(conferenceId); - if (chatRoom) static_pointer_cast<ServerChatRoom>(chatRoom)->subscriptionStateChanged(evSub, state); - else if (conference) static_pointer_cast<ServerConference>(conference)->subscriptionStateChanged(evSub, state); + LinphonePrivate::ConferenceId(conferenceAddress, conferenceAddress, core->createConferenceIdParams()); + shared_ptr<Conference> conference = core->findConference(conferenceId, false); + if (conference) static_pointer_cast<ServerConference>(conference)->subscriptionStateChanged(evSub, state); + else { + lWarning() << "Unable to handle subscripton state changed to " + << linphone_subscription_state_to_string(state) << " because no conference with id " + << conferenceId << " has been found"; + } } #else - ms_warning("Advanced IM such as group chat is disabled!"); + lWarning() << "Unable to handle subscripton state changed to " << linphone_subscription_state_to_string(state) + << " because advanced IM such as group chat is disabled!"; #endif } #ifndef _MSC_VER @@ -3214,15 +3235,15 @@ bool_t linphone_core_push_notification_enabled(LinphoneCore *core) { } void linphone_core_did_register_for_remote_push(LinphoneCore *lc, void *device_token) { - getPlatformHelpers(lc)->didRegisterForRemotePush(device_token); + if (lc->platform_helper) getPlatformHelpers(lc)->didRegisterForRemotePush(device_token); } void linphone_core_did_register_for_remote_push_with_stringified_token(LinphoneCore *lc, const char *device_token_str) { - getPlatformHelpers(lc)->didRegisterForRemotePushWithStringifiedToken(device_token_str); + if (lc->platform_helper) getPlatformHelpers(lc)->didRegisterForRemotePushWithStringifiedToken(device_token_str); } void linphone_core_set_push_and_app_delegate_dispatch_queue(LinphoneCore *lc, void *dispatch_queue) { - getPlatformHelpers(lc)->setPushAndAppDelegateDispatchQueue(dispatch_queue); + if (lc->platform_helper) getPlatformHelpers(lc)->setPushAndAppDelegateDispatchQueue(dispatch_queue); } void linphone_core_set_auto_iterate_enabled(LinphoneCore *core, bool_t enable) { @@ -3388,6 +3409,15 @@ static void linphone_core_init(LinphoneCore *lc, // LinphonePlayer/LinphoneRecorder. if (!lc->factory) { lc->factory = ms_factory_new_with_voip_and_directories(msplugins_dir, image_resources_dir); +#ifdef HAVE_STATIC_MSAAUDIO + libmsaaudio_init(lc->factory); +#endif +#ifdef HAVE_STATIC_MSANDROIDCAMERA2 + libmsandroidcamera2_init(lc->factory); +#endif +#ifdef HAVE_STATIC_MSWEBRTC + libmswebrtc_init(lc->factory); +#endif } lc->sal->setFactory(lc->factory); @@ -3863,9 +3893,10 @@ void linphone_core_remove_friend_list(LinphoneCore *lc, LinphoneFriendList *list lc->cppPtr->removeFriendList(cppList->getSharedFromThis()); cppList->removeFromDb(); + lc->friends_lists = bctbx_list_erase_link(lc->friends_lists, elem); + linphone_core_notify_friend_list_removed(lc, list); linphone_friend_list_unref(list); - lc->friends_lists = bctbx_list_erase_link(lc->friends_lists, elem); } void linphone_core_clear_bodyless_friend_lists(LinphoneCore *lc) { @@ -4711,7 +4742,7 @@ LinphoneProxyConfig *linphone_core_lookup_proxy_by_identity(LinphoneCore *lc, co } /* - * Returns an account matching the given identity address + * Returns an account matching the given conference factory address * Prefers registered, then first registering matching, otherwise first matching * returns NULL if none is found */ @@ -4885,7 +4916,8 @@ void linphone_configure_op_with_account(LinphoneCore *lc, linphone_account_params_get_identity_address(linphone_account_get_params(account)); if (!identity) { - lError() << "No from identity to configure the op."; + lError() << "Unable to set the from address of op [" << op << "] because the identity address of account " + << *Account::toCpp(account) << " has not been set"; return; } @@ -4898,7 +4930,7 @@ void linphone_configure_op_with_account(LinphoneCore *lc, const char *identity = linphone_core_get_primary_contact(lc); if (!identity) { - lError() << "No from identity to configure the op."; + lError() << "Unable to set the from address of op [" << op << "] because the primary contact is not known"; return; } @@ -4932,6 +4964,10 @@ void linphone_configure_op_with_account(LinphoneCore *lc, if (callOp && linphone_config_get_int(lc->config, "sip", "notify_all_ringings", 0)) { callOp->setNotifyAllRingings(true); } + + if (account && Account::toCpp(account)->getAccountParams()->useSupportedTags()) { + op->makeSupportedHeader(Account::toCpp(account)->getAccountParams()->getSupportedTagsList()); + } } void linphone_configure_op( @@ -6332,12 +6368,13 @@ void linphone_core_set_call_logs_database_path(LinphoneCore *lc, const char *pat CoreLogContextualizer logContextualizer(lc); if (!linphone_core_conference_server_enabled(lc)) { auto &mainDb = L_GET_PRIVATE_FROM_C_OBJECT(lc)->mainDb; - if (mainDb) { + if (mainDb && mainDb->isInitialized()) { mainDb->import(LinphonePrivate::MainDb::Sqlite3, path); linphone_core_migrate_logs_from_rc_to_db(lc); } else { - ms_warning("linphone_core_set_call_logs_database_path() needs to be called once linphone_core_start() has " - "been called"); + ms_warning("%s() needs to be called once linphone_core_start() has been called or Database has not been " + "initialized, therefore it is not possible to import call log database at %s", + __func__, path); } } } @@ -6464,7 +6501,7 @@ void linphone_core_resize_video_preview(LinphoneCore *lc, int width, int height) if (!auto_camera_preview_resize) return; #ifdef VIDEO_ENABLED bctbx_message("Resizing camera video preview to: %ix%i", width, height); - getPlatformHelpers(lc)->resizeVideoPreview(width, height); + if (lc->platform_helper) getPlatformHelpers(lc)->resizeVideoPreview(width, height); #endif } #ifndef _MSC_VER @@ -7043,7 +7080,7 @@ void _linphone_core_set_native_video_window_id(LinphoneCore *lc, void *id) { void linphone_core_set_native_video_window_id(LinphoneCore *lc, void *id) { #ifdef __ANDROID__ - getPlatformHelpers(lc)->setVideoWindow(id); + if (lc->platform_helper) getPlatformHelpers(lc)->setVideoWindow(id); #else _linphone_core_set_native_video_window_id(lc, id); #endif @@ -7111,7 +7148,7 @@ void _linphone_core_set_native_preview_window_id(LinphoneCore *lc, void *id) { #endif // _MSC_VER void linphone_core_set_native_preview_window_id(LinphoneCore *lc, void *id) { #ifdef __ANDROID__ - getPlatformHelpers(lc)->setVideoPreviewWindow(id); + if (lc->platform_helper) getPlatformHelpers(lc)->setVideoPreviewWindow(id); #else _linphone_core_set_native_preview_window_id(lc, id); #endif @@ -8098,10 +8135,9 @@ static void stop_refreshing_account(bool_t is_sip_reachable, LinphoneAccount *ac } static void set_sip_network_reachable(LinphoneCore *lc, bool_t is_sip_reachable, time_t curtime) { - if (is_sip_reachable) { // Update DNS servers even if network was reachable and is still is, a change might have occured - getPlatformHelpers(lc)->setDnsServers(); + if (lc->platform_helper) getPlatformHelpers(lc)->setDnsServers(); } if (lc->sip_network_state.global_state == is_sip_reachable) return; // no change, ignore. @@ -8887,12 +8923,13 @@ void linphone_core_set_chat_database_path(LinphoneCore *lc, const char *path) { CoreLogContextualizer logContextualizer(lc); if (!linphone_core_conference_server_enabled(lc)) { auto &mainDb = L_GET_PRIVATE_FROM_C_OBJECT(lc)->mainDb; - if (mainDb) { + if (mainDb && mainDb->isInitialized()) { mainDb->import(LinphonePrivate::MainDb::Sqlite3, path); L_GET_PRIVATE_FROM_C_OBJECT(lc)->loadChatRooms(); } else { - ms_warning("linphone_core_set_chat_database_path() needs to be called once linphone_core_start() has " - "been called"); + ms_warning("%s() needs to be called once linphone_core_start() has been called or Database has not been " + "initialized, therefore it is not possible to import chat database at %s", + __func__, path); } } } @@ -9133,6 +9170,14 @@ bool_t linphone_core_video_multicast_enabled(const LinphoneCore *lc) { return lc->rtp_conf.video_multicast_enabled; } +void linphone_core_disable_call_ringing(const LinphoneCore *lc, bool_t yesno) { + linphone_config_set_int(linphone_core_get_config(lc), "sound", "disable_ringing", yesno); +} + +bool_t linphone_core_call_ringing_disabled(const LinphoneCore *lc) { + return !!linphone_config_get_int(linphone_core_get_config(lc), "sound", "disable_ringing", FALSE); +} + void linphone_core_enable_call_tone_indications(const LinphoneCore *lc, bool_t yesno) { linphone_config_set_int(linphone_core_get_config(lc), "misc", "tone_indications", yesno); } @@ -9258,8 +9303,13 @@ static int _linphone_core_delayed_conference_destruction_cb(void *user_data, BCT static void _linphone_core_conference_state_changed(LinphoneConference *conf, LinphoneConferenceState cstate) { if (cstate == LinphoneConferenceStateDeleted) { LinphoneCore *lc = linphone_conference_get_core(conf); - linphone_core_queue_task(lc, _linphone_core_delayed_conference_destruction_cb, conf, - "Conference destruction task"); + if (linphone_core_get_global_state(lc) == LinphoneGlobalShutdown) { + // If the core is in the Shutdown state, there is no time to queue a task + linphone_conference_unref(conf); + } else { + linphone_core_queue_task(lc, _linphone_core_delayed_conference_destruction_cb, conf, + "Conference destruction task"); + } lc->conf_ctx = NULL; } } @@ -9278,98 +9328,103 @@ LinphoneConference *linphone_core_create_conference_with_params(LinphoneCore *lc LinphoneConference *conf = nullptr; bool serverMode = params && !linphone_conference_params_local_participant_enabled(params); - /* In server mode, it is allowed to create multiple conferences. */ - if (lc->conf_ctx == NULL || serverMode) { - bool errorOnRemoteConference = false; - // Get factory and identity from linphone conference params, or from default account. - LinphoneAddress *identity; - LinphoneConferenceParams *params2 = linphone_conference_params_clone(params); - linphone_conference_params_enable_audio(params2, TRUE); - LinphoneAccount *conference_account = linphone_conference_params_get_account(params2); - LinphoneAccount *default_account = linphone_core_get_default_account(lc); - if (!conference_account) { - lWarning() << "The application didn't explicitely specified the account to use to create a conference, " - "therefore the core is going to use the default account"; - linphone_conference_params_set_account(params2, default_account); - } - const LinphoneAddress *factory_uri_const = linphone_conference_params_get_conference_factory_address(params2); - conf_method_name = linphone_config_get_string(lc->config, "misc", "conference_type", NULL); - - // Get identity - if (conf_method_name) { - // backward compatibility : use default identity even if set in conference parameters - identity = linphone_address_new(linphone_core_get_identity(lc)); + bool errorOnRemoteConference = false; + // Get factory and identity from linphone conference params, or from default account. + LinphoneAddress *identity; + LinphoneConferenceParams *params2 = linphone_conference_params_clone(params); + linphone_conference_params_enable_audio(params2, TRUE); + LinphoneAccount *conference_account = linphone_conference_params_get_account(params2); + LinphoneAccount *default_account = linphone_core_get_default_account(lc); + if (!conference_account) { + lWarning() << "The application didn't explicitely specified the account to use to create a conference, " + "therefore the core is going to use the default account"; + conference_account = linphone_core_get_default_account(lc); + linphone_conference_params_set_account(params2, conference_account); + } + const LinphoneAddress *factory_uri_const = linphone_conference_params_get_conference_factory_address(params2); + conf_method_name = linphone_config_get_string(lc->config, "misc", "conference_type", NULL); + const char *core_identity = linphone_core_get_identity(lc); + + // Get identity + if (conf_method_name) { + // backward compatibility : use default identity even if set in conference parameters + identity = linphone_address_new(core_identity); + } else { + if (conference_account) { + identity = linphone_address_clone( + linphone_account_params_get_identity_address(linphone_account_get_params(conference_account))); + lInfo() << "Creating conference with identity from conference " << *Account::toCpp(conference_account); } else { - const std::shared_ptr<LinphonePrivate::Address> &identity_address = - LinphonePrivate::ConferenceParams::toCpp(params2)->getMe(); - if (identity_address && identity_address->isValid()) { - lInfo() << "Creating conference with identity from conference params : " << *identity_address; - identity = linphone_address_clone(identity_address->toC()); - } else { - identity = linphone_address_new(linphone_core_get_identity(lc)); - lInfo() << "Creating conference with identity from default account " - << *LinphonePrivate::Address::toCpp(identity); - } - } - - // Create a server conference if: - // - it is a conference server - // - conference type is not defined and conference factory address is not defined - // - conference type is local - if (linphone_core_conference_server_enabled(lc) || - (!conf_method_name && !linphone_address_is_valid(factory_uri_const)) || - (conf_method_name && strcasecmp(conf_method_name, "local") == 0)) { - lInfo() << "Creating server conference "; - conf = linphone_local_conference_new_with_params(lc, identity, params2); - } else if (!serverMode) { - // Get Factory URI - LinphoneAddress *factory_uri = nullptr; - if (conf_method_name) { // Priority for conf method. - if (strcasecmp(conf_method_name, "remote") == 0) { - if (default_account) { - factory_uri = linphone_address_clone(linphone_account_params_get_conference_factory_address( - linphone_account_get_params(default_account))); - char *factory_uri_str = factory_uri ? linphone_address_as_string(factory_uri) : NULL; - lInfo() << "Creating client conference with factory address from default account : " - << std::string(factory_uri_str); + const char *core_identity = linphone_core_get_identity(lc); + identity = linphone_address_new(core_identity); + lInfo() << "Creating conference with identity from core identity " << core_identity; + } + } + + // Create a server conference if: + // - it is a conference server + // - conference type is not defined and conference factory address is not defined + // - conference type is local + if (linphone_core_conference_server_enabled(lc) || + (!conf_method_name && !linphone_address_is_valid(factory_uri_const)) || + (conf_method_name && strcasecmp(conf_method_name, "local") == 0)) { + lInfo() << "Creating server conference "; + conf = linphone_local_conference_new_with_params(lc, identity, params2); + } else if (!serverMode) { + // Get Factory URI + LinphoneAddress *factory_uri = nullptr; + if (conf_method_name) { // Priority for conf method. + if (strcasecmp(conf_method_name, "remote") == 0) { + if (default_account) { + factory_uri = linphone_address_clone(linphone_account_params_get_conference_factory_address( + linphone_account_get_params(default_account))); + char *factory_uri_str = factory_uri ? linphone_address_as_string(factory_uri) : NULL; + lInfo() << "Creating client conference with factory address from default account : " + << std::string(factory_uri_str); + if (factory_uri_str) { ms_free(factory_uri_str); - } else { - ms_error("Cannot create a client conference from default account : no account available"); - errorOnRemoteConference = true; } } else { - ms_error("Creating client conference : '%s' is not a valid conference method", conf_method_name); + ms_error("Cannot create a client conference from default account : no account available"); errorOnRemoteConference = true; } - } else { // case of: !conf_method_name && factory_uri_str != "" - factory_uri = linphone_address_clone(factory_uri_const); - char *factory_uri_str = factory_uri ? linphone_address_as_string(factory_uri) : NULL; - lInfo() << "Creating client conference with factory address from conference params : " - << std::string(factory_uri_str); - ms_free(factory_uri_str); + } else { + ms_error("Creating client conference : '%s' is not a valid conference method", conf_method_name); + errorOnRemoteConference = true; } - if (!errorOnRemoteConference) { - conf = linphone_remote_conference_new_with_params(lc, factory_uri, identity, params2); - linphone_address_unref(factory_uri); + } else { // case of: !conf_method_name && factory_uri_str != "" + factory_uri = linphone_address_clone(factory_uri_const); + char *factory_uri_str = factory_uri ? linphone_address_as_string(factory_uri) : NULL; + lInfo() << "Creating client conference with factory address from conference params : " + << std::string(factory_uri_str); + if (factory_uri_str) { + ms_free(factory_uri_str); } - } else { - ms_error("Conference method '%s' or parameter factory URI '%s' are not valid for a local conference mode", - conf_method_name, factory_uri_const ? linphone_address_as_string(factory_uri_const) : "NULL"); - errorOnRemoteConference = true; - } - linphone_conference_params_unref(params2); - linphone_address_unref(identity); - if (errorOnRemoteConference) return NULL; - if (!serverMode) { - linphone_core_set_conference(lc, conf); - LinphoneConferenceCbs *cbs = linphone_factory_create_conference_cbs(linphone_factory_get()); - linphone_conference_cbs_set_state_changed(cbs, _linphone_core_conference_state_changed); - linphone_conference_add_callbacks(conf, cbs); - linphone_conference_cbs_unref(cbs); + } + if (!errorOnRemoteConference) { + conf = linphone_remote_conference_new_with_params(lc, factory_uri, identity, params2); + linphone_address_unref(factory_uri); } } else { - ms_error("Could not create a conference: a conference instance already exists"); - return NULL; + char *factory_uri_str = factory_uri_const ? linphone_address_as_string(factory_uri_const) : ms_strdup("NULL"); + ms_error("Conference method '%s' or parameter factory URI '%s' are not valid for a local conference mode", + conf_method_name, factory_uri_str); + + if (factory_uri_str) { + ms_free(factory_uri_str); + } + errorOnRemoteConference = true; + } + linphone_conference_params_unref(params2); + linphone_address_unref(identity); + if (errorOnRemoteConference) return NULL; + + if (!serverMode) { + linphone_core_set_conference(lc, conf); + LinphoneConferenceCbs *cbs = linphone_factory_create_conference_cbs(linphone_factory_get()); + linphone_conference_cbs_set_state_changed(cbs, _linphone_core_conference_state_changed); + linphone_conference_add_callbacks(conf, cbs); + linphone_conference_cbs_unref(cbs); } return conf; } @@ -9437,13 +9492,12 @@ LinphoneConference *linphone_core_search_conference(const LinphoneCore *lc, LinphonePrivate::Utils::bctbxListToCppSharedPtrList<LinphoneAddress, LinphonePrivate::Address>( participants); } - shared_ptr<const LinphonePrivate::Address> identityAddress = - localAddr ? LinphonePrivate::Address::getSharedFromThis(localAddr) - : L_GET_PRIVATE_FROM_C_OBJECT(lc)->getDefaultLocalAddress(nullptr, false); + shared_ptr<const LinphonePrivate::Address> localAddress = + localAddr ? LinphonePrivate::Address::getSharedFromThis(localAddr) : nullptr; shared_ptr<const LinphonePrivate::Address> remoteAddress = remoteAddr ? LinphonePrivate::Address::toCpp(remoteAddr)->getSharedFromThis() : nullptr; shared_ptr<LinphonePrivate::Conference> conf = L_GET_CPP_PTR_FROM_C_OBJECT(lc)->searchConference( - conferenceParams, identityAddress, remoteAddress, participantsList); + conferenceParams, localAddress, remoteAddress, participantsList); LinphoneConference *c_conference = NULL; if (conf) { c_conference = conf->toC(); @@ -9463,6 +9517,13 @@ LinphoneConference *linphone_core_search_conference_2(const LinphoneCore *lc, co return c_conference; } +LinphoneConference *linphone_core_search_conference_by_identifier(const LinphoneCore *lc, const char *identifier) { + shared_ptr<LinphonePrivate::Conference> conference = + L_GET_CPP_PTR_FROM_C_OBJECT(lc)->searchConference(L_C_TO_STRING(identifier)); + if (conference) return conference->toC(); + return NULL; +} + LinphoneConferenceParams *linphone_core_create_conference_params_2(LinphoneCore *lc, LinphoneConference *conference) { CoreLogContextualizer logContextualizer(lc); if (!conference) return linphone_conference_params_new(lc); @@ -9991,22 +10052,28 @@ int linphone_core_get_conference_max_thumbnails(const LinphoneCore *core) { const LinphoneEktInfo *linphone_core_create_ekt_info_from_xml(const LinphoneCore *core, const char *xml_body) { #ifdef HAVE_ADVANCED_IM - auto ei = L_GET_CPP_PTR_FROM_C_OBJECT(core)->createEktInfoFromXml(xml_body); - if (ei) { + if (const auto ei = L_GET_CPP_PTR_FROM_C_OBJECT(core)->createEktInfoFromXml(xml_body)) { ei->ref(); return ei->toC(); } #endif // HAVE_ADVANCED_IM - return NULL; + return nullptr; } char *linphone_core_create_xml_from_ekt_info(const LinphoneCore *core, const LinphoneEktInfo *ekt_info) { + return linphone_core_create_xml_from_ekt_info_2(core, ekt_info, nullptr); +} + +char *linphone_core_create_xml_from_ekt_info_2(const LinphoneCore *core, + const LinphoneEktInfo *ekt_info, + const LinphoneAccount *account) { #ifdef HAVE_ADVANCED_IM - auto ei = EktInfo::toCpp(ekt_info)->getSharedFromThis(); - string xmlBody = L_GET_CPP_PTR_FROM_C_OBJECT(core)->createXmlFromEktInfo(ei); + const auto ei = EktInfo::toCpp(ekt_info)->getSharedFromThis(); + const auto cppAccount = account ? Account::toCpp(account)->getSharedFromThis() : nullptr; + const string xmlBody = L_GET_CPP_PTR_FROM_C_OBJECT(core)->createXmlFromEktInfo(ei, cppAccount); return bctbx_strdup(xmlBody.c_str()); #else - return NULL; + return nullptr; #endif // HAVE_ADVANCED_IM } @@ -10043,3 +10110,11 @@ void linphone_core_enable_baudot(LinphoneCore *lc, bool_t enabled) { bool_t linphone_core_baudot_enabled(const LinphoneCore *lc) { return !!linphone_config_get_int(lc->config, "misc", "enable_baudot", FALSE); } + +void linphone_core_enable_gruu_in_conference_address(LinphoneCore *lc, bool_t enabled) { + linphone_config_set_int(linphone_core_get_config(lc), "misc", "keep_gruu_in_conference_address", enabled); +} + +bool_t linphone_core_gruu_in_conference_address_enabled(const LinphoneCore *lc) { + return !!linphone_config_get_bool(linphone_core_get_config(lc), "misc", "keep_gruu_in_conference_address", TRUE); +} diff --git a/coreapi/misc.c b/coreapi/misc.c index 2d881db27ebcd4b2797d64fc471fcfac23bfc238..284d1fdada685a67171056246d96bb88eaa83626 100644 --- a/coreapi/misc.c +++ b/coreapi/misc.c @@ -348,10 +348,6 @@ unsigned int linphone_core_get_audio_features(LinphoneCore *lc) { return ret; } -bool_t linphone_core_tone_indications_enabled(LinphoneCore *lc) { - return !!linphone_config_get_int(lc->config, "sound", "tone_indications", 1); -} - int linphone_core_get_local_ip_for(int type, const char *dest, char *result) { if (dest && dest[0] == '\0') dest = NULL; /*If null, bctbx_get_local_ip_for() will use an arbitrary public address instead.*/ diff --git a/coreapi/private_functions.h b/coreapi/private_functions.h index 9bcd2e68125aab9547df93f41f0db33d4a816399..8a0f4afe234561f60c578e4c9fdec7598c5ed5e7 100644 --- a/coreapi/private_functions.h +++ b/coreapi/private_functions.h @@ -648,7 +648,6 @@ void linphone_chat_message_set_message_state_changed_cb(LinphoneChatMessage *msg void linphone_chat_message_set_message_state_changed_cb_user_data(LinphoneChatMessage *msg, void *user_data); void *linphone_chat_message_get_message_state_changed_cb_user_data(LinphoneChatMessage *msg); -bool_t linphone_core_tone_indications_enabled(LinphoneCore *lc); const char *linphone_core_create_uuid(LinphoneCore *lc); void linphone_configure_op(LinphoneCore *lc, LinphonePrivate::SalOp *op, diff --git a/coreapi/proxy.c b/coreapi/proxy.c index dcf935e39ec947fa399cc2f76e7128849f18fd31..07ad2285048faa20394c648a7a560d7877bed548 100644 --- a/coreapi/proxy.c +++ b/coreapi/proxy.c @@ -75,7 +75,7 @@ linphone_proxy_config_is_server_config_changed(const LinphoneProxyConfig *cfg) { } static void linphone_proxy_config_init(LinphoneCore *lc, LinphoneProxyConfig *cfg) { - LinphoneAccountParams *params = linphone_account_params_new(lc); + LinphoneAccountParams *params = linphone_account_params_new(lc, TRUE); cfg->account = linphone_account_new_with_config(lc, params, cfg); linphone_account_params_unref(params); cfg->edit = NULL; @@ -553,7 +553,7 @@ const bctbx_list_t *linphone_core_get_proxy_config_list(const LinphoneCore *lc) } LinphoneAccountParams *linphone_core_create_account_params(LinphoneCore *core) { - return linphone_account_params_new(core); + return linphone_account_params_new(core, TRUE); } LinphoneAccount *linphone_core_create_account(LinphoneCore *core, LinphoneAccountParams *params) { diff --git a/coreapi/tester_utils.h b/coreapi/tester_utils.h index 2b70789e853c28096d93fd815405c13ab3bdd206..0d51f65c270aa93e079670635fe1e38d430bd25a 100644 --- a/coreapi/tester_utils.h +++ b/coreapi/tester_utils.h @@ -434,6 +434,22 @@ linphone_account_manager_services_create_delete_account_as_admin_request(Linphon LINPHONE_PUBLIC void linphone_core_enable_goog_remb(LinphoneCore *core, bool_t enable); +/** + * Do not prune gr parameter in conference address + * @param core #LinphoneCore object @notnil + * @param enabled TRUE if enabled, FALSE otherwise. + **/ +LINPHONE_PUBLIC void linphone_core_enable_gruu_in_conference_address(LinphoneCore *core, bool_t enabled); + +/** + * Return whether the gr parameter is kept in the conference address + * Returns enablement of text sending via Baudot tones in the audio stream. + * @ingroup media_parameters + * @param core #LinphoneCore object @notnil + * @return TRUE if the "gr" parameter is kept in the conference address, FALSE otherwise. + **/ +LINPHONE_PUBLIC bool_t linphone_core_gruu_in_conference_address_enabled(const LinphoneCore *core); + #ifdef __cplusplus } #endif diff --git a/daemon/commands/register.cc b/daemon/commands/register.cc index 8aca2f92bd00cb4351aa366bb2fbb5a659757ea5..e22a5c0b0dd92ea182cb9ad19b4798038125b592 100644 --- a/daemon/commands/register.cc +++ b/daemon/commands/register.cc @@ -81,7 +81,7 @@ void RegisterCommand::exec(Daemon *app, const string &args) { linphone_auth_info_unref(info); } } - LinphoneAccountParams *params = linphone_account_params_new(lc); + LinphoneAccountParams *params = linphone_account_params_new(lc, TRUE); linphone_account_params_set_identity_address(params, from); if (from) linphone_address_unref(from); linphone_account_params_set_server_addr(params, cproxy); diff --git a/include/linphone/api/c-account-params.h b/include/linphone/api/c-account-params.h index 4cc1bf697b7a81543af1b1de610b5c12e85217b9..0eb3f9f9171c82e47c489b0248af0d8b7f4b1ac4 100644 --- a/include/linphone/api/c-account-params.h +++ b/include/linphone/api/c-account-params.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2022 Belledonne Communications SARL. + * Copyright (c) 2010-2025 Belledonne Communications SARL. * * This file is part of Liblinphone * (see https://gitlab.linphone.org/BC/public/liblinphone). @@ -36,9 +36,10 @@ extern "C" { /** * Create a new #LinphoneAccountParams object. * @param lc The #LinphoneCore object. @maybenil + * @param use_default_values If TRUE, use proxy default values * @return The newly created #LinphoneAccountParams object. @notnil */ -LINPHONE_PUBLIC LinphoneAccountParams *linphone_account_params_new(LinphoneCore *lc); +LINPHONE_PUBLIC LinphoneAccountParams *linphone_account_params_new(LinphoneCore *lc, bool_t use_default_values); /** * Create a new #LinphoneAccountParams object from a configuration. @@ -95,7 +96,7 @@ LINPHONE_PUBLIC void *linphone_account_params_get_user_data(const LinphoneAccoun * @return 0 if successful, -1 otherwise. **/ LINPHONE_PUBLIC LinphoneStatus linphone_account_params_set_server_address(LinphoneAccountParams *params, - LinphoneAddress *server_address); + const LinphoneAddress *server_address); /** * Sets the proxy address @@ -125,7 +126,7 @@ linphone_account_params_set_server_addr(LinphoneAccountParams *params, const cha * @return 0 if successful, -1 otherwise. **/ LINPHONE_PUBLIC LinphoneStatus linphone_account_params_set_identity_address(LinphoneAccountParams *params, - LinphoneAddress *identity); + const LinphoneAddress *identity); /** * Sets a list of SIP route. @@ -990,6 +991,22 @@ linphone_account_params_get_instant_messaging_encryption_mandatory(const Linphon LINPHONE_PUBLIC void linphone_account_params_set_instant_messaging_encryption_mandatory(LinphoneAccountParams *params, bool_t mandatory); +/** + * Gets the list of supported tags. + * @param params The #LinphoneAccountParams object. @notnil + * @return The list of supported tags \bctbx_list{char *}. @maybenil + **/ +LINPHONE_PUBLIC const bctbx_list_t * +linphone_account_params_get_supported_tags_list(const LinphoneAccountParams *params); + +/** + * Sets the list of supported tags. + * @param params The #LinphoneAccountParams object. @notnil + * @param supported_tags The list of supported tags \bctbx_list{char *}. @maybenil + */ +LINPHONE_PUBLIC void linphone_account_params_set_supported_tags_list(LinphoneAccountParams *params, + const bctbx_list_t *supported_tags); + /** * @} */ diff --git a/include/linphone/api/c-address.h b/include/linphone/api/c-address.h index 7f710492aa090e9d5b5b4aaf8f6bbc5b3e585dc5..77365665d1d7cd39f97866bb8c763902d6439884 100644 --- a/include/linphone/api/c-address.h +++ b/include/linphone/api/c-address.h @@ -65,7 +65,7 @@ LINPHONE_PUBLIC void linphone_address_unref(LinphoneAddress *address); /** * Returns if address is valid. * @param address a #LinphoneAddress object. @maybenil - * @return the scheme if any, NULL otherwise. @maybenil + * @return TRUE is the address is valid, FALSE otherwise **/ LINPHONE_PUBLIC bool_t linphone_address_is_valid(const LinphoneAddress *address); diff --git a/include/linphone/api/c-callbacks.h b/include/linphone/api/c-callbacks.h index 5d9081ff49fd6f18d4a3ccdc3645f2b71ed227fe..06ab36cfabc31697c4fc7a3d35eaf05dc9cf5be1 100644 --- a/include/linphone/api/c-callbacks.h +++ b/include/linphone/api/c-callbacks.h @@ -917,7 +917,7 @@ typedef void (*LinphoneConferenceCbsParticipantDeviceJoiningRequestCb)(LinphoneC /** * Callback used to notify which participant device video is being displayed as "actively speaking". * @param[in] conference #LinphoneConference object @notnil - * @param[in] participant_device the participant device currently displayed as active speaker @notnil + * @param[in] participant_device the participant device currently displayed as active speaker @maybenil */ typedef void (*LinphoneConferenceCbsActiveSpeakerParticipantDeviceCb)( LinphoneConference *conference, const LinphoneParticipantDevice *participant_device); diff --git a/include/linphone/api/c-chat-room.h b/include/linphone/api/c-chat-room.h index 22b1071392dcbeeef9bcce1c120df2a8b49020d3..48e83898aea5c8abc474e3ff90aeddf44b461120 100644 --- a/include/linphone/api/c-chat-room.h +++ b/include/linphone/api/c-chat-room.h @@ -133,6 +133,14 @@ LINPHONE_PUBLIC const LinphoneAddress *linphone_chat_room_get_peer_address(Linph */ LINPHONE_PUBLIC const LinphoneAddress *linphone_chat_room_get_local_address(LinphoneChatRoom *chat_room); +/** + * Returns the chat room identifier + * @warning This method returns a NULL pointer if the ChatRoom is in the Instantiated state + * @param chat_room The #LinphoneChatRoom object. @notnil + * @return the conference identifier. @maybenil + */ +LINPHONE_PUBLIC const char *linphone_chat_room_get_identifier(const LinphoneChatRoom *chat_room); + /** * Used to receive a chat message when using async mechanism with IM enchat_roomyption engine * @param chat_room #LinphoneChatRoom object @notnil @@ -317,7 +325,7 @@ LINPHONE_PUBLIC bctbx_list_t *linphone_chat_room_get_history_range_near(Linphone * @param filters The #LinphoneChatRoomHistoryFilterMask mask to filter the results with #LinphoneChatRoomHistoryFilter * @return A list of \bctbx_list{LinphoneEventLog} between the two provided events, if any. @tobefreed */ -LINPHONE_PUBLIC bctbx_list_t *linphone_chat_room_get_history_range_between(LinphoneChatRoom *cr, +LINPHONE_PUBLIC bctbx_list_t *linphone_chat_room_get_history_range_between(LinphoneChatRoom *chat_room, LinphoneEventLog *first_event, LinphoneEventLog *last_event, LinphoneChatRoomHistoryFilterMask filters); @@ -740,7 +748,6 @@ LINPHONE_PUBLIC bctbx_list_t *linphone_chat_room_get_history_range(LinphoneChatR * @notnil * @param nb_events Number of events to retrieve. 0 means everything. * @return A list \bctbx_list{LinphoneEventLog} @tobefreed - * @deprecated 30/07/2024. Use linphone_chat_room_get_history_2() instead. */ LINPHONE_PUBLIC bctbx_list_t *linphone_chat_room_get_history_message_events(LinphoneChatRoom *chat_room, int nb_events); @@ -751,7 +758,6 @@ LINPHONE_PUBLIC bctbx_list_t *linphone_chat_room_get_history_message_events(Linp * @param begin The first event of the range to be retrieved. History most recent event has index 0. * @param end The last event of the range to be retrieved. History oldest event has index of history size - 1 * @return The list of chat message events. \bctbx_list{LinphoneEventLog} @tobefreed - * @deprecated 30/07/2024. Use linphone_chat_room_get_history_range_2() instead. */ LINPHONE_PUBLIC bctbx_list_t * linphone_chat_room_get_history_range_message_events(LinphoneChatRoom *chat_room, int begin, int end); @@ -762,7 +768,6 @@ linphone_chat_room_get_history_range_message_events(LinphoneChatRoom *chat_room, * @notnil * @param nb_events Number of events to retrieve. 0 means everything. * @return The list of the most recent events. \bctbx_list{LinphoneEventLog} @tobefreed - * @deprecated 30/07/2024. Use linphone_chat_room_get_history_2() instead. */ LINPHONE_PUBLIC bctbx_list_t *linphone_chat_room_get_history_events(LinphoneChatRoom *chat_room, int nb_events); @@ -773,7 +778,6 @@ LINPHONE_PUBLIC bctbx_list_t *linphone_chat_room_get_history_events(LinphoneChat * @param begin The first event of the range to be retrieved. History most recent event has index 0. * @param end The last event of the range to be retrieved. History oldest event has index of history size - 1 * @return The list of the found events. \bctbx_list{LinphoneEventLog} @tobefreed - * @deprecated 30/07/2024. Use linphone_chat_room_get_history_range_2() instead. */ LINPHONE_PUBLIC bctbx_list_t * linphone_chat_room_get_history_range_events(LinphoneChatRoom *chat_room, int begin, int end); @@ -783,7 +787,6 @@ linphone_chat_room_get_history_range_events(LinphoneChatRoom *chat_room, int beg * @param chat_room The #LinphoneChatRoom object corresponding to the conversation for which size has to be computed * @notnil * @return the number of events. - * @deprecated 30/07/2024. Use linphone_chat_room_get_history_size_2() instead. */ LINPHONE_PUBLIC int linphone_chat_room_get_history_events_size(LinphoneChatRoom *chat_room); diff --git a/include/linphone/api/c-conference.h b/include/linphone/api/c-conference.h index 1e300456fb8d3a72e0c574fa8832455213c87164..f0197556ad6a85fc874b1b2b7c9a131b320a8683 100644 --- a/include/linphone/api/c-conference.h +++ b/include/linphone/api/c-conference.h @@ -192,6 +192,14 @@ LINPHONE_PUBLIC LinphoneStatus linphone_conference_add_participant(LinphoneConfe LINPHONE_PUBLIC LinphoneStatus linphone_conference_add_participant_2(LinphoneConference *conference, const LinphoneAddress *uri); +/** + * Returns the conference identifier + * @warning This method returns a NULL pointer if the Conference is in the Instantiated state + * @param conference The #LinphoneConference object. @notnil + * @return the conference identifier. @maybenil + */ +LINPHONE_PUBLIC const char *linphone_conference_get_identifier(const LinphoneConference *conference); + /** * Update parameters of the conference. * This is typically used enable or disable the video stream in the conference. @@ -298,7 +306,7 @@ LINPHONE_PUBLIC LinphoneParticipant *linphone_conference_get_me(const LinphoneCo LINPHONE_PUBLIC int linphone_conference_terminate(LinphoneConference *conference); /** - * Retrieves the user pointer that was given to linphone_conference_new() + * Retrieves the user pointer that was given to linphone_conference_set_user_data() * @param conference #LinphoneConference object @notnil * @return The user data associated with the #LinphoneConference object. @maybenil * @ingroup initializing @@ -554,6 +562,13 @@ LINPHONE_PUBLIC LinphonePlayer *linphone_conference_get_player(LinphoneConferenc */ LINPHONE_PUBLIC const LinphoneConferenceInfo *linphone_conference_get_info(LinphoneConference *conference); +/** + * Gets the #LinphoneAccount object associated with the conference + * @param conference #LinphoneConference object. @notnil + * @return A #LinphoneAccount object. @maybenil + */ +LINPHONE_PUBLIC LinphoneAccount *linphone_conference_get_account(LinphoneConference *conference); + /************ */ /* DEPRECATED */ /* ********** */ diff --git a/include/linphone/api/c-content.h b/include/linphone/api/c-content.h index f75e0b3279a74910036ba0aaf99db310cfe5a40b..63b6c4818c0ee7b31b2953eb97c8d31426e7ca17 100644 --- a/include/linphone/api/c-content.h +++ b/include/linphone/api/c-content.h @@ -330,16 +330,25 @@ LINPHONE_PUBLIC bool_t linphone_content_is_file_transfer(const LinphoneContent * /** * Tells whether or not this content contains an encrypted file + * @param content #LinphoneContent object. @notnil * @return True is this content contains a file and this file is encrypted, false otherwise. */ LINPHONE_PUBLIC bool_t linphone_content_is_file_encrypted(const LinphoneContent *content); /** * Returns the creation timestamp if this content is a FileContent (received or sent by chat). + * @param content #LinphoneContent object. @notnil * @return The timestamp at which this content was created if available, -1 otherwise. */ LINPHONE_PUBLIC time_t linphone_content_get_creation_timestamp(const LinphoneContent *content); +/** + * Returns the chat message id for which this content is related to, if any. + * @param content #LinphoneContent object. @notnil + * @return The chat message ID if this content is related to a chat message, NULL otherwise. @maybenil + */ +LINPHONE_PUBLIC const char *linphone_content_get_related_chat_message_id(const LinphoneContent *content); + /************ */ /* DEPRECATED */ /* ********** */ diff --git a/include/linphone/api/c-friend-list.h b/include/linphone/api/c-friend-list.h index bf9966ce89c1f8da4d89d0ede3b278cd64c6cdd7..be4c95e8be59085492dc36a2c06b796a052ec0ec 100644 --- a/include/linphone/api/c-friend-list.h +++ b/include/linphone/api/c-friend-list.h @@ -422,7 +422,7 @@ LINPHONE_PUBLIC void linphone_friend_list_update_dirty_friends(LinphoneFriendLis /** * Returns the #LinphoneCore object attached to this LinphoneFriendList. * @param friend_list #LinphoneFriendList object. @notnil - * @return a #LinphoneCore object @notnil + * @return a #LinphoneCore object @maybenil */ LINPHONE_PUBLIC LinphoneCore *linphone_friend_list_get_core(const LinphoneFriendList *friend_list); diff --git a/include/linphone/api/c-participant-device.h b/include/linphone/api/c-participant-device.h index d909cb89ef54db04d6f2d8f02f990ffb21f60646..26df140f866eeb00012351f1158940c2210caa3b 100644 --- a/include/linphone/api/c-participant-device.h +++ b/include/linphone/api/c-participant-device.h @@ -39,7 +39,7 @@ extern "C" { */ /** - * Increment reference count of #LinphoneParticipantDevice object. + * Increments reference count of #LinphoneParticipantDevice object. * @param participant_device the #LinphoneParticipantDevice object @notnil * @return the same #LinphoneParticipantDevice object @notnil **/ @@ -47,20 +47,20 @@ LINPHONE_PUBLIC LinphoneParticipantDevice * linphone_participant_device_ref(LinphoneParticipantDevice *participant_device); /** - * Decrement reference count of #LinphoneParticipantDevice object. + * Decrements reference count of #LinphoneParticipantDevice object. * @param participant_device the #LinphoneParticipantDevice object @notnil **/ LINPHONE_PUBLIC void linphone_participant_device_unref(LinphoneParticipantDevice *participant_device); /** - * Retrieve the user pointer associated with the participant's device. + * Retrieves the user pointer associated with the participant's device. * @param participant_device A #LinphoneParticipantDevice object @notnil * @return The user pointer associated with the participant's device. @maybenil **/ LINPHONE_PUBLIC void *linphone_participant_device_get_user_data(const LinphoneParticipantDevice *participant_device); /** - * Assign a user pointer to the participant's device. + * Assigns a user pointer to the participant's device. * @param participant_device A #LinphoneParticipantDevice object @notnil * @param user_data The user pointer to associate with the participant's device. @maybenil **/ @@ -68,7 +68,7 @@ LINPHONE_PUBLIC void linphone_participant_device_set_user_data(LinphoneParticipa void *user_data); /** - * Get the address of a participant's device. + * Gets the address of a participant's device. * @param participant_device A #LinphoneParticipantDevice object @notnil * @return The #LinphoneAddress of the participant's device @notnil */ @@ -76,7 +76,7 @@ LINPHONE_PUBLIC const LinphoneAddress * linphone_participant_device_get_address(const LinphoneParticipantDevice *participant_device); /** - * Get the state of a participant device. + * Gets the state of a participant device. * @param participant_device A #LinphoneParticipantDevice object @notnil * @return The #LinphoneParticipantDeviceState of the device */ @@ -84,7 +84,7 @@ LINPHONE_PUBLIC LinphoneParticipantDeviceState linphone_participant_device_get_state(const LinphoneParticipantDevice *participant_device); /** - * Get the security level of a participant's device. + * Gets the security level of a participant's device. * @param participant_device A #LinphoneParticipantDevice object @notnil * @return The #LinphoneChatRoomSecurityLevel of the device */ @@ -92,14 +92,14 @@ LINPHONE_PUBLIC LinphoneChatRoomSecurityLevel linphone_participant_device_get_security_level(const LinphoneParticipantDevice *participant_device); /** - * Return the name of the device or NULL. + * Returns the name of the device. * @param participant_device A #LinphoneParticipantDevice object @notnil * @return the name of the device or NULL. @maybenil */ LINPHONE_PUBLIC const char *linphone_participant_device_get_name(const LinphoneParticipantDevice *participant_device); /** - * Return whether the participant device is in a conference or not. + * Returns whether the participant device is in a conference or not. * @param participant_device A #LinphoneParticipantDevice object @notnil * @return a boolean to state whether the device is in a conference */ @@ -107,25 +107,25 @@ LINPHONE_PUBLIC bool_t linphone_participant_device_is_in_conference(const LinphoneParticipantDevice *participant_device); /** - * Get the timestamp the device joined a conference. + * Gets the timestamp the device joined a conference. * @param participant_device A #LinphoneParticipantDevice object @notnil - * @return time of joining a conference as returned by time(nullptr). For UNIX based systems it is the number of seconds - * since 00:00hours of the 1st of January 1970 + * @return time of joining a conference expressed as a number of seconds + * since 00:00:00 of the 1st of January 1970 */ LINPHONE_PUBLIC time_t linphone_participant_device_get_time_of_joining(const LinphoneParticipantDevice *participant_device); /** - * Get the timestamp the device left a conference. + * Gets the timestamp the device left a conference. * @param participant_device A #LinphoneParticipantDevice object @notnil * @return time of disconnection a conference as returned by time(nullptr). For UNIX based systems it is the number of - * seconds since 00:00hours of the 1st of January 1970 + * seconds since 00:00:00 of the 1st of January 1970 */ LINPHONE_PUBLIC time_t linphone_participant_device_get_time_of_disconnection(const LinphoneParticipantDevice *participant_device); /** - * Get the joining method or it the device is the focus owner + * Gets the joining method or it the device is the focus owner * @param participant_device A #LinphoneParticipantDevice object @notnil * @return joining method or focus owner #LinphoneParticipantDeviceJoiningMethod */ @@ -133,7 +133,7 @@ LINPHONE_PUBLIC LinphoneParticipantDeviceJoiningMethod linphone_participant_device_get_joining_method(const LinphoneParticipantDevice *participant_device); /** - * Get the disconnection method + * Gets the disconnection method * @param participant_device A #LinphoneParticipantDevice object @notnil * @return disconnection method #LinphoneParticipantDeviceDisconnectionMethod */ @@ -141,7 +141,7 @@ LINPHONE_PUBLIC LinphoneParticipantDeviceDisconnectionMethod linphone_participant_device_get_disconnection_method(const LinphoneParticipantDevice *participant_device); /** - * Get the disconnection reason + * Gets the disconnection reason * @param participant_device A #LinphoneParticipantDevice object @notnil * @return disconnection reason @maybenil */ @@ -149,7 +149,7 @@ LINPHONE_PUBLIC const char * linphone_participant_device_get_disconnection_reason(const LinphoneParticipantDevice *participant_device); /** - * Get the stream label of the device. + * Gets the stream label of the device. * The capability information represents the capability for the #ParticipantDevice to handle a given stream type (audio, * video or text). * @param participant_device A #LinphoneParticipantDevice object @notnil @@ -161,7 +161,7 @@ linphone_participant_device_get_stream_label(const LinphoneParticipantDevice *pa const LinphoneStreamType stream_type); /** - * Get the thumbnail stream label of the device. + * Gets the thumbnail stream label of the device. * @param participant_device A #LinphoneParticipantDevice object @notnil * @return the label of the thumbnail stream of the device @maybenil */ @@ -169,7 +169,7 @@ LINPHONE_PUBLIC const char * linphone_participant_device_get_thumbnail_stream_label(const LinphoneParticipantDevice *participant_device); /** - * Get the stream capability of the device. + * Gets the stream capability of the device. * The capability information represents the capability for the #ParticipantDevice to handle a given stream type (audio, * video or text). * @param participant_device A #LinphoneParticipantDevice object @notnil @@ -180,7 +180,7 @@ LINPHONE_PUBLIC LinphoneMediaDirection linphone_participant_device_get_stream_ca const LinphoneParticipantDevice *participant_device, const LinphoneStreamType stream_type); /** - * Get the thumbnail stream capability of the device. + * Gets the thumbnail stream capability of the device. * @param participant_device A #LinphoneParticipantDevice object @notnil * @return the capability of the thumbnail stream of the device #LinphoneMediaDirection */ @@ -188,7 +188,7 @@ LINPHONE_PUBLIC LinphoneMediaDirection linphone_participant_device_get_thumbnail_stream_capability(const LinphoneParticipantDevice *participant_device); /** - * Get the stream availability of the device. + * Gets the stream availability of the device. * The availability information represents whether a given stream type is currently available to be presented in the * conference for a #LinphoneParticipantDevice * @param participant_device A #LinphoneParticipantDevice object @notnil @@ -199,7 +199,7 @@ LINPHONE_PUBLIC bool_t linphone_participant_device_get_stream_availability( const LinphoneParticipantDevice *participant_device, const LinphoneStreamType stream_type); /** - * Get the thumbnail stream availability of the device. + * Gets the thumbnail stream availability of the device. * The availability information represents whether a given stream type is currently available to be presented in the * conference for a #LinphoneParticipantDevice * @param participant_device A #LinphoneParticipantDevice object @notnil @@ -252,7 +252,7 @@ LINPHONE_PUBLIC LinphoneParticipantDeviceCbs * linphone_participant_device_get_current_callbacks(const LinphoneParticipantDevice *participant_device); /** - * Set window ID for a device. + * Sets the the native window ID where video for this participant device is to be rendered. * @param participant_device A #LinphoneParticipantDevice object @notnil * @param window_id the window ID of the device @maybenil */ @@ -260,7 +260,7 @@ LINPHONE_PUBLIC void linphone_participant_device_set_native_video_window_id(LinphoneParticipantDevice *participant_device, void *window_id); /** - * Get window ID. + * Gets the native window ID where video for this participant device is to be rendered. * @param participant_device A #LinphoneParticipantDevice object @notnil * @return the window ID of the device @maybenil */ @@ -268,7 +268,7 @@ LINPHONE_PUBLIC void * linphone_participant_device_get_native_video_window_id(const LinphoneParticipantDevice *participant_device); /** - * Create a window ID and return it. + * Creates a window ID and return it. * @param participant_device A #LinphoneParticipantDevice object @notnil * @return the window ID of the device @maybenil */ @@ -276,27 +276,35 @@ LINPHONE_PUBLIC void * linphone_participant_device_create_native_video_window_id(LinphoneParticipantDevice *participant_device); /** - * Return whether the participant device is speaking or not. - * @param participant_device The #LinphoneParticipantDeviceCbs object @notnil + * Returns whether the participant device is speaking or not. + * @param participant_device The #LinphoneParticipantDevice object @notnil * @return TRUE if the participant device is speaking, FALSE otherwise. */ LINPHONE_PUBLIC bool_t linphone_participant_device_get_is_speaking(const LinphoneParticipantDevice *participant_device); /** - * Return whether the participant device is muted or not. - * @param participant_device The #LinphoneParticipantDeviceCbs object @notnil + * Returns whether the participant device is muted or not. + * @param participant_device The #LinphoneParticipantDevice object @notnil * @return TRUE if the participant device is muted, FALSE otherwise. */ LINPHONE_PUBLIC bool_t linphone_participant_device_get_is_muted(const LinphoneParticipantDevice *participant_device); /** - * Return whether the participant device is screen sharing or not. - * @param participant_device The #LinphoneParticipantDeviceCbs object @notnil + * Returns whether the participant device is screen sharing or not. + * @param participant_device The #LinphoneParticipantDevice object @notnil * @return TRUE if the participant device is screen sharing, FALSE otherwise. */ LINPHONE_PUBLIC bool_t linphone_participant_device_screen_sharing_enabled(const LinphoneParticipantDevice *participant_device); +/** + * Returns the #LinphoneParticipant this #LinphoneParticipantDevice belongs to. + * @param participant_device The #LinphoneParticipantDevice object @notnil + * @return the #LinphoneParticipant this device belongs to @notnil + */ +LINPHONE_PUBLIC LinphoneParticipant * +linphone_participant_device_get_participant(const LinphoneParticipantDevice *participant_device); + /** * @} */ diff --git a/include/linphone/api/c-vcard.h b/include/linphone/api/c-vcard.h index 17a785cce6e9de7ac8bbec3417875654a04e5e2b..03a951842d2d10cc51c3fab8c6880b347dcf9ee1 100644 --- a/include/linphone/api/c-vcard.h +++ b/include/linphone/api/c-vcard.h @@ -59,6 +59,15 @@ LINPHONE_PUBLIC LinphoneVcard *linphone_vcard_clone(const LinphoneVcard *vCard); */ LINPHONE_PUBLIC const char *linphone_vcard_as_vcard4_string(LinphoneVcard *vCard); +/** + * Returns the vCard4 representation of the LinphoneVcard, + * but if a local file is detected in a PHOTO field, + * it will be converted to base64. + * @param vCard the #LinphoneVcard @notnil + * @return a const char * that represents the vCard. @maybenil + */ +LINPHONE_PUBLIC const char *linphone_vcard_as_vcard4_string_with_base_64_picture(LinphoneVcard *vCard); + /** * Sets the FN attribute of the vCard (which is mandatory). * @param vCard the #LinphoneVcard @notnil diff --git a/include/linphone/call_params.h b/include/linphone/call_params.h index 849e5b23d333d6cf16e45fa50a908201db6dc2e7..41cab59b075cb08d239bc8d0cfaa24b30b212147 100644 --- a/include/linphone/call_params.h +++ b/include/linphone/call_params.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2022 Belledonne Communications SARL. + * Copyright (c) 2010-2025 Belledonne Communications SARL. * * This file is part of Liblinphone * (see https://gitlab.linphone.org/BC/public/liblinphone). @@ -106,6 +106,22 @@ LINPHONE_PUBLIC void linphone_call_params_enable_low_bandwidth(LinphoneCallParam **/ LINPHONE_PUBLIC void linphone_call_params_enable_audio(LinphoneCallParams *call_params, bool_t enabled); +/** + * Check if ringing is disabled + * @param params the #LinphoneCallParams @notnil + * @return TRUE if ringing is disabled; FALSE otherwise. + * @ingroup media_parameters + */ +LINPHONE_PUBLIC bool_t linphone_call_params_ringing_disabled(const LinphoneCallParams *params); + +/** + * Define whether ringing is disabled + * @param params the #LinphoneCallParams @notnil + * @param disable TRUE to disable ringing; FALSE otherwise. + * @ingroup media_parameters + */ +LINPHONE_PUBLIC void linphone_call_params_disable_ringing(LinphoneCallParams *params, bool_t disable); + /** * Check if tone indications are enabled * @param params the #LinphoneCallParams @notnil @@ -127,7 +143,6 @@ LINPHONE_PUBLIC void linphone_call_params_enable_tone_indications(LinphoneCallPa * @param params the #LinphoneCallParams @notnil * @return TRUE if capability negotiation reINVITE is enabled; FALSE otherwise. * @ingroup media_parameters - * @deprecated 16/12/2021 Use linphone_call_params_capability_negotiation_reinvite_enabled() instead. */ LINPHONE_PUBLIC bool_t linphone_call_params_capability_negotiation_reinvite_enabled(const LinphoneCallParams *params); diff --git a/include/linphone/core.h b/include/linphone/core.h index 084127187739bd4a5f9f6adaa244c2221600bd5d..aa0be62b24c64b0cf92e5ed971cb476e42e0b11e 100644 --- a/include/linphone/core.h +++ b/include/linphone/core.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2024 Belledonne Communications SARL. + * Copyright (c) 2010-2025 Belledonne Communications SARL. * * This file is part of Liblinphone * (see https://gitlab.linphone.org/BC/public/liblinphone). @@ -4866,6 +4866,9 @@ LINPHONE_PUBLIC void linphone_core_reload_ms_plugins(LinphoneCore *core, const c * @param params Parameters of the conference. See #LinphoneConferenceParams. @notnil * @return A pointer on the freshly created conference #LinphoneConference. That object will be automatically * freed by the core after calling linphone_core_terminate_conference(). @maybenil + * @warning To guarantee the backward comatibility, this method will assign the created conference to the conference + * context held by the core. Nonetheless starting from release 5.4, the conference context will be override at every + * conference created by calling this method */ LINPHONE_PUBLIC LinphoneConference *linphone_core_create_conference_with_params(LinphoneCore *core, const LinphoneConferenceParams *params); @@ -4965,6 +4968,16 @@ LINPHONE_PUBLIC LinphoneConference *linphone_core_search_conference(const Linpho LINPHONE_PUBLIC LinphoneConference *linphone_core_search_conference_2(const LinphoneCore *core, const LinphoneAddress *conferenceAddr); +/** + * Find a conference by its identifier. + * + * @param core A #LinphoneCore object @notnil + * @param identifier The conference identifier @notnil + * @return A matching conference or NULL if none matches. @maybenil + */ +LINPHONE_PUBLIC LinphoneConference *linphone_core_search_conference_by_identifier(const LinphoneCore *core, + const char *identifier); + /** * Adds a participant to the conference. If no conference is going on * a new internal conference context is created and the participant is @@ -4972,8 +4985,10 @@ LINPHONE_PUBLIC LinphoneConference *linphone_core_search_conference_2(const Linp * @param core #LinphoneCore @notnil * @param call The current call with the participant to add @notnil * @return 0 if succeeded. Negative number if failed + * @deprecated 23/01/2025 Use linphone_conference_add_participant() instead. */ -LINPHONE_PUBLIC LinphoneStatus linphone_core_add_to_conference(LinphoneCore *core, LinphoneCall *call); +LINPHONE_DEPRECATED LINPHONE_PUBLIC LinphoneStatus linphone_core_add_to_conference(LinphoneCore *core, + LinphoneCall *call); /** * Adds all calls into the conference. If no conference is running @@ -4982,8 +4997,9 @@ LINPHONE_PUBLIC LinphoneStatus linphone_core_add_to_conference(LinphoneCore *cor * @param core #LinphoneCore @notnil * @return 0 if succeeded. Negative number if failed * @warning This function guarantees that the local endpoint is added to the conference. + * @deprecated 23/01/2025 Use linphone_conference_invite_participants() instead. */ -LINPHONE_PUBLIC LinphoneStatus linphone_core_add_all_to_conference(LinphoneCore *core); +LINPHONE_DEPRECATED LINPHONE_PUBLIC LinphoneStatus linphone_core_add_all_to_conference(LinphoneCore *core); /** * Removes a call from the conference. @@ -4999,8 +5015,10 @@ LINPHONE_PUBLIC LinphoneStatus linphone_core_add_all_to_conference(LinphoneCore *conference is automatically put in a simple call in running state. * * @return 0 if successful, -1 otherwise. + * @deprecated 23/01/2025 Use linphone_conference_remove_participant_2() instead. **/ -LINPHONE_PUBLIC LinphoneStatus linphone_core_remove_from_conference(LinphoneCore *core, LinphoneCall *call); +LINPHONE_DEPRECATED LINPHONE_PUBLIC LinphoneStatus linphone_core_remove_from_conference(LinphoneCore *core, + LinphoneCall *call); /** * Indicates whether the local participant is part of a conference. @@ -5011,7 +5029,7 @@ LINPHONE_PUBLIC LinphoneStatus linphone_core_remove_from_conference(LinphoneCore * @return TRUE if the local participant is in a conference, FALSE otherwise. * @deprecated 09/03/2021 Use linphone_conference_is_in() instead. */ -LINPHONE_PUBLIC bool_t linphone_core_is_in_conference(const LinphoneCore *core); +LINPHONE_DEPRECATED LINPHONE_PUBLIC bool_t linphone_core_is_in_conference(const LinphoneCore *core); /** * Joins the local participant to the running conference @@ -5019,7 +5037,7 @@ LINPHONE_PUBLIC bool_t linphone_core_is_in_conference(const LinphoneCore *core); * @return 0 if succeeded. Negative number if failed * @deprecated 09/03/2021 Use linphone_conference_enter() instead. */ -LINPHONE_PUBLIC LinphoneStatus linphone_core_enter_conference(LinphoneCore *core); +LINPHONE_DEPRECATED LINPHONE_PUBLIC LinphoneStatus linphone_core_enter_conference(LinphoneCore *core); /** * Makes the local participant leave the running conference @@ -5027,14 +5045,15 @@ LINPHONE_PUBLIC LinphoneStatus linphone_core_enter_conference(LinphoneCore *core * @return 0 if succeeded. Negative number if failed * @deprecated 09/03/2021 Use linphone_conference_leave() instead. */ -LINPHONE_PUBLIC LinphoneStatus linphone_core_leave_conference(LinphoneCore *core); +LINPHONE_DEPRECATED LINPHONE_PUBLIC LinphoneStatus linphone_core_leave_conference(LinphoneCore *core); /** * Returns the input volume of the local participant * @param core #LinphoneCore * @return A value inside [0.0 ; 1.0] + * @deprecated 23/01/2025 Use linphone_conference_get_input_volume() instead. */ -LINPHONE_PUBLIC float linphone_core_get_conference_local_input_volume(LinphoneCore *core); +LINPHONE_DEPRECATED LINPHONE_PUBLIC float linphone_core_get_conference_local_input_volume(LinphoneCore *core); /** * Terminates the running conference. If it is a local conference, all calls @@ -5043,8 +5062,9 @@ LINPHONE_PUBLIC float linphone_core_get_conference_local_input_volume(LinphoneCo * will be terminated. * @param core #LinphoneCore @notnil * @return 0 if succeeded. Negative number if failed + * @deprecated 23/01/2025 Use linphone_conference_terminate() instead. */ -LINPHONE_PUBLIC LinphoneStatus linphone_core_terminate_conference(LinphoneCore *core); +LINPHONE_DEPRECATED LINPHONE_PUBLIC LinphoneStatus linphone_core_terminate_conference(LinphoneCore *core); /** * Creates some default conference parameters for instanciating a conference with @@ -5821,9 +5841,27 @@ LINPHONE_PUBLIC void linphone_core_enable_video_multicast(LinphoneCore *core, bo **/ LINPHONE_PUBLIC bool_t linphone_core_video_multicast_enabled(const LinphoneCore *core); +/** + * Enables or disables call ringing. + * This value is taken into account from next time call parameters are created. + * This feature can also be enabled per-call using #LinphoneCallParams. + * @param core the #LinphoneCore @notnil + * @param yesno a boolean to indicate whether the feature is to be disabled. + * @ingroup media_parameters + */ +LINPHONE_PUBLIC void linphone_core_disable_call_ringing(const LinphoneCore *core, bool_t yesno); + +/** + * Check whether ringing of calls is disabled + * @param core #LinphoneCore @notnil + * @return TRUE if call ringing is disabled + * @ingroup media_parameters + **/ +LINPHONE_PUBLIC bool_t linphone_core_call_ringing_disabled(const LinphoneCore *core); + /** * Enables or disables call tone indications. - * This value is taken into account from next tine call parameters are created. + * This value is taken into account from next time call parameters are created. * This feature can also be enabled per-call using #LinphoneCallParams. * @param core the #LinphoneCore @notnil * @param yesno a boolean to indicate whether the feature is to be enabled. @@ -6171,36 +6209,17 @@ LINPHONE_PUBLIC bctbx_list_t *linphone_core_get_linphone_specs_list(LinphoneCore * @addtogroup chatroom * @{ */ - -/** - * Create a chat room. - * - * @param core A #LinphoneCore object @notnil - * @param params The chat room creation parameters #LinphoneChatRoomParams @notnil - * @param localAddr #LinphoneAddress of a local #LinphoneAccount identity or NULL @maybenil - * @param participants The initial list of participants of the chat room. \bctbx_list{LinphoneAddress} @notnil - * @return The newly created chat room (can be an existing one if backend is Basic) or NULL. @maybenil - * @deprecated 22/10/2024, use linphone_core_create_chat_room_7() instead - * @ingroup chatroom - */ -LINPHONE_PUBLIC LinphoneChatRoom *linphone_core_create_chat_room_6(LinphoneCore *core, - const LinphoneChatRoomParams *params, - const LinphoneAddress *localAddr, - const bctbx_list_t *participants); - /** * Create a chat room. * * @param core A #LinphoneCore object @notnil * @param params The chat room creation parameters #LinphoneConferenceParams @notnil - * @param localAddr #LinphoneAddress of a local #LinphoneAccount identity or NULL @maybenil * @param participants The initial list of participants of the chat room. \bctbx_list{LinphoneAddress} @notnil * @return The newly created chat room (can be an existing one if backend is Basic) or NULL. @maybenil * @ingroup chatroom */ LINPHONE_PUBLIC LinphoneChatRoom *linphone_core_create_chat_room_7(LinphoneCore *core, const LinphoneConferenceParams *params, - const LinphoneAddress *localAddr, const bctbx_list_t *participants); /** @@ -6239,6 +6258,16 @@ LINPHONE_PUBLIC LinphoneChatRoom *linphone_core_search_chat_room_2(const Linphon const LinphoneAddress *remoteAddr, const bctbx_list_t *participants); +/** + * Find a chat room by its identifier. + * + * @param core A #LinphoneCore object @notnil + * @param identifier The chat room identifier @notnil + * @return A matching chat room or NULL if none matches. @maybenil + */ +LINPHONE_PUBLIC LinphoneChatRoom *linphone_core_search_chat_room_by_identifier(const LinphoneCore *core, + const char *identifier); + /** * Removes a chatroom including all message history from the LinphoneCore. * @param core A #LinphoneCore object @notnil @@ -7759,13 +7788,15 @@ LINPHONE_PUBLIC bool_t linphone_core_get_register_only_when_network_is_up(const * @return #LinphoneCall or NULL if no match is found. @maybenil * @deprecated 27/10/2020. Use linphone_core_get_call_by_remote_address2() instead. */ -LINPHONE_PUBLIC LinphoneCall *linphone_core_find_call_from_uri(const LinphoneCore *core, const char *uri); +LINPHONE_PUBLIC LINPHONE_DEPRECATED LinphoneCall *linphone_core_find_call_from_uri(const LinphoneCore *core, + const char *uri); /** * Create some default conference parameters for instanciating a conference with *linphone_core_create_conference_with_params(). * @param core the #LinphoneCore object @notnil * @return a #LinphoneConferenceParams object. @notnil + * @deprecated 23/07/2024. Use linphone_core_create_conference_params_2() instead. **/ LINPHONE_PUBLIC LINPHONE_DEPRECATED LinphoneConferenceParams * linphone_core_create_conference_params(LinphoneCore *core); @@ -8954,7 +8985,7 @@ LINPHONE_PUBLIC void linphone_core_enable_database(LinphoneCore *core, bool_t va * @param fallback Boolean value telling whether we should plan on being able to fallback to a basic chat room if the * client-side group chat room creation fails * @return The newly created client-side group chat room. @maybenil - * @deprecated 02/07/2020, use linphone_core_create_chat_room_6() instead + * @deprecated 02/07/2020, use linphone_core_create_chat_room_7() instead * @ingroup chatroom */ LINPHONE_PUBLIC LINPHONE_DEPRECATED LinphoneChatRoom * @@ -8974,7 +9005,7 @@ linphone_core_create_client_group_chat_room(LinphoneCore *core, const char *subj * @param encrypted Boolean value telling whether we should apply encryption or not on chat messages sent and received * on this room. * @return The newly created client-side group chat room. @maybenil - * @deprecated 02/07/2020, use linphone_core_create_chat_room_6() instead + * @deprecated 02/07/2020, use linphone_core_create_chat_room_7() instead * @ingroup chatroom */ LINPHONE_PUBLIC LINPHONE_DEPRECATED LinphoneChatRoom *linphone_core_create_client_group_chat_room_2(LinphoneCore *core, @@ -8992,7 +9023,7 @@ LINPHONE_PUBLIC LINPHONE_DEPRECATED LinphoneChatRoom *linphone_core_create_clien * @param subject The subject of the group chat room @notnil * @param participants The initial list of participants of the chat room \bctbx_list{LinphoneAddress} @notnil * @return The newly created chat room. @maybenil - * @deprecated 02/07/2020, use linphone_core_create_chat_room_6() instead + * @deprecated 02/07/2020, use linphone_core_create_chat_room_7() instead * @ingroup chatroom */ LINPHONE_PUBLIC LINPHONE_DEPRECATED LinphoneChatRoom * @@ -9010,7 +9041,7 @@ linphone_core_create_chat_room(LinphoneCore *core, * @param subject The subject of the group chat room @notnil * @param participants The initial list of participants of the chat room. \bctbx_list{LinphoneAddress} @notnil * @return The newly created chat room. @maybenil - * @deprecated 02/07/2020, use linphone_core_create_chat_room_6() instead + * @deprecated 02/07/2020, use linphone_core_create_chat_room_7() instead * @ingroup chatroom */ LINPHONE_PUBLIC LINPHONE_DEPRECATED LinphoneChatRoom *linphone_core_create_chat_room_2( @@ -9022,7 +9053,7 @@ LINPHONE_PUBLIC LINPHONE_DEPRECATED LinphoneChatRoom *linphone_core_create_chat_ * @param subject The subject of the group chat room @notnil * @param participants The initial list of participants of the chat room. \bctbx_list{LinphoneAddress} @notnil * @return The newly created chat room. @maybenil - * @deprecated 02/07/2020, use linphone_core_create_chat_room_6() instead + * @deprecated 02/07/2020, use linphone_core_create_chat_room_7() instead * @ingroup chatroom */ LINPHONE_PUBLIC LINPHONE_DEPRECATED LinphoneChatRoom * @@ -9036,7 +9067,7 @@ linphone_core_create_chat_room_3(LinphoneCore *core, const char *subject, const * @notnil * @param participant #LinphoneAddress representing the initial participant to add to the chat room @notnil * @return The newly created chat room. @maybenil - * @deprecated 02/07/2020, use linphone_core_create_chat_room_6() instead + * @deprecated 02/07/2020, use linphone_core_create_chat_room_7() instead * @ingroup chatroom */ LINPHONE_PUBLIC LINPHONE_DEPRECATED LinphoneChatRoom * @@ -9050,12 +9081,29 @@ linphone_core_create_chat_room_4(LinphoneCore *core, * @param core A #LinphoneCore object @notnil * @param participant #LinphoneAddress representing the initial participant to add to the chat room @notnil * @return The newly created chat room. @maybenil - * @deprecated 02/07/2020, use linphone_core_create_chat_room_6() instead + * @deprecated 02/07/2020, use linphone_core_create_chat_room_7() instead * @ingroup chatroom */ LINPHONE_PUBLIC LINPHONE_DEPRECATED LinphoneChatRoom * linphone_core_create_chat_room_5(LinphoneCore *core, const LinphoneAddress *participant); +/** + * Create a chat room. + * + * @param core A #LinphoneCore object @notnil + * @param params The chat room creation parameters #LinphoneChatRoomParams @notnil + * @param localAddr #LinphoneAddress of a local #LinphoneAccount identity or NULL @maybenil + * @param participants The initial list of participants of the chat room. \bctbx_list{LinphoneAddress} @notnil + * @return The newly created chat room (can be an existing one if backend is Basic) or NULL. @maybenil + * @deprecated 22/10/2024, use linphone_core_create_chat_room_7() instead + * @ingroup chatroom + */ +LINPHONE_PUBLIC LINPHONE_DEPRECATED LinphoneChatRoom * +linphone_core_create_chat_room_6(LinphoneCore *core, + const LinphoneChatRoomParams *params, + const LinphoneAddress *localAddr, + const bctbx_list_t *participants); + /** * Get a chat room whose peer is the supplied address. If it does not exist yet, it will be created as a basic chat *room. No reference is transferred to the application. The #LinphoneCore keeps a reference on the chat room. @@ -9262,9 +9310,23 @@ LINPHONE_PUBLIC const LinphoneEktInfo *linphone_core_create_ekt_info_from_xml(co * @param core the #LinphoneCore * @param ekt_info the #LinphoneEktInfo @notnil * @return The XML body @maybenil @tobefreed + * @deprecated 06/02/2025 use linphone_core_create_xml_from_ekt_info_2(). + * @ingroup ekt_api + */ +LINPHONE_PUBLIC LINPHONE_DEPRECATED char *linphone_core_create_xml_from_ekt_info(const LinphoneCore *core, + const LinphoneEktInfo *ekt_info); + +/** + * Gets an XML body using a specific account + * @param core the #LinphoneCore + * @param ekt_info the #LinphoneEktInfo @notnil + * @param account the #LinphoneAccount associated with the conference @maybenil + * @return The XML body @maybenil @tobefreed * @ingroup ekt_api */ -LINPHONE_PUBLIC char *linphone_core_create_xml_from_ekt_info(const LinphoneCore *core, const LinphoneEktInfo *ekt_info); +LINPHONE_PUBLIC char *linphone_core_create_xml_from_ekt_info_2(const LinphoneCore *core, + const LinphoneEktInfo *ekt_info, + const LinphoneAccount *account); /** * Gets if the EKT plugin is currently loaded in the Linphone core instance. diff --git a/include/linphone/types.h b/include/linphone/types.h index 6bb33430dc93c9293c67c98af4b27c294003e777..2fbda5a93dd43a56d226b8afba8c3ba168f184ae 100644 --- a/include/linphone/types.h +++ b/include/linphone/types.h @@ -719,7 +719,7 @@ typedef enum _LinphoneLogCollectionState { } LinphoneLogCollectionState; /** - * @brief Used to notify if log collection upload have been succesfully delivered or not. + * @brief Used to notify if log collection upload have been successfully delivered or not. * @ingroup initializing */ typedef enum _LinphoneCoreLogCollectionUploadState { @@ -1353,7 +1353,7 @@ typedef struct _LinphoneRange LinphoneRange; /** * @brief Status code returned by some functions to - * notify whether the execution has been succesfully + * notify whether the execution has been successfully * done or not. * @ingroup misc */ diff --git a/include/linphone/utils/utils.h b/include/linphone/utils/utils.h index cee02bcfbe3099aa0c6125916f9f58cc784df078..b7668e2219682d9ffe68b38f59dc79e250ff475f 100644 --- a/include/linphone/utils/utils.h +++ b/include/linphone/utils/utils.h @@ -115,6 +115,7 @@ replaceAll(const std::string &source, const std::string &pattern, const std::str LINPHONE_PUBLIC std::string stringToLower(const std::string &str); LINPHONE_PUBLIC std::vector<std::string> stringToLower(const std::vector<std::string> &strs); LINPHONE_PUBLIC bool containsInsensitive(const std::string &haystack, const std::string &needle); +LINPHONE_PUBLIC bool endsWith(const std::string &haystack, const std::string &needle); LINPHONE_PUBLIC std::string unicodeToUtf8(uint32_t ic); LINPHONE_PUBLIC std::string unicodeToUtf8(const std::vector<uint32_t> &chars); @@ -270,7 +271,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<Address> &addresses); -std::string getXconId(const std::shared_ptr<Address> &address); +std::string getXconId(const std::shared_ptr<const Address> &address); std::shared_ptr<Address> getSipAddress(const std::string &str, const std::string &scheme); ConferenceInfo::participant_list_t parseResourceLists(const Content &content); ConferenceInfo::participant_list_t parseResourceLists(std::optional<std::reference_wrapper<const Content>> content); @@ -290,6 +291,10 @@ LINPHONE_PUBLIC void configSetStringList(LpConfig *lpconfig, LINPHONE_PUBLIC std::list<std::string> configGetStringList(LpConfig *lpconfig, const std::string §ion, const std::string &key); + +LINPHONE_PUBLIC std::string getFileExtension(const std::string &filePath); +LINPHONE_PUBLIC std::string convertFileToBase64(const std::string &filePath); + } // namespace Utils LINPHONE_PUBLIC std::ostream &operator<<(std::ostream &ostr, const Utils::Version &version); diff --git a/plugins/example/exampleplugin.cpp b/plugins/example/exampleplugin.cpp index 02294c0071af85376978360f8a671bab3e6ab780..f5056d53f1f88e8df2ad44635f82dda2e001de0c 100644 --- a/plugins/example/exampleplugin.cpp +++ b/plugins/example/exampleplugin.cpp @@ -34,7 +34,7 @@ extern "C" { #endif PLUGIN_EXPORT void liblinphone_exampleplugin_init(LinphoneCore *core) { lInfo() << "Example plugin for core " << std::string(linphone_core_get_identity(core)) - << " has been succesfully loaded"; + << " has been successfully loaded"; } #ifdef __cplusplus diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 376e2cc50319736b0c2662c13ec42e6e47ffcf57..c5baadd5a07a0a86125dcb79b95e5699da00ae74 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -98,6 +98,16 @@ endif() if(TurboJpeg_FOUND) list(APPEND LINK_LIBS ${TurboJpeg_TARGET}) endif() +if(MsAaudio_FOUND) + list(APPEND LINK_LIBS ${MsAaudio_TARGET}) +endif() +if(MsAndroidCamera2_FOUND) + list(APPEND LINK_LIBS ${MsAndroidCamera2_TARGET}) +endif() +if(MsWebRtc_FOUND) + list(APPEND LINK_LIBS ${MsWebRtc_TARGET}) +endif() + #define log domain for this part of code add_definitions(-DBCTBX_LOG_DOMAIN="liblinphone") @@ -165,6 +175,7 @@ set(LINPHONE_CXX_OBJECTS_PRIVATE_HEADER_FILES conference/conference-params-interface.h conference/conference-enums.h conference/conference-id.h + conference/conference-id-params.h conference/conference-info.h conference/conference-scheduler.h conference/sip-conference-scheduler.h @@ -172,6 +183,7 @@ set(LINPHONE_CXX_OBJECTS_PRIVATE_HEADER_FILES conference/conference-listener.h conference/notify-conference-listener.h conference/conference.h + conference/conference-context.h conference/server-conference.h conference/params/call-session-params-p.h conference/params/call-session-params.h @@ -483,9 +495,11 @@ set(LINPHONE_CXX_OBJECTS_SOURCE_FILES conference/conference-params-interface.cpp conference/conference-enums.cpp conference/conference-id.cpp + conference/conference-id-params.cpp conference/conference-info.cpp conference/conference-scheduler.cpp conference/conference.cpp + conference/conference-context.cpp conference/conference-interface.cpp conference/sip-conference-scheduler.cpp conference/ccmp-conference-scheduler.cpp @@ -746,6 +760,7 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 12 AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 15) set_source_files_properties(sal/potential_config_graph.cpp + nat/nat-policy.cpp utils/version.cpp search/magic-search.cpp PROPERTIES COMPILE_OPTIONS "-Wno-error=maybe-uninitialized") diff --git a/src/account/account-params.cpp b/src/account/account-params.cpp index b991965a35568580fc1276abe8bda439aaa4e86a..6b602b2e1969ad1241fdc7f73e8eb1949d05b718 100644 --- a/src/account/account-params.cpp +++ b/src/account/account-params.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2022 Belledonne Communications SARL. + * Copyright (c) 2010-2025 Belledonne Communications SARL. * * This file is part of Liblinphone * (see https://gitlab.linphone.org/BC/public/liblinphone). @@ -19,14 +19,15 @@ */ #include "account-params.h" + +#include <set> + #include "c-wrapper/internal/c-tools.h" #include "core/core.h" -#include "linphone/api/c-address.h" #include "linphone/types.h" #include "nat/nat-policy.h" #include "private.h" #include "push-notification/push-notification-config.h" -#include <set> // ============================================================================= @@ -40,47 +41,65 @@ static string generate_account_id() { return string("proxy_config_").append(id); // TODO: change to account } -AccountParams::AccountParams(LinphoneCore *lc) { - mExpires = lc ? linphone_config_get_default_int(lc->config, "proxy", "reg_expires", 3600) : 3600; - mRegisterEnabled = lc ? !!linphone_config_get_default_int(lc->config, "proxy", "reg_sendregister", 1) : 1; - mInternationalPrefix = lc ? linphone_config_get_default_string(lc->config, "proxy", "dial_prefix", "") : ""; +AccountParams::AccountParams(LinphoneCore *lc, bool useDefaultValues) { + if (!lc && useDefaultValues) { + lWarning() << "Unable to apply proxy default values: LinphoneCore is null."; + useDefaultValues = false; + } + + mExpires = useDefaultValues ? linphone_config_get_default_int(lc->config, "proxy", "reg_expires", 3600) : 3600; + mRegisterEnabled = + useDefaultValues ? !!linphone_config_get_default_int(lc->config, "proxy", "reg_sendregister", 1) : 1; + mInternationalPrefix = + useDefaultValues ? linphone_config_get_default_string(lc->config, "proxy", "dial_prefix", "") : ""; mInternationalPrefixIsoCountryCode = - lc ? linphone_config_get_default_string(lc->config, "proxy", "dial_prefix_iso_country_code", "") : ""; + useDefaultValues ? linphone_config_get_default_string(lc->config, "proxy", "dial_prefix_iso_country_code", "") + : ""; mUseInternationalPrefixForCallsAndChats = - lc ? !!linphone_config_get_default_int(lc->config, "proxy", "use_dial_prefix_for_calls_and_chats", true) : true; + useDefaultValues + ? !!linphone_config_get_default_int(lc->config, "proxy", "use_dial_prefix_for_calls_and_chats", true) + : true; mDialEscapePlusEnabled = - lc ? !!linphone_config_get_default_int(lc->config, "proxy", "dial_escape_plus", false) : false; - mPrivacy = lc ? (LinphonePrivacyMask)linphone_config_get_default_int(lc->config, "proxy", "privacy", - LinphonePrivacyDefault) - : (LinphonePrivacyMask)LinphonePrivacyDefault; - mIdentity = lc ? linphone_config_get_default_string(lc->config, "proxy", "reg_identity", "") : ""; + useDefaultValues ? !!linphone_config_get_default_int(lc->config, "proxy", "dial_escape_plus", false) : false; + mPrivacy = useDefaultValues ? (LinphonePrivacyMask)linphone_config_get_default_int(lc->config, "proxy", "privacy", + LinphonePrivacyDefault) + : (LinphonePrivacyMask)LinphonePrivacyDefault; + mIdentity = useDefaultValues ? linphone_config_get_default_string(lc->config, "proxy", "reg_identity", "") : ""; mIdentityAddress = Address::create(mIdentity); - mProxy = lc ? linphone_config_get_default_string(lc->config, "proxy", "reg_proxy", "") : ""; + mProxy = useDefaultValues ? linphone_config_get_default_string(lc->config, "proxy", "reg_proxy", "") : ""; mProxyAddress = Address::create(mProxy); - string route = lc ? linphone_config_get_default_string(lc->config, "proxy", "reg_route", "") : ""; + string route = useDefaultValues ? linphone_config_get_default_string(lc->config, "proxy", "reg_route", "") : ""; if (!route.empty()) { const std::list<std::shared_ptr<Address>> routes{Address::create(route)}; setRoutes(routes); } - mRealm = lc ? linphone_config_get_default_string(lc->config, "proxy", "realm", "") : ""; + mRealm = useDefaultValues ? linphone_config_get_default_string(lc->config, "proxy", "realm", "") : ""; mQualityReportingEnabled = - lc ? !!linphone_config_get_default_int(lc->config, "proxy", "quality_reporting_enabled", false) : false; + useDefaultValues ? !!linphone_config_get_default_int(lc->config, "proxy", "quality_reporting_enabled", false) + : false; mQualityReportingCollector = - lc ? linphone_config_get_default_string(lc->config, "proxy", "quality_reporting_collector", "") : ""; + useDefaultValues ? linphone_config_get_default_string(lc->config, "proxy", "quality_reporting_collector", "") + : ""; mQualityReportingInterval = - lc ? linphone_config_get_default_int(lc->config, "proxy", "quality_reporting_interval", 0) : 0; - mContactParameters = lc ? linphone_config_get_default_string(lc->config, "proxy", "contact_parameters", "") : ""; + useDefaultValues ? linphone_config_get_default_int(lc->config, "proxy", "quality_reporting_interval", 0) : 0; + mContactParameters = + useDefaultValues ? linphone_config_get_default_string(lc->config, "proxy", "contact_parameters", "") : ""; mContactUriParameters = - lc ? linphone_config_get_default_string(lc->config, "proxy", "contact_uri_parameters", "") : ""; + useDefaultValues ? linphone_config_get_default_string(lc->config, "proxy", "contact_uri_parameters", "") : ""; mAllowCpimMessagesInBasicChatRooms = - lc ? !!linphone_config_get_default_int(lc->config, "proxy", "cpim_in_basic_chat_rooms_enabled", false) : false; - - mAvpfMode = lc ? static_cast<LinphoneAVPFMode>( - linphone_config_get_default_int(lc->config, "proxy", "avpf", LinphoneAVPFDefault)) - : LinphoneAVPFDefault; - mAvpfRrInterval = lc ? !!linphone_config_get_default_int(lc->config, "proxy", "avpf_rr_interval", 5) : 5; - mPublishExpires = lc ? linphone_config_get_default_int(lc->config, "proxy", "publish_expires", 600) : 600; - mPublishEnabled = lc ? !!linphone_config_get_default_int(lc->config, "proxy", "publish", false) : false; + useDefaultValues + ? !!linphone_config_get_default_int(lc->config, "proxy", "cpim_in_basic_chat_rooms_enabled", false) + : false; + + mAvpfMode = useDefaultValues ? static_cast<LinphoneAVPFMode>(linphone_config_get_default_int( + lc->config, "proxy", "avpf", LinphoneAVPFDefault)) + : LinphoneAVPFDefault; + mAvpfRrInterval = + useDefaultValues ? !!linphone_config_get_default_int(lc->config, "proxy", "avpf_rr_interval", 5) : 5; + mPublishExpires = + useDefaultValues ? linphone_config_get_default_int(lc->config, "proxy", "publish_expires", 600) : 600; + mPublishEnabled = + useDefaultValues ? !!linphone_config_get_default_int(lc->config, "proxy", "publish", false) : false; bool pushAllowedDefault = false; bool remotePushAllowedDefault = false; @@ -88,26 +107,30 @@ AccountParams::AccountParams(LinphoneCore *lc) { pushAllowedDefault = true; #endif mPushNotificationAllowed = - lc ? !!linphone_config_get_default_int(lc->config, "proxy", "push_notification_allowed", pushAllowedDefault) - : pushAllowedDefault; + useDefaultValues + ? !!linphone_config_get_default_int(lc->config, "proxy", "push_notification_allowed", pushAllowedDefault) + : pushAllowedDefault; mRemotePushNotificationAllowed = - lc ? !!linphone_config_get_default_int(lc->config, "proxy", "remote_push_notification_allowed", - remotePushAllowedDefault) - : remotePushAllowedDefault; - mForceRegisterOnPush = - lc ? !!linphone_config_get_default_int(lc->config, "proxy", "force_register_on_push", false) : false; - mUnregisterAtStop = lc ? !!linphone_config_get_default_int(lc->config, "proxy", "unregister_at_stop", true) : true; - mRefKey = lc ? linphone_config_get_default_string(lc->config, "proxy", "refkey", "") : ""; + useDefaultValues ? !!linphone_config_get_default_int(lc->config, "proxy", "remote_push_notification_allowed", + remotePushAllowedDefault) + : remotePushAllowedDefault; + mForceRegisterOnPush = useDefaultValues + ? !!linphone_config_get_default_int(lc->config, "proxy", "force_register_on_push", false) + : false; + mUnregisterAtStop = + useDefaultValues ? !!linphone_config_get_default_int(lc->config, "proxy", "unregister_at_stop", true) : true; + mRefKey = useDefaultValues ? linphone_config_get_default_string(lc->config, "proxy", "refkey", "") : ""; /* CAUTION: the nat_policy_ref meaning in default values is different than in usual [nat_policy_%i] section. * This is not consistent and error-prone. * Normally, the nat_policy_ref refers to a "ref" entry within a [nat_policy_%i] section. */ - string natPolicyRef = lc ? linphone_config_get_default_string(lc->config, "proxy", "nat_policy_ref", "") : ""; + string natPolicyRef = + useDefaultValues ? linphone_config_get_default_string(lc->config, "proxy", "nat_policy_ref", "") : ""; if (!natPolicyRef.empty()) { std::shared_ptr<NatPolicy> policy = nullptr; - if (linphone_config_has_section(lc->config, natPolicyRef.c_str())) { + if (lc && linphone_config_has_section(lc->config, natPolicyRef.c_str())) { /* Odd method - to be deprecated, inconsistent */ policy = NatPolicy::create(L_GET_CPP_PTR_FROM_C_OBJECT(lc), NatPolicy::ConstructionMethod::FromSectionName, natPolicyRef); @@ -123,73 +146,98 @@ AccountParams::AccountParams(LinphoneCore *lc) { << "]"; } } - mDependsOn = lc ? linphone_config_get_default_string(lc->config, "proxy", "depends_on", "") : ""; - string idkey = lc ? linphone_config_get_default_string(lc->config, "proxy", "idkey", "") : ""; + mDependsOn = useDefaultValues ? linphone_config_get_default_string(lc->config, "proxy", "depends_on", "") : ""; + string idkey = useDefaultValues ? linphone_config_get_default_string(lc->config, "proxy", "idkey", "") : ""; if (!idkey.empty()) { mIdKey = idkey; } else { mIdKey = generate_account_id(); } string conferenceFactoryUri = - lc ? linphone_config_get_default_string(lc->config, "proxy", "conference_factory_uri", "") : ""; + useDefaultValues ? linphone_config_get_default_string(lc->config, "proxy", "conference_factory_uri", "") : ""; setConferenceFactoryUri(conferenceFactoryUri); string audioVideoConferenceFactoryUri = - lc ? linphone_config_get_default_string(lc->config, "proxy", "audio_video_conference_factory_uri", "") : ""; + useDefaultValues + ? linphone_config_get_default_string(lc->config, "proxy", "audio_video_conference_factory_uri", "") + : ""; mAudioVideoConferenceFactoryAddress = nullptr; if (!audioVideoConferenceFactoryUri.empty()) { mAudioVideoConferenceFactoryAddress = Address::create(audioVideoConferenceFactoryUri); } - mCcmpServerUrl = lc ? linphone_config_get_default_string(lc->config, "proxy", "ccmp_server_url", "") : ""; - mCcmpUserId = lc ? linphone_config_get_default_string(lc->config, "proxy", "ccmp_user_id", "") : ""; + mCcmpServerUrl = + useDefaultValues ? linphone_config_get_default_string(lc->config, "proxy", "ccmp_server_url", "") : ""; + mCcmpUserId = useDefaultValues ? linphone_config_get_default_string(lc->config, "proxy", "ccmp_user_id", "") : ""; if (lc && lc->push_config) { mPushNotificationConfig = PushNotificationConfig::toCpp(lc->push_config)->clone(); } else { mPushNotificationConfig = new PushNotificationConfig(); - mPushNotificationConfig->readPushParamsFromString( - string(lc ? linphone_config_get_default_string(lc->config, "proxy", "push_parameters", "") : "")); - } - mRtpBundleEnabled = - lc ? !!linphone_config_get_default_int(lc->config, "proxy", "rtp_bundle", linphone_core_rtp_bundle_enabled(lc)) - : false; - mRtpBundleAssumption = - lc ? !!linphone_config_get_default_int(lc->config, "proxy", "rtp_bundle_assumption", false) : false; - - string customContact = lc ? linphone_config_get_default_string(lc->config, "proxy", "custom_contact", "") : ""; + mPushNotificationConfig->readPushParamsFromString(string( + useDefaultValues ? linphone_config_get_default_string(lc->config, "proxy", "push_parameters", "") : "")); + } + mRtpBundleEnabled = useDefaultValues ? !!linphone_config_get_default_int(lc->config, "proxy", "rtp_bundle", + linphone_core_rtp_bundle_enabled(lc)) + : false; + mRtpBundleAssumption = useDefaultValues + ? !!linphone_config_get_default_int(lc->config, "proxy", "rtp_bundle_assumption", false) + : false; + + string customContact = + useDefaultValues ? linphone_config_get_default_string(lc->config, "proxy", "custom_contact", "") : ""; setCustomContact(customContact); - string limeServerUrl = lc ? linphone_config_get_default_string(lc->config, "proxy", "lime_server_url", "") : ""; + string limeServerUrl = + useDefaultValues ? linphone_config_get_default_string(lc->config, "proxy", "lime_server_url", "") : ""; setLimeServerUrl(limeServerUrl); - string limeAlgo = lc ? linphone_config_get_default_string(lc->config, "proxy", "lime_algo", "") : ""; + string limeAlgo = useDefaultValues ? linphone_config_get_default_string(lc->config, "proxy", "lime_algo", "") : ""; setLimeAlgo(limeAlgo); - string pictureUri = lc ? linphone_config_get_default_string(lc->config, "proxy", "picture_uri", "") : ""; + string pictureUri = + useDefaultValues ? linphone_config_get_default_string(lc->config, "proxy", "picture_uri", "") : ""; setPictureUri(pictureUri); - string mwiServerUri = lc ? linphone_config_get_default_string(lc->config, "proxy", "mwi_server_uri", "") : ""; + string mwiServerUri = + useDefaultValues ? linphone_config_get_default_string(lc->config, "proxy", "mwi_server_uri", "") : ""; mMwiServerAddress = nullptr; if (!mwiServerUri.empty()) { setMwiServerAddress(Address::create(mwiServerUri)); } - string voicemailUri = lc ? linphone_config_get_default_string(lc->config, "proxy", "voicemail_uri", "") : ""; + string voicemailUri = + useDefaultValues ? linphone_config_get_default_string(lc->config, "proxy", "voicemail_uri", "") : ""; mVoicemailAddress = nullptr; if (!voicemailUri.empty()) { setVoicemailAddress(Address::create(voicemailUri)); } mInstantMessagingEncryptionMandatory = - lc ? !!linphone_config_get_default_int(lc->config, "proxy", "im_encryption_mandatory", 0) : 0; -} - -AccountParams::AccountParams(LinphoneCore *lc, int index) : AccountParams(nullptr) { + useDefaultValues ? !!linphone_config_get_default_int(lc->config, "proxy", "im_encryption_mandatory", 0) : 0; + + string supportedTags = lc ? linphone_config_get_default_string(lc->config, "proxy", "supported", "empty") : "empty"; + if (useDefaultValues && supportedTags != "empty") { + vector<string> splitTags = bctoolbox::Utils::split(supportedTags, ","); + list<string> supportedTagsList; + for (const auto &tag : splitTags) + supportedTagsList.push_back(Utils::trim(tag)); + mSupportedTagsList.mList = supportedTagsList; + mUseSupportedTags = true; + } else if (lc) { + vector<string> splitTags = bctoolbox::Utils::split(lc->sal->getSupportedTags(), ","); + list<string> supportedTagsList; + for (const auto &tag : splitTags) + supportedTagsList.push_back(Utils::trim(tag)); + mSupportedTagsList.mList = supportedTagsList; + } +} + +AccountParams::AccountParams(LinphoneCore *lc, int index) : AccountParams(lc, false) { LpConfig *config = lc->config; char key[50]; - sprintf(key, "proxy_%i", index); // TODO: change to account + snprintf(key, sizeof(key), "proxy_%i", index); // TODO: change to account mIdentity = linphone_config_get_string(config, key, "reg_identity", mIdentity.c_str()); std::shared_ptr<Address> identity_address = Address::create(mIdentity); @@ -261,8 +309,8 @@ AccountParams::AccountParams(LinphoneCore *lc, int index) : AccountParams(nullpt mPublishExpires = linphone_config_get_int(config, key, "publish_expires", mPublishExpires); - const char *nat_policy_ref = linphone_config_get_string(config, key, "nat_policy_ref", NULL); - if (nat_policy_ref != NULL) { + const char *nat_policy_ref = linphone_config_get_string(config, key, "nat_policy_ref", nullptr); + if (nat_policy_ref != nullptr) { /* CAUTION: the nat_policy_ref meaning in default values is different than in usual [nat_policy_%i] section. * This is not consistent and error-prone. * Normally, the nat_policy_ref refers to a "ref" entry within a [nat_policy_%i] section. @@ -313,6 +361,16 @@ AccountParams::AccountParams(LinphoneCore *lc, int index) : AccountParams(nullpt mInstantMessagingEncryptionMandatory = !!linphone_config_get_bool(config, key, "im_encryption_mandatory", false); + string supported_tags = linphone_config_get_string(config, key, "supported", "empty"); + if (supported_tags != "empty") { + vector<string> splitTags = bctoolbox::Utils::split(supported_tags, ","); + list<string> supportedTagsList; + for (const auto &tag : splitTags) + supportedTagsList.push_back(Utils::trim(tag)); + mSupportedTagsList.mList = supportedTagsList; + mUseSupportedTags = true; + } + readCustomParamsFromConfigFile(config, key); } @@ -404,6 +462,9 @@ AccountParams::AccountParams(const AccountParams &other) : HybridObject(other), } mInstantMessagingEncryptionMandatory = other.mInstantMessagingEncryptionMandatory; + + mSupportedTagsList = other.mSupportedTagsList; + mUseSupportedTags = other.mUseSupportedTags; } AccountParams::~AccountParams() { @@ -568,7 +629,7 @@ void AccountParams::setConferenceFactoryUri(const std::string &conferenceFactory setConferenceFactoryAddress(conferenceFactoryUri.empty() ? nullptr : Address::create(conferenceFactoryUri)); } -void AccountParams::setConferenceFactoryAddress(const std::shared_ptr<const Address> conferenceFactoryAddress) { +void AccountParams::setConferenceFactoryAddress(const std::shared_ptr<const Address> &conferenceFactoryAddress) { if (mConferenceFactoryAddress != nullptr) { mConferenceFactoryAddress = nullptr; } @@ -578,7 +639,7 @@ void AccountParams::setConferenceFactoryAddress(const std::shared_ptr<const Addr } void AccountParams::setAudioVideoConferenceFactoryAddress( - const std::shared_ptr<const Address> audioVideoConferenceFactoryAddress) { + const std::shared_ptr<const Address> &audioVideoConferenceFactoryAddress) { if (mAudioVideoConferenceFactoryAddress != nullptr) { mAudioVideoConferenceFactoryAddress = nullptr; } @@ -587,11 +648,11 @@ void AccountParams::setAudioVideoConferenceFactoryAddress( } } -void AccountParams::setCcmpServerUrl(const std::string ccmpServerUrl) { +void AccountParams::setCcmpServerUrl(const std::string &ccmpServerUrl) { mCcmpServerUrl = ccmpServerUrl; } -void AccountParams::setFileTranferServer(const std::string &fileTransferServer) { +void AccountParams::setFileTransferServer(const std::string &fileTransferServer) { mFileTransferServer = fileTransferServer; } @@ -607,7 +668,7 @@ LinphoneStatus AccountParams::setRoutesFromStringList(const bctbx_list_t *routes bool error = false; while (iterator != nullptr) { char *route = (char *)bctbx_list_get_data(iterator); - if (route != NULL && route[0] != '\0') { + if (route != nullptr && route[0] != '\0') { string tmp; /*try to prepend 'sip:' */ if (strstr(route, "sip:") == nullptr && strstr(route, "sips:") == nullptr) { @@ -616,7 +677,7 @@ LinphoneStatus AccountParams::setRoutesFromStringList(const bctbx_list_t *routes tmp.append(route); SalAddress *addr = sal_address_new(tmp.c_str()); - if (addr != NULL) { + if (addr != nullptr) { mRoutes.emplace_back(Address::create(addr, true)); } else { error = true; @@ -632,7 +693,7 @@ void AccountParams::setPrivacy(LinphonePrivacyMask privacy) { mPrivacy = privacy; } -LinphoneStatus AccountParams::setIdentityAddress(const std::shared_ptr<Address> identityAddress) { +LinphoneStatus AccountParams::setIdentityAddress(const std::shared_ptr<const Address> &identityAddress) { if (!identityAddress || identityAddress->getUsername().empty()) { lWarning() << "Invalid sip identity: " << identityAddress->toString(); return -1; @@ -667,7 +728,7 @@ void AccountParams::enableRtpBundleAssumption(bool value) { mRtpBundleAssumption = value; } -void AccountParams::setCustomContact(const std::shared_ptr<Address> contact) { +void AccountParams::setCustomContact(const std::shared_ptr<const Address> &contact) { mCustomContact = contact ? contact->clone()->toSharedPtr() : nullptr; } @@ -819,11 +880,11 @@ const char *AccountParams::getConferenceFactoryCstr() const { return mConferenceFactoryAddressCstr; } -std::shared_ptr<Address> AccountParams::getConferenceFactoryAddress() const { +std::shared_ptr<const Address> AccountParams::getConferenceFactoryAddress() const { return mConferenceFactoryAddress; } -std::shared_ptr<Address> AccountParams::getAudioVideoConferenceFactoryAddress() const { +std::shared_ptr<const Address> AccountParams::getAudioVideoConferenceFactoryAddress() const { return mAudioVideoConferenceFactoryAddress; } @@ -888,7 +949,7 @@ LinphonePrivacyMask AccountParams::getPrivacy() const { return mPrivacy; } -const std::shared_ptr<Address> &AccountParams::getIdentityAddress() const { +std::shared_ptr<Address> AccountParams::getIdentityAddress() const { return mIdentityAddress; } @@ -912,7 +973,7 @@ bool AccountParams::rtpBundleAssumptionEnabled() const { return mRtpBundleAssumption; } -const std::shared_ptr<Address> &AccountParams::getCustomContact() const { +std::shared_ptr<const Address> AccountParams::getCustomContact() const { return mCustomContact; } @@ -944,7 +1005,7 @@ void AccountParams::setMwiServerAddress(const std::shared_ptr<Address> &address) mMwiServerAddress = address; } -const std::shared_ptr<Address> &AccountParams::getMwiServerAddress() const { +std::shared_ptr<const Address> AccountParams::getMwiServerAddress() const { return mMwiServerAddress; } @@ -952,13 +1013,18 @@ void AccountParams::setVoicemailAddress(const std::shared_ptr<Address> &address) mVoicemailAddress = address; } -const std::shared_ptr<Address> &AccountParams::getVoicemailAddress() const { +std::shared_ptr<const Address> AccountParams::getVoicemailAddress() const { return mVoicemailAddress; } // ----------------------------------------------------------------------------- -LinphoneStatus AccountParams::setServerAddress(const std::shared_ptr<Address> serverAddr) { +LinphoneStatus AccountParams::setServerAddress(const std::shared_ptr<const Address> &serverAddr) { + if (serverAddr == nullptr) { + mProxyAddress.reset(); + mProxy = ""; + return 0; + } bool outboundProxyEnabled = getOutboundProxyEnabled(); mProxyAddress = serverAddr->clone()->toSharedPtr(); @@ -973,7 +1039,7 @@ LinphoneStatus AccountParams::setServerAddress(const std::shared_ptr<Address> se return 0; } -const std::shared_ptr<Address> &AccountParams::getServerAddress() const { +std::shared_ptr<const Address> AccountParams::getServerAddress() const { return mProxyAddress; } @@ -1056,10 +1122,27 @@ void AccountParams::setInstantMessagingEncryptionMandatory(bool mandatory) { mInstantMessagingEncryptionMandatory = mandatory; } +const std::list<std::string> &AccountParams::getSupportedTagsList() const { + return mSupportedTagsList.mList; +} + +const bctbx_list_t *AccountParams::getSupportedTagsCList() const { + return mSupportedTagsList.getCList(); +} + +void AccountParams::setSupportedTagsList(const std::list<std::string> &supportedTagsList) { + mSupportedTagsList.mList = supportedTagsList; + mUseSupportedTags = true; +} + +bool AccountParams::useSupportedTags() const { + return mUseSupportedTags; +} + void AccountParams::writeToConfigFile(LinphoneConfig *config, int index) { char key[50]; - sprintf(key, "proxy_%i", index); + snprintf(key, sizeof(key), "proxy_%i", index); linphone_config_clean_section(config, key); if (!mProxy.empty()) { @@ -1115,7 +1198,7 @@ void AccountParams::writeToConfigFile(LinphoneConfig *config, int index) { if (!mIdKey.empty()) linphone_config_set_string(config, key, "idkey", mIdKey.c_str()); linphone_config_set_int(config, key, "publish_expires", mPublishExpires); - if (mNatPolicy != NULL) { + if (mNatPolicy != nullptr) { linphone_config_set_string(config, key, "nat_policy_ref", mNatPolicy->getRef().c_str()); } @@ -1152,6 +1235,18 @@ void AccountParams::writeToConfigFile(LinphoneConfig *config, int index) { } linphone_config_set_bool(config, key, "im_encryption_mandatory", mInstantMessagingEncryptionMandatory); + + if (mUseSupportedTags) { + std::ostringstream result; + auto cppList = mSupportedTagsList.mList; + for (auto &tag : cppList) { + if (tag != cppList.front()) { + result << ", "; + } + result << tag; + } + linphone_config_set_string(config, key, "supported", result.str().c_str()); + } } LINPHONE_END_NAMESPACE diff --git a/src/account/account-params.h b/src/account/account-params.h index 4da2952d4af6134cb72f4688016e0ff031d68ee6..fb14d4bdd5ba2e16b2b9b59baa264b1f31a398a7 100644 --- a/src/account/account-params.h +++ b/src/account/account-params.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2022 Belledonne Communications SARL. + * Copyright (c) 2010-2025 Belledonne Communications SARL. * * This file is part of Liblinphone * (see https://gitlab.linphone.org/BC/public/liblinphone). @@ -21,8 +21,10 @@ #ifndef _L_ACCOUNT_PARAMS_H_ #define _L_ACCOUNT_PARAMS_H_ -#include "address/address.h" #include "belle-sip/object++.hh" + +#include "address/address.h" +#include "c-wrapper/list-holder.h" #include "linphone/api/c-push-notification-config.h" #include "linphone/api/c-types.h" #include "linphone/types.h" @@ -39,7 +41,7 @@ class LINPHONE_PUBLIC AccountParams : public bellesip::HybridObject<LinphoneAcco friend class Account; public: - AccountParams(LinphoneCore *lc); + AccountParams(LinphoneCore *lc, bool useDefaultValues); AccountParams(LinphoneCore *lc, int index); AccountParams(const AccountParams &other); virtual ~AccountParams(); @@ -74,20 +76,21 @@ public: void setDependsOn(const std::string &dependsOn); void setIdKey(const std::string &idKey); void setConferenceFactoryUri(const std::string &conferenceFactoryUri); - void setConferenceFactoryAddress(const std::shared_ptr<const Address> factoryAddress); - void setAudioVideoConferenceFactoryAddress(const std::shared_ptr<const Address> audioVideoConferenceFactoryAddress); - void setCcmpServerUrl(const std::string ccmpServerAddress); - void setFileTranferServer(const std::string &fileTransferServer); + void setConferenceFactoryAddress(const std::shared_ptr<const Address> &factoryAddress); + void + setAudioVideoConferenceFactoryAddress(const std::shared_ptr<const Address> &audioVideoConferenceFactoryAddress); + void setCcmpServerUrl(const std::string &ccmpServerAddress); + void setFileTransferServer(const std::string &fileTransferServer); void setPrivacy(LinphonePrivacyMask privacy); void setAvpfMode(LinphoneAVPFMode avpfMode); void setNatPolicy(const std::shared_ptr<NatPolicy> &natPolicy); void setPushNotificationConfig(PushNotificationConfig *pushNotificationConfig); - LinphoneStatus setIdentityAddress(const std::shared_ptr<Address> identityAddress); + LinphoneStatus setIdentityAddress(const std::shared_ptr<const Address> &identityAddress); LinphoneStatus setRoutes(const std::list<std::shared_ptr<Address>> &routes); LinphoneStatus setRoutesFromStringList(const bctbx_list_t *routes); void enableRtpBundle(bool value); void enableRtpBundleAssumption(bool value); - void setCustomContact(const std::shared_ptr<Address> contact); + void setCustomContact(const std::shared_ptr<const Address> &contact); void setLimeServerUrl(const std::string &url); /** * valid algorithms are: c25519, c448 and c25519k512. Empty string is also valid, it will unset the value @@ -98,6 +101,7 @@ public: void setMwiServerAddress(const std::shared_ptr<Address> &address); void setVoicemailAddress(const std::shared_ptr<Address> &address); void setInstantMessagingEncryptionMandatory(bool mandatory); + void setSupportedTagsList(const std::list<std::string> &supportedTagsList); // Getters int getExpires() const; @@ -127,8 +131,8 @@ public: const std::string &getRefKey() const; const std::string &getDependsOn() const; const std::string &getIdKey() const; - std::shared_ptr<Address> getConferenceFactoryAddress() const; - std::shared_ptr<Address> getAudioVideoConferenceFactoryAddress() const; + std::shared_ptr<const Address> getConferenceFactoryAddress() const; + std::shared_ptr<const Address> getAudioVideoConferenceFactoryAddress() const; const char *getCcmpServerUrlCstr() const; const std::string &getCcmpServerUrl() const; const std::string &getCcmpUserId() const; @@ -141,23 +145,26 @@ public: const std::list<std::string> getRoutesString() const; const bctbx_list_t *getRoutesCString() const; LinphonePrivacyMask getPrivacy() const; - const std::shared_ptr<Address> &getIdentityAddress() const; + std::shared_ptr<Address> getIdentityAddress() const; LinphoneAVPFMode getAvpfMode() const; std::shared_ptr<NatPolicy> getNatPolicy() const; PushNotificationConfig *getPushNotificationConfig() const; bool rtpBundleEnabled() const; bool rtpBundleAssumptionEnabled() const; - const std::shared_ptr<Address> &getCustomContact() const; + std::shared_ptr<const Address> getCustomContact() const; const std::string &getLimeServerUrl() const; const std::string &getLimeAlgo() const; const std::string &getPictureUri() const; - const std::shared_ptr<Address> &getMwiServerAddress() const; - const std::shared_ptr<Address> &getVoicemailAddress() const; + std::shared_ptr<const Address> getMwiServerAddress() const; + std::shared_ptr<const Address> getVoicemailAddress() const; bool isInstantMessagingEncryptionMandatory() const; + const std::list<std::string> &getSupportedTagsList() const; + const bctbx_list_t *getSupportedTagsCList() const; + bool useSupportedTags() const; // Other - LinphoneStatus setServerAddress(const std::shared_ptr<Address> serverAddr); - const std::shared_ptr<Address> &getServerAddress() const; + LinphoneStatus setServerAddress(const std::shared_ptr<const Address> &serverAddr); + std::shared_ptr<const Address> getServerAddress() const; LinphoneStatus setServerAddressAsString(const std::string &serverAddr); const std::string &getServerAddressAsString() const; @@ -237,6 +244,9 @@ private: std::shared_ptr<Address> mCustomContact = nullptr; std::shared_ptr<Address> mMwiServerAddress = nullptr; std::shared_ptr<Address> mVoicemailAddress = nullptr; + + ListHolder<std::string> mSupportedTagsList = {}; + bool mUseSupportedTags = false; }; LINPHONE_END_NAMESPACE diff --git a/src/account/account.cpp b/src/account/account.cpp index 17f5a0fe73797d02973c7d118687fb666cbf0ebe..1300d019d7c8df310b79acfddec8ede0b1324a3c 100644 --- a/src/account/account.cpp +++ b/src/account/account.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2022 Belledonne Communications SARL. + * Copyright (c) 2010-2025 Belledonne Communications SARL. * * This file is part of Liblinphone * (see https://gitlab.linphone.org/BC/public/liblinphone). @@ -126,15 +126,15 @@ bool Account::computePublishParamsHash() { belle_sip_auth_helper_compute_ha1(source.c_str(), "dummy", "dummy", hash); saved = hash[16]; hash[16] = '\0'; - mPreviousPublishParamsHash[0] = strtoull(hash, (char **)NULL, 16); + mPreviousPublishParamsHash[0] = strtoull(hash, (char **)nullptr, 16); hash[16] = saved; - mPreviousPublishParamsHash[1] = strtoull(&hash[16], (char **)NULL, 16); + mPreviousPublishParamsHash[1] = strtoull(&hash[16], (char **)nullptr, 16); return previous_hash[0] != mPreviousPublishParamsHash[0] || previous_hash[1] != mPreviousPublishParamsHash[1]; } LinphoneAccountAddressComparisonResult Account::compareLinphoneAddresses(const std::shared_ptr<const Address> &a, const std::shared_ptr<const Address> &b) { - if (a == NULL && b == NULL) return LinphoneAccountAddressEqual; + if (a == nullptr && b == nullptr) return LinphoneAccountAddressEqual; else if (!a || !b) return LinphoneAccountAddressDifferent; if (*a == *b) return LinphoneAccountAddressEqual; @@ -150,12 +150,12 @@ LinphoneAccountAddressComparisonResult Account::compareLinphoneAddresses(const s LinphoneAccountAddressComparisonResult Account::isServerConfigChanged(std::shared_ptr<AccountParams> oldParams, std::shared_ptr<AccountParams> newParams) { std::shared_ptr<Address> oldProxy = - oldParams != nullptr && !oldParams->mProxy.empty() ? Address::create(oldParams->mProxy) : NULL; - std::shared_ptr<Address> newProxy = !newParams->mProxy.empty() ? Address::create(newParams->mProxy) : NULL; + oldParams != nullptr && !oldParams->mProxy.empty() ? Address::create(oldParams->mProxy) : nullptr; + std::shared_ptr<Address> newProxy = !newParams->mProxy.empty() ? Address::create(newParams->mProxy) : nullptr; LinphoneAccountAddressComparisonResult result_identity; LinphoneAccountAddressComparisonResult result; - result = compareLinphoneAddresses(oldParams != nullptr ? oldParams->mIdentityAddress : NULL, + result = compareLinphoneAddresses(oldParams != nullptr ? oldParams->mIdentityAddress : nullptr, newParams->mIdentityAddress); if (result == LinphoneAccountAddressDifferent) goto end; result_identity = result; @@ -652,7 +652,7 @@ std::shared_ptr<Address> Account::guessContactForRegister() { std::shared_ptr<Address> contactParamsWrapper = Address::create(string("sip:dummy;" + newParams->mContactUriParameters)); bool didRemoveParams = false; - for (auto pushParam : newParams->mPushNotificationConfig->getPushParamsMap()) { + for (const auto &pushParam : newParams->mPushNotificationConfig->getPushParamsMap()) { string paramName = pushParam.first; if (!contactParamsWrapper->getUriParamValue(paramName).empty()) { contactParamsWrapper->removeUriParam(paramName); @@ -950,7 +950,7 @@ void Account::updateChatRoomList() const { auto localAddress = mParams->mIdentityAddress; const list<shared_ptr<AbstractChatRoom>> chatRooms = getCore()->getChatRooms(); - for (auto chatRoom : chatRooms) { + for (const auto &chatRoom : chatRooms) { if (localAddress->weakEqual(chatRoom->getLocalAddress())) { results.push_back(chatRoom); } @@ -1153,7 +1153,7 @@ void Account::writeAllToConfigFile(const std::shared_ptr<Core> core) { void Account::writeToConfigFile(LpConfig *config, const std::shared_ptr<Account> &account, int index) { char key[50]; - sprintf(key, "proxy_%i", index); + snprintf(key, sizeof(key), "proxy_%i", index); linphone_config_clean_section(config, key); if (account == nullptr) { return; @@ -1205,10 +1205,10 @@ int Account::done() { if (mOp) { if (res == LinphoneAccountAddressDifferent) { unregister(); + mOp->setUserPointer(nullptr); /*we don't want to receive status for this un register*/ + mOp->unref(); /*but we keep refresher to handle authentication if needed*/ + mOp = nullptr; } - mOp->setUserPointer(NULL); /*we don't want to receive status for this un register*/ - mOp->unref(); /*but we keep refresher to handle authentication if needed*/ - mOp = nullptr; } if (mPresencePublishEvent) { if (res == LinphoneAccountAddressDifferent) { @@ -1263,7 +1263,7 @@ void Account::update() { mLimeUserAccountStatus == LimeUserAccountStatus::LimeUserAccountNeedCreation) { shared_ptr<Address> addr = mContactAddress; if (!addr) { - shared_ptr<Address> sip = getAccountParams()->getIdentityAddress(); + shared_ptr<const Address> sip = getAccountParams()->getIdentityAddress(); if (sip) { auto gr = getCCore()->sal->getUuid(); if (gr.empty()) return; @@ -1314,13 +1314,13 @@ void Account::apply(LinphoneCore *lc) { done(); } -shared_ptr<EventPublish> Account::createPublish(const std::string event, int expires) { +shared_ptr<EventPublish> Account::createPublish(const std::string &event, int expires) { if (!getCore()) { lError() << "Cannot create publish from " << *this << " not attached to any core"; return nullptr; } return dynamic_pointer_cast<EventPublish>( - (new EventPublish(getCore(), getSharedFromThis(), NULL, event, expires))->toSharedPtr()); + (new EventPublish(getCore(), getSharedFromThis(), nullptr, event, expires))->toSharedPtr()); } void Account::setPresenceModel(LinphonePresenceModel *presence) { @@ -1362,7 +1362,7 @@ int Account::sendPublish() { mPresencePublishEvent->setManualRefresherMode(true); } const auto &identityAddress = mParams->getIdentityAddress(); - mPresencePublishEvent->setUserData(identityAddress->toC()); + mPresencePublishEvent->setUserData(this); LinphoneConfig *config = linphone_core_get_config(getCCore()); if (linphone_config_get_bool(config, "sip", "update_presence_model_timestamp_before_publish_expires_refresh", @@ -1375,7 +1375,7 @@ int Account::sendPublish() { } } - if (linphone_presence_model_get_presentity(mPresenceModel) == NULL) { + if (linphone_presence_model_get_presentity(mPresenceModel) == nullptr) { lInfo() << "No presentity set for model [" << mPresenceModel << "], using identity from account [" << this << "]: " << *identityAddress; linphone_presence_model_set_presentity(mPresenceModel, identityAddress->toC()); @@ -1383,7 +1383,7 @@ int Account::sendPublish() { const auto currentPresentity = linphone_presence_model_get_presentity(mPresenceModel); std::shared_ptr<const Address> presentityAddress = nullptr; - char *contact = NULL; + char *contact = nullptr; if (!linphone_address_equal(currentPresentity, identityAddress->toC())) { lInfo() << "Presentity for model [" << mPresenceModel << "] differs account [" << this << "], using account " << *identityAddress; @@ -1392,7 +1392,7 @@ int Account::sendPublish() { contact = bctbx_strdup(linphone_presence_model_get_contact(mPresenceModel)); } linphone_presence_model_set_presentity(mPresenceModel, identityAddress->toC()); - linphone_presence_model_set_contact(mPresenceModel, NULL); /*it will be automatically computed*/ + linphone_presence_model_set_contact(mPresenceModel, nullptr); /*it will be automatically computed*/ } char *presence_body; @@ -1436,7 +1436,7 @@ bool Account::check() { lWarning() << "No proxy given for " << *this; return false; } - if (mParams->mIdentityAddress == NULL) { + if (mParams->mIdentityAddress == nullptr) { lWarning() << "Identity address of " << *this << " has not been set"; return false; } @@ -1476,7 +1476,7 @@ void Account::resolveDependencies() { } } if (!dependsOn.empty() && dependency == nullptr) { - if (dependentAccount == NULL) { + if (dependentAccount == nullptr) { lWarning() << "Account marked as dependent but no account found for idkey [" << dependsOn << "]"; return; } else { @@ -1492,7 +1492,7 @@ std::shared_ptr<Event> Account::getMwiEvent() const { } void Account::subscribeToMessageWaitingIndication() { - const std::shared_ptr<Address> &mwiServerAddress = mParams->getMwiServerAddress(); + std::shared_ptr<const Address> mwiServerAddress = mParams->getMwiServerAddress(); if (mwiServerAddress) { int expires = linphone_config_get_int(getCore()->getCCore()->config, "sip", "mwi_expires", 86400); if (mMwiEvent) mMwiEvent->terminate(); @@ -1720,7 +1720,7 @@ void Account::onLimeAlgoChanged(const std::string &limeAlgo) { // just call the create lime user function, it will get the info from the account shared_ptr<Address> addr = mContactAddress; if (!addr) { - shared_ptr<Address> sip = getAccountParams()->getIdentityAddress(); + auto sip = getAccountParams()->getIdentityAddress(); if (sip) { auto gr = getCCore()->sal->getUuid(); if (gr.empty()) return; @@ -1761,6 +1761,36 @@ void Account::ccmpConferenceInformationResponseReceived() { } } +void Account::handleCCMPResponseConferenceList(const HttpResponse &response) { + switch (response.getStatus()) { + case HttpResponse::Status::Valid: + handleResponseConferenceList(this, response); + break; + case HttpResponse::Status::Timeout: + handleTimeout(this, response); + break; + case HttpResponse::Status::IOError: + case HttpResponse::Status::InvalidRequest: + handleIoError(this, response); + break; + } +} + +void Account::handleCCMPResponseConferenceInformation(const HttpResponse &response) { + switch (response.getStatus()) { + case HttpResponse::Status::Valid: + handleResponseConferenceInformation(this, response); + break; + case HttpResponse::Status::Timeout: + handleTimeout(this, response); + break; + case HttpResponse::Status::IOError: + case HttpResponse::Status::InvalidRequest: + handleIoError(this, response); + break; + } +} + void Account::updateConferenceInfoListWithCcmp() const { const auto ccmpServerUrl = mParams->getCcmpServerUrl(); if (ccmpServerUrl.empty()) { @@ -1789,299 +1819,269 @@ void Account::updateConferenceInfoListWithCcmp() const { serializeCcmpRequest(httpBody, requestBody, map); const auto body = httpBody.str(); - belle_http_request_listener_callbacks_t internalCallbacks = {}; - internalCallbacks.process_response = Account::handleResponseConferenceList; - internalCallbacks.process_io_error = Account::handleIoError; - internalCallbacks.process_timeout = Account::handleTimeout; - internalCallbacks.process_auth_requested = Account::handleAuthRequested; - - belle_http_request_listener_t *listener = - belle_http_request_listener_create_from_callbacks(&internalCallbacks, const_cast<Account *>(this)); - // Send request to retrieve the list of all conferences on the CCMP server - if (!XmlUtils::sendCcmpRequest(getCore(), ccmpServerUrl, identity, body, listener)) { + auto *weakThis = const_cast<Account *>(this); + if (!XmlUtils::sendCcmpRequest(getCore(), ccmpServerUrl, identity, body, [weakThis](const HttpResponse &response) { + weakThis->handleCCMPResponseConferenceList(response); + })) { lError() << "An error occurred when requesting informations list linked to " << *this << " to server " << ccmpServerUrl; } } -void Account::handleResponseConferenceList(void *ctx, const belle_http_response_event_t *event) { - if (event->response) { - auto account = static_cast<Account *>(ctx); - int code = belle_http_response_get_status_code(event->response); - std::shared_ptr<Address> conferenceAddress; - if (code >= 200 && code < 300) { - belle_sip_body_handler_t *body = belle_sip_message_get_body_handler(BELLE_SIP_MESSAGE(event->response)); - char *content = belle_sip_object_to_string(body); - if (content) { - try { - istringstream data(content); - auto responseType = parseCcmpResponse(data, Xsd::XmlSchema::Flags::dont_validate); - ms_free(content); - CcmpConfsResponseMessageType &response = - dynamic_cast<CcmpConfsResponseMessageType &>(responseType->getCcmpResponse()); - const auto responseCodeType = response.getResponseCode(); - code = static_cast<int>(responseCodeType); - if (code >= 200 && code < 300) { - auto &confsResponse = response.getConfsResponse(); - auto &confsInfo = confsResponse.getConfsInfo(); - if (!confsInfo.present()) return; - auto &infos = confsInfo->getEntry(); - // For every conference that has been retrieved, send one more request to get all the details - for (auto &info : infos) { - ConfRequestType confRequest = ConfRequestType(); - auto confObjId = info.getUri(); - - CcmpConfRequestMessageType requestBody = CcmpConfRequestMessageType(confRequest); - // CCMP URI (conference object ID) if update or delete - if (!confObjId.empty()) { - requestBody.setConfObjID(confObjId); - } +void Account::handleResponseConferenceList(void *ctx, const HttpResponse &event) { + auto account = static_cast<Account *>(ctx); + int code = event.getHttpStatusCode(); + std::shared_ptr<Address> conferenceAddress; + if (code >= 200 && code < 300) { + const auto &body = event.getBody(); + auto content = body.getBodyAsString(); + if (!content.empty()) { + try { + istringstream data(content); + auto responseType = parseCcmpResponse(data, Xsd::XmlSchema::Flags::dont_validate); + auto &response = dynamic_cast<CcmpConfsResponseMessageType &>(responseType->getCcmpResponse()); + const auto responseCodeType = response.getResponseCode(); + code = static_cast<int>(responseCodeType); + if (code >= 200 && code < 300) { + auto &confsResponse = response.getConfsResponse(); + auto &confsInfo = confsResponse.getConfsInfo(); + if (!confsInfo.present()) return; + auto &infos = confsInfo->getEntry(); + // For every conference that has been retrieved, send one more request to get all the details + for (auto &info : infos) { + ConfRequestType confRequest = ConfRequestType(); + auto confObjId = info.getUri(); + + CcmpConfRequestMessageType requestBody = CcmpConfRequestMessageType(confRequest); + // CCMP URI (conference object ID) if update or delete + if (!confObjId.empty()) { + requestBody.setConfObjID(confObjId); + } - // Conference user ID - const auto &accountParams = account->getAccountParams(); - const auto &identity = accountParams->getIdentityAddress(); - const auto ccmpServerUrl = accountParams->getCcmpServerUrl(); - std::string identityXconUserId = Utils::getXconId(identity); - if (identityXconUserId.empty()) { - lError() << "Aborting creation of body of POST to request the list of conferences on " - "the CCMP server " - << ccmpServerUrl << " where account [" << account - << "] is a participant because its CCMP User ID is unknwon"; - return; - } - requestBody.setConfUserID(identityXconUserId); - requestBody.setOperation(OperationType::retrieve); - - stringstream httpBody; - Xsd::XmlSchema::NamespaceInfomap map; - map["conference-info"].name = "urn:ietf:params:xml:ns:conference-info"; - map["xcon-conference-info"].name = "urn:ietf:params:xml:ns:xcon-conference-info"; - map["xcon-ccmp"].name = "urn:ietf:params:xml:ns:xcon-ccmp"; - serializeCcmpRequest(httpBody, requestBody, map); - const auto body = httpBody.str(); - - belle_http_request_listener_callbacks_t internalCallbacks = {}; - internalCallbacks.process_response = Account::handleResponseConferenceInformation; - internalCallbacks.process_io_error = Account::handleIoError; - internalCallbacks.process_timeout = Account::handleTimeout; - internalCallbacks.process_auth_requested = Account::handleAuthRequested; - - belle_http_request_listener_t *listener = - belle_http_request_listener_create_from_callbacks(&internalCallbacks, ctx); - - if (XmlUtils::sendCcmpRequest(account->getCore(), ccmpServerUrl, identity, body, - listener)) { - account->ccmpConferenceInformationRequestSent(); - } else { - lError() << "An error occurred when requesting informations of conference " << confObjId - << " linked to Account [" << account << "] (" << *identity << ") to server " - << ccmpServerUrl; - } + // Conference user ID + const auto &accountParams = account->getAccountParams(); + const auto &identity = accountParams->getIdentityAddress(); + const auto ccmpServerUrl = accountParams->getCcmpServerUrl(); + std::string identityXconUserId = Utils::getXconId(identity); + if (identityXconUserId.empty()) { + lError() << "Aborting creation of body of POST to request the list of conferences on " + "the CCMP server " + << ccmpServerUrl << " where account [" << account + << "] is a participant because its CCMP User ID is unknwon"; + return; + } + requestBody.setConfUserID(identityXconUserId); + requestBody.setOperation(OperationType::retrieve); + + stringstream httpBody; + Xsd::XmlSchema::NamespaceInfomap map; + map["conference-info"].name = "urn:ietf:params:xml:ns:conference-info"; + map["xcon-conference-info"].name = "urn:ietf:params:xml:ns:xcon-conference-info"; + map["xcon-ccmp"].name = "urn:ietf:params:xml:ns:xcon-ccmp"; + serializeCcmpRequest(httpBody, requestBody, map); + const auto stringBody = httpBody.str(); + + auto weakThis = account; + if (XmlUtils::sendCcmpRequest(account->getCore(), ccmpServerUrl, identity, stringBody, + [weakThis](const HttpResponse &response) { + weakThis->handleCCMPResponseConferenceInformation(response); + })) { + account->ccmpConferenceInformationRequestSent(); + } else { + lError() << "An error occurred when requesting informations of conference " << confObjId + << " linked to Account [" << account << "] (" << *identity << ") to server " + << ccmpServerUrl; } } - } catch (const std::bad_cast &e) { - lError() << "Error while casting parsed CCMP response (CcmpConfsResponseMessageType) in " - << *account << ": " << e.what(); - } catch (const exception &e) { - lError() << "Error while parsing CCMP response (CcmpConfsResponseMessageType) in " << *account - << ": " << e.what(); } + } catch (const std::bad_cast &e) { + lError() << "Error while casting parsed CCMP response (CcmpConfsResponseMessageType) in " << *account + << ": " << e.what(); + } catch (const exception &e) { + lError() << "Error while parsing CCMP response (CcmpConfsResponseMessageType) in " << *account << ": " + << e.what(); } } } } -void Account::handleResponseConferenceInformation(void *ctx, const belle_http_response_event_t *event) { - if (event->response) { - auto account = static_cast<Account *>(ctx); - int code = belle_http_response_get_status_code(event->response); - std::shared_ptr<Address> conferenceAddress; - if (code >= 200 && code < 300) { - belle_sip_body_handler_t *body = belle_sip_message_get_body_handler(BELLE_SIP_MESSAGE(event->response)); - char *content = belle_sip_object_to_string(body); - if (content) { - try { - istringstream data(content); - auto responseType = parseCcmpResponse(data, Xsd::XmlSchema::Flags::dont_validate); - ms_free(content); - CcmpConfResponseMessageType &response = - dynamic_cast<CcmpConfResponseMessageType &>(responseType->getCcmpResponse()); - const auto responseCodeType = response.getResponseCode(); - code = static_cast<int>(responseCodeType); - if (code >= 200 && code < 300) { - auto &confResponse = response.getConfResponse(); - auto &confInfo = confResponse.getConfInfo(); - if (!confInfo.present()) return; - std::shared_ptr<ConferenceInfo> info = ConferenceInfo::create(); - info->setCcmpUri(confInfo->getEntity()); - auto &confDescription = confInfo->getConferenceDescription(); - if (confDescription.present()) { - auto &subject = confDescription.get().getSubject(); - if (subject.present() && !subject.get().empty()) { - info->setSubject(subject.get()); - } - auto &confUris = confDescription.get().getConfUris(); - auto &uris = confUris->getEntry(); - for (auto &uri : uris) { - info->setUri(Address::create(uri.getUri())); - } - const auto &availableMedia = confDescription.get().getAvailableMedia(); - if (availableMedia.present()) { - for (auto &mediaEntry : availableMedia.get().getEntry()) { - const std::string mediaType = mediaEntry.getType(); - const LinphoneMediaDirection mediaDirection = - XmlUtils::mediaStatusToMediaDirection(mediaEntry.getStatus().get()); - LinphoneStreamType streamType = LinphoneStreamTypeUnknown; - if (mediaType.compare("audio") == 0) { - streamType = LinphoneStreamTypeAudio; - } else if (mediaType.compare("video") == 0) { - streamType = LinphoneStreamTypeVideo; - } else if (mediaType.compare("text") == 0) { - streamType = LinphoneStreamTypeText; - } else { - lError() << "Unrecognized media type " << mediaType; - } - info->setCapability(streamType, (mediaDirection == LinphoneMediaDirectionSendRecv)); +void Account::handleResponseConferenceInformation(void *ctx, const HttpResponse &event) { + auto account = static_cast<Account *>(ctx); + int code = event.getHttpStatusCode(); + std::shared_ptr<Address> conferenceAddress; + if (code >= 200 && code < 300) { + const auto &body = event.getBody(); + auto content = body.getBodyAsString(); + if (!content.empty()) { + try { + istringstream data(content); + auto responseType = parseCcmpResponse(data, Xsd::XmlSchema::Flags::dont_validate); + auto &response = dynamic_cast<CcmpConfResponseMessageType &>(responseType->getCcmpResponse()); + const auto responseCodeType = response.getResponseCode(); + code = static_cast<int>(responseCodeType); + if (code >= 200 && code < 300) { + auto &confResponse = response.getConfResponse(); + auto &confInfo = confResponse.getConfInfo(); + if (!confInfo.present()) return; + std::shared_ptr<ConferenceInfo> info = ConferenceInfo::create(); + info->setCcmpUri(confInfo->getEntity()); + auto &confDescription = confInfo->getConferenceDescription(); + if (confDescription.present()) { + auto &subject = confDescription.get().getSubject(); + if (subject.present() && !subject.get().empty()) { + info->setSubject(subject.get()); + } + auto &confUris = confDescription.get().getConfUris(); + auto &uris = confUris->getEntry(); + for (auto &uri : uris) { + info->setUri(Address::create(uri.getUri())); + } + const auto &availableMedia = confDescription.get().getAvailableMedia(); + if (availableMedia.present()) { + for (auto &mediaEntry : availableMedia.get().getEntry()) { + const std::string &mediaType = mediaEntry.getType(); + const LinphoneMediaDirection mediaDirection = + XmlUtils::mediaStatusToMediaDirection(mediaEntry.getStatus().get()); + LinphoneStreamType streamType = LinphoneStreamTypeUnknown; + if (mediaType == "audio") { + streamType = LinphoneStreamTypeAudio; + } else if (mediaType == "video") { + streamType = LinphoneStreamTypeVideo; + } else if (mediaType == "text") { + streamType = LinphoneStreamTypeText; + } else { + lError() << "Unrecognized media type " << mediaType; } + info->setCapability(streamType, (mediaDirection == LinphoneMediaDirectionSendRecv)); } - auto &anySequence(confDescription.get().getAny()); - for (const auto &anyElement : anySequence) { - auto name = xsd::cxx::xml::transcode<char>(anyElement.getLocalName()); - auto nodeName = xsd::cxx::xml::transcode<char>(anyElement.getNodeName()); - auto nodeValue = xsd::cxx::xml::transcode<char>(anyElement.getNodeValue()); - if (nodeName == "xcon:conference-time") { - ConferenceTimeType conferenceTime{anyElement}; - for (auto &conferenceTimeEntry : conferenceTime.getEntry()) { - const std::string base = conferenceTimeEntry.getBase(); - if (!base.empty()) { - auto ics = Ics::Icalendar::createFromString(base.c_str()); - if (ics) { - auto timeInfo = ics->toConferenceInfo(); - info->setDateTime(timeInfo->getDateTime()); - info->setDuration(timeInfo->getDuration()); - } + } + auto &anySequence(confDescription.get().getAny()); + for (const auto &anyElement : anySequence) { + auto name = xsd::cxx::xml::transcode<char>(anyElement.getLocalName()); + auto nodeName = xsd::cxx::xml::transcode<char>(anyElement.getNodeName()); + auto nodeValue = xsd::cxx::xml::transcode<char>(anyElement.getNodeValue()); + if (nodeName == "xcon:conference-time") { + ConferenceTimeType conferenceTime{anyElement}; + for (auto &conferenceTimeEntry : conferenceTime.getEntry()) { + const std::string base = conferenceTimeEntry.getBase(); + if (!base.empty()) { + auto ics = Ics::Icalendar::createFromString(base); + if (ics) { + auto timeInfo = ics->toConferenceInfo(); + info->setDateTime(timeInfo->getDateTime()); + info->setDuration(timeInfo->getDuration()); } } } } } - auto &users = confInfo->getUsers(); - if (users.present()) { - auto &anySequence(users.get().getAny()); - for (const auto &anyElement : anySequence) { - auto name = xsd::cxx::xml::transcode<char>(anyElement.getLocalName()); - auto nodeName = xsd::cxx::xml::transcode<char>(anyElement.getNodeName()); - auto nodeValue = xsd::cxx::xml::transcode<char>(anyElement.getNodeValue()); - if (nodeName == "xcon:allowed-users-list") { - ConferenceInfo::participant_list_t participantInfos; - AllowedUsersListType allowedUserList{anyElement}; - auto users = allowedUserList.getTarget(); - for (auto &user : users) { - auto participantInfo = ParticipantInfo::create(user.getUri()); - participantInfos.push_back(participantInfo); - } - info->setParticipants(participantInfos); + } + auto &users = confInfo->getUsers(); + if (users.present()) { + auto &anySequence(users.get().getAny()); + for (const auto &anyElement : anySequence) { + auto name = xsd::cxx::xml::transcode<char>(anyElement.getLocalName()); + auto nodeName = xsd::cxx::xml::transcode<char>(anyElement.getNodeName()); + auto nodeValue = xsd::cxx::xml::transcode<char>(anyElement.getNodeValue()); + if (nodeName == "xcon:allowed-users-list") { + ConferenceInfo::participant_list_t participantInfos; + AllowedUsersListType allowedUserList{anyElement}; + auto allowedUsers = allowedUserList.getTarget(); + for (auto &user : allowedUsers) { + auto participantInfo = ParticipantInfo::create(user.getUri()); + participantInfos.push_back(participantInfo); } + info->setParticipants(participantInfos); } - for (auto &user : users->getUser()) { - auto &roles = user.getRoles(); - bool isOrganizer = false; - auto ccmpUri = user.getEntity().get(); - auto participantInfo = info->findParticipant(ccmpUri); - if (roles) { - auto &entry = roles->getEntry(); - isOrganizer = (find(entry, "organizer") != entry.end() ? true : false); - } - // The server CCMP sends responses using the XCON-USERID and, unfortunately, they are - // generated by the CCMP server and the SDK has no knowledge of them. RFC4475 - // fortunately details the tag <associated-aors> that can be used to communicate - // additional URIs that should be associated to the entity attribute. For example the - // following XML code will allow to associated the participant with address - // sip:bob@sip.example.org to the XCON-USERID bob: - //<user entity="xcon-userid:bob"> - // <associated-aors> - // <entry> - // <uri>sip:bob@sip.example.com</uri> - // </entry> - // </associated-aors> - //</user> - auto &associatedAors = user.getAssociatedAors(); - std::shared_ptr<Address> address; - if (associatedAors.present()) { - for (auto &aor : associatedAors->getEntry()) { - auto tmpAddress = Address::create(aor.getUri()); - if (tmpAddress) { - address = tmpAddress; - } + } + for (auto &user : users->getUser()) { + auto &roles = user.getRoles(); + bool isOrganizer = false; + auto ccmpUri = user.getEntity().get(); + auto participantInfo = info->findParticipant(ccmpUri); + if (roles) { + auto &entry = roles->getEntry(); + isOrganizer = (find(entry, "organizer") != entry.end() ? true : false); + } + // The server CCMP sends responses using the XCON-USERID and, unfortunately, they are + // generated by the CCMP server and the SDK has no knowledge of them. RFC4475 + // fortunately details the tag <associated-aors> that can be used to communicate + // additional URIs that should be associated to the entity attribute. For example the + // following XML code will allow to associated the participant with address + // sip:bob@sip.example.org to the XCON-USERID bob: + //<user entity="xcon-userid:bob"> + // <associated-aors> + // <entry> + // <uri>sip:bob@sip.example.com</uri> + // </entry> + // </associated-aors> + //</user> + auto &associatedAors = user.getAssociatedAors(); + std::shared_ptr<Address> address; + if (associatedAors.present()) { + for (auto &aor : associatedAors->getEntry()) { + auto tmpAddress = Address::create(aor.getUri()); + if (tmpAddress) { + address = tmpAddress; } } - decltype(participantInfo) updatedParticipantInfo; - if (participantInfo) { - updatedParticipantInfo = participantInfo->clone()->toSharedPtr(); - } else { - updatedParticipantInfo = ParticipantInfo::create(ccmpUri); - } - if (address) { - updatedParticipantInfo->setAddress(address); - } - if (participantInfo) { - info->updateParticipant(updatedParticipantInfo); - } else { - info->addParticipant(updatedParticipantInfo); - } - if (isOrganizer) { - info->setOrganizer(updatedParticipantInfo); - } + } + decltype(participantInfo) updatedParticipantInfo; + if (participantInfo) { + updatedParticipantInfo = participantInfo->clone()->toSharedPtr(); + } else { + updatedParticipantInfo = ParticipantInfo::create(ccmpUri); + } + if (address) { + updatedParticipantInfo->setAddress(address); + } + if (participantInfo) { + info->updateParticipant(updatedParticipantInfo); + } else { + info->addParticipant(updatedParticipantInfo); + } + if (isOrganizer) { + info->setOrganizer(updatedParticipantInfo); } } - account->addConferenceInfo(info); + } + account->addConferenceInfo(info); #ifdef HAVE_DB_STORAGE - auto &mainDb = account->getCore()->getPrivate()->mainDb; - if (mainDb) { - mainDb->insertConferenceInfo(info); - } -#endif // HAVE_DB_STORAGE + auto &mainDb = account->getCore()->getPrivate()->mainDb; + if (mainDb) { + mainDb->insertConferenceInfo(info); } - } catch (const std::bad_cast &e) { - lError() << "Error while casting parsed CCMP response (CcmpConfResponseMessageType) in account [" - << *account << ": " << e.what(); - } catch (const exception &e) { - lError() << "Error while parsing CCMP response (CcmpConfResponseMessageType) in " << *account - << ": " << e.what(); +#endif // HAVE_DB_STORAGE } + } catch (const std::bad_cast &e) { + lError() << "Error while casting parsed CCMP response (CcmpConfResponseMessageType) in account [" + << *account << ": " << e.what(); + } catch (const exception &e) { + lError() << "Error while parsing CCMP response (CcmpConfResponseMessageType) in " << *account << ": " + << e.what(); } } - account->ccmpConferenceInformationResponseReceived(); } + account->ccmpConferenceInformationResponseReceived(); } -void Account::handleIoError(void *ctx, const belle_sip_io_error_event_t *event) { +void Account::handleIoError(void *ctx, const HttpResponse &event) { auto account = static_cast<Account *>(ctx); - belle_sip_message_t *msg = BELLE_SIP_MESSAGE(belle_sip_io_error_event_get_source(event)); - belle_sip_body_handler_t *body = belle_sip_message_get_body_handler(msg); - std::string content = L_C_TO_STRING(belle_sip_object_to_string(body)); - lInfo() << "Account [" << account << "] I/O error on host [" << belle_sip_io_error_event_get_host(event) - << "] for message [" << msg << "]: " << content; + const auto &body = event.getBody(); + auto content = body.getBodyAsString(); + lInfo() << "Account [" << account << "] I/O error : " << content; account->ccmpConferenceInformationResponseReceived(); } -void Account::handleTimeout(void *ctx, const belle_sip_timeout_event_t *event) { +void Account::handleTimeout(void *ctx, const HttpResponse &event) { auto account = static_cast<Account *>(ctx); - belle_sip_client_transaction_t *transaction = belle_sip_timeout_event_get_client_transaction(event); - belle_sip_message_t *msg = BELLE_SIP_MESSAGE(belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(transaction))); - belle_sip_body_handler_t *body = belle_sip_message_get_body_handler(msg); - std::string content = L_C_TO_STRING(belle_sip_object_to_string(body)); - lInfo() << "Account [" << account << "] Timeout error for message [" << msg << "]: " << content; + const auto &body = event.getBody(); + auto content = body.getBodyAsString(); + lInfo() << "Account [" << account << "] Timeout error : " << content; account->ccmpConferenceInformationResponseReceived(); } -void Account::handleAuthRequested(void *ctx, belle_sip_auth_event_t *event) { - const char *username = belle_sip_auth_event_get_username(event); - const char *domain = belle_sip_auth_event_get_domain(event); - auto account = static_cast<Account *>(ctx); - linphone_core_fill_belle_sip_auth_event(account->getCore()->getCCore(), event, username, domain); -} // ----------------------------------------------------------------------------- LinphoneAccountCbsRegistrationStateChangedCb AccountCbs::getRegistrationStateChanged() const { diff --git a/src/account/account.h b/src/account/account.h index 94201e7a997c2ec47c006f7223a7099010dfcc87..81d1062fab90dca26c29654f068dfe92a9dd768f 100644 --- a/src/account/account.h +++ b/src/account/account.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2023 Belledonne Communications SARL. + * Copyright (c) 2010-2025 Belledonne Communications SARL. * * This file is part of Liblinphone * (see https://gitlab.linphone.org/BC/public/liblinphone). @@ -28,6 +28,7 @@ #include "c-wrapper/internal/c-sal.h" #include "call/call-log.h" #include "conference/conference-info.h" +#include "http/http-client.h" #include "linphone/api/c-callbacks.h" #include "linphone/api/c-types.h" #include "sal/register-op.h" @@ -55,6 +56,7 @@ class Address; class Event; class AbstractChatRoom; class EventPublish; +class HttpResponse; class LINPHONE_PUBLIC Account : public bellesip::HybridObject<LinphoneAccount, Account>, public UserDataAccessor, @@ -151,7 +153,7 @@ public: const std::string &getCustomParam(const std::string &key) const; void writeToConfigFile(int index); const LinphoneAuthInfo *findAuthInfo() const; - std::shared_ptr<EventPublish> createPublish(const std::string event, int expires); + std::shared_ptr<EventPublish> createPublish(const std::string &event, int expires); LinphoneReason getError(); LinphoneTransportType getTransport(); std::shared_ptr<Event> getMwiEvent() const; @@ -171,12 +173,14 @@ public: LinphoneAccountAddressComparisonResult isServerConfigChanged(); void updateConferenceInfoListWithCcmp() const; + + void handleCCMPResponseConferenceList(const HttpResponse &response); + void handleCCMPResponseConferenceInformation(const HttpResponse &response); // CCMP request callback (conference list) - static void handleResponseConferenceList(void *ctx, const belle_http_response_event_t *event); - static void handleResponseConferenceInformation(void *ctx, const belle_http_response_event_t *event); - static void handleAuthRequested(void *ctx, belle_sip_auth_event_t *event); - static void handleTimeout(void *ctx, const belle_sip_timeout_event_t *event); - static void handleIoError(void *ctx, const belle_sip_io_error_event_t *event); + static void handleResponseConferenceList(void *ctx, const HttpResponse &event); + static void handleResponseConferenceInformation(void *ctx, const HttpResponse &event); + static void handleTimeout(void *ctx, const HttpResponse &event); + static void handleIoError(void *ctx, const HttpResponse &event); private: LinphoneCore *getCCore() const; diff --git a/src/account_creator/main.cpp b/src/account_creator/main.cpp index 7718c0caf30594c838d454daf331dc98c9789a68..44a5d0b11f5017d072d25677008292e1e03af31d 100644 --- a/src/account_creator/main.cpp +++ b/src/account_creator/main.cpp @@ -50,7 +50,7 @@ static void reset_field(char **field) { static unsigned int validate_uri(LinphoneCore *lc, const char *username, const char *domain, const char *display_name) { LinphoneAddress *addr; unsigned int status = 0; - LinphoneAccountParams *params = linphone_account_params_new(lc); + LinphoneAccountParams *params = linphone_account_params_new(lc, TRUE); addr = linphone_address_new("sip:?@domain.com"); linphone_account_params_set_identity_address(params, addr); if (addr) linphone_address_unref(addr); @@ -94,7 +94,7 @@ LinphoneProxyConfig *linphone_account_creator_create_proxy_config(const Linphone LinphoneAccount *linphone_account_creator_create_account_in_core(const LinphoneAccountCreator *creator) { LinphoneAuthInfo *info; - LinphoneAccountParams *params = linphone_account_params_new(creator->core); + LinphoneAccountParams *params = linphone_account_params_new(creator->core, TRUE); char *identity_str = linphone_account_creator_get_identity(creator); LinphoneAddress *identity = linphone_address_new(identity_str); ms_free(identity_str); @@ -256,7 +256,7 @@ LinphoneAccountCreatorPhoneNumberStatusMask linphone_account_creator_set_phone_n return LinphoneAccountCreatorPhoneNumberStatusInvalidCountryCode; if (!creator->account) { - LinphoneAccountParams *params = linphone_account_params_new(creator->core); + LinphoneAccountParams *params = linphone_account_params_new(creator->core, TRUE); creator->account = linphone_core_create_account(creator->core, params); linphone_account_params_unref(params); } @@ -272,12 +272,20 @@ LinphoneAccountCreatorPhoneNumberStatusMask linphone_account_creator_set_phone_n return LinphoneAccountCreatorPhoneNumberStatusInvalid; } - // if phone is valid, we lastly want to check that length is OK in case phone_nunber was normilized + // if phone is valid, we lastly want to check that length is OK in case phone_number was normalized if (strcmp(normalized_phone_number, phone_number) != 0 || phone_number[0] != '+') { std::shared_ptr<DialPlan> plan = DialPlan::findByCcc(creator->phone_country_code); int size = (int)strlen(phone_number); if (plan->isGeneric()) { return_status = LinphoneAccountCreatorPhoneNumberStatusInvalidCountryCode; + goto end; + } + if (phone_number[0] == '+') { + // + 1 because of the '+' sign + size -= (int)(strlen(country_code) + 1); + } else if (strncmp("00", phone_number, 2) == 0) { + // + 2 because of the starting 00<country_code> + size -= (int)(strlen(country_code) + 2); } // DO NOT NOTIFY ABOUT PHONE NUMBER BEING TOO SHORT, // OUR DIAL PLAN IMPLEMENTATION ISNT PRECISE ENOUGH TO GARANTY @@ -433,7 +441,7 @@ LinphoneTransportType linphone_account_creator_get_transport(const LinphoneAccou LinphoneAccountCreatorStatus linphone_account_creator_set_route(LinphoneAccountCreator *creator, const char *route) { if (!creator->account) { - LinphoneAccountParams *params = linphone_account_params_new(creator->core); + LinphoneAccountParams *params = linphone_account_params_new(creator->core, TRUE); creator->account = linphone_core_create_account(creator->core, params); linphone_account_params_unref(params); } @@ -542,7 +550,7 @@ LinphoneAccountCreator *linphone_account_creator_new(LinphoneCore *core, const c creator->set_as_default = TRUE; - LinphoneAccountParams *params = linphone_account_params_new(core); + LinphoneAccountParams *params = linphone_account_params_new(core, TRUE); creator->account = linphone_core_create_account(core, params); linphone_account_params_unref(params); diff --git a/src/account_creator/utils.cpp b/src/account_creator/utils.cpp index 40a455dc40ffc891efc24668f1b9c81ad1458a30..19af98c57b1a0cf718f2d17fdec071733635b991 100644 --- a/src/account_creator/utils.cpp +++ b/src/account_creator/utils.cpp @@ -46,7 +46,7 @@ char *linphone_account_creator_get_identity(const LinphoneAccountCreator *creato if (username) { // we must escape username LinphoneCore *lc = creator->core; - LinphoneAccountParams *params = linphone_account_params_new(lc); + LinphoneAccountParams *params = linphone_account_params_new(lc, TRUE); LinphoneAccount *account = linphone_core_create_account(lc, params); linphone_account_params_unref(params); LinphoneAddress *addr = linphone_account_normalize_sip_uri(account, username); diff --git a/src/address/address.cpp b/src/address/address.cpp index 1456e2b290a461e3d40ed217954a7d3470c342ee..ea6a1de26a1e166290279ad5dde84562bb04b0cb 100644 --- a/src/address/address.cpp +++ b/src/address/address.cpp @@ -450,7 +450,7 @@ bool Address::removeUriParam(const string &uriParamName) { } void Address::copyUriParams(const Address &other) { - const auto otherUriParams = other.getParams(); + const auto otherUriParams = other.getUriParams(); for (const auto &[name, value] : otherUriParams) { setUriParam(name, value); } diff --git a/src/c-wrapper/api/c-account-params.cpp b/src/c-wrapper/api/c-account-params.cpp index a464d0a90ae3eb734a2b9be4d9d1d730ba845141..cc603a49ea1f74c153f08ce39456edd2125b362e 100644 --- a/src/c-wrapper/api/c-account-params.cpp +++ b/src/c-wrapper/api/c-account-params.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2022 Belledonne Communications SARL. + * Copyright (c) 2010-2025 Belledonne Communications SARL. * * This file is part of Liblinphone * (see https://gitlab.linphone.org/BC/public/liblinphone). @@ -19,10 +19,9 @@ */ #include "linphone/api/c-account-params.h" + #include "account/account-params.h" -#include "c-wrapper/c-wrapper.h" #include "c-wrapper/internal/c-tools.h" -#include "linphone/api/c-address.h" #include "linphone/core.h" #include "linphone/lpconfig.h" #include "linphone/utils/utils.h" @@ -33,16 +32,16 @@ using namespace LinphonePrivate; -LinphoneAccountParams *linphone_account_params_new(LinphoneCore *lc) { - return AccountParams::createCObject(lc); +LinphoneAccountParams *linphone_account_params_new(LinphoneCore *lc, bool_t use_default_values) { + return AccountParams::createCObject(lc, !!use_default_values); } LinphoneAccountParams *linphone_account_params_new_with_config(LinphoneCore *lc, int index) { char key[50]; - sprintf(key, "proxy_%i", index); // TODO: change to account + snprintf(key, sizeof(key), "proxy_%i", index); // TODO: change to account if (!linphone_config_has_section(linphone_core_get_config(lc), key)) { - return NULL; + return nullptr; } return AccountParams::createCObject(lc, index); @@ -70,16 +69,17 @@ void *linphone_account_params_get_user_data(const LinphoneAccountParams *params) } LinphoneStatus linphone_account_params_set_server_address(LinphoneAccountParams *params, - LinphoneAddress *server_address) { - return AccountParams::toCpp(params)->setServerAddress(Address::toCpp(server_address)->getSharedFromThis()); + const LinphoneAddress *server_address) { + return AccountParams::toCpp(params)->setServerAddress(bellesip::getSharedPtr<Address>(server_address)); } LinphoneStatus linphone_account_params_set_server_addr(LinphoneAccountParams *params, const char *server_address) { return AccountParams::toCpp(params)->setServerAddressAsString(L_C_TO_STRING(server_address)); } -LinphoneStatus linphone_account_params_set_identity_address(LinphoneAccountParams *params, LinphoneAddress *identity) { - return AccountParams::toCpp(params)->setIdentityAddress(Address::toCpp(identity)->getSharedFromThis()); +LinphoneStatus linphone_account_params_set_identity_address(LinphoneAccountParams *params, + const LinphoneAddress *identity) { + return AccountParams::toCpp(params)->setIdentityAddress(bellesip::getSharedPtr<Address>(identity)); } LinphoneStatus linphone_account_params_set_routes_addresses(LinphoneAccountParams *params, const bctbx_list_t *routes) { @@ -183,7 +183,7 @@ void linphone_account_params_set_realm(LinphoneAccountParams *params, const char } bctbx_list_t *linphone_account_params_get_routes_addresses(const LinphoneAccountParams *params) { - bctbx_list_t *route_list = NULL; + bctbx_list_t *route_list = nullptr; for (const auto &route : AccountParams::toCpp(params)->getRoutes()) { route_list = bctbx_list_append(route_list, route->toC()); } @@ -191,7 +191,7 @@ bctbx_list_t *linphone_account_params_get_routes_addresses(const LinphoneAccount } const LinphoneAddress *linphone_account_params_get_identity_address(const LinphoneAccountParams *params) { - return AccountParams::toCpp(params)->getIdentityAddress()->toC(); + return toC(AccountParams::toCpp(params)->getIdentityAddress()); } const char *linphone_account_params_get_identity(const LinphoneAccountParams *params) { @@ -271,7 +271,7 @@ LinphonePrivacyMask linphone_account_params_get_privacy(const LinphoneAccountPar } void linphone_account_params_set_file_transfer_server(LinphoneAccountParams *params, const char *server_url) { - AccountParams::toCpp(params)->setFileTranferServer(std::string(server_url)); + AccountParams::toCpp(params)->setFileTransferServer(std::string(server_url)); } const char *linphone_account_params_get_file_transfer_server(const LinphoneAccountParams *params) { @@ -491,7 +491,7 @@ void linphone_account_params_set_mwi_server_address(LinphoneAccountParams *param } const LinphoneAddress *linphone_account_params_get_mwi_server_address(const LinphoneAccountParams *params) { - const std::shared_ptr<Address> addr = AccountParams::toCpp(params)->getMwiServerAddress(); + const std::shared_ptr<const Address> addr = AccountParams::toCpp(params)->getMwiServerAddress(); return addr ? addr->toC() : nullptr; } @@ -500,7 +500,7 @@ void linphone_account_params_set_voicemail_address(LinphoneAccountParams *params } const LinphoneAddress *linphone_account_params_get_voicemail_address(const LinphoneAccountParams *params) { - const std::shared_ptr<Address> addr = AccountParams::toCpp(params)->getVoicemailAddress(); + const std::shared_ptr<const Address> addr = AccountParams::toCpp(params)->getVoicemailAddress(); return addr ? addr->toC() : nullptr; } @@ -512,3 +512,16 @@ void linphone_account_params_set_instant_messaging_encryption_mandatory(Linphone bool_t mandatory) { AccountParams::toCpp(params)->setInstantMessagingEncryptionMandatory(mandatory); } + +const bctbx_list_t *linphone_account_params_get_supported_tags_list(const LinphoneAccountParams *params) { + return AccountParams::toCpp(params)->getSupportedTagsCList(); +} + +void linphone_account_params_set_supported_tags_list(LinphoneAccountParams *params, + const bctbx_list_t *supported_tags) { + list<string> supportedTagsList = {}; + for (auto tag = supported_tags; tag != nullptr; tag = tag->next) { + supportedTagsList.push_back(string((char *)tag->data)); + } + AccountParams::toCpp(params)->setSupportedTagsList(supportedTagsList); +} \ No newline at end of file diff --git a/src/c-wrapper/api/c-account.cpp b/src/c-wrapper/api/c-account.cpp index 9d193a23efba00e9457af2a5aca41da23b19e03c..99769b8a161fc8d53363307c3efea373d33b3993 100644 --- a/src/c-wrapper/api/c-account.cpp +++ b/src/c-wrapper/api/c-account.cpp @@ -362,7 +362,7 @@ char *linphone_account_normalize_phone_number(const LinphoneAccount *account, co dial_prefix = linphone_account_params_get_international_prefix(accountParams); dial_escape_plus = linphone_account_params_dial_escape_plus_enabled(accountParams); } else { - LinphoneAccountParams *accountParams = linphone_account_params_new(NULL); + LinphoneAccountParams *accountParams = linphone_account_params_new(nullptr, FALSE); dial_prefix = linphone_account_params_get_international_prefix(accountParams); dial_escape_plus = linphone_account_params_dial_escape_plus_enabled(accountParams); linphone_account_params_unref(accountParams); @@ -464,6 +464,7 @@ LinphoneAddress *linphone_account_normalize_sip_uri(LinphoneAccount *account, co if (!username || *username == '\0') return NULL; +#ifndef _WIN32 if (is_enum(username, &enum_domain)) { if (enum_lookup(enum_domain, &enumres) < 0) { ms_free(enum_domain); @@ -475,6 +476,8 @@ LinphoneAddress *linphone_account_normalize_sip_uri(LinphoneAccount *account, co enum_lookup_res_free(enumres); return _destroy_addr_if_not_sip(uri); } +#endif + /* check if we have a "sip:" or a "sips:" */ if ((strstr(username, "sip:") == NULL) && (strstr(username, "sips:") == NULL)) { /* this doesn't look like a true sip uri */ diff --git a/src/c-wrapper/api/c-call-params.cpp b/src/c-wrapper/api/c-call-params.cpp index f31b89471eac06fd6be47ecff1fe086428c055f3..c03f091d8ce343da46039687941ea1cbce5db819 100644 --- a/src/c-wrapper/api/c-call-params.cpp +++ b/src/c-wrapper/api/c-call-params.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2022 Belledonne Communications SARL. + * Copyright (c) 2010-2025 Belledonne Communications SARL. * * This file is part of Liblinphone * (see https://gitlab.linphone.org/BC/public/liblinphone). @@ -86,6 +86,14 @@ bool_t linphone_call_params_is_valid(const LinphoneCallParams *params) { return !!L_GET_CPP_PTR_FROM_C_OBJECT(params)->isValid(); } +bool_t linphone_call_params_ringing_disabled(const LinphoneCallParams *params) { + return !!L_GET_PRIVATE_FROM_C_OBJECT(params)->ringingDisabled(); +} + +void linphone_call_params_disable_ringing(LinphoneCallParams *params, bool_t disable) { + L_GET_PRIVATE_FROM_C_OBJECT(params)->disableRinging(!!disable); +} + bool_t linphone_call_params_tone_indications_enabled(const LinphoneCallParams *params) { return !!L_GET_PRIVATE_FROM_C_OBJECT(params)->toneIndicationsEnabled(); } diff --git a/src/c-wrapper/api/c-chat-room.cpp b/src/c-wrapper/api/c-chat-room.cpp index a2889119d5b0f7de354745dae854b5089edd838d..56f5aeb61257b5610ed95b5ef88b1e3ed51f927a 100644 --- a/src/c-wrapper/api/c-chat-room.cpp +++ b/src/c-wrapper/api/c-chat-room.cpp @@ -64,31 +64,31 @@ void linphone_chat_room_allow_cpim(LinphoneChatRoom *room) { // Public functions. // ============================================================================= -const LinphoneChatRoomParams *linphone_chat_room_get_current_params(const LinphoneChatRoom *cr) { - return AbstractChatRoom::toCpp(cr)->getCurrentParams()->toC(); +const LinphoneChatRoomParams *linphone_chat_room_get_current_params(const LinphoneChatRoom *chat_room) { + return AbstractChatRoom::toCpp(chat_room)->getCurrentParams()->toC(); } // Deprecated -void linphone_chat_room_send_message(LinphoneChatRoom *cr, const char *msg) { - ChatRoomLogContextualizer logContextualizer(cr); - AbstractChatRoom::toCpp(cr)->createChatMessage(msg)->send(); +void linphone_chat_room_send_message(LinphoneChatRoom *chat_room, const char *msg) { + ChatRoomLogContextualizer logContextualizer(chat_room); + AbstractChatRoom::toCpp(chat_room)->createChatMessage(msg)->send(); } -bool_t linphone_chat_room_is_remote_composing(const LinphoneChatRoom *cr) { - ChatRoomLogContextualizer logContextualizer(cr); - return AbstractChatRoom::toCpp(cr)->isRemoteComposing(); +bool_t linphone_chat_room_is_remote_composing(const LinphoneChatRoom *chat_room) { + ChatRoomLogContextualizer logContextualizer(chat_room); + return AbstractChatRoom::toCpp(chat_room)->isRemoteComposing(); } -LinphoneCore *linphone_chat_room_get_lc(const LinphoneChatRoom *cr) { - return linphone_chat_room_get_core(cr); +LinphoneCore *linphone_chat_room_get_lc(const LinphoneChatRoom *chat_room) { + return linphone_chat_room_get_core(chat_room); } -LinphoneCore *linphone_chat_room_get_core(const LinphoneChatRoom *cr) { - return AbstractChatRoom::toCpp(cr)->getCore()->getCCore(); +LinphoneCore *linphone_chat_room_get_core(const LinphoneChatRoom *chat_room) { + return AbstractChatRoom::toCpp(chat_room)->getCore()->getCCore(); } -const LinphoneAddress *linphone_chat_room_get_peer_address(LinphoneChatRoom *cr) { - const auto &address = AbstractChatRoom::toCpp(cr)->getPeerAddress(); +const LinphoneAddress *linphone_chat_room_get_peer_address(LinphoneChatRoom *chat_room) { + const auto &address = AbstractChatRoom::toCpp(chat_room)->getPeerAddress(); if (address) { return address->toC(); } else { @@ -96,8 +96,8 @@ const LinphoneAddress *linphone_chat_room_get_peer_address(LinphoneChatRoom *cr) } } -const LinphoneAddress *linphone_chat_room_get_local_address(LinphoneChatRoom *cr) { - const auto &address = AbstractChatRoom::toCpp(cr)->getLocalAddress(); +const LinphoneAddress *linphone_chat_room_get_local_address(LinphoneChatRoom *chat_room) { + const auto &address = AbstractChatRoom::toCpp(chat_room)->getLocalAddress(); if (address) { return address->toC(); } else { @@ -105,81 +105,91 @@ const LinphoneAddress *linphone_chat_room_get_local_address(LinphoneChatRoom *cr } } -LinphoneChatMessage *linphone_chat_room_create_empty_message(LinphoneChatRoom *cr) { - ChatRoomLogContextualizer logContextualizer(cr); - shared_ptr<ChatMessage> cppPtr = AbstractChatRoom::toCpp(cr)->createChatMessage(); +const char *linphone_chat_room_get_identifier(const LinphoneChatRoom *chat_room) { + const auto &identifier = AbstractChatRoom::toCpp(chat_room)->getIdentifier(); + if (identifier) { + return L_STRING_TO_C(identifier.value()); + } + return NULL; +} + +LinphoneChatMessage *linphone_chat_room_create_empty_message(LinphoneChatRoom *chat_room) { + ChatRoomLogContextualizer logContextualizer(chat_room); + shared_ptr<ChatMessage> cppPtr = AbstractChatRoom::toCpp(chat_room)->createChatMessage(); LinphoneChatMessage *object = L_INIT(ChatMessage); L_SET_CPP_PTR_FROM_C_OBJECT(object, cppPtr); return object; } -LinphoneChatMessage *linphone_chat_room_create_message_from_utf8(LinphoneChatRoom *cr, const char *message) { - ChatRoomLogContextualizer logContextualizer(cr); - shared_ptr<ChatMessage> cppPtr = AbstractChatRoom::toCpp(cr)->createChatMessageFromUtf8(L_C_TO_STRING(message)); +LinphoneChatMessage *linphone_chat_room_create_message_from_utf8(LinphoneChatRoom *chat_room, const char *message) { + ChatRoomLogContextualizer logContextualizer(chat_room); + shared_ptr<ChatMessage> cppPtr = + AbstractChatRoom::toCpp(chat_room)->createChatMessageFromUtf8(L_C_TO_STRING(message)); LinphoneChatMessage *object = L_INIT(ChatMessage); L_SET_CPP_PTR_FROM_C_OBJECT(object, cppPtr); return object; } // Deprecated -LinphoneChatMessage *linphone_chat_room_create_message(LinphoneChatRoom *cr, const char *message) { - ChatRoomLogContextualizer logContextualizer(cr); - shared_ptr<ChatMessage> cppPtr = AbstractChatRoom::toCpp(cr)->createChatMessage(L_C_TO_STRING(message)); +LinphoneChatMessage *linphone_chat_room_create_message(LinphoneChatRoom *chat_room, const char *message) { + ChatRoomLogContextualizer logContextualizer(chat_room); + shared_ptr<ChatMessage> cppPtr = AbstractChatRoom::toCpp(chat_room)->createChatMessage(L_C_TO_STRING(message)); LinphoneChatMessage *object = L_INIT(ChatMessage); L_SET_CPP_PTR_FROM_C_OBJECT(object, cppPtr); return object; } -LinphoneChatMessage *linphone_chat_room_create_file_transfer_message(LinphoneChatRoom *cr, +LinphoneChatMessage *linphone_chat_room_create_file_transfer_message(LinphoneChatRoom *chat_room, LinphoneContent *initial_content) { - ChatRoomLogContextualizer logContextualizer(cr); - LinphoneChatMessage *msg = linphone_chat_room_create_empty_message(cr); + ChatRoomLogContextualizer logContextualizer(chat_room); + LinphoneChatMessage *msg = linphone_chat_room_create_empty_message(chat_room); linphone_chat_message_add_file_content(msg, initial_content); return msg; } // Deprecated -LinphoneChatMessage *linphone_chat_room_create_message_2(LinphoneChatRoom *cr, +LinphoneChatMessage *linphone_chat_room_create_message_2(LinphoneChatRoom *chat_room, const char *message, const char *external_body_url, LinphoneChatMessageState state, time_t time, BCTBX_UNUSED(bool_t is_read), BCTBX_UNUSED(bool_t is_incoming)) { - ChatRoomLogContextualizer logContextualizer(cr); - LinphoneChatMessage *msg = linphone_chat_room_create_message(cr, message); + ChatRoomLogContextualizer logContextualizer(chat_room); + LinphoneChatMessage *msg = linphone_chat_room_create_message(chat_room, message); linphone_chat_message_set_external_body_url(msg, external_body_url ? ms_strdup(external_body_url) : NULL); ChatMessagePrivate *dMsg = L_GET_PRIVATE_FROM_C_OBJECT(msg); dMsg->setTime(time); - dMsg->setParticipantState(AbstractChatRoom::toCpp(cr)->getMe()->getAddress(), + dMsg->setParticipantState(AbstractChatRoom::toCpp(chat_room)->getMe()->getAddress(), static_cast<ChatMessage::State>(state), ::ms_time(NULL)); return msg; } -LinphoneChatMessage *linphone_chat_room_create_forward_message(LinphoneChatRoom *cr, LinphoneChatMessage *msg) { - ChatRoomLogContextualizer logContextualizer(cr); +LinphoneChatMessage *linphone_chat_room_create_forward_message(LinphoneChatRoom *chat_room, LinphoneChatMessage *msg) { + ChatRoomLogContextualizer logContextualizer(chat_room); shared_ptr<ChatMessage> cppPtr = - AbstractChatRoom::toCpp(cr)->createForwardMessage(L_GET_CPP_PTR_FROM_C_OBJECT(msg)); + AbstractChatRoom::toCpp(chat_room)->createForwardMessage(L_GET_CPP_PTR_FROM_C_OBJECT(msg)); LinphoneChatMessage *object = L_INIT(ChatMessage); L_SET_CPP_PTR_FROM_C_OBJECT(object, cppPtr); return object; } -LinphoneChatMessage *linphone_chat_room_create_reply_message(LinphoneChatRoom *cr, LinphoneChatMessage *msg) { - ChatRoomLogContextualizer logContextualizer(cr); - shared_ptr<ChatMessage> cppPtr = AbstractChatRoom::toCpp(cr)->createReplyMessage(L_GET_CPP_PTR_FROM_C_OBJECT(msg)); +LinphoneChatMessage *linphone_chat_room_create_reply_message(LinphoneChatRoom *chat_room, LinphoneChatMessage *msg) { + ChatRoomLogContextualizer logContextualizer(chat_room); + shared_ptr<ChatMessage> cppPtr = + AbstractChatRoom::toCpp(chat_room)->createReplyMessage(L_GET_CPP_PTR_FROM_C_OBJECT(msg)); LinphoneChatMessage *object = L_INIT(ChatMessage); L_SET_CPP_PTR_FROM_C_OBJECT(object, cppPtr); return object; } -LinphoneChatMessage *linphone_chat_room_create_voice_recording_message(LinphoneChatRoom *cr, +LinphoneChatMessage *linphone_chat_room_create_voice_recording_message(LinphoneChatRoom *chat_room, LinphoneRecorder *recorder) { - ChatRoomLogContextualizer logContextualizer(cr); - LinphoneChatMessage *chat_message = linphone_chat_room_create_empty_message(cr); + ChatRoomLogContextualizer logContextualizer(chat_room); + LinphoneChatMessage *chat_message = linphone_chat_room_create_empty_message(chat_room); LinphoneContent *c_content = linphone_recorder_create_content(recorder); if (c_content != nullptr) { @@ -190,211 +200,214 @@ LinphoneChatMessage *linphone_chat_room_create_voice_recording_message(LinphoneC return chat_message; } -void linphone_chat_room_send_chat_message_2(BCTBX_UNUSED(LinphoneChatRoom *cr), LinphoneChatMessage *msg) { +void linphone_chat_room_send_chat_message_2(BCTBX_UNUSED(LinphoneChatRoom *chat_room), LinphoneChatMessage *msg) { linphone_chat_message_ref(msg); L_GET_CPP_PTR_FROM_C_OBJECT(msg)->send(); } -void linphone_chat_room_send_chat_message(BCTBX_UNUSED(LinphoneChatRoom *cr), LinphoneChatMessage *msg) { +void linphone_chat_room_send_chat_message(BCTBX_UNUSED(LinphoneChatRoom *chat_room), LinphoneChatMessage *msg) { L_GET_CPP_PTR_FROM_C_OBJECT(msg)->send(); } -void linphone_chat_room_receive_chat_message(BCTBX_UNUSED(LinphoneChatRoom *cr), LinphoneChatMessage *msg) { +void linphone_chat_room_receive_chat_message(BCTBX_UNUSED(LinphoneChatRoom *chat_room), LinphoneChatMessage *msg) { L_GET_PRIVATE_FROM_C_OBJECT(msg)->receive(); } -uint32_t linphone_chat_room_get_char(LinphoneChatRoom *cr) { - ChatRoomLogContextualizer logContextualizer(cr); - return AbstractChatRoom::toCpp(cr)->getChar(); +uint32_t linphone_chat_room_get_char(LinphoneChatRoom *chat_room) { + ChatRoomLogContextualizer logContextualizer(chat_room); + return AbstractChatRoom::toCpp(chat_room)->getChar(); } -void linphone_chat_room_compose(LinphoneChatRoom *cr) { - ChatRoomLogContextualizer logContextualizer(cr); - AbstractChatRoom::toCpp(cr)->compose(); +void linphone_chat_room_compose(LinphoneChatRoom *chat_room) { + ChatRoomLogContextualizer logContextualizer(chat_room); + AbstractChatRoom::toCpp(chat_room)->compose(); } -LinphoneCall *linphone_chat_room_get_call(const LinphoneChatRoom *cr) { - ChatRoomLogContextualizer logContextualizer(cr); - shared_ptr<Call> call = AbstractChatRoom::toCpp(cr)->getCall(); +LinphoneCall *linphone_chat_room_get_call(const LinphoneChatRoom *chat_room) { + ChatRoomLogContextualizer logContextualizer(chat_room); + shared_ptr<Call> call = AbstractChatRoom::toCpp(chat_room)->getCall(); if (call) return call->toC(); return nullptr; } -void linphone_chat_room_set_call(LinphoneChatRoom *cr, LinphoneCall *call) { - AbstractChatRoom::toCpp(cr)->setCallId(linphone_call_log_get_call_id(linphone_call_get_call_log(call))); +void linphone_chat_room_set_call(LinphoneChatRoom *chat_room, LinphoneCall *call) { + AbstractChatRoom::toCpp(chat_room)->setCallId(linphone_call_log_get_call_id(linphone_call_get_call_log(call))); } -void linphone_chat_room_mark_as_read(LinphoneChatRoom *cr) { - ChatRoomLogContextualizer logContextualizer(cr); - AbstractChatRoom::toCpp(cr)->markAsRead(); +void linphone_chat_room_mark_as_read(LinphoneChatRoom *chat_room) { + ChatRoomLogContextualizer logContextualizer(chat_room); + AbstractChatRoom::toCpp(chat_room)->markAsRead(); } -void linphone_chat_room_set_ephemeral_mode(LinphoneChatRoom *cr, LinphoneChatRoomEphemeralMode mode) { - ChatRoomLogContextualizer logContextualizer(cr); - AbstractChatRoom::toCpp(cr)->setEphemeralMode(static_cast<AbstractChatRoom::EphemeralMode>(mode), true); +void linphone_chat_room_set_ephemeral_mode(LinphoneChatRoom *chat_room, LinphoneChatRoomEphemeralMode mode) { + ChatRoomLogContextualizer logContextualizer(chat_room); + AbstractChatRoom::toCpp(chat_room)->setEphemeralMode(static_cast<AbstractChatRoom::EphemeralMode>(mode), true); } -LinphoneChatRoomEphemeralMode linphone_chat_room_get_ephemeral_mode(const LinphoneChatRoom *cr) { - ChatRoomLogContextualizer logContextualizer(cr); - return static_cast<LinphoneChatRoomEphemeralMode>(AbstractChatRoom::toCpp(cr)->getEphemeralMode()); +LinphoneChatRoomEphemeralMode linphone_chat_room_get_ephemeral_mode(const LinphoneChatRoom *chat_room) { + ChatRoomLogContextualizer logContextualizer(chat_room); + return static_cast<LinphoneChatRoomEphemeralMode>(AbstractChatRoom::toCpp(chat_room)->getEphemeralMode()); } -void linphone_chat_room_enable_ephemeral(LinphoneChatRoom *cr, bool_t ephem) { - ChatRoomLogContextualizer logContextualizer(cr); - AbstractChatRoom::toCpp(cr)->enableEphemeral(!!ephem, true); +void linphone_chat_room_enable_ephemeral(LinphoneChatRoom *chat_room, bool_t ephem) { + ChatRoomLogContextualizer logContextualizer(chat_room); + AbstractChatRoom::toCpp(chat_room)->enableEphemeral(!!ephem, true); } -bool_t linphone_chat_room_ephemeral_enabled(const LinphoneChatRoom *cr) { - ChatRoomLogContextualizer logContextualizer(cr); - return (bool_t)AbstractChatRoom::toCpp(cr)->ephemeralEnabled(); +bool_t linphone_chat_room_ephemeral_enabled(const LinphoneChatRoom *chat_room) { + ChatRoomLogContextualizer logContextualizer(chat_room); + return (bool_t)AbstractChatRoom::toCpp(chat_room)->ephemeralEnabled(); } -void linphone_chat_room_set_ephemeral_lifetime(LinphoneChatRoom *cr, long time) { - ChatRoomLogContextualizer logContextualizer(cr); - AbstractChatRoom::toCpp(cr)->setEphemeralLifetime(time, true); +void linphone_chat_room_set_ephemeral_lifetime(LinphoneChatRoom *chat_room, long time) { + ChatRoomLogContextualizer logContextualizer(chat_room); + AbstractChatRoom::toCpp(chat_room)->setEphemeralLifetime(time, true); } -long linphone_chat_room_get_ephemeral_lifetime(const LinphoneChatRoom *cr) { - ChatRoomLogContextualizer logContextualizer(cr); - return AbstractChatRoom::toCpp(cr)->getEphemeralLifetime(); +long linphone_chat_room_get_ephemeral_lifetime(const LinphoneChatRoom *chat_room) { + ChatRoomLogContextualizer logContextualizer(chat_room); + return AbstractChatRoom::toCpp(chat_room)->getEphemeralLifetime(); } -bool_t linphone_chat_room_ephemeral_supported_by_all_participants(const LinphoneChatRoom *cr) { - ChatRoomLogContextualizer logContextualizer(cr); - return (bool_t)AbstractChatRoom::toCpp(cr)->ephemeralSupportedByAllParticipants(); +bool_t linphone_chat_room_ephemeral_supported_by_all_participants(const LinphoneChatRoom *chat_room) { + ChatRoomLogContextualizer logContextualizer(chat_room); + return (bool_t)AbstractChatRoom::toCpp(chat_room)->ephemeralSupportedByAllParticipants(); } -int linphone_chat_room_get_unread_messages_count(LinphoneChatRoom *cr) { - ChatRoomLogContextualizer logContextualizer(cr); - return AbstractChatRoom::toCpp(cr)->getUnreadChatMessageCount(); +int linphone_chat_room_get_unread_messages_count(LinphoneChatRoom *chat_room) { + ChatRoomLogContextualizer logContextualizer(chat_room); + return AbstractChatRoom::toCpp(chat_room)->getUnreadChatMessageCount(); } -int linphone_chat_room_get_history_size(LinphoneChatRoom *cr) { - ChatRoomLogContextualizer logContextualizer(cr); - return AbstractChatRoom::toCpp(cr)->getChatMessageCount(); +int linphone_chat_room_get_history_size(LinphoneChatRoom *chat_room) { + ChatRoomLogContextualizer logContextualizer(chat_room); + return AbstractChatRoom::toCpp(chat_room)->getChatMessageCount(); } -int linphone_chat_room_get_history_size_2(LinphoneChatRoom *cr, LinphoneChatRoomHistoryFilterMask filters) { - ChatRoomLogContextualizer logContextualizer(cr); - return AbstractChatRoom::toCpp(cr)->getHistorySize(filters); +int linphone_chat_room_get_history_size_2(LinphoneChatRoom *chat_room, LinphoneChatRoomHistoryFilterMask filters) { + ChatRoomLogContextualizer logContextualizer(chat_room); + return AbstractChatRoom::toCpp(chat_room)->getHistorySize(filters); } -bool_t linphone_chat_room_is_empty(LinphoneChatRoom *cr) { - ChatRoomLogContextualizer logContextualizer(cr); - return (bool_t)AbstractChatRoom::toCpp(cr)->isEmpty(); +bool_t linphone_chat_room_is_empty(LinphoneChatRoom *chat_room) { + ChatRoomLogContextualizer logContextualizer(chat_room); + return (bool_t)AbstractChatRoom::toCpp(chat_room)->isEmpty(); } -void linphone_chat_room_delete_message(LinphoneChatRoom *cr, LinphoneChatMessage *msg) { - ChatRoomLogContextualizer logContextualizer(cr); - AbstractChatRoom::toCpp(cr)->deleteMessageFromHistory(L_GET_CPP_PTR_FROM_C_OBJECT(msg)); +void linphone_chat_room_delete_message(LinphoneChatRoom *chat_room, LinphoneChatMessage *msg) { + ChatRoomLogContextualizer logContextualizer(chat_room); + AbstractChatRoom::toCpp(chat_room)->deleteMessageFromHistory(L_GET_CPP_PTR_FROM_C_OBJECT(msg)); } -void linphone_chat_room_delete_history(LinphoneChatRoom *cr) { - ChatRoomLogContextualizer logContextualizer(cr); - AbstractChatRoom::toCpp(cr)->deleteHistory(); +void linphone_chat_room_delete_history(LinphoneChatRoom *chat_room) { + ChatRoomLogContextualizer logContextualizer(chat_room); + AbstractChatRoom::toCpp(chat_room)->deleteHistory(); } -bctbx_list_t *linphone_chat_room_get_media_contents(LinphoneChatRoom *cr) { - LinphonePrivate::ChatRoomLogContextualizer logContextualizer(cr); - list<shared_ptr<LinphonePrivate::Content>> contents = AbstractChatRoom::toCpp(cr)->getMediaContents(); +bctbx_list_t *linphone_chat_room_get_media_contents(LinphoneChatRoom *chat_room) { + LinphonePrivate::ChatRoomLogContextualizer logContextualizer(chat_room); + list<shared_ptr<LinphonePrivate::Content>> contents = AbstractChatRoom::toCpp(chat_room)->getMediaContents(); return LinphonePrivate::Content::getCListFromCppList(contents, true); } -bctbx_list_t *linphone_chat_room_get_document_contents(LinphoneChatRoom *cr) { - LinphonePrivate::ChatRoomLogContextualizer logContextualizer(cr); - list<shared_ptr<LinphonePrivate::Content>> contents = AbstractChatRoom::toCpp(cr)->getDocumentContents(); +bctbx_list_t *linphone_chat_room_get_document_contents(LinphoneChatRoom *chat_room) { + LinphonePrivate::ChatRoomLogContextualizer logContextualizer(chat_room); + list<shared_ptr<LinphonePrivate::Content>> contents = AbstractChatRoom::toCpp(chat_room)->getDocumentContents(); return LinphonePrivate::Content::getCListFromCppList(contents, true); } -bctbx_list_t *linphone_chat_room_get_history_range(LinphoneChatRoom *cr, int startm, int endm) { - ChatRoomLogContextualizer logContextualizer(cr); +bctbx_list_t *linphone_chat_room_get_history_range(LinphoneChatRoom *chat_room, int startm, int endm) { + ChatRoomLogContextualizer logContextualizer(chat_room); list<shared_ptr<ChatMessage>> chatMessages; - for (auto &event : AbstractChatRoom::toCpp(cr)->getMessageHistoryRange(startm, endm)) + for (auto &event : AbstractChatRoom::toCpp(chat_room)->getMessageHistoryRange(startm, endm)) chatMessages.push_back(static_pointer_cast<ConferenceChatMessageEvent>(event)->getChatMessage()); return L_GET_RESOLVED_C_LIST_FROM_CPP_LIST(chatMessages); } -bctbx_list_t *linphone_chat_room_get_history_range_2(LinphoneChatRoom *cr, +bctbx_list_t *linphone_chat_room_get_history_range_2(LinphoneChatRoom *chat_room, int startm, int endm, LinphoneChatRoomHistoryFilterMask filters) { - ChatRoomLogContextualizer logContextualizer(cr); - return L_GET_RESOLVED_C_LIST_FROM_CPP_LIST(AbstractChatRoom::toCpp(cr)->getHistoryRange(startm, endm, filters)); + ChatRoomLogContextualizer logContextualizer(chat_room); + return L_GET_RESOLVED_C_LIST_FROM_CPP_LIST( + AbstractChatRoom::toCpp(chat_room)->getHistoryRange(startm, endm, filters)); } -bctbx_list_t *linphone_chat_room_get_history(LinphoneChatRoom *cr, int nb_message) { - ChatRoomLogContextualizer logContextualizer(cr); - return linphone_chat_room_get_history_range(cr, 0, nb_message); +bctbx_list_t *linphone_chat_room_get_history(LinphoneChatRoom *chat_room, int nb_message) { + ChatRoomLogContextualizer logContextualizer(chat_room); + return linphone_chat_room_get_history_range(chat_room, 0, nb_message); } -bctbx_list_t * -linphone_chat_room_get_history_2(LinphoneChatRoom *cr, int nb_message, LinphoneChatRoomHistoryFilterMask filters) { - ChatRoomLogContextualizer logContextualizer(cr); - return linphone_chat_room_get_history_range_2(cr, 0, nb_message, filters); +bctbx_list_t *linphone_chat_room_get_history_2(LinphoneChatRoom *chat_room, + int nb_message, + LinphoneChatRoomHistoryFilterMask filters) { + ChatRoomLogContextualizer logContextualizer(chat_room); + return linphone_chat_room_get_history_range_2(chat_room, 0, nb_message, filters); } -bctbx_list_t *linphone_chat_room_get_history_range_near(LinphoneChatRoom *cr, +bctbx_list_t *linphone_chat_room_get_history_range_near(LinphoneChatRoom *chat_room, unsigned int before, unsigned int after, LinphoneEventLog *event, LinphoneChatRoomHistoryFilterMask filters) { - ChatRoomLogContextualizer logContextualizer(cr); - return L_GET_RESOLVED_C_LIST_FROM_CPP_LIST(AbstractChatRoom::toCpp(cr)->getHistoryRangeNear( + ChatRoomLogContextualizer logContextualizer(chat_room); + return L_GET_RESOLVED_C_LIST_FROM_CPP_LIST(AbstractChatRoom::toCpp(chat_room)->getHistoryRangeNear( before, after, event ? L_GET_CPP_PTR_FROM_C_OBJECT(event) : nullptr, filters)); } -bctbx_list_t *linphone_chat_room_get_history_range_between(LinphoneChatRoom *cr, +bctbx_list_t *linphone_chat_room_get_history_range_between(LinphoneChatRoom *chat_room, LinphoneEventLog *first_event, LinphoneEventLog *last_event, LinphoneChatRoomHistoryFilterMask filters) { - ChatRoomLogContextualizer logContextualizer(cr); - return L_GET_RESOLVED_C_LIST_FROM_CPP_LIST(AbstractChatRoom::toCpp(cr)->getHistoryRangeBetween( + ChatRoomLogContextualizer logContextualizer(chat_room); + return L_GET_RESOLVED_C_LIST_FROM_CPP_LIST(AbstractChatRoom::toCpp(chat_room)->getHistoryRangeBetween( first_event ? L_GET_CPP_PTR_FROM_C_OBJECT(first_event) : nullptr, last_event ? L_GET_CPP_PTR_FROM_C_OBJECT(last_event) : nullptr, filters)); } -bctbx_list_t *linphone_chat_room_get_unread_history(LinphoneChatRoom *cr) { - ChatRoomLogContextualizer logContextualizer(cr); - return L_GET_RESOLVED_C_LIST_FROM_CPP_LIST(AbstractChatRoom::toCpp(cr)->getUnreadChatMessages()); +bctbx_list_t *linphone_chat_room_get_unread_history(LinphoneChatRoom *chat_room) { + ChatRoomLogContextualizer logContextualizer(chat_room); + return L_GET_RESOLVED_C_LIST_FROM_CPP_LIST(AbstractChatRoom::toCpp(chat_room)->getUnreadChatMessages()); } -bctbx_list_t *linphone_chat_room_get_history_range_message_events(LinphoneChatRoom *cr, int startm, int endm) { - ChatRoomLogContextualizer logContextualizer(cr); - return L_GET_RESOLVED_C_LIST_FROM_CPP_LIST(AbstractChatRoom::toCpp(cr)->getMessageHistoryRange(startm, endm)); +bctbx_list_t *linphone_chat_room_get_history_range_message_events(LinphoneChatRoom *chat_room, int startm, int endm) { + ChatRoomLogContextualizer logContextualizer(chat_room); + return L_GET_RESOLVED_C_LIST_FROM_CPP_LIST( + AbstractChatRoom::toCpp(chat_room)->getMessageHistoryRange(startm, endm)); } -bctbx_list_t *linphone_chat_room_get_history_message_events(LinphoneChatRoom *cr, int nb_events) { - ChatRoomLogContextualizer logContextualizer(cr); - return L_GET_RESOLVED_C_LIST_FROM_CPP_LIST(AbstractChatRoom::toCpp(cr)->getMessageHistory(nb_events)); +bctbx_list_t *linphone_chat_room_get_history_message_events(LinphoneChatRoom *chat_room, int nb_events) { + ChatRoomLogContextualizer logContextualizer(chat_room); + return L_GET_RESOLVED_C_LIST_FROM_CPP_LIST(AbstractChatRoom::toCpp(chat_room)->getMessageHistory(nb_events)); } -bctbx_list_t *linphone_chat_room_get_history_events(LinphoneChatRoom *cr, int nb_events) { - ChatRoomLogContextualizer logContextualizer(cr); - return L_GET_RESOLVED_C_LIST_FROM_CPP_LIST(AbstractChatRoom::toCpp(cr)->getHistory(nb_events)); +bctbx_list_t *linphone_chat_room_get_history_events(LinphoneChatRoom *chat_room, int nb_events) { + ChatRoomLogContextualizer logContextualizer(chat_room); + return L_GET_RESOLVED_C_LIST_FROM_CPP_LIST(AbstractChatRoom::toCpp(chat_room)->getHistory(nb_events)); } -bctbx_list_t *linphone_chat_room_get_history_range_events(LinphoneChatRoom *cr, int begin, int end) { - ChatRoomLogContextualizer logContextualizer(cr); - return L_GET_RESOLVED_C_LIST_FROM_CPP_LIST(AbstractChatRoom::toCpp(cr)->getHistoryRange(begin, end)); +bctbx_list_t *linphone_chat_room_get_history_range_events(LinphoneChatRoom *chat_room, int begin, int end) { + ChatRoomLogContextualizer logContextualizer(chat_room); + return L_GET_RESOLVED_C_LIST_FROM_CPP_LIST(AbstractChatRoom::toCpp(chat_room)->getHistoryRange(begin, end)); } -int linphone_chat_room_get_history_events_size(LinphoneChatRoom *cr) { - ChatRoomLogContextualizer logContextualizer(cr); - return AbstractChatRoom::toCpp(cr)->getHistorySize(); +int linphone_chat_room_get_history_events_size(LinphoneChatRoom *chat_room) { + ChatRoomLogContextualizer logContextualizer(chat_room); + return AbstractChatRoom::toCpp(chat_room)->getHistorySize(); } -LinphoneChatMessage *linphone_chat_room_get_last_message_in_history(LinphoneChatRoom *cr) { - ChatRoomLogContextualizer logContextualizer(cr); - shared_ptr<ChatMessage> cppPtr = AbstractChatRoom::toCpp(cr)->getLastChatMessageInHistory(); +LinphoneChatMessage *linphone_chat_room_get_last_message_in_history(LinphoneChatRoom *chat_room) { + ChatRoomLogContextualizer logContextualizer(chat_room); + shared_ptr<ChatMessage> cppPtr = AbstractChatRoom::toCpp(chat_room)->getLastChatMessageInHistory(); if (!cppPtr) return nullptr; return linphone_chat_message_ref(L_GET_C_BACK_PTR(cppPtr)); } -LinphoneChatMessage *linphone_chat_room_find_message(LinphoneChatRoom *cr, const char *message_id) { - ChatRoomLogContextualizer logContextualizer(cr); - shared_ptr<ChatMessage> cppPtr = AbstractChatRoom::toCpp(cr)->findChatMessage(message_id); +LinphoneChatMessage *linphone_chat_room_find_message(LinphoneChatRoom *chat_room, const char *message_id) { + ChatRoomLogContextualizer logContextualizer(chat_room); + shared_ptr<ChatMessage> cppPtr = AbstractChatRoom::toCpp(chat_room)->findChatMessage(message_id); if (!cppPtr) return nullptr; return linphone_chat_message_ref(L_GET_C_BACK_PTR(cppPtr)); @@ -408,92 +421,89 @@ LinphoneEventLog *linphone_chat_room_find_event_log(LinphoneChatRoom *chat_room, return linphone_event_log_ref(L_GET_C_BACK_PTR(cppPtr)); } -LinphoneEventLog *linphone_chat_room_search_chat_message_by_text(LinphoneChatRoom *cr, +LinphoneEventLog *linphone_chat_room_search_chat_message_by_text(LinphoneChatRoom *chat_room, const char *text, const LinphoneEventLog *from, LinphoneSearchDirection direction) { - ChatRoomLogContextualizer logContextualizer(cr); - shared_ptr<EventLog> cppPtr = AbstractChatRoom::toCpp(cr)->searchChatMessageByText( + ChatRoomLogContextualizer logContextualizer(chat_room); + shared_ptr<EventLog> cppPtr = AbstractChatRoom::toCpp(chat_room)->searchChatMessageByText( L_C_TO_STRING(text), from ? L_GET_CPP_PTR_FROM_C_OBJECT(from) : nullptr, direction); if (!cppPtr) return nullptr; return linphone_event_log_ref(L_GET_C_BACK_PTR(cppPtr)); } -LinphoneChatRoomState linphone_chat_room_get_state(const LinphoneChatRoom *cr) { - ChatRoomLogContextualizer logContextualizer(cr); +LinphoneChatRoomState linphone_chat_room_get_state(const LinphoneChatRoom *chat_room) { + ChatRoomLogContextualizer logContextualizer(chat_room); return linphone_conference_state_to_chat_room_state( - static_cast<LinphoneConferenceState>(AbstractChatRoom::toCpp(cr)->getState())); + static_cast<LinphoneConferenceState>(AbstractChatRoom::toCpp(chat_room)->getState())); } -bool_t linphone_chat_room_has_been_left(const LinphoneChatRoom *cr) { - ChatRoomLogContextualizer logContextualizer(cr); - return (bool_t)AbstractChatRoom::toCpp(cr)->hasBeenLeft(); +bool_t linphone_chat_room_has_been_left(const LinphoneChatRoom *chat_room) { + ChatRoomLogContextualizer logContextualizer(chat_room); + return (bool_t)AbstractChatRoom::toCpp(chat_room)->hasBeenLeft(); } -bool_t linphone_chat_room_is_read_only(const LinphoneChatRoom *cr) { - ChatRoomLogContextualizer logContextualizer(cr); - return (bool_t)AbstractChatRoom::toCpp(cr)->isReadOnly(); +bool_t linphone_chat_room_is_read_only(const LinphoneChatRoom *chat_room) { + ChatRoomLogContextualizer logContextualizer(chat_room); + return (bool_t)AbstractChatRoom::toCpp(chat_room)->isReadOnly(); } -time_t linphone_chat_room_get_creation_time(const LinphoneChatRoom *cr) { - ChatRoomLogContextualizer logContextualizer(cr); - return AbstractChatRoom::toCpp(cr)->getCreationTime(); +time_t linphone_chat_room_get_creation_time(const LinphoneChatRoom *chat_room) { + ChatRoomLogContextualizer logContextualizer(chat_room); + return AbstractChatRoom::toCpp(chat_room)->getCreationTime(); } -time_t linphone_chat_room_get_last_update_time(const LinphoneChatRoom *cr) { - ChatRoomLogContextualizer logContextualizer(cr); - return AbstractChatRoom::toCpp(cr)->getLastUpdateTime(); +time_t linphone_chat_room_get_last_update_time(const LinphoneChatRoom *chat_room) { + ChatRoomLogContextualizer logContextualizer(chat_room); + return AbstractChatRoom::toCpp(chat_room)->getLastUpdateTime(); } -void linphone_chat_room_add_participant(LinphoneChatRoom *cr, LinphoneAddress *addr) { - ChatRoomLogContextualizer logContextualizer(cr); - if (linphone_chat_room_can_handle_participants(cr)) { - AbstractChatRoom::toCpp(cr)->getConference()->addParticipant(Address::toCpp(addr)->getSharedFromThis()); +void linphone_chat_room_add_participant(LinphoneChatRoom *chat_room, LinphoneAddress *addr) { + ChatRoomLogContextualizer logContextualizer(chat_room); + if (linphone_chat_room_can_handle_participants(chat_room)) { + AbstractChatRoom::toCpp(chat_room)->getConference()->addParticipant(Address::toCpp(addr)->getSharedFromThis()); } } -bool_t linphone_chat_room_add_participants(LinphoneChatRoom *cr, const bctbx_list_t *addresses) { - LinphonePrivate::ChatRoomLogContextualizer logContextualizer(cr); - if (linphone_chat_room_can_handle_participants(cr)) { - std::list<std::shared_ptr<const LinphonePrivate::Address>> addressList; - for (const bctbx_list_t *elem = addresses; elem != NULL; elem = elem->next) { - const LinphoneAddress *data = static_cast<const LinphoneAddress *>(bctbx_list_get_data(elem)); - addressList.push_back(LinphonePrivate::Address::toCpp(data)->getSharedFromThis()); - } - return AbstractChatRoom::toCpp(cr)->getConference()->addParticipants(addressList); +bool_t linphone_chat_room_add_participants(LinphoneChatRoom *chat_room, const bctbx_list_t *addresses) { + LinphonePrivate::ChatRoomLogContextualizer logContextualizer(chat_room); + if (linphone_chat_room_can_handle_participants(chat_room)) { + std::list<std::shared_ptr<LinphonePrivate::Address>> addressList = + LinphonePrivate::Utils::bctbxListToCppSharedPtrList<LinphoneAddress, LinphonePrivate::Address>(addresses); + return AbstractChatRoom::toCpp(chat_room)->getConference()->addParticipants(addressList); } return FALSE; } -LinphoneParticipant *linphone_chat_room_find_participant(const LinphoneChatRoom *cr, LinphoneAddress *addr) { - ChatRoomLogContextualizer logContextualizer(cr); +LinphoneParticipant *linphone_chat_room_find_participant(const LinphoneChatRoom *chat_room, LinphoneAddress *addr) { + ChatRoomLogContextualizer logContextualizer(chat_room); std::shared_ptr<Participant> participant = - AbstractChatRoom::toCpp(cr)->findParticipant(Address::toCpp(addr)->getSharedFromThis()); + AbstractChatRoom::toCpp(chat_room)->findParticipant(Address::toCpp(addr)->getSharedFromThis()); if (participant) { return participant->toC(); } return NULL; } -bool_t linphone_chat_room_can_handle_participants(const LinphoneChatRoom *cr) { - ChatRoomLogContextualizer logContextualizer(cr); - return AbstractChatRoom::toCpp(cr)->canHandleParticipants(); +bool_t linphone_chat_room_can_handle_participants(const LinphoneChatRoom *chat_room) { + ChatRoomLogContextualizer logContextualizer(chat_room); + return AbstractChatRoom::toCpp(chat_room)->canHandleParticipants(); } -LinphoneChatRoomCapabilitiesMask linphone_chat_room_get_capabilities(const LinphoneChatRoom *cr) { - ChatRoomLogContextualizer logContextualizer(cr); - return AbstractChatRoom::toCpp(cr)->getCapabilities(); +LinphoneChatRoomCapabilitiesMask linphone_chat_room_get_capabilities(const LinphoneChatRoom *chat_room) { + ChatRoomLogContextualizer logContextualizer(chat_room); + return AbstractChatRoom::toCpp(chat_room)->getCapabilities(); } -bool_t linphone_chat_room_has_capability(const LinphoneChatRoom *cr, int mask) { - ChatRoomLogContextualizer logContextualizer(cr); - return static_cast<bool_t>(AbstractChatRoom::toCpp(cr)->getCapabilities() & mask); +bool_t linphone_chat_room_has_capability(const LinphoneChatRoom *chat_room, int mask) { + ChatRoomLogContextualizer logContextualizer(chat_room); + return static_cast<bool_t>(AbstractChatRoom::toCpp(chat_room)->getCapabilities() & mask); } -const LinphoneAddress *linphone_chat_room_get_conference_address(const LinphoneChatRoom *cr) { - ChatRoomLogContextualizer logContextualizer(cr); - std::shared_ptr<Conference> conference = AbstractChatRoom::toCpp(cr)->getConference(); +const LinphoneAddress *linphone_chat_room_get_conference_address(const LinphoneChatRoom *chat_room) { + ChatRoomLogContextualizer logContextualizer(chat_room); + std::shared_ptr<Conference> conference = AbstractChatRoom::toCpp(chat_room)->getConference(); if (conference) { const auto &confAddress = conference->getConferenceAddress(); if (confAddress && confAddress->isValid()) { @@ -503,77 +513,77 @@ const LinphoneAddress *linphone_chat_room_get_conference_address(const LinphoneC return NULL; } -LinphoneParticipant *linphone_chat_room_get_me(const LinphoneChatRoom *cr) { - ChatRoomLogContextualizer logContextualizer(cr); - std::shared_ptr<Participant> me = AbstractChatRoom::toCpp(cr)->getMe(); +LinphoneParticipant *linphone_chat_room_get_me(const LinphoneChatRoom *chat_room) { + ChatRoomLogContextualizer logContextualizer(chat_room); + std::shared_ptr<Participant> me = AbstractChatRoom::toCpp(chat_room)->getMe(); if (me) { return me->toC(); } return NULL; } -int linphone_chat_room_get_nb_participants(const LinphoneChatRoom *cr) { - ChatRoomLogContextualizer logContextualizer(cr); - if (linphone_chat_room_can_handle_participants(cr)) { - return AbstractChatRoom::toCpp(cr)->getConference()->getParticipantCount(); +int linphone_chat_room_get_nb_participants(const LinphoneChatRoom *chat_room) { + ChatRoomLogContextualizer logContextualizer(chat_room); + if (linphone_chat_room_can_handle_participants(chat_room)) { + return AbstractChatRoom::toCpp(chat_room)->getConference()->getParticipantCount(); } return -1; } -bctbx_list_t *linphone_chat_room_get_participants(const LinphoneChatRoom *cr) { - ChatRoomLogContextualizer logContextualizer(cr); - return Participant::getCListFromCppList(AbstractChatRoom::toCpp(cr)->getParticipants()); +bctbx_list_t *linphone_chat_room_get_participants(const LinphoneChatRoom *chat_room) { + ChatRoomLogContextualizer logContextualizer(chat_room); + return Participant::getCListFromCppList(AbstractChatRoom::toCpp(chat_room)->getParticipants()); } -const char *linphone_chat_room_get_subject(const LinphoneChatRoom *cr) { - ChatRoomLogContextualizer logContextualizer(cr); - return L_STRING_TO_C(AbstractChatRoom::toCpp(cr)->getSubject()); +const char *linphone_chat_room_get_subject(const LinphoneChatRoom *chat_room) { + ChatRoomLogContextualizer logContextualizer(chat_room); + return L_STRING_TO_C(AbstractChatRoom::toCpp(chat_room)->getSubject()); } -const char *linphone_chat_room_get_subject_utf8(const LinphoneChatRoom *cr) { - ChatRoomLogContextualizer logContextualizer(cr); - return L_STRING_TO_C(AbstractChatRoom::toCpp(cr)->getSubjectUtf8()); +const char *linphone_chat_room_get_subject_utf8(const LinphoneChatRoom *chat_room) { + ChatRoomLogContextualizer logContextualizer(chat_room); + return L_STRING_TO_C(AbstractChatRoom::toCpp(chat_room)->getSubjectUtf8()); } -LinphoneChatRoomSecurityLevel linphone_chat_room_get_security_level(LinphoneChatRoom *cr) { - ChatRoomLogContextualizer logContextualizer(cr); - return (LinphoneChatRoomSecurityLevel)AbstractChatRoom::toCpp(cr)->getSecurityLevel(); +LinphoneChatRoomSecurityLevel linphone_chat_room_get_security_level(LinphoneChatRoom *chat_room) { + ChatRoomLogContextualizer logContextualizer(chat_room); + return (LinphoneChatRoomSecurityLevel)AbstractChatRoom::toCpp(chat_room)->getSecurityLevel(); } -void linphone_chat_room_leave(LinphoneChatRoom *cr) { - ChatRoomLogContextualizer logContextualizer(cr); - AbstractChatRoom::toCpp(cr)->getConference()->leave(); +void linphone_chat_room_leave(LinphoneChatRoom *chat_room) { + ChatRoomLogContextualizer logContextualizer(chat_room); + AbstractChatRoom::toCpp(chat_room)->getConference()->leave(); } -void linphone_chat_room_remove_participant(LinphoneChatRoom *cr, LinphoneParticipant *participant) { - ChatRoomLogContextualizer logContextualizer(cr); - if (linphone_chat_room_can_handle_participants(cr)) { - AbstractChatRoom::toCpp(cr)->getConference()->removeParticipant( +void linphone_chat_room_remove_participant(LinphoneChatRoom *chat_room, LinphoneParticipant *participant) { + ChatRoomLogContextualizer logContextualizer(chat_room); + if (linphone_chat_room_can_handle_participants(chat_room)) { + AbstractChatRoom::toCpp(chat_room)->getConference()->removeParticipant( Participant::toCpp(participant)->getSharedFromThis()); } } -void linphone_chat_room_remove_participants(LinphoneChatRoom *cr, const bctbx_list_t *participants) { - ChatRoomLogContextualizer logContextualizer(cr); - if (linphone_chat_room_can_handle_participants(cr)) { - AbstractChatRoom::toCpp(cr)->getConference()->removeParticipants( - Participant::getCppListFromCList(participants)); +void linphone_chat_room_remove_participants(LinphoneChatRoom *chat_room, const bctbx_list_t *participants) { + ChatRoomLogContextualizer logContextualizer(chat_room); + if (linphone_chat_room_can_handle_participants(chat_room)) { + AbstractChatRoom::toCpp(chat_room)->getConference()->removeParticipants( + Utils::bctbxListToCppSharedPtrList<LinphoneParticipant, Participant>(participants)); } } -void linphone_chat_room_set_participant_admin_status(LinphoneChatRoom *cr, +void linphone_chat_room_set_participant_admin_status(LinphoneChatRoom *chat_room, LinphoneParticipant *participant, bool_t isAdmin) { - ChatRoomLogContextualizer logContextualizer(cr); - if (linphone_chat_room_can_handle_participants(cr)) { + ChatRoomLogContextualizer logContextualizer(chat_room); + if (linphone_chat_room_can_handle_participants(chat_room)) { shared_ptr<Participant> p = Participant::toCpp(participant)->getSharedFromThis(); - AbstractChatRoom::toCpp(cr)->getConference()->setParticipantAdminStatus(p, !!isAdmin); + AbstractChatRoom::toCpp(chat_room)->getConference()->setParticipantAdminStatus(p, !!isAdmin); } } -void linphone_chat_room_set_subject(LinphoneChatRoom *cr, const char *subject) { - ChatRoomLogContextualizer logContextualizer(cr); - AbstractChatRoom::toCpp(cr)->setSubject(L_C_TO_STRING(subject)); +void linphone_chat_room_set_subject(LinphoneChatRoom *chat_room, const char *subject) { + ChatRoomLogContextualizer logContextualizer(chat_room); + AbstractChatRoom::toCpp(chat_room)->setSubject(L_C_TO_STRING(subject)); } void linphone_chat_room_set_subject_utf8(LinphoneChatRoom *chat_room, const char *subject) { @@ -581,9 +591,9 @@ void linphone_chat_room_set_subject_utf8(LinphoneChatRoom *chat_room, const char AbstractChatRoom::toCpp(chat_room)->setUtf8Subject(L_C_TO_STRING(subject)); } -const bctbx_list_t *linphone_chat_room_get_composing_addresses(LinphoneChatRoom *cr) { - ChatRoomLogContextualizer logContextualizer(cr); - return AbstractChatRoom::toCpp(cr)->getComposingCAddresses(); +const bctbx_list_t *linphone_chat_room_get_composing_addresses(LinphoneChatRoom *chat_room) { + ChatRoomLogContextualizer logContextualizer(chat_room); + return AbstractChatRoom::toCpp(chat_room)->getComposingCAddresses(); } bool_t linphone_chat_room_get_muted(const LinphoneChatRoom *chat_room) { @@ -606,10 +616,10 @@ const LinphoneConferenceInfo *linphone_chat_room_get_conference_info(LinphoneCha #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" #endif // _MSC_VER -void linphone_chat_room_set_conference_address(LinphoneChatRoom *cr, LinphoneAddress *confAddr) { +void linphone_chat_room_set_conference_address(LinphoneChatRoom *chat_room, LinphoneAddress *confAddr) { #ifdef HAVE_ADVANCED_IM shared_ptr<ServerChatRoom> sgcr = - dynamic_pointer_cast<ServerChatRoom>(AbstractChatRoom::toCpp(cr)->getSharedFromThis()); + dynamic_pointer_cast<ServerChatRoom>(AbstractChatRoom::toCpp(chat_room)->getSharedFromThis()); if (sgcr) { std::shared_ptr<Address> idAddr = Address::toCpp(confAddr)->getSharedFromThis(); sgcr->getConference()->setConferenceAddress(idAddr); @@ -626,14 +636,15 @@ void linphone_chat_room_set_conference_address(LinphoneChatRoom *cr, LinphoneAdd #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" #endif // _MSC_VER -void linphone_chat_room_set_participant_devices(LinphoneChatRoom *cr, +void linphone_chat_room_set_participant_devices(LinphoneChatRoom *chat_room, LinphoneAddress *partAddr, const bctbx_list_t *deviceIdentities) { #ifdef HAVE_ADVANCED_IM list<shared_ptr<ParticipantDeviceIdentity>> lDevicesIdentities = - ParticipantDeviceIdentity::getCppListFromCList(deviceIdentities); + Utils::bctbxListToCppSharedPtrList<LinphoneParticipantDeviceIdentity, + LinphonePrivate::ParticipantDeviceIdentity>(deviceIdentities); shared_ptr<ServerChatRoom> sgcr = - dynamic_pointer_cast<ServerChatRoom>(AbstractChatRoom::toCpp(cr)->getSharedFromThis()); + dynamic_pointer_cast<ServerChatRoom>(AbstractChatRoom::toCpp(chat_room)->getSharedFromThis()); if (sgcr) dynamic_pointer_cast<ServerConference>(sgcr->getConference()) ->setParticipantDevices(Address::toCpp(partAddr)->getSharedFromThis(), lDevicesIdentities); @@ -649,11 +660,11 @@ void linphone_chat_room_set_participant_devices(LinphoneChatRoom *cr, #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" #endif // _MSC_VER -void linphone_chat_room_notify_participant_device_registration(LinphoneChatRoom *cr, +void linphone_chat_room_notify_participant_device_registration(LinphoneChatRoom *chat_room, const LinphoneAddress *participant_device) { #ifdef HAVE_ADVANCED_IM shared_ptr<ServerChatRoom> sgcr = - dynamic_pointer_cast<ServerChatRoom>(AbstractChatRoom::toCpp(cr)->getSharedFromThis()); + dynamic_pointer_cast<ServerChatRoom>(AbstractChatRoom::toCpp(chat_room)->getSharedFromThis()); if (sgcr) { sgcr->notifyParticipantDeviceRegistration( LinphonePrivate::Address::toCpp(participant_device)->getSharedFromThis()); @@ -670,225 +681,229 @@ void linphone_chat_room_notify_participant_device_registration(LinphoneChatRoom // Callbacks // ============================================================================= -void linphone_chat_room_add_callbacks(LinphoneChatRoom *cr, LinphoneChatRoomCbs *cbs) { - AbstractChatRoom::toCpp(cr)->addCallbacks(ChatRoomCbs::toCpp(cbs)->getSharedFromThis()); +void linphone_chat_room_add_callbacks(LinphoneChatRoom *chat_room, LinphoneChatRoomCbs *cbs) { + AbstractChatRoom::toCpp(chat_room)->addCallbacks(ChatRoomCbs::toCpp(cbs)->getSharedFromThis()); } -void linphone_chat_room_remove_callbacks(LinphoneChatRoom *cr, LinphoneChatRoomCbs *cbs) { - AbstractChatRoom::toCpp(cr)->removeCallbacks(ChatRoomCbs::toCpp(cbs)->getSharedFromThis()); +void linphone_chat_room_remove_callbacks(LinphoneChatRoom *chat_room, LinphoneChatRoomCbs *cbs) { + AbstractChatRoom::toCpp(chat_room)->removeCallbacks(ChatRoomCbs::toCpp(cbs)->getSharedFromThis()); } -void linphone_chat_room_set_current_callbacks(LinphoneChatRoom *cr, LinphoneChatRoomCbs *cbs) { - AbstractChatRoom::toCpp(cr)->setCurrentCallbacks(cbs ? ChatRoomCbs::toCpp(cbs)->getSharedFromThis() : nullptr); +void linphone_chat_room_set_current_callbacks(LinphoneChatRoom *chat_room, LinphoneChatRoomCbs *cbs) { + AbstractChatRoom::toCpp(chat_room)->setCurrentCallbacks(cbs ? ChatRoomCbs::toCpp(cbs)->getSharedFromThis() + : nullptr); } -LinphoneChatRoomCbs *linphone_chat_room_get_current_callbacks(const LinphoneChatRoom *cr) { - return AbstractChatRoom::toCpp(cr)->getCurrentCallbacks()->toC(); +LinphoneChatRoomCbs *linphone_chat_room_get_current_callbacks(const LinphoneChatRoom *chat_room) { + return AbstractChatRoom::toCpp(chat_room)->getCurrentCallbacks()->toC(); } -const bctbx_list_t *linphone_chat_room_get_callbacks_list(const LinphoneChatRoom *cr) { - return AbstractChatRoom::toCpp(cr)->getCCallbacksList(); +const bctbx_list_t *linphone_chat_room_get_callbacks_list(const LinphoneChatRoom *chat_room) { + return AbstractChatRoom::toCpp(chat_room)->getCCallbacksList(); } -void _linphone_chat_room_notify_is_composing_received(LinphoneChatRoom *cr, +void _linphone_chat_room_notify_is_composing_received(LinphoneChatRoom *chat_room, const LinphoneAddress *remoteAddr, bool_t isComposing) { - LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(cr), + LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(chat_room), linphone_chat_room_cbs_get_is_composing_received, remoteAddr, isComposing); } -void linphone_chat_room_notify_session_state_changed(LinphoneChatRoom *cr, +void linphone_chat_room_notify_session_state_changed(LinphoneChatRoom *chat_room, LinphoneCallState cstate, const char *message) { - LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(cr), + LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(chat_room), linphone_chat_room_cbs_get_session_state_changed, cstate, message); } -void _linphone_chat_room_notify_message_received(LinphoneChatRoom *cr, LinphoneChatMessage *msg) { - LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(cr), +void _linphone_chat_room_notify_message_received(LinphoneChatRoom *chat_room, LinphoneChatMessage *msg) { + LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(chat_room), linphone_chat_room_cbs_get_message_received, msg); } -void _linphone_chat_room_notify_messages_received(LinphoneChatRoom *cr, const bctbx_list_t *chat_messages) { - LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(cr), +void _linphone_chat_room_notify_messages_received(LinphoneChatRoom *chat_room, const bctbx_list_t *chat_messages) { + LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(chat_room), linphone_chat_room_cbs_get_messages_received, chat_messages); } -void _linphone_chat_room_notify_new_event(LinphoneChatRoom *cr, const LinphoneEventLog *event_log) { - LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(cr), linphone_chat_room_cbs_get_new_event, - event_log); +void _linphone_chat_room_notify_new_event(LinphoneChatRoom *chat_room, const LinphoneEventLog *event_log) { + LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(chat_room), + linphone_chat_room_cbs_get_new_event, event_log); } -void _linphone_chat_room_notify_new_events(LinphoneChatRoom *cr, const bctbx_list_t *event_logs) { - LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(cr), linphone_chat_room_cbs_get_new_events, - event_logs); +void _linphone_chat_room_notify_new_events(LinphoneChatRoom *chat_room, const bctbx_list_t *event_logs) { + LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(chat_room), + linphone_chat_room_cbs_get_new_events, event_logs); } -void _linphone_chat_room_notify_participant_added(LinphoneChatRoom *cr, const LinphoneEventLog *event_log) { - _linphone_chat_room_notify_new_event(cr, event_log); - LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(cr), +void _linphone_chat_room_notify_participant_added(LinphoneChatRoom *chat_room, const LinphoneEventLog *event_log) { + _linphone_chat_room_notify_new_event(chat_room, event_log); + LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(chat_room), linphone_chat_room_cbs_get_participant_added, event_log); } -void _linphone_chat_room_notify_participant_removed(LinphoneChatRoom *cr, const LinphoneEventLog *event_log) { - _linphone_chat_room_notify_new_event(cr, event_log); - LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(cr), +void _linphone_chat_room_notify_participant_removed(LinphoneChatRoom *chat_room, const LinphoneEventLog *event_log) { + _linphone_chat_room_notify_new_event(chat_room, event_log); + LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(chat_room), linphone_chat_room_cbs_get_participant_removed, event_log); } -void _linphone_chat_room_notify_participant_device_added(LinphoneChatRoom *cr, const LinphoneEventLog *event_log) { - _linphone_chat_room_notify_new_event(cr, event_log); - LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(cr), +void _linphone_chat_room_notify_participant_device_added(LinphoneChatRoom *chat_room, + const LinphoneEventLog *event_log) { + _linphone_chat_room_notify_new_event(chat_room, event_log); + LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(chat_room), linphone_chat_room_cbs_get_participant_device_added, event_log); } -void _linphone_chat_room_notify_participant_device_removed(LinphoneChatRoom *cr, const LinphoneEventLog *event_log) { - _linphone_chat_room_notify_new_event(cr, event_log); - LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(cr), +void _linphone_chat_room_notify_participant_device_removed(LinphoneChatRoom *chat_room, + const LinphoneEventLog *event_log) { + _linphone_chat_room_notify_new_event(chat_room, event_log); + LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(chat_room), linphone_chat_room_cbs_get_participant_device_removed, event_log); } -void _linphone_chat_room_notify_participant_device_state_changed(LinphoneChatRoom *cr, +void _linphone_chat_room_notify_participant_device_state_changed(LinphoneChatRoom *chat_room, const LinphoneEventLog *event_log, const LinphoneParticipantDeviceState state) { - _linphone_chat_room_notify_new_event(cr, event_log); - LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(cr), + _linphone_chat_room_notify_new_event(chat_room, event_log); + LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(chat_room), linphone_chat_room_cbs_get_participant_device_state_changed, event_log, state); } -void _linphone_chat_room_notify_participant_device_media_availability_changed(LinphoneChatRoom *cr, +void _linphone_chat_room_notify_participant_device_media_availability_changed(LinphoneChatRoom *chat_room, const LinphoneEventLog *event_log) { - _linphone_chat_room_notify_new_event(cr, event_log); - LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(cr), + _linphone_chat_room_notify_new_event(chat_room, event_log); + LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(chat_room), linphone_chat_room_cbs_get_participant_device_media_availability_changed, event_log); } -void _linphone_chat_room_notify_participant_admin_status_changed(LinphoneChatRoom *cr, +void _linphone_chat_room_notify_participant_admin_status_changed(LinphoneChatRoom *chat_room, const LinphoneEventLog *event_log) { - _linphone_chat_room_notify_new_event(cr, event_log); - LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(cr), + _linphone_chat_room_notify_new_event(chat_room, event_log); + LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(chat_room), linphone_chat_room_cbs_get_participant_admin_status_changed, event_log); } -void _linphone_chat_room_notify_state_changed(LinphoneChatRoom *cr, LinphoneChatRoomState newState) { - LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(cr), linphone_chat_room_cbs_get_state_changed, - newState); +void _linphone_chat_room_notify_state_changed(LinphoneChatRoom *chat_room, LinphoneChatRoomState newState) { + LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(chat_room), + linphone_chat_room_cbs_get_state_changed, newState); } -void _linphone_chat_room_notify_security_event(LinphoneChatRoom *cr, const LinphoneEventLog *event_log) { - _linphone_chat_room_notify_new_event(cr, event_log); - LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(cr), linphone_chat_room_cbs_get_security_event, - event_log); +void _linphone_chat_room_notify_security_event(LinphoneChatRoom *chat_room, const LinphoneEventLog *event_log) { + _linphone_chat_room_notify_new_event(chat_room, event_log); + LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(chat_room), + linphone_chat_room_cbs_get_security_event, event_log); } -void _linphone_chat_room_notify_subject_changed(LinphoneChatRoom *cr, const LinphoneEventLog *event_log) { - _linphone_chat_room_notify_new_event(cr, event_log); - LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(cr), linphone_chat_room_cbs_get_subject_changed, - event_log); +void _linphone_chat_room_notify_subject_changed(LinphoneChatRoom *chat_room, const LinphoneEventLog *event_log) { + _linphone_chat_room_notify_new_event(chat_room, event_log); + LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(chat_room), + linphone_chat_room_cbs_get_subject_changed, event_log); } -void _linphone_chat_room_notify_conference_joined(LinphoneChatRoom *cr, const LinphoneEventLog *event_log) { - _linphone_chat_room_notify_new_event(cr, event_log); - LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(cr), +void _linphone_chat_room_notify_conference_joined(LinphoneChatRoom *chat_room, const LinphoneEventLog *event_log) { + _linphone_chat_room_notify_new_event(chat_room, event_log); + LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(chat_room), linphone_chat_room_cbs_get_conference_joined, event_log); } -void _linphone_chat_room_notify_conference_left(LinphoneChatRoom *cr, const LinphoneEventLog *event_log) { - _linphone_chat_room_notify_new_event(cr, event_log); - LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(cr), linphone_chat_room_cbs_get_conference_left, - event_log); +void _linphone_chat_room_notify_conference_left(LinphoneChatRoom *chat_room, const LinphoneEventLog *event_log) { + _linphone_chat_room_notify_new_event(chat_room, event_log); + LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(chat_room), + linphone_chat_room_cbs_get_conference_left, event_log); } -void _linphone_chat_room_notify_ephemeral_event(LinphoneChatRoom *cr, const LinphoneEventLog *event_log) { - _linphone_chat_room_notify_new_event(cr, event_log); - LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(cr), linphone_chat_room_cbs_get_ephemeral_event, - event_log); +void _linphone_chat_room_notify_ephemeral_event(LinphoneChatRoom *chat_room, const LinphoneEventLog *event_log) { + _linphone_chat_room_notify_new_event(chat_room, event_log); + LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(chat_room), + linphone_chat_room_cbs_get_ephemeral_event, event_log); } -void _linphone_chat_room_notify_ephemeral_message_timer_started(LinphoneChatRoom *cr, +void _linphone_chat_room_notify_ephemeral_message_timer_started(LinphoneChatRoom *chat_room, const LinphoneEventLog *event_log) { - _linphone_chat_room_notify_new_event(cr, event_log); - LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(cr), + _linphone_chat_room_notify_new_event(chat_room, event_log); + LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(chat_room), linphone_chat_room_cbs_get_ephemeral_message_timer_started, event_log); } -void _linphone_chat_room_notify_ephemeral_message_deleted(LinphoneChatRoom *cr, const LinphoneEventLog *event_log) { - _linphone_chat_room_notify_new_event(cr, event_log); - LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(cr), +void _linphone_chat_room_notify_ephemeral_message_deleted(LinphoneChatRoom *chat_room, + const LinphoneEventLog *event_log) { + _linphone_chat_room_notify_new_event(chat_room, event_log); + LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(chat_room), linphone_chat_room_cbs_get_ephemeral_message_deleted, event_log); } -void _linphone_chat_room_notify_undecryptable_message_received(LinphoneChatRoom *cr, LinphoneChatMessage *msg) { - LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(cr), +void _linphone_chat_room_notify_undecryptable_message_received(LinphoneChatRoom *chat_room, LinphoneChatMessage *msg) { + LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(chat_room), linphone_chat_room_cbs_get_undecryptable_message_received, msg); } -void _linphone_chat_room_notify_chat_message_received(LinphoneChatRoom *cr, const LinphoneEventLog *event_log) { - _linphone_chat_room_notify_new_event(cr, event_log); - LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(cr), +void _linphone_chat_room_notify_chat_message_received(LinphoneChatRoom *chat_room, const LinphoneEventLog *event_log) { + _linphone_chat_room_notify_new_event(chat_room, event_log); + LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(chat_room), linphone_chat_room_cbs_get_chat_message_received, event_log); } -void _linphone_chat_room_notify_chat_messages_received(LinphoneChatRoom *cr, const bctbx_list_t *event_logs) { - _linphone_chat_room_notify_new_events(cr, event_logs); - LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(cr), +void _linphone_chat_room_notify_chat_messages_received(LinphoneChatRoom *chat_room, const bctbx_list_t *event_logs) { + _linphone_chat_room_notify_new_events(chat_room, event_logs); + LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(chat_room), linphone_chat_room_cbs_get_chat_messages_received, event_logs); } -void _linphone_chat_room_notify_chat_message_sending(LinphoneChatRoom *cr, const LinphoneEventLog *event_log) { - _linphone_chat_room_notify_new_event(cr, event_log); - LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(cr), +void _linphone_chat_room_notify_chat_message_sending(LinphoneChatRoom *chat_room, const LinphoneEventLog *event_log) { + _linphone_chat_room_notify_new_event(chat_room, event_log); + LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(chat_room), linphone_chat_room_cbs_get_chat_message_sending, event_log); } -void _linphone_chat_room_notify_chat_message_sent(LinphoneChatRoom *cr, const LinphoneEventLog *event_log) { - _linphone_chat_room_notify_new_event(cr, event_log); - LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(cr), +void _linphone_chat_room_notify_chat_message_sent(LinphoneChatRoom *chat_room, const LinphoneEventLog *event_log) { + _linphone_chat_room_notify_new_event(chat_room, event_log); + LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(chat_room), linphone_chat_room_cbs_get_chat_message_sent, event_log); } -void _linphone_chat_room_notify_conference_address_generation(LinphoneChatRoom *cr) { - LINPHONE_HYBRID_OBJECT_INVOKE_CBS_NO_ARG(ChatRoom, AbstractChatRoom::toCpp(cr), +void _linphone_chat_room_notify_conference_address_generation(LinphoneChatRoom *chat_room) { + LINPHONE_HYBRID_OBJECT_INVOKE_CBS_NO_ARG(ChatRoom, AbstractChatRoom::toCpp(chat_room), linphone_chat_room_cbs_get_conference_address_generation); } void _linphone_chat_room_notify_participant_registration_subscription_requested( - LinphoneChatRoom *cr, const LinphoneAddress *participantAddr) { - LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(cr), + LinphoneChatRoom *chat_room, const LinphoneAddress *participantAddr) { + LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(chat_room), linphone_chat_room_cbs_get_participant_registration_subscription_requested, participantAddr); } void _linphone_chat_room_notify_participant_registration_unsubscription_requested( - LinphoneChatRoom *cr, const LinphoneAddress *participantAddr) { - LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(cr), + LinphoneChatRoom *chat_room, const LinphoneAddress *participantAddr) { + LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(chat_room), linphone_chat_room_cbs_get_participant_registration_unsubscription_requested, participantAddr); } -void _linphone_chat_room_notify_chat_message_should_be_stored(LinphoneChatRoom *cr, LinphoneChatMessage *msg) { - LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(cr), +void _linphone_chat_room_notify_chat_message_should_be_stored(LinphoneChatRoom *chat_room, LinphoneChatMessage *msg) { + LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(chat_room), linphone_chat_room_cbs_get_chat_message_should_be_stored, msg); } -void _linphone_chat_room_notify_chat_message_participant_imdn_state_changed(LinphoneChatRoom *cr, +void _linphone_chat_room_notify_chat_message_participant_imdn_state_changed(LinphoneChatRoom *chat_room, LinphoneChatMessage *msg, const LinphoneParticipantImdnState *state) { - LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(cr), + LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(chat_room), linphone_chat_room_cbs_get_chat_message_participant_imdn_state_changed, msg, state); } -void _linphone_chat_room_notify_chat_room_read(LinphoneChatRoom *cr) { - LINPHONE_HYBRID_OBJECT_INVOKE_CBS_NO_ARG(ChatRoom, AbstractChatRoom::toCpp(cr), +void _linphone_chat_room_notify_chat_room_read(LinphoneChatRoom *chat_room) { + LINPHONE_HYBRID_OBJECT_INVOKE_CBS_NO_ARG(ChatRoom, AbstractChatRoom::toCpp(chat_room), linphone_chat_room_cbs_get_chat_room_read); } -void _linphone_chat_room_notify_new_reaction_received(LinphoneChatRoom *cr, +void _linphone_chat_room_notify_new_reaction_received(LinphoneChatRoom *chat_room, LinphoneChatMessage *message, const LinphoneChatMessageReaction *reaction) { - LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(cr), + LINPHONE_HYBRID_OBJECT_INVOKE_CBS(ChatRoom, AbstractChatRoom::toCpp(chat_room), linphone_chat_room_cbs_get_new_message_reaction, message, reaction); } @@ -896,22 +911,22 @@ void _linphone_chat_room_notify_new_reaction_received(LinphoneChatRoom *cr, // Reference and user data handling functions. // ============================================================================= -LinphoneChatRoom *linphone_chat_room_ref(LinphoneChatRoom *cr) { - belle_sip_object_ref(cr); - return cr; +LinphoneChatRoom *linphone_chat_room_ref(LinphoneChatRoom *chat_room) { + belle_sip_object_ref(chat_room); + return chat_room; } -void linphone_chat_room_unref(LinphoneChatRoom *cr) { - ChatRoomLogContextualizer logContextualizer(cr); - belle_sip_object_unref(cr); +void linphone_chat_room_unref(LinphoneChatRoom *chat_room) { + ChatRoomLogContextualizer logContextualizer(chat_room); + belle_sip_object_unref(chat_room); } -void *linphone_chat_room_get_user_data(const LinphoneChatRoom *cr) { - return AbstractChatRoom::toCpp(cr)->getUserData(); +void *linphone_chat_room_get_user_data(const LinphoneChatRoom *chat_room) { + return AbstractChatRoom::toCpp(chat_room)->getUserData(); } -void linphone_chat_room_set_user_data(LinphoneChatRoom *cr, void *ud) { - AbstractChatRoom::toCpp(cr)->setUserData(ud); +void linphone_chat_room_set_user_data(LinphoneChatRoom *chat_room, void *ud) { + AbstractChatRoom::toCpp(chat_room)->setUserData(ud); } // ============================================================================= diff --git a/src/c-wrapper/api/c-conference-info.cpp b/src/c-wrapper/api/c-conference-info.cpp index 0ecbe58d17847f30450d627d61f58425c16e6d1d..8d38aa8ff2886a26c31663f52b0d5070fe884058 100644 --- a/src/c-wrapper/api/c-conference-info.cpp +++ b/src/c-wrapper/api/c-conference-info.cpp @@ -87,7 +87,7 @@ void linphone_conference_info_set_participant_infos(LinphoneConferenceInfo *conf void linphone_conference_info_add_participant_infos(LinphoneConferenceInfo *conference_info, const bctbx_list_t *participant_infos) { const std::list<std::shared_ptr<LinphonePrivate::ParticipantInfo>> participantInfos = - ParticipantInfo::getCppListFromCList(participant_infos); + Utils::bctbxListToCppSharedPtrList<LinphoneParticipantInfo, ParticipantInfo>(participant_infos); ConferenceInfo::toCpp(conference_info)->addParticipants(participantInfos); } diff --git a/src/c-wrapper/api/c-conference.cpp b/src/c-wrapper/api/c-conference.cpp index f6a1a9c1a5cbb6e5766dac8667d96a024b5a4fb4..45b90f6d1e70eab79c42d0fa45292be19ac28c77 100644 --- a/src/c-wrapper/api/c-conference.cpp +++ b/src/c-wrapper/api/c-conference.cpp @@ -71,12 +71,22 @@ LinphoneConference *linphone_local_conference_new(LinphoneCore *core, LinphoneAd LinphoneConference *linphone_local_conference_new_with_params(LinphoneCore *core, LinphoneAddress *addr, const LinphoneConferenceParams *params) { + + LinphoneConferenceParams *cloned_params = NULL; + if (params) { + cloned_params = linphone_conference_params_clone(params); + } else { + cloned_params = linphone_core_create_conference_params_2(core, NULL); + } + if (!linphone_conference_params_get_account(cloned_params) && addr) { + linphone_conference_params_set_account(cloned_params, linphone_core_lookup_account_by_identity(core, addr)); + } + std::shared_ptr<ConferenceParams> conf_params = ConferenceParams::toCpp(cloned_params)->getSharedFromThis(); std::shared_ptr<Conference> localConf = - (new ServerConference( - L_GET_CPP_PTR_FROM_C_OBJECT(core), Address::toCpp(addr)->getSharedFromThis(), nullptr, - params ? ConferenceParams::toCpp(const_cast<LinphoneConferenceParams *>(params))->getSharedFromThis() - : ConferenceParams::create(L_GET_CPP_PTR_FROM_C_OBJECT(core)))) - ->toSharedPtr(); + (new ServerConference(L_GET_CPP_PTR_FROM_C_OBJECT(core), nullptr, conf_params))->toSharedPtr(); + if (cloned_params) { + linphone_conference_params_unref(cloned_params); + } localConf->init(); localConf->ref(); return localConf->toC(); @@ -90,22 +100,35 @@ LinphoneConference *linphone_remote_conference_new_with_params(LinphoneCore *cor LinphoneAddress *focus, LinphoneAddress *addr, const LinphoneConferenceParams *params) { - std::shared_ptr<const ConferenceParams> conf_params = NULL; + LinphoneConferenceParams *cloned_params = NULL; if (params) { - conf_params = ConferenceParams::toCpp(params)->getSharedFromThis(); + cloned_params = linphone_conference_params_clone(params); } else { - conf_params = ConferenceParams::create(L_GET_CPP_PTR_FROM_C_OBJECT(core)); + cloned_params = linphone_core_create_conference_params_2(core, NULL); + } + if (!linphone_conference_params_get_account(cloned_params) && addr) { + linphone_conference_params_set_account(cloned_params, linphone_core_lookup_account_by_identity(core, addr)); } + std::shared_ptr<const ConferenceParams> conf_params = ConferenceParams::toCpp(cloned_params)->getSharedFromThis(); std::shared_ptr<Conference> conference = - (new ClientConference(L_GET_CPP_PTR_FROM_C_OBJECT(core), Address::toCpp(addr)->getSharedFromThis(), nullptr, - conf_params)) - ->toSharedPtr(); + (new ClientConference(L_GET_CPP_PTR_FROM_C_OBJECT(core), nullptr, conf_params))->toSharedPtr(); + if (cloned_params) { + linphone_conference_params_unref(cloned_params); + } static_pointer_cast<ClientConference>(conference) ->initWithFocus(Address::toCpp(focus)->getSharedFromThis(), nullptr, nullptr, conference.get()); conference->ref(); return conference->toC(); } +const char *linphone_conference_get_identifier(const LinphoneConference *conference) { + const auto &identifier = Conference::toCpp(conference)->getIdentifier(); + if (identifier) { + return L_STRING_TO_C(identifier.value()); + } + return NULL; +} + LinphoneConferenceState linphone_conference_get_state(const LinphoneConference *conference) { return (LinphoneConferenceState)Conference::toCpp(conference)->getState(); } @@ -117,7 +140,7 @@ linphone_conference_get_active_speaker_participant_device(const LinphoneConferen if (p) { return p->toC(); } - return NULL; + return nullptr; } const LinphoneConferenceParams *linphone_conference_get_current_params(const LinphoneConference *conference) { @@ -164,7 +187,7 @@ LinphoneParticipant *linphone_conference_find_participant(LinphoneConference *co if (p) { return p->toC(); } - return NULL; + return nullptr; } int linphone_conference_update_params(LinphoneConference *conference, const LinphoneConferenceParams *params) { @@ -218,7 +241,7 @@ const LinphoneAudioDevice *linphone_conference_get_input_audio_device(const Linp if (audioDevice) { return audioDevice->toC(); } - return NULL; + return nullptr; } const LinphoneAudioDevice *linphone_conference_get_output_audio_device(const LinphoneConference *conference) { ConferenceLogContextualizer logContextualizer(conference); @@ -226,7 +249,7 @@ const LinphoneAudioDevice *linphone_conference_get_output_audio_device(const Lin if (audioDevice) { return audioDevice->toC(); } - return NULL; + return nullptr; } int linphone_conference_get_participant_device_volume(LinphoneConference *conference, @@ -272,7 +295,7 @@ int linphone_conference_get_participant_count(const LinphoneConference *conferen bctbx_list_t *linphone_conference_get_participants(const LinphoneConference *conference) { ConferenceLogContextualizer logContextualizer(conference); bctbx_list_t *participants = linphone_conference_get_participant_list(conference); - bctbx_list_t *participant_addresses = NULL; + bctbx_list_t *participant_addresses = nullptr; for (bctbx_list_t *iterator = participants; iterator; iterator = bctbx_list_next(iterator)) { LinphoneParticipant *p = (LinphoneParticipant *)bctbx_list_get_data(iterator); LinphoneAddress *a = linphone_address_clone(linphone_participant_get_address(p)); @@ -341,11 +364,8 @@ LinphoneStatus linphone_conference_invite_participants(LinphoneConference *confe const bctbx_list_t *addresses, const LinphoneCallParams *params) { ConferenceLogContextualizer logContextualizer(conference); - std::list<std::shared_ptr<const LinphonePrivate::Address>> addressList; - for (const bctbx_list_t *elem = addresses; elem != NULL; elem = elem->next) { - const LinphoneAddress *data = static_cast<const LinphoneAddress *>(bctbx_list_get_data(elem)); - addressList.push_back(LinphonePrivate::Address::toCpp(data)->getSharedFromThis()); - } + std::list<std::shared_ptr<LinphonePrivate::Address>> addressList = + LinphonePrivate::Utils::bctbxListToCppSharedPtrList<LinphoneAddress, LinphonePrivate::Address>(addresses); return Conference::toCpp(conference)->inviteAddresses(addressList, params); } @@ -357,11 +377,8 @@ LinphoneStatus linphone_conference_add_participants(LinphoneConference *conferen LinphoneStatus linphone_conference_add_participants_2(LinphoneConference *conference, const bctbx_list_t *addresses) { ConferenceLogContextualizer logContextualizer(conference); - std::list<std::shared_ptr<const LinphonePrivate::Address>> addressList; - for (const bctbx_list_t *elem = addresses; elem != NULL; elem = elem->next) { - const LinphoneAddress *data = static_cast<const LinphoneAddress *>(bctbx_list_get_data(elem)); - addressList.push_back(LinphonePrivate::Address::toCpp(data)->getSharedFromThis()); - } + std::list<std::shared_ptr<LinphonePrivate::Address>> addressList = + LinphonePrivate::Utils::bctbxListToCppSharedPtrList<LinphoneAddress, LinphonePrivate::Address>(addresses); return Conference::toCpp(conference)->addParticipants(addressList); } @@ -420,7 +437,7 @@ LinphoneCall *linphone_conference_get_call(const LinphoneConference *conference) if (call) { return call->toC(); } - return NULL; + return nullptr; } void linphone_conference_set_local_participant_stream_capability(LinphoneConference *conference, @@ -617,7 +634,7 @@ const char *linphone_conference_layout_to_string(const LinphoneConferenceLayout case LinphoneConferenceLayoutActiveSpeaker: return "LinphoneConferenceLayoutActiveSpeaker"; } - return NULL; + return nullptr; } LinphonePlayer *linphone_conference_get_player(LinphoneConference *conference) { @@ -629,3 +646,8 @@ const LinphoneConferenceInfo *linphone_conference_get_info(LinphoneConference *c std::shared_ptr<ConferenceInfo> info = Conference::toCpp(conference)->createOrGetConferenceInfo(); return info ? info->toC() : nullptr; } + +LinphoneAccount *linphone_conference_get_account(LinphoneConference *conference) { + shared_ptr<Account> account = Conference::toCpp(conference)->getAccount(); + return account ? account->toC() : nullptr; +} diff --git a/src/c-wrapper/api/c-content.cpp b/src/c-wrapper/api/c-content.cpp index c156fee8cbf11ede35634ff0aeb307efbb0eb7bd..602f62f69e474f918457976bb710f53dbfaf6072 100644 --- a/src/c-wrapper/api/c-content.cpp +++ b/src/c-wrapper/api/c-content.cpp @@ -380,6 +380,11 @@ time_t linphone_content_get_creation_timestamp(const LinphoneContent *content) { return -1; } +const char *linphone_content_get_related_chat_message_id(const LinphoneContent *content) { + const auto c = Content::toCpp(content); + return L_STRING_TO_C(c->getRelatedChatMessageId()); +} + // ============================================================================= // Private functions. // ============================================================================= diff --git a/src/c-wrapper/api/c-friend-list.cpp b/src/c-wrapper/api/c-friend-list.cpp index 02f03d9b2496aec1a3f21f2633c07bee04a2d000..32f363ddbaefc6325e1268143bed1cf8fa553dd3 100644 --- a/src/c-wrapper/api/c-friend-list.cpp +++ b/src/c-wrapper/api/c-friend-list.cpp @@ -313,7 +313,12 @@ void linphone_friend_list_subscription_state_changed(LinphoneCore *lc, } LinphoneCore *linphone_friend_list_get_core(const LinphoneFriendList *list) { - return FriendList::toCpp(list)->getCore()->getCCore(); + try { + auto core = FriendList::toCpp(list)->getCore(); + return core->getCCore(); + } catch (std::exception &) { + } + return nullptr; } LinphoneStatus linphone_friend_list_import_friends_from_vcard4_file(LinphoneFriendList *list, const char *vcard_file) { diff --git a/src/c-wrapper/api/c-friend.cpp b/src/c-wrapper/api/c-friend.cpp index 3958025e23573bb292a0773eb0766bc135326c03..dce4d693be45d703a3a9a051f7b804d4d33a4e45 100644 --- a/src/c-wrapper/api/c-friend.cpp +++ b/src/c-wrapper/api/c-friend.cpp @@ -898,9 +898,14 @@ void linphone_core_set_friends_database_path(LinphoneCore *lc, const char *path) lc->friends_db_file = ms_strdup(path); } - if (L_GET_PRIVATE(lc->cppPtr)->mainDb) { - L_GET_PRIVATE(lc->cppPtr)->mainDb->import(LinphonePrivate::MainDb::Sqlite3, path); - linphone_core_friends_storage_resync_friends_lists(lc); + auto &mainDb = L_GET_PRIVATE(lc->cppPtr)->mainDb; + if (mainDb && mainDb->isInitialized()) { + if (mainDb->import(LinphonePrivate::MainDb::Sqlite3, path)) + linphone_core_friends_storage_resync_friends_lists(lc); + } else { + ms_warning( + "Database has not been initialized, therefore it is not possible to import friends database at path %s", + path); } } } diff --git a/src/c-wrapper/api/c-participant-device.cpp b/src/c-wrapper/api/c-participant-device.cpp index 2828b891fdfa3de473b5d9de5aa367b81e865ad3..f74dcf08b97ab77e1ffe662540816682ad03b9ea 100644 --- a/src/c-wrapper/api/c-participant-device.cpp +++ b/src/c-wrapper/api/c-participant-device.cpp @@ -23,6 +23,7 @@ #include "address/address.h" #include "c-wrapper/c-wrapper.h" #include "conference/participant-device.h" +#include "conference/participant.h" #include "private.h" // ============================================================================= @@ -149,6 +150,10 @@ bool_t linphone_participant_device_screen_sharing_enabled(const LinphoneParticip return ParticipantDevice::toCpp(participant_device)->screenSharingEnabled(); } +LinphoneParticipant *linphone_participant_device_get_participant(const LinphoneParticipantDevice *participant_device) { + return bellesip::toC(ParticipantDevice::toCpp(participant_device)->getParticipant()); +} + LinphoneCore *linphone_participant_device_get_core(const LinphoneParticipantDevice *participant_device) { const auto &core = ParticipantDevice::toCpp(participant_device)->getCore(); return core ? core->getCCore() : nullptr; diff --git a/src/c-wrapper/api/c-vcard.cpp b/src/c-wrapper/api/c-vcard.cpp index 603d27e2070ed12f58a2901a4b6e27342551248d..aec2582cedb18c68b8e72775808133a07baf1173 100644 --- a/src/c-wrapper/api/c-vcard.cpp +++ b/src/c-wrapper/api/c-vcard.cpp @@ -52,6 +52,11 @@ const char *linphone_vcard_as_vcard4_string(LinphoneVcard *vCard) { return L_STRING_TO_C(Vcard::toCpp(vCard)->asVcard4String()); } +const char *linphone_vcard_as_vcard4_string_with_base_64_picture(LinphoneVcard *vCard) { + if (!vCard) return nullptr; + return L_STRING_TO_C(Vcard::toCpp(vCard)->asVcard4StringWithBase64Picture()); +} + void *linphone_vcard_get_belcard(LinphoneVcard *vCard) { return Vcard::toCpp(vCard)->getBelcard(); } diff --git a/src/call/call-log.cpp b/src/call/call-log.cpp index 38a9843891860fb9c3df5120ee070d88c797a322..7d059d20329019545aeedf79eb9a1236395e18ba 100644 --- a/src/call/call-log.cpp +++ b/src/call/call-log.cpp @@ -232,8 +232,7 @@ std::shared_ptr<ConferenceInfo> CallLog::getConferenceInfo() const { const std::shared_ptr<AbstractChatRoom> CallLog::getChatRoom() const { auto conferenceInfo = getConferenceInfo(); if (conferenceInfo && conferenceInfo->getCapability(LinphoneStreamTypeText)) { - return getCore()->getPrivate()->searchChatRoom(nullptr, nullptr, conferenceInfo->getUri(), - std::list<std::shared_ptr<Address>>{}); + return getCore()->getPrivate()->searchChatRoom(nullptr, nullptr, conferenceInfo->getUri(), {}); } return nullptr; } diff --git a/src/call/call.cpp b/src/call/call.cpp index f13a7bd27697848e27526cc5d0e5b353c7fc5a90..4b698114b5531d02c879689ec00163324ce4a6c5 100644 --- a/src/call/call.cpp +++ b/src/call/call.cpp @@ -426,7 +426,8 @@ void Call::onCallSessionStateChanged(const shared_ptr<CallSession> &session, if (!op->getTo().empty()) { const auto to = Address::create(op->getTo()); - conference = L_GET_CPP_PTR_FROM_C_OBJECT(lc)->findConference(ConferenceId(to, to)); + conference = L_GET_CPP_PTR_FROM_C_OBJECT(lc)->findConference( + ConferenceId(to, to, getCore()->createConferenceIdParams()), false); } } @@ -492,7 +493,8 @@ void Call::onCallSessionStateChanged(const shared_ptr<CallSession> &session, } else if (!confId.empty()) { auto localAddress = session->getContactAddress(); if (localAddress && localAddress->isValid()) { - ConferenceId serverConferenceId = ConferenceId(localAddress, localAddress); + ConferenceId serverConferenceId = + ConferenceId(localAddress, localAddress, getCore()->createConferenceIdParams()); conference = getCore()->findConference(serverConferenceId, false); if (conference) { setConference(conference); @@ -543,7 +545,8 @@ void Call::createClientConference(const shared_ptr<CallSession> &session) { const auto op = session->getPrivate()->getOp(); std::shared_ptr<Address> remoteContactAddress = Address::create(); remoteContactAddress->setImpl(op->getRemoteContactAddress()); - ConferenceId conferenceId = ConferenceId(remoteContactAddress, getLocalAddress()); + ConferenceId conferenceId = + ConferenceId(remoteContactAddress, getLocalAddress(), getCore()->createConferenceIdParams()); const auto &conference = getCore()->findConference(conferenceId, false); @@ -561,6 +564,7 @@ void Call::createClientConference(const shared_ptr<CallSession> &session) { } } else { auto confParams = ConferenceParams::create(getCore()); + confParams->setAccount(getParams()->getAccount()); std::shared_ptr<SalMediaDescription> md = (op) ? op->getFinalMediaDescription() : nullptr; if (op && op->getSal()->mediaDisabled()) md = op->getRemoteMediaDescription(); @@ -573,7 +577,7 @@ void Call::createClientConference(const shared_ptr<CallSession> &session) { if (confParams->audioEnabled() || confParams->videoEnabled() || confParams->chatEnabled()) { clientConference = dynamic_pointer_cast<ClientConference>( - (new ClientConference(getCore(), conferenceId.getLocalAddress(), nullptr, confParams))->toSharedPtr()); + (new ClientConference(getCore(), nullptr, confParams))->toSharedPtr()); clientConference->initWithFocus(remoteContactAddress, session, op); } else { lError() << "Unable to attach call (local address " << *session->getLocalAddress() << " remote address " diff --git a/src/chat/chat-message/chat-message.cpp b/src/chat/chat-message/chat-message.cpp index caaadd3941338d653eb44b1719812fdcfc6ef635..fc5dd10c2be729a5b34c5630efd82ec31782b235 100644 --- a/src/chat/chat-message/chat-message.cpp +++ b/src/chat/chat-message/chat-message.cpp @@ -171,11 +171,6 @@ void ChatMessagePrivate::setParticipantState(const std::shared_ptr<Address> &par const auto &conferenceAddress = chatRoom->getConferenceAddress(); const auto conferenceAddressStr = conferenceAddress ? conferenceAddress->toString() : std::string("sip:"); - if (currentState == ChatMessage::State::Displayed) { - // Prevents "invalid state transition from [Displayed] to [DeliveredToUser]" error logs when a participant has - // multiple devices. - return; - } if (!chatMessageFsmChecker.isValid(currentState, newState)) { if (isBasicChatRoom) { lWarning() << "Chat message " << sharedMessage << ": Invalid transaction of message in basic chat room " @@ -229,6 +224,82 @@ void ChatMessagePrivate::setParticipantState(const std::shared_ptr<Address> &par return; } + if (linphone_config_get_bool(linphone_core_get_config(chatRoom->getCore()->getCCore()), "misc", + "enable_simple_group_chat_message_state", FALSE)) { + setState(newState); + } else { + lInfo() << "Chat message " << sharedMessage << ": moving participant '" << *participantAddress << "' state to " + << Utils::toString(newState); + if (eventLog) { + mainDb->setChatMessageParticipantState(eventLog, participantAddress, newState, stateChangeTime); + } + + // Update chat message state if it doesn't depend on IMDN + if (isMe && !isImdnControlledState(newState)) { + setState(newState); + } + + if (isImdnControlledState(newState)) { + const auto imdnStates = q->getParticipantsState(); + size_t nbRecipients = 0; + size_t nbDisplayedStates = 0; + size_t nbDeliveredStates = 0; + size_t nbDeliveredToUserStates = 0; + size_t nbNotDeliveredStates = 0; + for (const auto &imdnState : imdnStates) { + const auto &participantState = imdnState.getState(); + const auto &imdnParticipant = imdnState.getParticipant(); + if (fromAddress->weakEqual(*(imdnParticipant->getAddress()))) { + if (participantState == ChatMessage::State::NotDelivered) { + nbNotDeliveredStates++; + } + } else { + nbRecipients++; + switch (participantState) { + case ChatMessage::State::Displayed: + nbDisplayedStates++; + break; + case ChatMessage::State::DeliveredToUser: + nbDeliveredToUserStates++; + break; + case ChatMessage::State::Delivered: + nbDeliveredStates++; + break; + case ChatMessage::State::NotDelivered: + nbNotDeliveredStates++; + break; + default: + break; + } + } + } + + if (nbNotDeliveredStates > 0) { + setState(ChatMessage::State::NotDelivered); + } else if ((nbRecipients > 0) && (nbDisplayedStates == nbRecipients)) { + setState(ChatMessage::State::Displayed); + } else if ((nbRecipients > 0) && ((nbDisplayedStates + nbDeliveredToUserStates) == nbRecipients)) { + setState(ChatMessage::State::DeliveredToUser); + } else if ((nbRecipients > 0) && + ((nbDeliveredStates + nbDisplayedStates + nbDeliveredToUserStates) == nbRecipients)) { + setState(ChatMessage::State::Delivered); + } + } + + if (isMe) { + // Set me participant state to displayed if we are the sender, set the message as Displayed as soon as we + // received the 202 Accepted response + if (fromAddress->weakEqual(*participantAddress) && (newState == ChatMessage::State::DeliveredToUser)) { + setParticipantState(participantAddress, ChatMessage::State::Displayed, ::ms_time(nullptr)); + } + if ((newState == ChatMessage::State::NotDelivered) && (reason == LinphoneReasonForbidden)) { + // Try to recover from a situation where the server replied 403 to an outgoing message + chatRoom->handleMessageRejected(sharedMessage); + } + } + } + + // Once the participant and IMDN state are updated, it is possible to notify the application LinphoneChatMessage *msg = L_GET_C_BACK_PTR(q); LinphoneChatRoom *cr = chatRoom->toC(); auto participant = isMe ? me : chatRoom->findParticipant(participantAddress); @@ -244,78 +315,6 @@ void ChatMessagePrivate::setParticipantState(const std::shared_ptr<Address> &par const LinphoneParticipantImdnState *c_state = _linphone_participant_imdn_state_from_cpp_obj(imdnState); _linphone_chat_message_notify_participant_imdn_state_changed(msg, c_state); _linphone_chat_room_notify_chat_message_participant_imdn_state_changed(cr, msg, c_state); - - if (linphone_config_get_bool(linphone_core_get_config(chatRoom->getCore()->getCCore()), "misc", - "enable_simple_group_chat_message_state", FALSE)) { - setState(newState); - return; - } - - lInfo() << "Chat message " << sharedMessage << ": moving participant '" << *participantAddress << "' state to " - << Utils::toString(newState); - if (eventLog) { - mainDb->setChatMessageParticipantState(eventLog, participantAddress, newState, stateChangeTime); - } - - // Update chat message state if it doesn't depend on IMDN - if (isMe && !isImdnControlledState(newState)) { - setState(newState); - } - - if (isImdnControlledState(newState)) { - const auto imdnStates = q->getParticipantsState(); - size_t nbRecipients = 0; - size_t nbDisplayedStates = 0; - size_t nbDeliveredStates = 0; - size_t nbDeliveredToUserStates = 0; - size_t nbNotDeliveredStates = 0; - for (const auto &imdnState : imdnStates) { - const auto &participantState = imdnState.getState(); - const auto &imdnParticipant = imdnState.getParticipant(); - if (fromAddress->weakEqual(*(imdnParticipant->getAddress()))) { - if (participantState == ChatMessage::State::NotDelivered) { - nbNotDeliveredStates++; - } - } else { - nbRecipients++; - switch (participantState) { - case ChatMessage::State::Displayed: - nbDisplayedStates++; - break; - case ChatMessage::State::DeliveredToUser: - nbDeliveredToUserStates++; - break; - case ChatMessage::State::Delivered: - nbDeliveredStates++; - break; - case ChatMessage::State::NotDelivered: - nbNotDeliveredStates++; - break; - default: - break; - } - } - } - - if (nbNotDeliveredStates > 0) { - setState(ChatMessage::State::NotDelivered); - } else if ((nbRecipients > 0) && (nbDisplayedStates == nbRecipients)) { - setState(ChatMessage::State::Displayed); - } else if ((nbRecipients > 0) && ((nbDisplayedStates + nbDeliveredToUserStates) == nbRecipients)) { - setState(ChatMessage::State::DeliveredToUser); - } else if ((nbRecipients > 0) && - ((nbDeliveredStates + nbDisplayedStates + nbDeliveredToUserStates) == nbRecipients)) { - setState(ChatMessage::State::Delivered); - } - } - - if (isMe) { - // Set me participant state to displayed if we are the sender, set the message as Displayed as soon as we - // received the 202 Accepted response - if (fromAddress->weakEqual(*participantAddress) && (newState == ChatMessage::State::DeliveredToUser)) { - setParticipantState(participantAddress, ChatMessage::State::Displayed, ::ms_time(nullptr)); - } - } } void ChatMessagePrivate::setAutomaticallyResent(bool enable) { @@ -801,27 +800,55 @@ std::string ChatMessagePrivate::createFakeFileTransferFromUrl(const std::string } void ChatMessagePrivate::setChatRoom(const shared_ptr<AbstractChatRoom> &chatRoom) { + L_Q(); mChatRoom = chatRoom; const ConferenceId &conferenceId = chatRoom->getConferenceId(); const auto &account = chatRoom->getAccount(); - // If an account is attached to a chatroom, use its contact address otherwise use the local address of the - // conference ID. Note that the conference ID's local address may not have the "gr" parameter hence this may lead to - // issues such as IMDN not received + const bool isBasicChatRoom = + (chatRoom->getCurrentParams()->getChatParams()->getBackend() == ChatParams::Backend::Basic); + // If an account is attached to a chatroom, use its contact or the identity address otherwise use the local address + // of the conference ID. For Flexisip based chatrooms, it is paramount to use a contact address as the From header. + // RFC3428 forbids adding a Contact header to MESSAGE requests, therefore the From header is the only way a + // conference server knows which device sent the request and can be forwarded to the other tchat members as well as + // other devices of the same participant. std::shared_ptr<Address> localAddress = nullptr; - if (account && account->getContactAddress()) { - localAddress = account->getContactAddress(); - } else { - lInfo() << "It looks that chatroom " << chatRoom << " with ID " << conferenceId - << " has no account associated to or the contact address is not available yet, setting conference ID's " - "local address as message local address"; + if (account) { + if (isBasicChatRoom) { + localAddress = account->getAccountParams()->getIdentityAddress(); + lInfo() << *chatRoom << " with ID " << conferenceId + << " is a basic chatroom therefore set the local address of message [" << q << "] to the account [" + << account << "]'s identity address " << *localAddress; + } else { + localAddress = account->getContactAddress(); + if (localAddress) { + lInfo() << *chatRoom << " with ID " << conferenceId + << " is not a basic chatroom therefore set the local address of message [" << q + << "] to the account contact address " << *localAddress; + } + } + } + if (!localAddress) { localAddress = conferenceId.getLocalAddress(); + if (localAddress) { + lInfo() + << *chatRoom << " with ID " << conferenceId + << " has no account associated to or the contact or the identity address is not available yet, setting " + "conference ID's local address as message local address " + << *localAddress; + } } - const auto peerAddress = conferenceId.getPeerAddress(); - const bool isBasicChatRoom = - (chatRoom->getCurrentParams()->getChatParams()->getBackend() == ChatParams::Backend::Basic); - if (isBasicChatRoom && localAddress) { - localAddress = Address::create(localAddress->getUriWithoutGruu()); + + if (localAddress) { + if (isBasicChatRoom) { + localAddress = Address::create(localAddress->getUriWithoutGruu()); + } else { + localAddress = localAddress->clone()->toSharedPtr(); + } + } else { + lError() << "Unable to set local address of message [" << q << "] in " << *chatRoom; } + + auto peerAddress = conferenceId.getPeerAddress()->clone()->toSharedPtr(); if (direction == ChatMessage::Direction::Outgoing) { fromAddress = localAddress; toAddress = peerAddress; @@ -829,7 +856,10 @@ void ChatMessagePrivate::setChatRoom(const shared_ptr<AbstractChatRoom> &chatRoo fromAddress = peerAddress; toAddress = localAddress; } - mMeAddress = chatRoom->getMe()->getAddress()->clone()->toSharedPtr(); + const auto &me = chatRoom->getMe(); + if (me) { + mMeAddress = me->getAddress()->clone()->toSharedPtr(); + } } // ----------------------------------------------------------------------------- @@ -1174,6 +1204,8 @@ void ChatMessagePrivate::handleAutoDownload() { } for (auto &c : contents) { + c->setRelatedChatMessageId(imdnId); + ContentType contentType = c->getContentType(); if (contentType.strongEqual(ContentType::Icalendar)) { LinphoneConferenceInfo *cConfInfo = @@ -1226,7 +1258,9 @@ void ChatMessagePrivate::restoreFileTransferContentAsFileContent() { if (content && content->isFileTransfer()) { auto fileTransferContent = static_pointer_cast<FileTransferContent>(content); auto fileContent = fileTransferContent->getFileContent(); + if (fileContent) { + fileContent->setRelatedChatMessageId(imdnId); it = contents.erase(it); it = contents.insert(it, fileContent); } else { @@ -1688,6 +1722,10 @@ const string &ChatMessage::getImdnMessageId() const { void ChatMessagePrivate::setImdnMessageId(const string &id) { imdnId = id; + + for (auto &content : contents) { + content->setRelatedChatMessageId(id); + } } const string &ChatMessagePrivate::getCallId() const { diff --git a/src/chat/chat-room/abstract-chat-room.h b/src/chat/chat-room/abstract-chat-room.h index 4ece4d6df8aa8cc73d9ed94c6346f46983675102..dda0d01ddf704bc1a247da1814a2f0a0fa6d3baa 100644 --- a/src/chat/chat-room/abstract-chat-room.h +++ b/src/chat/chat-room/abstract-chat-room.h @@ -116,6 +116,7 @@ public: virtual const std::shared_ptr<Address> &getPeerAddress() const = 0; virtual const std::shared_ptr<Address> &getLocalAddress() const = 0; + virtual std::optional<std::reference_wrapper<const std::string>> getIdentifier() const = 0; virtual time_t getCreationTime() const = 0; virtual time_t getLastUpdateTime() const = 0; @@ -203,6 +204,7 @@ public: virtual std::shared_ptr<Address> getConferenceAddress() const = 0; virtual std::shared_ptr<Participant> findParticipant(const std::shared_ptr<Address> &address) const = 0; virtual std::list<std::shared_ptr<Participant>> getParticipants() const = 0; + virtual std::list<std::shared_ptr<Address>> getParticipantAddresses() const = 0; virtual bool canHandleParticipants() const = 0; virtual std::shared_ptr<Conference> getConference() const = 0; @@ -242,6 +244,7 @@ public: virtual LinphoneReason onSipMessageReceived(SalOp *op, const SalMessage *message) = 0; virtual void onChatMessageReceived(const std::shared_ptr<ChatMessage> &chatMessage) = 0; + virtual void handleMessageRejected(const std::shared_ptr<ChatMessage> &chatMessage) = 0; virtual void addTransientChatMessage(const std::shared_ptr<ChatMessage> &message) = 0; virtual void removeTransientChatMessage(const std::shared_ptr<ChatMessage> &message) = 0; @@ -282,6 +285,13 @@ std::ostream &operator<<(std::ostream &lhs, AbstractChatRoom::SecurityLevel e); std::ostream &operator<<(std::ostream &lhs, AbstractChatRoom::EphemeralMode e); +inline std::ostream &operator<<(std::ostream &str, const AbstractChatRoom &chatRoom) { + const auto &conferenceAddress = chatRoom.getConferenceAddress(); + str << "ChatRoom [" << &chatRoom << "] (" + << (conferenceAddress ? conferenceAddress->toString() : std::string("sip:")) << ")"; + return str; +} + LINPHONE_END_NAMESPACE #endif // ifndef _L_ABSTRACT_CHAT_ROOM_H_ diff --git a/src/chat/chat-room/basic-chat-room.cpp b/src/chat/chat-room/basic-chat-room.cpp index 1b5a82c3dff929cef2a1d5eff5c0b9d065ecb78c..819abe5449f20b5ba29e924481cdfad7016f7975 100644 --- a/src/chat/chat-room/basic-chat-room.cpp +++ b/src/chat/chat-room/basic-chat-room.cpp @@ -116,6 +116,13 @@ const ConferenceId &BasicChatRoom::getConferenceId() const { return mConferenceId; } +std::optional<std::reference_wrapper<const std::string>> BasicChatRoom::getIdentifier() const { + if (mState == ConferenceInterface::State::Instantiated) { + return std::nullopt; + } + return mConferenceId.getIdentifier(); +} + ConferenceInterface::State BasicChatRoom::getState() const { return mState; } @@ -147,4 +154,12 @@ std::list<std::shared_ptr<Participant>> BasicChatRoom::getParticipants() const { return mParticipants; } +std::list<std::shared_ptr<Address>> BasicChatRoom::getParticipantAddresses() const { + list<std::shared_ptr<Address>> addresses; + for (auto &participant : mParticipants) { + addresses.push_back(participant->getAddress()); + } + return addresses; +} + LINPHONE_END_NAMESPACE diff --git a/src/chat/chat-room/basic-chat-room.h b/src/chat/chat-room/basic-chat-room.h index 321134efa85d264cb2703770e5b5529564246242..b94259b7d3bc2eecb14bd8361495c10ccd21825e 100644 --- a/src/chat/chat-room/basic-chat-room.h +++ b/src/chat/chat-room/basic-chat-room.h @@ -44,6 +44,7 @@ public: bool isReadOnly() const override; const ConferenceId &getConferenceId() const override; + std::optional<std::reference_wrapper<const std::string>> getIdentifier() const override; void invalidateAccount() override; std::shared_ptr<Account> getAccount() override; @@ -57,6 +58,7 @@ public: bool isMe(const std::shared_ptr<Address> &address) const override; std::shared_ptr<Participant> getMe() const override; std::list<std::shared_ptr<Participant>> getParticipants() const override; + std::list<std::shared_ptr<Address>> getParticipantAddresses() const override; protected: explicit BasicChatRoom(const std::shared_ptr<Core> &core, diff --git a/src/chat/chat-room/chat-room.cpp b/src/chat/chat-room/chat-room.cpp index b43ddb95f3fcebe7b16c0ff4b99c74eb6dc5b54e..b7f37db5dc4daea7a721a8f84ccd5300d1151c22 100644 --- a/src/chat/chat-room/chat-room.cpp +++ b/src/chat/chat-room/chat-room.cpp @@ -329,12 +329,13 @@ std::shared_ptr<AbstractChatRoom> ChatRoom::getImdnChatRoom(const std::shared_pt if (*peerAddress == chatRoomPeerAddress->getUriWithoutGruu()) { chatRoom = getSharedFromThis(); } else { - auto isEncrypted = getCurrentParams()->getChatParams()->isEncrypted(); - chatRoom = getCore()->findOneToOneChatRoom(getLocalAddress(), peerAddress, false, false, isEncrypted); + chatRoom = getCore()->getPrivate()->searchChatRoom(getCurrentParams(), getLocalAddress(), peerAddress, {}); if (!chatRoom) { shared_ptr<ConferenceParams> params = ConferenceParams::create(getCore()); + params->setAccount(getCurrentParams()->getAccount()); params->setChatDefaults(); params->setGroup(false); + auto isEncrypted = getCurrentParams()->getChatParams()->isEncrypted(); if (isEncrypted) { // Try to infer chat room type based on requested participants number params->getChatParams()->setBackend(ChatParams::Backend::FlexisipChat); @@ -345,7 +346,7 @@ std::shared_ptr<AbstractChatRoom> ChatRoom::getImdnChatRoom(const std::shared_pt params->getChatParams()->setBackend(ChatParams::Backend::Basic); params->setSecurityLevel(ConferenceParams::SecurityLevel::None); } - chatRoom = getCore()->getPrivate()->createChatRoom(params, getLocalAddress(), peerAddress); + chatRoom = getCore()->getPrivate()->createChatRoom(params, peerAddress); } } return chatRoom; @@ -583,6 +584,9 @@ void ChatRoom::notifyUndecryptableChatMessageReceived(const shared_ptr<ChatMessa } // ----------------------------------------------------------------------------- +void ChatRoom::handleMessageRejected(BCTBX_UNUSED(const std::shared_ptr<ChatMessage> &chatMessage)) { + lDebug() << __func__ << " not implemented in chatroom [" << this; +} std::shared_ptr<ChatMessage> ChatRoom::getMessageFromSal(SalOp *op, const SalMessage *message) { shared_ptr<ChatMessage> msg; @@ -666,10 +670,9 @@ void ChatRoom::onChatMessageReceived(const shared_ptr<ChatMessage> &chatMessage) // No aggregation, notify right away lDebug() << "[Chat Room] [" << getConferenceId() << "] No aggregation, notify right away"; - bool chatMessagesAggregationEnabled = !!linphone_core_get_chat_messages_aggregation_enabled(cCore); - if (chatMessagesAggregationEnabled) { // Need to notify using aggregated callback even if there is only on - // message - // This can happen when auto download is enabled and auto download takes longer than the aggregation delay + // Need to notify using aggregated callback even if there is only on message + // This can happen when auto download is enabled and auto download takes longer than the aggregation delay + if (core->canAggregateChatMessages()) { aggregatedMessages.push_back(chatMessage); notifyAggregatedChatMessages(); } else { @@ -721,10 +724,12 @@ void ChatRoom::notifyAggregatedChatMessages() { bctbx_list_t *cEvents = L_GET_RESOLVED_C_LIST_FROM_CPP_LIST(eventsList); _linphone_chat_room_notify_chat_messages_received(cChatRoom, cEvents); - // Notify delivery + // Notify delivery - do the same things as when chat messages are not aggregated: send the delivery notification + // when the message is in the Delivered state for (auto &chatMessage : aggregatedMessages) { - chatMessage->getPrivate()->setParticipantState(getMe()->getAddress(), ChatMessage::State::DeliveredToUser, + chatMessage->getPrivate()->setParticipantState(getMe()->getAddress(), ChatMessage::State::Delivered, ::ms_time(nullptr)); + sendDeliveryNotification(chatMessage); } bctbx_list_free_with_data(cMessages, (bctbx_list_free_func)linphone_chat_message_unref); @@ -869,6 +874,14 @@ void ChatRoom::deleteFromDb() { shared_ptr<AbstractChatRoom> ref = this->getSharedFromThis(); Core::deleteChatRoom(ref); setState(ConferenceInterface::State::Deleted); + + // Clear all transient events after deleting the chatroom. + // The application might still keep a reference to the chatroom, therefore the destructor may not be called + // immediately after the chatroom reference is freed by the core + remoteIsComposing.clear(); + transientEvents.clear(); + transientMessages.clear(); + aggregatedMessages.clear(); } void ChatRoom::deleteHistory() { @@ -1026,6 +1039,22 @@ std::list<std::shared_ptr<Participant>> ChatRoom::getParticipants() const { return conference->getParticipants(); } +std::list<std::shared_ptr<Address>> ChatRoom::getParticipantAddresses() const { + const auto conference = getConference(); + if (!conference) { + return {}; + } + return conference->getParticipantAddresses(); +} + +std::optional<std::reference_wrapper<const std::string>> ChatRoom::getIdentifier() const { + const auto conference = getConference(); + if (!conference) { + return std::nullopt; + } + return conference->getIdentifier(); +} + std::shared_ptr<ConferenceParams> ChatRoom::getCurrentParams() const { const auto conference = getConference(); if (!conference) { diff --git a/src/chat/chat-room/chat-room.h b/src/chat/chat-room/chat-room.h index 9c4059ac7df07c408eb87feca62b4051bb30ad2c..e7d4c9bd1f043f2ac232d03d32750ed394ecfdfd 100644 --- a/src/chat/chat-room/chat-room.h +++ b/src/chat/chat-room/chat-room.h @@ -47,6 +47,7 @@ public: const std::shared_ptr<Address> &getPeerAddress() const override; const std::shared_ptr<Address> &getLocalAddress() const override; + std::optional<std::reference_wrapper<const std::string>> getIdentifier() const override; time_t getCreationTime() const override; time_t getLastUpdateTime() const override; @@ -136,6 +137,7 @@ public: std::shared_ptr<Address> getConferenceAddress() const override; std::shared_ptr<Participant> findParticipant(const std::shared_ptr<Address> &address) const override; std::list<std::shared_ptr<Participant>> getParticipants() const override; + std::list<std::shared_ptr<Address>> getParticipantAddresses() const override; virtual std::shared_ptr<Conference> getConference() const override; @@ -199,6 +201,7 @@ public: LinphoneReason onSipMessageReceived(SalOp *op, const SalMessage *message) override; void onChatMessageReceived(const std::shared_ptr<ChatMessage> &chatMessage) override; + void handleMessageRejected(const std::shared_ptr<ChatMessage> &chatMessage) override; void onImdnReceived(const std::shared_ptr<ChatMessage> &chatMessage); void onIsComposingReceived(const std::shared_ptr<Address> &remoteAddress, const std::string &text); void onIsComposingRefreshNeeded() override; diff --git a/src/chat/chat-room/client-chat-room.cpp b/src/chat/chat-room/client-chat-room.cpp index e6a963ed32e120650178148a56cc42b170eae382..12b63d281f2cd8bc5bdac5dbdf2f72427d212d8c 100644 --- a/src/chat/chat-room/client-chat-room.cpp +++ b/src/chat/chat-room/client-chat-room.cpp @@ -61,29 +61,6 @@ ClientChatRoom::ClientChatRoom(const shared_ptr<Core> &core, const std::shared_p getCurrentParams()->setSecurityLevel(ConferenceParams::SecurityLevel::EndToEnd); } -ClientChatRoom::ClientChatRoom(const shared_ptr<Core> &core, bool hasBeenLeft) : ChatRoom(core) { - auto chatParams = getCurrentParams()->getChatParams(); - chatParams->setBackend(ChatParams::Backend::FlexisipChat); - if (chatParams->getEphemeralMode() == AbstractChatRoom::EphemeralMode::AdminManaged) { - chatParams->enableEphemeral(chatParams->getEphemeralLifetime() > 0); - } - - if (linphone_core_get_global_state(getCore()->getCCore()) == LinphoneGlobalStartup) { - lDebug() << "Last notify set to [" << getConference()->getLastNotify() << "] for conference [" << this << "]"; - } else { - lInfo() << "Last notify set to [" << getConference()->getLastNotify() << "] for conference [" << this << "]"; - } - - if (!hasBeenLeft) { - getCore()->getPrivate()->clientListEventHandler->addHandler( - static_pointer_cast<ClientConference>(getConference())->eventHandler); - mListHandlerUsed = getCore()->getPrivate()->clientListEventHandler->findHandler(getConferenceId()) != nullptr; - if (!mListHandlerUsed) { - static_pointer_cast<ClientConference>(getConference())->eventHandler->subscribe(getConferenceId()); - } - } -} - // ----------------------------------------------------------------------------- void ClientChatRoom::addPendingMessage(const std::shared_ptr<ChatMessage> &chatMessage) { auto it = std::find(mPendingCreationMessages.begin(), mPendingCreationMessages.end(), chatMessage); @@ -91,18 +68,27 @@ void ClientChatRoom::addPendingMessage(const std::shared_ptr<ChatMessage> &chatM } void ClientChatRoom::onChatRoomCreated(const std::shared_ptr<Address> &remoteContact) { - getConference()->onConferenceCreated(remoteContact); - if (remoteContact->hasParam(Conference::IsFocusParameter)) { - if (!getCore()->getPrivate()->clientListEventHandler->findHandler(getConferenceId())) { - mBgTask.start(getCore(), 32); // It will be stopped when receiving the first notify - auto conference = dynamic_pointer_cast<ClientConference>(getConference()); - auto eventHandler = conference->eventHandler; - if (!eventHandler) { - conference->initializeHandlers(conference.get(), true); - eventHandler = conference->eventHandler; - } - eventHandler->subscribe(getConferenceId()); - } + auto conference = dynamic_pointer_cast<ClientConference>(getConference()); + conference->onConferenceCreated(remoteContact); + if (remoteContact->hasParam(Conference::IsFocusParameter) && + !getCore()->getPrivate()->clientListEventHandler->findHandler(getConferenceId())) { + mBgTask.start(getCore(), 32); // It will be stopped when receiving the first notify + conference->subscribe(true, false); + } +} + +void ClientChatRoom::handleMessageRejected(const std::shared_ptr<ChatMessage> &chatMessage) { + // Only the one-to-one chatroom case is dealt as it has a particular behaviour. + // In fact, when a client deletes a one-to-one chatroom, the server is meant to destroy it on the other side as + // well. If, for watever reason, the BYE is not answered, the chatroom is not destroyed and therefore future message + // may be replied with a 403 Forbidden response. A way to recover it is to initiate the destruction of the chatroom + // and then exhume it + if (!getCurrentParams()->isGroup()) { + lInfo() << "ChatMessage [" << chatMessage << "] could not be sent. Terminating chatroom [" << getConferenceId() + << "] and retrying"; + getConference()->leave(); + setState(ConferenceInterface::State::Terminated); + sendChatMessage(chatMessage); } } @@ -197,21 +183,23 @@ int ClientChatRoom::getHistorySize(HistoryFilterMask filters) const { } void ClientChatRoom::exhume() { + auto conference = getConference(); + auto confId = getConferenceId(); if (getState() != Conference::State::Terminated) { - lError() << "Cannot exhume a non terminated chat room"; + lError() << *conference << ": Cannot exhume non terminated chat room [" << confId << "]"; return; } if (getCurrentParams()->isGroup()) { - lError() << "Cannot exhume a non one-to-one chat room"; + lError() << *conference << ": Cannot exhume non one-to-one chat room [" << confId << "]"; return; } if (getParticipants().size() == 0) { - lError() << "Cannot exhume a chat room without any participant"; + lError() << *conference << ": Cannot exhume chat room [" << confId << "] without any participant"; return; } const std::shared_ptr<Address> &remoteParticipant = getParticipants().front()->getAddress(); - lInfo() << "Exhuming chat room [" << getConferenceId() << "] with participant [" << *remoteParticipant << "]"; + lInfo() << *conference << ": Exhuming chat room [" << confId << "] with participant [" << *remoteParticipant << "]"; mLocalExhumePending = true; auto content = Content::create(); @@ -224,10 +212,9 @@ void ClientChatRoom::exhume() { content->setContentEncoding("deflate"); } - const auto &conferenceFactoryAddress = - Core::getConferenceFactoryAddress(getCore(), getConferenceId().getLocalAddress()); - auto session = static_pointer_cast<ClientConference>(getConference())->createSessionTo(conferenceFactoryAddress); - session->startInvite(nullptr, getConference()->getUtf8Subject(), content); + const auto &conferenceFactoryAddress = Core::getConferenceFactoryAddress(getCore(), confId.getLocalAddress()); + auto session = static_pointer_cast<ClientConference>(conference)->createSessionTo(conferenceFactoryAddress); + session->startInvite(nullptr, conference->getUtf8Subject(), content); setState(ConferenceInterface::State::CreationPending); } @@ -236,7 +223,7 @@ void ClientChatRoom::onExhumedConference(const ConferenceId &oldConfId, const Co auto chatRoom = getCore()->findChatRoom(oldConfId, false); auto conference = getConference(); getCurrentParams()->setConferenceAddress(addr); - auto focus = static_pointer_cast<ClientConference>(getConference())->mFocus; + auto focus = static_pointer_cast<ClientConference>(conference)->mFocus; focus->setAddress(addr); focus->clearDevices(); focus->addDevice(addr); @@ -244,29 +231,23 @@ void ClientChatRoom::onExhumedConference(const ConferenceId &oldConfId, const Co conference->setConferenceId(newConfId); getCore()->getPrivate()->updateChatRoomConferenceId(chatRoom, oldConfId); - getConference()->setLastNotify(0); + conference->resetLastNotify(); } // Will be called on A when A is sending a message into a chat room with B previously terminated by B void ClientChatRoom::onLocallyExhumedConference(const std::shared_ptr<Address> &remoteContact) { + auto conference = dynamic_pointer_cast<ClientConference>(getConference()); ConferenceId oldConfId = getConferenceId(); - ConferenceId newConfId = ConferenceId(remoteContact, oldConfId.getLocalAddress()); + ConferenceId newConfId = + ConferenceId(remoteContact, oldConfId.getLocalAddress(), getCore()->createConferenceIdParams()); - lInfo() << "Conference [" << oldConfId << "] has been locally exhumed into [" << newConfId << "]"; + lInfo() << *conference << ": old conference ID [" << oldConfId << "] has been locally exhumed into [" << newConfId + << "]"; onExhumedConference(oldConfId, newConfId); setState(ConferenceInterface::State::Created); - auto conference = dynamic_pointer_cast<ClientConference>(getConference()); - auto eventHandler = conference->eventHandler; - if (eventHandler) { - eventHandler->unsubscribe(); // Required for next subscribe to be sent - } else { - conference->initializeHandlers(conference.get(), true); - eventHandler = conference->eventHandler; - } - getCore()->getPrivate()->clientListEventHandler->addHandler(eventHandler); - eventHandler->subscribe(getConferenceId()); + conference->subscribe(true); lInfo() << "Found " << mPendingExhumeMessages.size() << " messages waiting for exhume"; for (auto &chatMessage : mPendingExhumeMessages) { @@ -280,20 +261,23 @@ void ClientChatRoom::onLocallyExhumedConference(const std::shared_ptr<Address> & // Will be called on A when B exhumes a chat room previously terminated by B void ClientChatRoom::onRemotelyExhumedConference(SalCallOp *op) { + const auto &conference = static_pointer_cast<ClientConference>(getConference()); ConferenceId oldConfId = getConferenceId(); - ConferenceId newConfId = ConferenceId(Address::create(op->getRemoteContact()), oldConfId.getLocalAddress()); + ConferenceId newConfId = ConferenceId(Address::create(op->getRemoteContact()), oldConfId.getLocalAddress(), + getCore()->createConferenceIdParams()); if (getState() != Conference::State::Terminated) { - lWarning() << "Conference is being exhumed but wasn't terminated first!"; + lWarning() << *conference << " is being exhumed but wasn't terminated first!"; if (oldConfId == newConfId) { - lWarning() << "Conference is being exhumed but with the same conference id " << oldConfId << " !"; + lWarning() << *conference << " is being exhumed but with the same conference id " << oldConfId << " !"; } else { addConferenceIdToPreviousList(oldConfId); } } - lInfo() << "Conference [" << oldConfId << "] is being remotely exhumed into [" << newConfId << "]"; + lInfo() << *conference << ": old conference ID [" << oldConfId << "] is being remotely exhumed into [" << newConfId + << "]"; onExhumedConference(oldConfId, newConfId); @@ -304,15 +288,10 @@ void ClientChatRoom::onRemotelyExhumedConference(SalCallOp *op) { } } - const auto &conference = static_pointer_cast<ClientConference>(getConference()); conference->confirmJoining(op); setState(ConferenceInterface::State::Created); - - auto eventHandler = conference->eventHandler; - eventHandler->unsubscribe(); // Required for next subscribe to be sent - getCore()->getPrivate()->clientListEventHandler->addHandler(eventHandler); - eventHandler->subscribe(getConferenceId()); + conference->subscribe(true); } void ClientChatRoom::removeConferenceIdFromPreviousList(const ConferenceId &confId) { @@ -327,16 +306,15 @@ void ClientChatRoom::addExhumeMessage(const std::shared_ptr<ChatMessage> msg) { void ClientChatRoom::sendChatMessage(const shared_ptr<ChatMessage> &chatMessage) { const auto &conference = getConference(); - const auto &conferenceId = conference->getConferenceId(); const auto &state = getState(); if ((state == ConferenceInterface::State::Terminated) && !getCurrentParams()->isGroup()) { - lInfo() << "Trying to send message into a terminated 1-1 chat room [" << conferenceId << "], exhuming it first"; + lInfo() << *conference << ": Trying to send message into a terminated 1-1 chat room, exhuming it first"; exhume(); addExhumeMessage(chatMessage); } else if (state == ConferenceInterface::State::Instantiated || state == ConferenceInterface::State::CreationPending) { - lInfo() << "Trying to send a message [" << chatMessage << "] in chat room " << this << " [" << conferenceId + lInfo() << *conference << ": Trying to send a message [" << chatMessage << "] in a chat room that's not created yet, queuing the message and it will be sent later"; auto it = std::find(mPendingCreationMessages.begin(), mPendingCreationMessages.end(), chatMessage); if (it == mPendingCreationMessages.end()) addPendingMessage(chatMessage); @@ -344,9 +322,9 @@ void ClientChatRoom::sendChatMessage(const shared_ptr<ChatMessage> &chatMessage) auto encryptionEngine = getCore()->getEncryptionEngine(); if (getCurrentParams()->getChatParams()->isEncrypted() && encryptionEngine && encryptionEngine->participantListRequired() && conference->getParticipantDevices().empty()) { - lInfo() << "Delaying sending of message [" << chatMessage << "] in the encrypted chat room " << this << " [" - << conferenceId - << "] because the list of participant devices has not been received yet and the encryption engine " + lInfo() << *conference << ": Delaying sending of message [" << chatMessage + << "] in an encrypted chat room because the list of participant devices has not been received yet " + "and the encryption engine " << encryptionEngine << " requires it"; auto it = std::find(mPendingCreationMessages.begin(), mPendingCreationMessages.end(), chatMessage); if (it == mPendingCreationMessages.end()) mPendingCreationMessages.push_back(chatMessage); @@ -354,7 +332,8 @@ void ClientChatRoom::sendChatMessage(const shared_ptr<ChatMessage> &chatMessage) ChatRoom::sendChatMessage(chatMessage); } } else { - lError() << "Can't send a chat message in a chat room that is in state " << Utils::toString(state); + lError() << *conference << ": Can't send a chat message in a chat room that is in state " + << Utils::toString(state); chatMessage->getPrivate()->setParticipantState(getMe()->getAddress(), ChatMessage::State::NotDelivered, ::ms_time(nullptr)); } @@ -362,11 +341,11 @@ void ClientChatRoom::sendChatMessage(const shared_ptr<ChatMessage> &chatMessage) // ----------------------------------------------------------------------------- void ClientChatRoom::sendPendingMessages() { - const auto &conferenceId = getConferenceId(); + const auto &conference = getConference(); // Now that chat room has been inserted in database, we can send any pending message for (const auto &message : mPendingCreationMessages) { - lInfo() << "Found message [" << message << "] waiting for chat room " << this << " [" << conferenceId - << "] to be created, sending it now"; + lInfo() << "Found message [" << message << "] waiting for chat room " << *conference + << " to be created, sending it now"; // First we need to update from & to address of the message, // as it was created at a time where the remote address of the chat room may not have been known message->getPrivate()->setChatRoom(getSharedFromThis()); @@ -381,8 +360,8 @@ void ClientChatRoom::sendEphemeralUpdate() { auto focus = conference->mFocus; shared_ptr<MediaSession> session = dynamic_pointer_cast<MediaSession>(focus->getSession()); const std::shared_ptr<Address> &remoteParticipant = getParticipants().front()->getAddress(); - lInfo() << "Re-INVITing " << *remoteParticipant << " because ephemeral settings of chat room [" << getConferenceId() - << "] have changed"; + lInfo() << "Re-INVITing " << *remoteParticipant << " because ephemeral settings of chat room " << *conference + << " have changed"; if (session) { auto csp = session->getMediaParams()->clone(); csp->removeCustomHeader("Ephemeral-Life-Time"); @@ -396,20 +375,21 @@ void ClientChatRoom::sendEphemeralUpdate() { } void ClientChatRoom::setEphemeralMode(AbstractChatRoom::EphemeralMode mode, bool updateDb) { - if (!getConference()->getMe()->isAdmin()) { - lError() << "Only admins can choose who can manage ephemeral messages on chatroom " - << *getConference()->getConferenceAddress(); + auto conference = static_pointer_cast<ClientConference>(getConference()); + if (!conference->getMe()->isAdmin()) { + lError() << *conference << ": Only admins can choose who can manage ephemeral messages"; return; } if (getEphemeralMode() == mode) { - lWarning() << "Ephemeral messages are already managed by " + lWarning() << *conference << ": Ephemeral messages are already managed by " << ((mode == AbstractChatRoom::EphemeralMode::AdminManaged) ? "the admins" : "each participant"); return; } if (!getCurrentParams()->getChatParams()->ephemeralAllowed()) { - lWarning() << "Ephemeral message mode cannot be changed if chatroom has capability Ephemeral disabled"; + lWarning() << *conference + << ": Ephemeral message mode cannot be changed if chatroom has capability Ephemeral disabled"; return; } @@ -418,7 +398,6 @@ void ClientChatRoom::setEphemeralMode(AbstractChatRoom::EphemeralMode mode, bool const auto &lifetime = getEphemeralLifetime(); if (getState() == ConferenceInterface::State::Created) { - auto conference = static_pointer_cast<ClientConference>(getConference()); auto utf8Subject = conference->getUtf8Subject(); auto focus = conference->mFocus; shared_ptr<MediaSession> session = dynamic_pointer_cast<MediaSession>(focus->getSession()); @@ -427,11 +406,12 @@ void ClientChatRoom::setEphemeralMode(AbstractChatRoom::EphemeralMode mode, bool if (mode == AbstractChatRoom::EphemeralMode::AdminManaged) { csp->addCustomHeader("Ephemeral-Life-Time", to_string(lifetime)); } - lInfo() << "Changing ephemeral mode to " << Utils::toString(mode); + lInfo() << *conference << ": Changing ephemeral mode to " << Utils::toString(mode); session->update(csp, CallSession::UpdateMethod::Default, false, utf8Subject); delete csp; } else { - lError() << "Cannot change the ClientConference ephemeral lifetime in a state other than Created"; + lError() << *conference + << ": Cannot change the ClientConference ephemeral lifetime in a state other than Created"; } if (updateDb) { @@ -454,28 +434,27 @@ AbstractChatRoom::EphemeralMode ClientChatRoom::getEphemeralMode() const { } void ClientChatRoom::enableEphemeral(bool ephem, bool updateDb) { + auto conference = getConference(); if (ephemeralEnabled() == ephem) { if (linphone_core_get_global_state(getCore()->getCCore()) != LinphoneGlobalStartup) { - lWarning() << "Ephemeral messages of chat room " << getConferenceId() << " are already " - << (ephem ? "enabled" : "disabled"); + lWarning() << *conference << ": Ephemeral messages are already " << (ephem ? "enabled" : "disabled"); } return; } LinphoneImNotifPolicy *policy = linphone_core_get_im_notif_policy(getCore()->getCCore()); if (!linphone_im_notif_policy_get_send_imdn_displayed(policy) && ephem) { - lWarning() << "Ephemeral messages may not work correctly because IMDN messages are disabled"; + lWarning() << *conference << ": Ephemeral messages may not work correctly because IMDN messages are disabled"; } getCurrentParams()->getChatParams()->enableEphemeral(ephem); const string active = ephem ? "enabled" : "disabled"; - lDebug() << "Ephemeral message is " << active << " in chat room [" << getConferenceId() << "]"; + lDebug() << *conference << ": Ephemeral message is " << active; auto lifetime = getEphemeralLifetime(); if (getEphemeralMode() == AbstractChatRoom::EphemeralMode::AdminManaged) { - if (!getConference()->getMe()->isAdmin()) { - lError() << "Only admins can enable or disable ephemeral messages on chatroom " - << *getConference()->getConferenceAddress(); + if (!conference->getMe()->isAdmin()) { + lError() << *conference << ": Only admins can enable or disable ephemeral messages"; return; } @@ -484,9 +463,8 @@ void ClientChatRoom::enableEphemeral(bool ephem, bool updateDb) { lifetime = linphone_core_get_default_ephemeral_lifetime(getCore()->getCCore()); getCurrentParams()->getChatParams()->setEphemeralLifetime(lifetime); if (updateDb) { - lInfo() << "Reset ephemeral lifetime of chat room " << getConferenceId() - << " to the default value of " << lifetime - << " because ephemeral messages were enabled with a time equal to 0."; + lInfo() << "Reset ephemeral lifetime of chat room " << *conference << " to the default value of " + << lifetime << " because ephemeral messages were enabled with a time equal to 0."; getCore()->getPrivate()->mainDb->updateChatRoomEphemeralLifetime(getConferenceId(), lifetime); shared_ptr<ConferenceEphemeralMessageEvent> event = make_shared<ConferenceEphemeralMessageEvent>( @@ -497,7 +475,8 @@ void ClientChatRoom::enableEphemeral(bool ephem, bool updateDb) { } sendEphemeralUpdate(); } else { - lError() << "Cannot change the ClientConference ephemeral lifetime in a state other than Created"; + lError() << *conference + << ": Cannot change the ClientConference ephemeral lifetime in a state other than Created"; } } @@ -520,10 +499,13 @@ bool ClientChatRoom::ephemeralEnabled() const { } void ClientChatRoom::setEphemeralLifetime(long lifetime, bool updateDb) { + auto conference = getConference(); if (lifetime == getEphemeralLifetime()) { if (updateDb) - lWarning() << "Ephemeral lifetime of chat room " << getConferenceId() - << " will not be changed! Trying to set the same ephemaral lifetime as before : " << lifetime; + lWarning() + << *conference + << ": Ephemeral lifetime will not be changed! Trying to set the same ephemaral lifetime as before : " + << lifetime; return; } @@ -531,23 +513,23 @@ void ClientChatRoom::setEphemeralLifetime(long lifetime, bool updateDb) { (getState() == ConferenceInterface::State::CreationPending)) { // Do not print this log when creating chat room from DB if (updateDb) - lInfo() << "Set new ephemeral lifetime of chat room " << getConferenceId() << " to " << lifetime + lInfo() << *conference << ": Set new ephemeral lifetime to " << lifetime << " while creating the chat room, used to be " << getEphemeralLifetime() << "."; getCurrentParams()->getChatParams()->setEphemeralLifetime(lifetime); return; } if (getEphemeralMode() == AbstractChatRoom::EphemeralMode::AdminManaged) { - if (!getConference()->getMe()->isAdmin()) { - lError() << "Cannot change the ClientConference ephemeral lifetime because I am not admin"; + if (!conference->getMe()->isAdmin()) { + lError() << *conference << ": Cannot change the ClientConference ephemeral lifetime because I am not admin"; return; } const auto &state = getState(); if (state == ConferenceInterface::State::Created) { if (updateDb) - lInfo() << "Set new ephemeral lifetime of chat room " << getConferenceId() << " to " << lifetime - << ", used to be " << getEphemeralLifetime() << "."; + lInfo() << *conference << ": Set new ephemeral lifetime to " << lifetime << ", used to be " + << getEphemeralLifetime() << "."; getCurrentParams()->getChatParams()->setEphemeralLifetime(lifetime); const bool enable = (lifetime != 0); // If only changing the value of the message lifetime @@ -557,12 +539,13 @@ void ClientChatRoom::setEphemeralLifetime(long lifetime, bool updateDb) { enableEphemeral(enable, updateDb); } } else { - lError() << "Cannot change the ephemeral lifetime of chat room " << getConferenceId() << " to " << lifetime + lError() << *conference << ": Cannot change the ephemeral lifetime to " << lifetime << " in a state other than Created - currently it is in state " << Utils::toString(state); } } else { if (updateDb) - lInfo() << "Set new ephemeral lifetime to " << lifetime << ", used to be " << getEphemeralLifetime() << "."; + lInfo() << *conference << ": Set new ephemeral lifetime to " << lifetime << ", used to be " + << getEphemeralLifetime() << "."; getCurrentParams()->getChatParams()->setEphemeralLifetime(lifetime); } diff --git a/src/chat/chat-room/client-chat-room.h b/src/chat/chat-room/client-chat-room.h index a9436968ceb7ca9a4127bbc032de620e6bef7afd..66ecdce5c18e5a406275e5d1f705b4c40e4ba03b 100644 --- a/src/chat/chat-room/client-chat-room.h +++ b/src/chat/chat-room/client-chat-room.h @@ -98,6 +98,7 @@ public: void sendChatMessage(const std::shared_ptr<ChatMessage> &chatMessage) override; virtual void addPendingMessage(const std::shared_ptr<ChatMessage> &chatMessage) override; + virtual void handleMessageRejected(const std::shared_ptr<ChatMessage> &chatMessage) override; void onExhumedConference(const ConferenceId &oldConfId, const ConferenceId &newConfId); void onLocallyExhumedConference(const std::shared_ptr<Address> &remoteContact); @@ -117,9 +118,6 @@ public: private: ClientChatRoom(const std::shared_ptr<Core> &core, const std::shared_ptr<Conference> &conf); - // Create a chat room from the main database. - ClientChatRoom(const std::shared_ptr<Core> &core, bool hasBeenLeft = false); - virtual void sendPendingMessages() override; void sendEphemeralUpdate(); diff --git a/src/chat/chat-room/server-chat-room.cpp b/src/chat/chat-room/server-chat-room.cpp index 3075e28ca7ae043a64ffa3410fce1ecf47b4d6dc..8f8ba863465785743fbe0185562437aa10a147ce 100644 --- a/src/chat/chat-room/server-chat-room.cpp +++ b/src/chat/chat-room/server-chat-room.cpp @@ -90,11 +90,6 @@ void ServerChatRoom::confirmRecreation(SalCallOp *op) { session->redirect(confAddr); } -void ServerChatRoom::subscriptionStateChanged(const shared_ptr<EventSubscribe> &event, - LinphoneSubscriptionState state) { - static_pointer_cast<ServerConference>(getConference())->eventHandler->subscriptionStateChanged(event, state); -} - // ----------------------------------------------------------------------------- LinphoneReason ServerChatRoom::onSipMessageReceived(SalOp *op, const SalMessage *message) { @@ -385,10 +380,12 @@ void ServerChatRoom::sendMessage(BCTBX_UNUSED(const shared_ptr<ServerChatRoom::M bool ServerChatRoom::dispatchMessagesAfterFullState(BCTBX_UNUSED(const shared_ptr<CallSession> &session)) const { #ifdef HAVE_ADVANCED_IM - auto device = getConference()->findInvitedParticipantDevice(session); + const auto &conference = getConference(); + auto device = conference->findInvitedParticipantDevice(session); if (!device) { - lWarning() << "Conference " << *getConference()->getConferenceAddress() - << " dispatchMessagesAfterFullState on unknown device."; + lWarning() << *conference << ": unable to dispatch messages to a device attached to " << *session + << " (local address " << *session->getLocalAddress() << " remote address " + << *session->getRemoteAddress() << ")"; return false; // Assume it is a recent device. } return dispatchMessagesAfterFullState(device); diff --git a/src/chat/chat-room/server-chat-room.h b/src/chat/chat-room/server-chat-room.h index 32d63b21bc737c7bd9dad924db3950d7e65b2fdb..f2cf5eeb01d79c646badc478e54da1d881d0f10a 100644 --- a/src/chat/chat-room/server-chat-room.h +++ b/src/chat/chat-room/server-chat-room.h @@ -96,8 +96,6 @@ public: void confirmCreation(); void confirmRecreation(SalCallOp *op); - void subscriptionStateChanged(const std::shared_ptr<EventSubscribe> &event, LinphoneSubscriptionState state); - LinphoneReason onSipMessageReceived(SalOp *op, const SalMessage *message) override; /*These are the two methods called by the registration subscription module*/ diff --git a/src/chat/encryption/encryption-engine.h b/src/chat/encryption/encryption-engine.h index f4c832376b1e13d5e21ff936830683778a36cb50..0b4ef8c947c5e3f39ab02014cd44ebdb54283eb1 100644 --- a/src/chat/encryption/encryption-engine.h +++ b/src/chat/encryption/encryption-engine.h @@ -125,8 +125,6 @@ public: return nullptr; } - virtual void cleanDb() { - } virtual EngineType getEngineType() { return EngineType::Undefined; } diff --git a/src/chat/encryption/lime-x3dh-encryption-engine.cpp b/src/chat/encryption/lime-x3dh-encryption-engine.cpp index 95ae943c9a57b4183dbb1b447ebd23f94e2b9bae..d53f3c9d8e9d50af9fcec68922439bd5a0f3f0ed 100644 --- a/src/chat/encryption/lime-x3dh-encryption-engine.cpp +++ b/src/chat/encryption/lime-x3dh-encryption-engine.cpp @@ -43,6 +43,7 @@ #include "core/core.h" #include "event-log/conference/conference-security-event.h" #include "factory/factory.h" +#include "http/http-client.h" #include "lime-x3dh-encryption-engine.h" #include "private.h" #include "sqlite3_bctbx_vfs.h" @@ -86,110 +87,9 @@ std::vector<lime::CurveId> parseBaseAlgo(const std::string &csv) { } return algos; } -} // namespace - -struct X3dhServerPostContext { - const lime::limeX3DHServerResponseProcess responseProcess; - const string username; - shared_ptr<Core> core; - X3dhServerPostContext(const lime::limeX3DHServerResponseProcess &response, - const string &username, - shared_ptr<Core> core) - : responseProcess(response), username{username}, core{core} {}; -}; - -void LimeManager::processIoError(void *data, BCTBX_UNUSED(const belle_sip_io_error_event_t *event)) noexcept { - X3dhServerPostContext *userData = static_cast<X3dhServerPostContext *>(data); - try { - (userData->responseProcess)(0, vector<uint8_t>{}); - } catch (const exception &e) { - lError() << "Processing IoError on lime server request triggered an exception: " << e.what(); - } - delete (userData); -} - -void LimeManager::processResponse(void *data, const belle_http_response_event_t *event) noexcept { - X3dhServerPostContext *userData = static_cast<X3dhServerPostContext *>(data); - - if (event->response) { - auto code = belle_http_response_get_status_code(event->response); - belle_sip_message_t *message = BELLE_SIP_MESSAGE(event->response); - auto body = reinterpret_cast<const uint8_t *>(belle_sip_message_get_body(message)); - auto bodySize = belle_sip_message_get_body_size(message); - try { // the response processing might generate an exception, make sure it will not flow up to belle-sip - // otherwise it will cause an abort - (userData->responseProcess)(code, vector<uint8_t>{body, body + bodySize}); - } catch (const exception &e) { - lError() << "Processing lime server response triggered an exception: " << e.what(); - } - } else { - try { - (userData->responseProcess)(0, vector<uint8_t>{}); - } catch (const exception &e) { - lError() << "Processing empty response event on lime server request triggered an exception: " << e.what(); - } - } - delete (userData); -} - -void LimeManager::processAuthRequested(void *data, belle_sip_auth_event_t *event) noexcept { - X3dhServerPostContext *userData = static_cast<X3dhServerPostContext *>(data); - shared_ptr<Core> core = userData->core; - - /* extract username and domain from the GRUU stored in userData->username */ - auto address = Address::create(userData->username); - /* we are not using a From field in the request so our event username and domain is empty, - * load it with the ones stored in userData */ - belle_sip_auth_event_set_username(event, address->getUsername().c_str()); - belle_sip_auth_event_set_domain(event, address->getDomain().c_str()); - - /* Notes: when registering on the Lime server, the user is already registered on the flexisip server - * the requested auth info shall thus be present in linphone core (except if registering methods are differents on - * flexisip and lime server - very unlikely) This request will thus not use the auth requested callback to get the - * information - * - Stored auth information in linphone core are indexed by username/domain */ - linphone_core_fill_belle_sip_auth_event(core->getCCore(), event, address->getUsername().data(), - address->getDomain().data()); -} - -LimeManager::LimeManager(const string &dbAccess, belle_http_provider_t *prov, shared_ptr<Core> core) - : lime::LimeManager( - dbAccess, - [prov, core](const string &url, - const string &from, - const vector<uint8_t> &message, - const lime::limeX3DHServerResponseProcess &responseProcess) { - belle_http_request_listener_callbacks_t cbs = {}; - belle_http_request_listener_t *l; - belle_generic_uri_t *uri; - belle_http_request_t *req; - belle_sip_memory_body_handler_t *bh; - - stringstream userAgent; - userAgent << "Linphone/" << linphone_core_get_version() << " (Lime)" - << " Belle-sip/" << belle_sip_version_to_string(); - - bh = belle_sip_memory_body_handler_new_copy_from_buffer(message.data(), message.size(), NULL, NULL); - uri = belle_generic_uri_parse(url.data()); - req = belle_http_request_create("POST", uri, - belle_http_header_create("User-Agent", userAgent.str().c_str()), - belle_http_header_create("Content-type", "x3dh/octet-stream"), - belle_http_header_create("From", from.data()), NULL); - - belle_sip_message_set_body_handler(BELLE_SIP_MESSAGE(req), BELLE_SIP_BODY_HANDLER(bh)); - cbs.process_response = processResponse; - cbs.process_io_error = processIoError; - cbs.process_auth_requested = processAuthRequested; - X3dhServerPostContext *userData = new X3dhServerPostContext(responseProcess, from, core); - l = belle_http_request_listener_create_from_callbacks(&cbs, userData); - belle_sip_object_data_set(BELLE_SIP_OBJECT(req), "http_request_listener", l, belle_sip_object_unref); - belle_http_provider_send_request(prov, req, l); - }) { -} +} // anonymous namespace -LimeX3dhEncryptionEngine::LimeX3dhEncryptionEngine(const std::string &dbAccess, - belle_http_provider_t *prov, - const shared_ptr<Core> core) +LimeX3dhEncryptionEngine::LimeX3dhEncryptionEngine(const std::string &dbAccess, const shared_ptr<Core> core) : EncryptionEngine(core) { engineType = EncryptionEngine::EngineType::LimeX3dh; // Default curve core config is c25519 - keep it for retrocompatibility @@ -201,13 +101,50 @@ LimeX3dhEncryptionEngine::LimeX3dhEncryptionEngine(const std::string &dbAccess, coreCurve = lime::string2CurveId(curveConfig); lInfo() << "[LIME] instanciate a LimeX3dhEncryption engine " << this << " - default server is [" << core->getX3dhServerUrl() << "] and default curve [" << curveConfig << "] DB path: " << dbAccess; - _dbAccess = dbAccess; std::string dbAccessWithParam = std::string("db=\"").append(dbAccess).append("\" vfs=").append( BCTBX_SQLITE3_VFS); // force sqlite3 to use the bctbx_sqlite3_vfs try { - limeManager = std::make_shared<LimeManager>(dbAccessWithParam, prov, core); + limeManager = std::make_shared<lime::LimeManager>( + dbAccessWithParam, [core](const string &url, const string &from, vector<uint8_t> &&message, + const lime::limeX3DHServerResponseProcess &responseProcess) { + HttpClient &httpClient = core->getHttpClient(); + HttpRequest &request = httpClient.createRequest("POST", url); + request.addHeader("From", from); + // We should not use "From" header but X-Lime-user-identity, switch to it when + // lime-server 1.2.1(released 2024/05) or above is massively deployed + // request.addHeader("X-Lime-user-identity", from); + + /* extract username and domain from the GRUU given in from parameter */ + auto address = Address::create(from); + request.setAuthInfo(address->getUsername(), address->getDomain()); + + request.setBody(Content(ContentType("x3dh/octet-stream"), std::move(message))); + request.execute([responseProcess](const HttpResponse &resp) { + switch (resp.getStatus()) { + case HttpResponse::Status::Valid: + try { + (responseProcess(resp.getHttpStatusCode(), resp.getBody().getBody())); + } catch (const exception &e) { + lError() << "Processing lime server response triggered an exception: " << e.what(); + } + break; + case HttpResponse::Status::InvalidRequest: + case HttpResponse::Status::IOError: + case HttpResponse::Status::Timeout: + try { + (responseProcess)(0, vector<uint8_t>{}); + } catch (const exception &e) { + lError() << "Processing communication error response with lime server " + "request triggered an exception: " + << e.what(); + } + break; + } + }); + }); } catch (const BctbxException &e) { lInfo() << "[LIME] exception at Encryption engine instanciation" << e.what(); + engineType = EncryptionEngine::EngineType::Undefined; } } @@ -294,16 +231,12 @@ ChatMessageModifier::Result LimeX3dhEncryptionEngine::processOutgoingMessage(con } const auto &chatRoomParams = chatRoom->getCurrentParams(); - auto conferenceAddress = chatRoom->getConferenceAddress(); - auto conferenceAddressStr = conferenceAddress ? conferenceAddress->asString() : std::string("sip:"); // Check if chatroom is encrypted or not if (chatRoomParams->getChatParams()->isEncrypted()) { - lInfo() << "[LIME] chatroom " << chatRoom << " (address " << conferenceAddressStr - << ") is encrypted, proceed to encrypt outgoing message"; + lInfo() << "[LIME] " << *chatRoom << " is encrypted, proceed to encrypt outgoing message"; } else { - lInfo() << "[LIME] chatroom " << chatRoom << " (address " << conferenceAddressStr - << ") is not encrypted, no need to encrypt outgoing message"; + lInfo() << "[LIME] " << *chatRoom << " is not encrypted, no need to encrypt outgoing message"; return ChatMessageModifier::Result::Skipped; } @@ -342,7 +275,7 @@ ChatMessageModifier::Result LimeX3dhEncryptionEngine::processOutgoingMessage(con nbDevice++; } else { lWarning() << "Multiple instances of participant device with address " << address - << " have been found in chat room " << conferenceAddressStr; + << " have been found in " << *chatRoom; requestFullState = true; } } @@ -369,7 +302,7 @@ ChatMessageModifier::Result LimeX3dhEncryptionEngine::processOutgoingMessage(con nbDevice++; } else { lWarning() << "Multiple instances of me participant device with address " << address - << " have been found in chat room " << conferenceAddressStr; + << " have been found in " << *chatRoom; requestFullState = true; } } @@ -378,8 +311,7 @@ ChatMessageModifier::Result LimeX3dhEncryptionEngine::processOutgoingMessage(con // Check if there is at least one recipient if (recipientAddresses.empty()) { - lError() << "[LIME] encrypting message on chatroom " << chatRoom << " (address " << conferenceAddressStr - << ") with no recipient"; + lError() << "[LIME] encrypting message on " << *chatRoom << " with no recipient"; errorCode = 488; return ChatMessageModifier::Result::Error; } @@ -428,12 +360,20 @@ ChatMessageModifier::Result LimeX3dhEncryptionEngine::processOutgoingMessage(con // Compress plain text message - compression after encryption is much less efficient // To keep compatibility with version not supporting the encryption at this stage, do it (for now: may 2024) - // only when the only supported algorithm is c25519k512 + // only when all the supported algorithm are in c25519k512, c25519mlk512, c448mlk1024 bool compressedPlain = false; Content plainContent(message->getInternalContent()); const string localDeviceId = localDevice->asStringUriOnly(); if (usersAlgos.find(localDeviceId) != usersAlgos.end()) { - if (usersAlgos[localDeviceId].size() == 1 && usersAlgos[localDeviceId][0] == lime::CurveId::c25519k512) { + bool skipDeflate = false; + for (const auto &algo : usersAlgos[localDeviceId]) { + if (algo < + lime::CurveId::c25519k512) { // enum class is ordered, values under c2519k512 are the older base algo + skipDeflate = true; + break; + } + } + if (!skipDeflate) { compressedPlain = plainContent.deflateBody(); } } @@ -552,9 +492,9 @@ ChatMessageModifier::Result LimeX3dhEncryptionEngine::processIncomingMessage(con // Check if chatroom is encrypted or not const auto &chatRoomParams = chatRoom->getCurrentParams(); if (chatRoomParams->getChatParams()->isEncrypted()) { - lInfo() << "[LIME] this chatroom is encrypted, proceed to decrypt incoming message"; + lInfo() << "[LIME] " << *chatRoom << " is encrypted, proceed to decrypt incoming message"; } else { - lInfo() << "[LIME] this chatroom is not encrypted, no need to decrypt incoming message"; + lInfo() << "[LIME] " << *chatRoom << " is not encrypted, no need to decrypt incoming message"; return ChatMessageModifier::Result::Skipped; } @@ -564,7 +504,7 @@ ChatMessageModifier::Result LimeX3dhEncryptionEngine::processIncomingMessage(con // Check if the message is encrypted and unwrap the multipart if (!isMessageEncrypted(*internalContent)) { - lError() << "[LIME] unexpected content-type: " << internalContent->getContentType(); + lError() << "[LIME] " << *chatRoom << ": unexpected content-type: " << internalContent->getContentType(); // Set unencrypted content warning flag because incoming message type is unexpected message->getPrivate()->setUnencryptedContentWarning(true); // Disable sender authentication otherwise the unexpected message will always be discarded @@ -575,7 +515,7 @@ ChatMessageModifier::Result LimeX3dhEncryptionEngine::processIncomingMessage(con const auto &account = chatRoom->getAccount(); if (!account) { - lWarning() << "Receiving encrypted message with unknown account"; + lWarning() << "[LIME] " << *chatRoom << ": Receiving encrypted message with unknown account"; errorCode = 488; // Not Acceptable return ChatMessageModifier::Result::Error; } @@ -583,7 +523,7 @@ ChatMessageModifier::Result LimeX3dhEncryptionEngine::processIncomingMessage(con const auto &localDevice = accountContactAddress ? accountContactAddress : chatRoom->getConferenceId().getLocalAddress(); if (!localDevice) { - lWarning() << "Receiving encrypted message but the local device address is unknown"; + lWarning() << "[LIME] " << *chatRoom << ": Receiving encrypted message but the local device address is unknown"; errorCode = 488; // Not Acceptable return ChatMessageModifier::Result::Error; } @@ -617,8 +557,7 @@ ChatMessageModifier::Result LimeX3dhEncryptionEngine::processIncomingMessage(con // Early discard of malformed incoming message: we must have a sender Id to decrypt the message if (senderDeviceId.empty()) { - lWarning() << "[LIME] discard malformed incoming message [" << message << "] for [" << localDeviceId - << "]: no sender Device Id found "; + lWarning() << "[LIME] " << *chatRoom << " for [" << localDeviceId << "]: no sender Device Id found "; errorCode = 488; // Not Acceptable return ChatMessageModifier::Result::Error; } @@ -628,7 +567,8 @@ ChatMessageModifier::Result LimeX3dhEncryptionEngine::processIncomingMessage(con if (linphone_config_get_int(linphone_core_get_config(chatRoom->getCore()->getCCore()), "lime", "allow_message_in_unsafe_chatroom", 0) == 0) { if (peerDeviceStatus == lime::PeerDeviceStatus::unsafe) { - lWarning() << "[LIME] discard incoming message from unsafe sender device " << senderDeviceId; + lWarning() << "[LIME] " << *chatRoom << ": discard incoming message from unsafe sender device " + << senderDeviceId; errorCode = 488; // Not Acceptable return ChatMessageModifier::Result::Error; } @@ -661,13 +601,15 @@ ChatMessageModifier::Result LimeX3dhEncryptionEngine::processIncomingMessage(con } if (forceFailure) { - lError() << "No key found (on purpose for tests) for [" << localDeviceId << "] for message [" << message << "]"; + lError() << "[LIME] " << *chatRoom << ": No key found (on purpose for tests) for [" << localDeviceId + << "] for message [" << message << "]"; errorCode = 488; // Not Acceptable return ChatMessageModifier::Result::Error; } if (cipherHeader.empty()) { - lError() << "No key found for [" << localDeviceId << "] for message [" << message << "]"; + lError() << "[LIME] " << *chatRoom << ": No key found for [" << localDeviceId << "] for message [" << message + << "]"; errorCode = 488; // Not Acceptable return ChatMessageModifier::Result::Error; } @@ -682,12 +624,12 @@ ChatMessageModifier::Result LimeX3dhEncryptionEngine::processIncomingMessage(con peerDeviceStatus = limeManager->decrypt(localDeviceId, recipientUserId, senderDeviceId, decodedCipherHeader, decodedCipherMessage, plainMessage); } catch (const exception &e) { - lError() << e.what() << " while decrypting message"; + lError() << "[LIME] " << *chatRoom << ": " << e.what() << " while decrypting message"; peerDeviceStatus = lime::PeerDeviceStatus::fail; } if (peerDeviceStatus == lime::PeerDeviceStatus::fail) { - lError() << "Failed to decrypt message from " << senderDeviceId; + lError() << "[LIME] " << *chatRoom << ": Failed to decrypt message from " << senderDeviceId; errorCode = 488; // Not Acceptable return ChatMessageModifier::Result::Error; } @@ -1177,14 +1119,6 @@ LimeX3dhEncryptionEngine::onDeviceAdded(const std::shared_ptr<Address> &newDevic return securityEvent; } -void LimeX3dhEncryptionEngine::cleanDb() { - remove(_dbAccess.c_str()); -} - -std::shared_ptr<LimeManager> LimeX3dhEncryptionEngine::getLimeManager() { - return limeManager; -} - void LimeX3dhEncryptionEngine::staleSession(const std::string localDeviceId, const std::string peerDeviceId) { try { auto curveIds = usersAlgos.at(localDeviceId); @@ -1354,7 +1288,11 @@ void LimeX3dhEncryptionEngine::createLimeUser(shared_ptr<Account> &account, cons linphone_core_notify_imee_user_registration(lc, TRUE, gruu.data(), info.data()); } } catch (const exception &e) { - lError() << "[LIME] user for id [" << gruu << "] cannot be created" << e.what(); + LinphoneCore *lc = L_GET_C_BACK_PTR(account->getCore()); + lError() << "[LIME] user for id [" << gruu << "] cannot be created: " << e.what(); + account->setLimeUserAccountStatus(LimeUserAccountStatus::LimeUserAccountCreationSkiped); + linphone_core_notify_imee_user_registration(lc, false, gruu.data(), NULL); + return; } } diff --git a/src/chat/encryption/lime-x3dh-encryption-engine.h b/src/chat/encryption/lime-x3dh-encryption-engine.h index 39f1ad87c6860b96528384ab8e9f895b61813fca..8c9ee58bf100e1d6812f21371f7d6726352e1419 100644 --- a/src/chat/encryption/lime-x3dh-encryption-engine.h +++ b/src/chat/encryption/lime-x3dh-encryption-engine.h @@ -31,28 +31,12 @@ LINPHONE_BEGIN_NAMESPACE -class LimeManager : public lime::LimeManager { -public: - LimeManager(const std::string &db_access, - belle_http_provider_t *prov, - std::shared_ptr<Core> core); // LinphoneCore *lc - -private: - static void processIoError(void *data, const belle_sip_io_error_event_t *event) noexcept; - static void processResponse(void *data, const belle_http_response_event_t *event) noexcept; - static void processAuthRequested(void *data, belle_sip_auth_event_t *event) noexcept; -}; - class LimeX3dhEncryptionEngine : public EncryptionEngine, public CoreListener, private LimeX3dhUtils { public: - LimeX3dhEncryptionEngine(const std::string &db_access, - belle_http_provider_t *prov, - const std::shared_ptr<Core> core); + LimeX3dhEncryptionEngine(const std::string &db_access, const std::shared_ptr<Core> core); ~LimeX3dhEncryptionEngine(); - std::shared_ptr<LimeManager> getLimeManager(); - // EncryptionEngine overrides ChatMessageModifier::Result processIncomingMessage(const std::shared_ptr<ChatMessage> &message, @@ -105,7 +89,6 @@ public: AbstractChatRoom::SecurityLevel getSecurityLevel(const std::list<std::string> &deviceIds) const override; EncryptionEngine::EngineType getEngineType() override; std::list<EncryptionParameter> getEncryptionParameters(const std::shared_ptr<Account> &account) override; - void cleanDb() override; // CoreListener overrides @@ -144,9 +127,8 @@ private: void update(const std::string localDeviceId); std::vector<lime::CurveId> getAllConfiguredAlgos(); /**< return a vector with all supported algorithm - from all users, in no specific order */ - std::shared_ptr<LimeManager> + std::shared_ptr<lime::LimeManager> limeManager; /**< the actual lime manager - only one is intanciated, it will manage all lime users */ - std::string _dbAccess; /**< lime DB path */ lime::CurveId coreCurve; /**< default base algo setting found in core, this is deprecated so it can store one algorithm only */ std::unordered_map<std::string, std::vector<lime::CurveId>> diff --git a/src/chat/encryption/lime-x3dh-server-engine.cpp b/src/chat/encryption/lime-x3dh-server-engine.cpp index 820b0efbfe71f58838892232174ed6a084bbb479..65cc47e03ffee2922fa9c0bbf33ef8e7e752740d 100644 --- a/src/chat/encryption/lime-x3dh-server-engine.cpp +++ b/src/chat/encryption/lime-x3dh-server-engine.cpp @@ -63,9 +63,9 @@ LimeX3dhEncryptionServerEngine::processOutgoingMessage(const std::shared_ptr<Cha // Check if chatroom is encrypted or not const auto &chatRoomParams = chatRoom->getCurrentParams(); if (chatRoomParams->getChatParams()->isEncrypted()) { - lInfo() << "[LIME][server] this chatroom is encrypted, proceed to encrypt outgoing message"; + lInfo() << "[LIME][server] " << *chatRoom << " is encrypted, proceed to encrypt outgoing message"; } else { - lInfo() << "[LIME][server] this chatroom is not encrypted, no need to encrypt outgoing message"; + lInfo() << "[LIME][server] " << *chatRoom << " is not encrypted, no need to encrypt outgoing message"; return ChatMessageModifier::Result::Skipped; } @@ -92,7 +92,8 @@ LimeX3dhEncryptionServerEngine::processOutgoingMessage(const std::shared_ptr<Cha } if (!hasKey) { - lError() << "[LIME][server] this message doesn't contain the cipher key for participant " << toDeviceId; + lError() << "[LIME][server] " << *chatRoom << ": message [" << message + << "] doesn't contain the cipher key for participant " << toDeviceId; return ChatMessageModifier::Result::Error; } @@ -105,7 +106,9 @@ LimeX3dhEncryptionServerEngine::processOutgoingMessage(const std::shared_ptr<Cha if (linphone_core_content_encoding_supported(message->getChatRoom()->getCore()->getCCore(), "deflate")) { finalContent.setContentEncoding("deflate"); } else { - lWarning() << "Cannot use 'deflate' Content-Encoding to compress body - consider rebuilding with libz support."; + lWarning() + << "[LIME][server] " << *chatRoom + << ": Cannot use 'deflate' Content-Encoding to compress body - consider rebuilding with libz support."; } message->setInternalContent(finalContent); return ChatMessageModifier::Result::Done; diff --git a/src/chat/modifier/cpim-chat-message-modifier.cpp b/src/chat/modifier/cpim-chat-message-modifier.cpp index d135445de005fd52eac8ca39d6230f1d1ce6304a..88d21220d7e7c2a5bb060078c3693a36e6098b7a 100644 --- a/src/chat/modifier/cpim-chat-message-modifier.cpp +++ b/src/chat/modifier/cpim-chat-message-modifier.cpp @@ -59,11 +59,29 @@ const string imdnDispositionNotificationHeader = "Disposition-Notification"; ChatMessageModifier::Result CpimChatMessageModifier::encode(const shared_ptr<ChatMessage> &message, BCTBX_UNUSED(int &errorCode)) { Cpim::Message cpimMessage; - - cpimMessage.addMessageHeader( - Cpim::FromHeader(cpimAddressUri(message->getFromAddress()), cpimAddressDisplayName(message->getFromAddress()))); - cpimMessage.addMessageHeader( - Cpim::ToHeader(cpimAddressUri(message->getToAddress()), cpimAddressDisplayName(message->getToAddress()))); + shared_ptr<AbstractChatRoom> chatRoom = message->getChatRoom(); + const auto &account = chatRoom->getAccount(); + if (!account) { + lWarning() << "Unable to encode CPIM because the account attached to the message is unknown"; + return ChatMessageModifier::Result::Error; + } + const bool isBasicChatRoom = + (chatRoom->getCurrentParams()->getChatParams()->getBackend() == ChatParams::Backend::Basic); + std::shared_ptr<Address> localDevice; + // For non-basic tchat, prefer using the account contact address in case the message from address is the account's + // identity address. It will help the server to correctly dispatch requests to the other participants and sender + // devices + if (!isBasicChatRoom) { + localDevice = account->getContactAddress(); + } + if (!localDevice) { + localDevice = message->getFromAddress(); + } + localDevice = localDevice->clone()->toSharedPtr(); + localDevice->setDisplayName(""); + cpimMessage.addMessageHeader(Cpim::FromHeader(cpimAddressUri(localDevice), cpimAddressDisplayName(localDevice))); + const auto &to = message->getToAddress(); + cpimMessage.addMessageHeader(Cpim::ToHeader(cpimAddressUri(to), cpimAddressDisplayName(to))); cpimMessage.addMessageHeader(Cpim::DateTimeHeader(message->getTime())); bool linphoneNamespaceHeaderSet = false; @@ -313,7 +331,8 @@ CpimChatMessageModifier::createMinimalCpimContentForLimeMessage(const shared_ptr const auto &localDevice = accountContactAddress ? accountContactAddress : chatRoom->getConferenceId().getLocalAddress(); if (!localDevice) { - lWarning() << "Unable to create CPIM because account [" << account << "] has not registered yet and the chatroom local address is unknown"; + lWarning() << "Unable to create CPIM because " << *account + << " has not registered yet and the chatroom local address is unknown"; return Content::create(); } Cpim::Message cpimMessage; diff --git a/src/chat/modifier/file-transfer-chat-message-modifier.cpp b/src/chat/modifier/file-transfer-chat-message-modifier.cpp index 258f53185787171ffc8000a40c3a4ba6efa0da8f..fb735584c5b7c00e465747616cedca002deb7a6f 100644 --- a/src/chat/modifier/file-transfer-chat-message-modifier.cpp +++ b/src/chat/modifier/file-transfer-chat-message-modifier.cpp @@ -620,6 +620,7 @@ ChatMessageModifier::Result FileTransferChatMessageModifier::decode(const shared fileTransferContent->setBody(internalContent.getBody()); string xml_body = fileTransferContent->getBodyAsUtf8String(); parseFileTransferXmlIntoContent(xml_body.c_str(), fileTransferContent); + fileTransferContent->setRelatedChatMessageId(message->getImdnMessageId()); message->addContent(fileTransferContent); return ChatMessageModifier::Result::Done; } @@ -629,6 +630,7 @@ ChatMessageModifier::Result FileTransferChatMessageModifier::decode(const shared auto fileTransferContent = static_pointer_cast<FileTransferContent>(content); string xml_body = fileTransferContent->getBodyAsUtf8String(); parseFileTransferXmlIntoContent(xml_body.c_str(), fileTransferContent); + fileTransferContent->setRelatedChatMessageId(message->getImdnMessageId()); } } return ChatMessageModifier::Result::Done; @@ -881,6 +883,7 @@ void FileTransferChatMessageModifier::processResponseHeadersFromGetFile(const be } else { lWarning() << "No file transfer information for message [" << message << "]: creating..."; auto content = createFileTransferInformationFromHeaders(response); + content->setRelatedChatMessageId(message->getImdnMessageId()); message->addContent(content); } @@ -993,6 +996,7 @@ static void createFileContentFromFileTransferContent(std::shared_ptr<FileTransfe fileContent->setFilePath(fileTransferContent->getFilePath()); fileContent->setContentType(fileTransferContent->getFileContentType()); fileContent->setFileDuration(fileTransferContent->getFileDuration()); + fileContent->setRelatedChatMessageId(fileTransferContent->getRelatedChatMessageId()); // Link the FileContent to the FileTransferContent fileTransferContent->setFileContent(fileContent); diff --git a/src/chat/modifier/multipart-chat-message-modifier.cpp b/src/chat/modifier/multipart-chat-message-modifier.cpp index 2536c34cd3287da0649ab8a87bd584bb4b109b3f..863a327750a7e3dc27a9c8a06704352d6b34b08e 100644 --- a/src/chat/modifier/multipart-chat-message-modifier.cpp +++ b/src/chat/modifier/multipart-chat-message-modifier.cpp @@ -64,6 +64,7 @@ ChatMessageModifier::Result MultipartChatMessageModifier::decode(const shared_pt } else { content = Content::create(c); } + content->setRelatedChatMessageId(message->getImdnMessageId()); message->addContent(content); } return ChatMessageModifier::Result::Done; diff --git a/src/conference/ccmp-conference-scheduler.cpp b/src/conference/ccmp-conference-scheduler.cpp index 4568bc72170a7901b824d9a932b0b7a9bf741568..d7f60199651738b1f31d7fff7adb37c0ecfff6ee 100644 --- a/src/conference/ccmp-conference-scheduler.cpp +++ b/src/conference/ccmp-conference-scheduler.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2024 Belledonne Communications SARL. + * Copyright (c) 2010-2025 Belledonne Communications SARL. * * This file is part of Liblinphone * (see https://gitlab.linphone.org/BC/public/liblinphone). @@ -18,11 +18,11 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include <bctoolbox/defs.h> +#include "conference/ccmp-conference-scheduler.h" -#include <belle-sip/http-message.h> +#include "bctoolbox/defs.h" +#include "belle-sip/http-message.h" -#include "conference/ccmp-conference-scheduler.h" #include "conference/conference.h" #include "conference/handlers/server-conference-event-handler.h" #include "conference/participant-info.h" @@ -66,10 +66,25 @@ CCMPConferenceScheduler::CCMPConferenceScheduler(const shared_ptr<Core> &core, c } } -void CCMPConferenceScheduler::createOrUpdateConference(const std::shared_ptr<ConferenceInfo> &conferenceInfo, - const std::shared_ptr<Address> &creator) { +void CCMPConferenceScheduler::handleCCMPResponse(const HttpResponse &response) { + switch (response.getStatus()) { + case HttpResponse::Status::Valid: + handleResponse(this, response); + break; + case HttpResponse::Status::IOError: + case HttpResponse::Status::InvalidRequest: + handleIoError(this, response); + break; + case HttpResponse::Status::Timeout: + handleTimeout(this, response); + break; + } +} + +void CCMPConferenceScheduler::createOrUpdateConference(const std::shared_ptr<ConferenceInfo> &conferenceInfo) { const auto &account = getAccount() ? getAccount() : getCore()->getDefaultAccount(); const auto accountParams = account ? account->getAccountParams() : nullptr; + const auto &creator = accountParams->getIdentityAddress(); if (!accountParams) { lError() << "Aborting creation of conference because the account of conference scheduler [" << this @@ -210,16 +225,8 @@ void CCMPConferenceScheduler::createOrUpdateConference(const std::shared_ptr<Con serializeCcmpRequest(httpBody, requestBody, map); const auto body = httpBody.str(); - belle_http_request_listener_callbacks_t internalCallbacks = {}; - internalCallbacks.process_response = CCMPConferenceScheduler::handleResponse; - internalCallbacks.process_io_error = CCMPConferenceScheduler::handleIoError; - internalCallbacks.process_timeout = CCMPConferenceScheduler::handleTimeout; - internalCallbacks.process_auth_requested = CCMPConferenceScheduler::handleAuthRequested; - - belle_http_request_listener_t *listener = - belle_http_request_listener_create_from_callbacks(&internalCallbacks, this); - - if (!XmlUtils::sendCcmpRequest(getCore(), ccmpServerUrl, from, body, listener)) { + if (!XmlUtils::sendCcmpRequest(getCore(), ccmpServerUrl, from, body, + [this](const HttpResponse &response) { this->handleCCMPResponse(response); })) { lError() << "An error occurred when sending the HTTP request of CCMPConferenceScheduler [" << this << "] to server " << ccmpServerUrl; } @@ -244,66 +251,62 @@ void CCMPConferenceScheduler::processResponse(const LinphoneErrorInfo *errorInfo } } -void CCMPConferenceScheduler::handleResponse(void *ctx, const belle_http_response_event_t *event) { - if (event->response) { - auto ccmpScheduler = static_cast<CCMPConferenceScheduler *>(ctx); - LinphoneErrorInfo *ei = linphone_error_info_new(); - int code = belle_http_response_get_status_code(event->response); - std::shared_ptr<Address> conferenceAddress; - if (code >= 200 && code < 300) { - belle_sip_body_handler_t *body = belle_sip_message_get_body_handler(BELLE_SIP_MESSAGE(event->response)); - char *content = belle_sip_object_to_string(body); - if (content) { - try { - istringstream data(content); - auto responseType = parseCcmpResponse(data, Xsd::XmlSchema::Flags::dont_validate); - ms_free(content); - CcmpConfResponseMessageType &response = - dynamic_cast<CcmpConfResponseMessageType &>(responseType->getCcmpResponse()); - const auto responseCodeType = response.getResponseCode(); - code = static_cast<int>(responseCodeType); - auto confObjId = response.getConfObjID(); - if (confObjId.present()) { - ccmpScheduler->setCcmpUri(confObjId.get()); - } else { - lError() << "Unable to create conference with conference scheduler [" << ccmpScheduler - << "] as its address is unknown"; - throw std::runtime_error("undefined conference address"); - } - auto &confResponse = response.getConfResponse(); - auto &confInfo = confResponse.getConfInfo(); - if (confInfo.present()) { - auto confDescription = confInfo.get().getConferenceDescription(); - if (confDescription.present()) { - auto confUris = confDescription.get().getConfUris(); - if (confUris.present()) { - auto uriEntry = confUris.get().getEntry(); - if (uriEntry.size() > 1) { - throw std::invalid_argument("Multiple conference addresses received"); - } - conferenceAddress = Address::create(uriEntry.front().getUri()); +void CCMPConferenceScheduler::handleResponse(void *ctx, const HttpResponse &event) { + auto ccmpScheduler = static_cast<CCMPConferenceScheduler *>(ctx); + LinphoneErrorInfo *ei = linphone_error_info_new(); + int code = event.getHttpStatusCode(); + std::shared_ptr<Address> conferenceAddress; + if (code >= 200 && code < 300) { + const auto &body = event.getBody(); + auto content = body.getBodyAsString(); + if (!content.empty()) { + try { + istringstream data(content); + auto responseType = parseCcmpResponse(data, Xsd::XmlSchema::Flags::dont_validate); + auto &response = dynamic_cast<CcmpConfResponseMessageType &>(responseType->getCcmpResponse()); + const auto responseCodeType = response.getResponseCode(); + code = static_cast<int>(responseCodeType); + auto confObjId = response.getConfObjID(); + if (confObjId.present()) { + ccmpScheduler->setCcmpUri(confObjId.get()); + } else { + lError() << "Unable to create conference with conference scheduler [" << ccmpScheduler + << "] as its address is unknown"; + throw std::runtime_error("undefined conference address"); + } + auto &confResponse = response.getConfResponse(); + auto &confInfo = confResponse.getConfInfo(); + if (confInfo.present()) { + auto confDescription = confInfo.get().getConferenceDescription(); + if (confDescription.present()) { + auto confUris = confDescription.get().getConfUris(); + if (confUris.present()) { + auto uriEntry = confUris.get().getEntry(); + if (uriEntry.size() > 1) { + throw std::invalid_argument("Multiple conference addresses received"); } + conferenceAddress = Address::create(uriEntry.front().getUri()); } } - } catch (const std::bad_cast &e) { - lError() << "Error while casting parsed CCMP response in conference scheduler [" << ccmpScheduler - << "] " << e.what(); - code = 400; - } catch (const exception &e) { - lError() << "Error while parsing CCMP response in conference scheduler [" << ccmpScheduler << "] " - << e.what(); - code = 400; } + } catch (const std::bad_cast &e) { + lError() << "Error while casting parsed CCMP response in conference scheduler [" << ccmpScheduler + << "] " << e.what(); + code = 400; + } catch (const exception &e) { + lError() << "Error while parsing CCMP response in conference scheduler [" << ccmpScheduler << "] " + << e.what(); + code = 400; } } - LinphoneReason reason = linphone_error_code_to_reason(code); - linphone_error_info_set(ei, nullptr, reason, code, nullptr, nullptr); - ccmpScheduler->processResponse(ei, conferenceAddress); - linphone_error_info_unref(ei); } + LinphoneReason reason = linphone_error_code_to_reason(code); + linphone_error_info_set(ei, nullptr, reason, code, nullptr, nullptr); + ccmpScheduler->processResponse(ei, conferenceAddress); + linphone_error_info_unref(ei); } -void CCMPConferenceScheduler::handleIoError(void *ctx, BCTBX_UNUSED(const belle_sip_io_error_event_t *event)) { +void CCMPConferenceScheduler::handleIoError(void *ctx, BCTBX_UNUSED(const HttpResponse &event)) { LinphoneErrorInfo *ei = linphone_error_info_new(); int code = 503; LinphoneReason reason = linphone_error_code_to_reason(code); @@ -313,7 +316,7 @@ void CCMPConferenceScheduler::handleIoError(void *ctx, BCTBX_UNUSED(const belle_ linphone_error_info_unref(ei); } -void CCMPConferenceScheduler::handleTimeout(void *ctx, BCTBX_UNUSED(const belle_sip_timeout_event_t *event)) { +void CCMPConferenceScheduler::handleTimeout(void *ctx, BCTBX_UNUSED(const HttpResponse &event)) { LinphoneErrorInfo *ei = linphone_error_info_new(); int code = 504; LinphoneReason reason = linphone_error_code_to_reason(code); @@ -323,11 +326,4 @@ void CCMPConferenceScheduler::handleTimeout(void *ctx, BCTBX_UNUSED(const belle_ linphone_error_info_unref(ei); } -void CCMPConferenceScheduler::handleAuthRequested(void *ctx, belle_sip_auth_event_t *event) { - const char *username = belle_sip_auth_event_get_username(event); - const char *domain = belle_sip_auth_event_get_domain(event); - auto ccmpScheduler = static_cast<CCMPConferenceScheduler *>(ctx); - linphone_core_fill_belle_sip_auth_event(ccmpScheduler->getCore()->getCCore(), event, username, domain); -} - LINPHONE_END_NAMESPACE diff --git a/src/conference/ccmp-conference-scheduler.h b/src/conference/ccmp-conference-scheduler.h index b7b9c52608b4c56ce833f1f222f48da6782582d6..de55fee907417e1417918f8e6b7208e151641dfe 100644 --- a/src/conference/ccmp-conference-scheduler.h +++ b/src/conference/ccmp-conference-scheduler.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2024 Belledonne Communications SARL. + * Copyright (c) 2010-2025 Belledonne Communications SARL. * * This file is part of Liblinphone * (see https://gitlab.linphone.org/BC/public/liblinphone). @@ -22,6 +22,7 @@ #define _L_CCMP_CONFERENCE_SCHEDULER_H_ #include "conference/conference-scheduler.h" +#include "http/http-client.h" // ============================================================================= @@ -32,18 +33,18 @@ public: CCMPConferenceScheduler(const std::shared_ptr<Core> &core, const std::shared_ptr<Account> &account = nullptr); virtual ~CCMPConferenceScheduler() = default; - virtual void createOrUpdateConference(const std::shared_ptr<ConferenceInfo> &conferenceInfo, - const std::shared_ptr<Address> &creator) override; + virtual void createOrUpdateConference(const std::shared_ptr<ConferenceInfo> &conferenceInfo) override; virtual void processResponse(const LinphoneErrorInfo *errorInfo, const std::shared_ptr<Address> conferenceAddress) override; void setCcmpUri(const std::string &ccmpUri); - static void handleResponse(void *ctx, const belle_http_response_event_t *event); - static void handleAuthRequested(void *ctx, belle_sip_auth_event_t *event); - static void handleTimeout(void *ctx, const belle_sip_timeout_event_t *event); - static void handleIoError(void *ctx, const belle_sip_io_error_event_t *event); + void handleCCMPResponse(const HttpResponse &response); + + static void handleResponse(void *ctx, const HttpResponse &event); + static void handleTimeout(void *ctx, const HttpResponse &event); + static void handleIoError(void *ctx, const HttpResponse &event); private: }; diff --git a/src/conference/client-conference.cpp b/src/conference/client-conference.cpp index cede57fe8745cf5d08a41d3523332edb0e54ed6d..0b1169d2fdba32abf5753761407b64a65e067e4a 100644 --- a/src/conference/client-conference.cpp +++ b/src/conference/client-conference.cpp @@ -53,27 +53,31 @@ using namespace std; LINPHONE_BEGIN_NAMESPACE ClientConference::ClientConference(const shared_ptr<Core> &core, - const std::shared_ptr<Address> &myAddress, std::shared_ptr<CallSessionListener> listener, const std::shared_ptr<const ConferenceParams> params) - : Conference(core, myAddress, listener, params) { + : Conference(core, listener, params) { } ClientConference::~ClientConference() { - terminate(); + if (getState() != ConferenceInterface::State::Deleted) { + terminate(); + } + if (mJoiningParams) { + delete mJoiningParams; + } #ifdef HAVE_ADVANCED_IM - eventHandler.reset(); + mEventHandler.reset(); #endif // HAVE_ADVANCED_IM } -void ClientConference::createFocus(const std::shared_ptr<Address> focusAddr, +void ClientConference::createFocus(const std::shared_ptr<const Address> &focusAddr, const std::shared_ptr<CallSession> focusSession) { mFocus = Participant::create(getSharedFromThis(), focusAddr, focusSession); if (focusSession) { focusSession->addListener(getSharedFromThis()); } - lInfo() << "Create focus '" << *mFocus->getAddress() << "' from address : " << *focusAddr; + lInfo() << *this << ": Create focus '" << *mFocus->getAddress() << "' from address : " << *focusAddr; } void ClientConference::configure(SalCallOp *op) { @@ -95,7 +99,7 @@ void ClientConference::initFromDb(const std::shared_ptr<Participant> &me, bool hasBeenLeft) { const auto &conferenceAddress = mConfParams->getConferenceAddress(); createFocus(conferenceAddress, nullptr); - mMe = Participant::create(getSharedFromThis(), mConfParams->getMe()); + mMe = Participant::create(getSharedFromThis(), me->getAddress()); mMe->setAdmin(me->isAdmin()); for (const auto &device : me->getDevices()) { mMe->addDevice(device); @@ -120,12 +124,13 @@ void ClientConference::initFromDb(const std::shared_ptr<Participant> &me, } if (!hasBeenLeft) { - initializeHandlers(this, true); + // No need to add a conference with media to the client conference list event handler + initializeHandlers(this, isChatOnly()); } } -void ClientConference::initWithFocus(const std::shared_ptr<Address> focusAddr, - const std::shared_ptr<CallSession> focusSession, +void ClientConference::initWithFocus(const std::shared_ptr<const Address> &focusAddr, + const std::shared_ptr<CallSession> &focusSession, SalCallOp *op, ConferenceListener *confListener) { createFocus(focusAddr, focusSession); @@ -137,11 +142,11 @@ void ClientConference::init(SalCallOp *op, BCTBX_UNUSED(ConferenceListener *conf // Local conference sets last notify to 1 in its constructor setLastNotify(0); - const auto &meAddress = mConfParams->getMe(); - mMe = Participant::create(getSharedFromThis(), meAddress); + inititializeMe(); + + const auto &core = getCore(); std::shared_ptr<Address> organizerAddress = nullptr; auto conferenceAddress = mFocus ? mFocus->getAddress() : nullptr; - const auto &core = getCore(); std::shared_ptr<ConferenceInfo> conferenceInfo = nullptr; #ifdef HAVE_DB_STORAGE if (conferenceAddress && core->getPrivate()->mainDb && supportsMedia()) { @@ -191,19 +196,20 @@ void ClientConference::init(SalCallOp *op, BCTBX_UNUSED(ConferenceListener *conf mPendingSubject = mConfParams->getUtf8Subject(); mConfParams->enableLocalParticipant(false); - if (mConfParams->chatEnabled()) { #ifdef HAVE_ADVANCED_IM + if (isChatOnly()) { setChatRoom((new ClientChatRoom(core, getSharedFromThis()))->toSharedPtr()); -#endif // HAVE_ADVANCED_IM } +#endif // HAVE_ADVANCED_IM + const auto &meAddress = mMe->getAddress(); if (supportsMedia()) { getMe()->setAdmin((focusSession == nullptr) || (organizerAddress == nullptr) || organizerAddress->weakEqual(*meAddress)); } if (focusSession || conferenceInfo) { - ConferenceId conferenceId(conferenceAddress, meAddress); + ConferenceId conferenceId(conferenceAddress, meAddress, getCore()->createConferenceIdParams()); setConferenceId(conferenceId); } @@ -213,7 +219,7 @@ void ClientConference::init(SalCallOp *op, BCTBX_UNUSED(ConferenceListener *conf setConferenceAddress(conferenceAddress); } - if (focusSession || mConfParams->chatEnabled()) { + if (focusSession || isChatOnly()) { finalizeCreation(); } } @@ -224,15 +230,16 @@ void ClientConference::createEventHandler(BCTBX_UNUSED(ConferenceListener *confL bool eventLogEnabled = !!linphone_config_get_bool(linphone_core_get_config(getCore()->getCCore()), "misc", "conference_event_log_enabled", TRUE); if (eventLogEnabled) { - if (!eventHandler) { - eventHandler = std::make_shared<ClientConferenceEventHandler>(getCore(), getSharedFromThis(), confListener); + if (!mEventHandler) { + mEventHandler = + std::make_shared<ClientConferenceEventHandler>(getCore(), getSharedFromThis(), confListener); } if (addToListEventHandler) { - getCore()->getPrivate()->clientListEventHandler->addHandler(eventHandler); + getCore()->getPrivate()->clientListEventHandler->addHandler(mEventHandler); } const auto &conferenceId = getConferenceId(); if (conferenceId.isValid()) { - eventHandler->subscribe(conferenceId); + mEventHandler->subscribe(conferenceId); } } else { #endif // HAVE_ADVANCED_IM @@ -289,7 +296,7 @@ std::shared_ptr<ConferenceInfo> ClientConference::createConferenceInfo() const { return createConferenceInfoWithCustomParticipantList(organizer, getFullParticipantList()); } -std::shared_ptr<CallSession> ClientConference::createSessionTo(const std::shared_ptr<Address> &sessionTo) { +std::shared_ptr<CallSession> ClientConference::createSessionTo(const std::shared_ptr<const Address> &sessionTo) { MediaSessionParams csp; csp.addCustomHeader("Require", "recipient-list-invite"); csp.addCustomContactParameter(Conference::TextParameter); @@ -306,6 +313,7 @@ std::shared_ptr<CallSession> ClientConference::createSessionTo(const std::shared csp.enableAudio(mConfParams->audioEnabled()); csp.enableVideo(mConfParams->videoEnabled()); + csp.getPrivate()->disableRinging(!supportsMedia()); csp.getPrivate()->enableToneIndications(supportsMedia()); auto chatRoom = getChatRoom(); @@ -355,8 +363,8 @@ void ClientConference::confirmJoining(BCTBX_UNUSED(SalCallOp *op)) { if (focusOp) { auto focusOpFrom = Address::create(focusOp->getFrom()); auto focusOpTo = Address::create(focusOp->getTo()); - lInfo() << "Releasing focus session " << focusSession << " (from: " << *focusOpFrom << " to " << *focusOpTo - << ")"; + lInfo() << *this << ": Releasing focus session " << focusSession << " (from: " << *focusOpFrom << " to " + << *focusOpTo << ")"; focusOp->terminate(); focusOp->release(); } @@ -426,13 +434,20 @@ void ClientConference::setConferenceId(const ConferenceId &conferenceId) { if (mFocus) { shared_ptr<CallSession> session = mFocus->getSession(); if (session) { - shared_ptr<CallLog> sessionLog = session->getLog(); + std::shared_ptr<Address> address; if (conferenceId.getPeerAddress()->isValid()) { - // Use the peer address of the conference ID because it has also the conf-id param hence the To field - // can be used to search in the map of chat rooms - sessionLog->setToAddress(conferenceId.getPeerAddress()); + // Use the peer address of the conference ID because it has also the conf-id param hence the To or From + // field can be used to search in the map of conferences or chat rooms + address = conferenceId.getPeerAddress(); + } else { + address = mFocus->getAddress(); + } + const auto &direction = session->getDirection(); + auto sessionLog = session->getLog(); + if (direction == LinphoneCallIncoming) { + sessionLog->setFromAddress(address); } else { - sessionLog->setToAddress(mFocus->getAddress()); + sessionLog->setToAddress(address); } } } @@ -444,7 +459,7 @@ int ClientConference::startRecording(const std::string &path) { session->setRecordPath(path); session->startRecording(); } else { - lError() << "ClientConference::startRecording(): no audio session."; + lError() << *this << ": ClientConference::startRecording(): no audio session."; return -1; } return 0; @@ -464,7 +479,8 @@ bool ClientConference::isIn() const { void ClientConference::setUtf8Subject(const std::string &subject) { if (!getMe()->isAdmin()) { - lError() << "Unable to update conference subject because focus " << *getMe()->getAddress() << " is not admin"; + lError() << *this << ": Unable to update conference subject because focus " << *getMe()->getAddress() + << " is not admin"; return; } auto session = dynamic_pointer_cast<MediaSession>(getMainSession()); @@ -493,14 +509,15 @@ void ClientConference::setUtf8Subject(const std::string &subject) { } } } else if (!supportsMedia()) { - lInfo() << "Sending re-INVITE to update subject from \"" << getUtf8Subject() << "\" to \"" << subject << "\""; + lInfo() << *this << ": Sending re-INVITE to update subject from \"" << getUtf8Subject() << "\" to \"" << subject + << "\""; session = dynamic_pointer_cast<MediaSession>(createSession()); if (session) { session->startInvite(nullptr, subject, nullptr); } } else { mPendingSubject = subject; - lInfo() << "Unable to update subject to \"" << subject + lInfo() << *this << ": Unable to update subject to \"" << subject << "\" right now because the focus session has not been established yet."; } } @@ -511,7 +528,7 @@ bool ClientConference::update(const ConferenceParamsInterface &newParameters) { if (getMe()->isAdmin()) { ret = Conference::update(newParameters); } else { - lError() << "Unable to update conference parameters because focus " << getMe()->getAddress()->toString() + lError() << *this << " Unable to update conference parameters because focus " << *getMe()->getAddress() << " is not admin"; } return ret; @@ -520,7 +537,8 @@ bool ClientConference::update(const ConferenceParamsInterface &newParameters) { std::shared_ptr<Call> ClientConference::getCall() const { auto session = getMainSession(); if (session) { - return getCore()->getCallByRemoteAddress(session->getRemoteAddress()); + auto op = session->getPrivate()->getOp(); + return getCore()->getCallByCallId(op->getCallId()); } return nullptr; } @@ -529,7 +547,7 @@ void ClientConference::setParticipantAdminStatus(const shared_ptr<Participant> & if (isAdmin == participant->isAdmin()) return; if (!getMe()->isAdmin()) { - lError() << "Unable to set admin status of participant " << *participant->getAddress() << " to " + lError() << *this << ": Unable to set admin status of participant " << *participant->getAddress() << " to " << (isAdmin ? "true" : "false") << " because focus " << *getMe()->getAddress() << " is not admin"; return; } @@ -550,7 +568,7 @@ void ClientConference::setParticipantAdminStatus(const shared_ptr<Participant> & void ClientConference::callFocus() { if (!mFocus->getSession()) { std::shared_ptr<Address> focusAddress = mFocus->getAddress(); - lInfo() << "Calling the conference focus (" << *focusAddress + lInfo() << *this << ": Calling the conference focus (" << *focusAddress << ") as there is no session towards the focus yet"; LinphoneCallParams *params = linphone_core_create_call_params(getCore()->getCCore(), nullptr); // Participant with the focus call is admin @@ -558,8 +576,7 @@ void ClientConference::callFocus() { Utils::toString(true)); linphone_call_params_enable_video(params, mConfParams->videoEnabled()); Conference::setUtf8Subject(mPendingSubject); - const std::list<std::shared_ptr<const Address>> addresses; - inviteAddresses(addresses, params); + inviteAddresses({}, params); linphone_call_params_unref(params); } } @@ -572,8 +589,6 @@ std::shared_ptr<ParticipantDevice> ClientConference::createParticipantDevice(std device->setSession(nullptr); const auto &p = device->getParticipant(); if (p) { - time_t creationTime = time(nullptr); - notifyParticipantDeviceAdded(creationTime, false, p, device); const_cast<MediaSessionParams *>(call->getParams()) ->addCustomContactParameter(Conference::AdminParameter, Utils::toString(p->isAdmin())); } @@ -582,11 +597,11 @@ std::shared_ptr<ParticipantDevice> ClientConference::createParticipantDevice(std } bool ClientConference::addParticipant(BCTBX_UNUSED(const std::shared_ptr<ParticipantInfo> &info)) { - lError() << "addParticipant() with a participant info is not allowed on a ClientConference"; + lError() << *this << ": addParticipant() with a participant info is not allowed on a ClientConference"; return false; } -bool ClientConference::addParticipant(const std::shared_ptr<const Address> &participantAddress) { +bool ClientConference::addParticipant(const std::shared_ptr<Address> &participantAddress) { // Search call that matches participant session const std::list<std::shared_ptr<Call>> &coreCalls = getCore()->getCalls(); auto callIt = std::find_if(coreCalls.cbegin(), coreCalls.cend(), [&](const std::shared_ptr<Call> &c) { @@ -597,7 +612,7 @@ bool ClientConference::addParticipant(const std::shared_ptr<const Address> &part std::shared_ptr<Call> call = *callIt; ret = addParticipant(call); } else { - const list<std::shared_ptr<const Address>> addresses{participantAddress}; + const list<std::shared_ptr<Address>> addresses{participantAddress}; ret = addParticipants(addresses); } return ret; @@ -638,7 +653,7 @@ bool ClientConference::addParticipant(std::shared_ptr<Call> call) { return false; } } else if (!getMe()->isAdmin()) { - lError() << "Could not add call " << call << " to the conference because local participant " + lError() << "Could not add call " << *call << " to " << *this << " because local participant " << *getMe()->getAddress() << " is not admin."; } else { const auto &endTime = mConfParams->getEndTime(); @@ -663,7 +678,7 @@ bool ClientConference::addParticipant(std::shared_ptr<Call> call) { // Removes own address and existing participants from the list. // Also removes gruu from kept addresses -std::list<Address> ClientConference::cleanAddressesList(const list<std::shared_ptr<const Address>> &addresses) const { +std::list<Address> ClientConference::cleanAddressesList(const list<std::shared_ptr<Address>> &addresses) const { std::list<Address> cleanedList; const auto &meAddress = getMe()->getAddress(); for (const auto &address : addresses) { @@ -699,7 +714,7 @@ bool ClientConference::addParticipants(const std::list<std::shared_ptr<Call>> &c return false; } -bool ClientConference::addParticipants(const list<std::shared_ptr<const Address>> &addresses) { +bool ClientConference::addParticipants(const list<std::shared_ptr<Address>> &addresses) { const auto isChat = mConfParams->chatEnabled(); if (getMe()->isAdmin() || ((mState == ConferenceInterface::State::Instantiated) && isChat)) { const auto mediaSupported = supportsMedia(); @@ -710,11 +725,11 @@ bool ClientConference::addParticipants(const list<std::shared_ptr<const Address> } else if (isChat) { auto addressesList = cleanAddressesList(addresses); if (addressesList.empty()) { - lError() << "No new participants were given."; + lError() << *this << ": no new participants were given."; return false; } if (!mConfParams->isGroup() && (addressesList.size() > 1 || getParticipantCount() != 0)) { - lError() << "Cannot add more than one participant in a one-to-one chatroom"; + lError() << *this << ": Cannot add more than one participant in a one-to-one chatroom"; return false; } @@ -850,6 +865,18 @@ void ClientConference::onFocusCallStateChanged(CallSession::State state, BCTBX_U list<std::shared_ptr<Call>>::iterator it; switch (state) { case CallSession::State::StreamsRunning: { +#ifdef HAVE_ADVANCED_IM + // Handle session reconnection if network dropped only for a short period of time + if (mEventHandler && mEventHandler->needToSubscribe()) { + const auto &conferenceId = getConferenceId(); + if (conferenceId.isValid()) { + mEventHandler->subscribe(conferenceId); + } + } + if (mClientEktManager) { + mClientEktManager->subscribe(); + } +#endif // HAVE_ADVANCED_IM updateParticipantInConferenceInfo(getMe()); for (const auto &device : getParticipantDevices()) { device->updateStreamAvailabilities(); @@ -905,7 +932,8 @@ void ClientConference::onFocusCallStateChanged(CallSession::State state, BCTBX_U } if (!mFinalized) { - setConferenceId(ConferenceId(focusContactAddress, getMe()->getAddress())); + setConferenceId(ConferenceId(focusContactAddress, getMe()->getAddress(), + getCore()->createConferenceIdParams())); if (call) { if (focusContactAddress->hasUriParam(Conference::ConfIdParameter)) { call->setConferenceId( @@ -1002,8 +1030,8 @@ void ClientConference::onFocusCallStateChanged(CallSession::State state, BCTBX_U const auto errorInfo = session->getErrorInfo(); const auto code = linphone_error_info_get_protocol_code(errorInfo); if (errorInfo != nullptr && code > 299) { - lWarning() << "Chat room [" << getConferenceId() - << "] received a BYE with reason: " << linphone_error_info_get_protocol_code(errorInfo) + lWarning() << *this + << ": received a BYE with reason: " << linphone_error_info_get_protocol_code(errorInfo) << ", not leaving it."; } else { const auto &clientConferenceAddress = session->getRemoteAddress(); @@ -1012,7 +1040,7 @@ void ClientConference::onFocusCallStateChanged(CallSession::State state, BCTBX_U for (auto it = previousConferenceIds.begin(); it != previousConferenceIds.end(); it++) { ConferenceId confId = static_cast<ConferenceId>(*it); if (*confId.getPeerAddress() == *clientConferenceAddress) { - lInfo() << "Found previous chat room conference ID [" << confId + lInfo() << *this << ": found previous chat room conference ID [" << confId << "] for chat room with current ID [" << getConferenceId() << "]"; clientGroupChatRoom->removeConferenceIdFromPreviousList(confId); found = true; @@ -1025,10 +1053,10 @@ void ClientConference::onFocusCallStateChanged(CallSession::State state, BCTBX_U if (found) { /* This is the case where we are accepting a BYE for an already exhumed chat room, don't change * it's state */ - lInfo() << "Chat room [" << *clientConferenceAddress - << "] from before the exhume has been terminated"; + lInfo() << *this << ": received a BYE referred to previous conference address [" + << *clientConferenceAddress << "] before the exhume has been terminated"; } else if (isLocalExhume) { - lInfo() << "Chat room [" << *clientConferenceAddress << "] has been successfully recreated"; + lInfo() << *this << " has been successfully recreated"; } else { setState(ConferenceInterface::State::TerminationPending); } @@ -1201,8 +1229,8 @@ void ClientConference::onStateChanged(ConferenceInterface::State state) { break; case ConferenceInterface::State::TerminationPending: #ifdef HAVE_ADVANCED_IM - if (eventHandler) { - eventHandler->unsubscribe(); + if (mEventHandler) { + mEventHandler->unsubscribe(); } #endif // HAVE_ADVANCED_IM resetLastNotify(); @@ -1215,8 +1243,10 @@ void ClientConference::onStateChanged(ConferenceInterface::State state) { } } if (mediaSupported) { - Conference::terminate(); - setState(ConferenceInterface::State::Terminated); + getCore()->doLater([this]() { + Conference::terminate(); + setState(ConferenceInterface::State::Terminated); + }); } break; case ConferenceInterface::State::Deleted: @@ -1366,11 +1396,18 @@ void ClientConference::onParticipantDeviceRemoved(const std::shared_ptr<Conferen const auto videoNeedsReInvite = (mConfParams->videoEnabled() && params->videoEnabled()); updateMinatureRequestedFlag(); + + // If the device was screen sharing, then notify the application that it isn't anymore if (device->enableScreenSharing(false)) { notifyParticipantDeviceScreenSharingChanged(ms_time(NULL), event->getFullState(), device->getParticipant(), device); } + // If the device was the active speaker, then notify the application that it isn't anymore + if (device == getActiveSpeakerParticipantDevice()) { + notifyActiveSpeakerParticipantDevice(nullptr); + } + if ((audioNeedsReInvite || videoNeedsReInvite) && (mState == ConferenceInterface::State::Created) && !isMe(deviceAddress) && (device->getTimeOfJoining() >= 0)) { auto updateSession = [this, deviceAddress]() -> LinphoneStatus { @@ -1457,11 +1494,11 @@ void ClientConference::onParticipantDeviceMediaAvailabilityChanged( if ((!isMe(deviceAddress)) && (mState == ConferenceInterface::State::Created) && isIn()) { updateMinatureRequestedFlag(); auto updateSession = [this, deviceAddress]() -> LinphoneStatus { - lInfo() << "Sending re-INVITE because device " << *deviceAddress + lInfo() << *this << ": Sending re-INVITE because device " << *deviceAddress << " has changed its stream availability"; auto ret = updateMainSession(); if (ret != 0) { - lInfo() << "re-INVITE due to device " << *deviceAddress + lInfo() << *this << ": re-INVITE due to device " << *deviceAddress << " changing its stream availability cannot be sent right now"; } return ret; @@ -1488,11 +1525,10 @@ void ClientConference::onAvailableMediaChanged( const bool videoEnabled = (session) ? session->getCurrentParams()->videoEnabled() : false; if (!mConfParams->videoEnabled() && videoEnabled && isIn()) { auto updateSession = [this]() -> LinphoneStatus { - lInfo() << "Sending re-INVITE because the conference has no longer video capabilities"; + lInfo() << *this << ": Sending re-INVITE because the conference has no longer video capabilities"; auto ret = updateMainSession(); if (ret != 0) { - lInfo() << "Sending re-INVITE because the conference has no longer video capabilities"; - lInfo() << "re-INVITE to remove video cannot be sent right now"; + lInfo() << *this << ": re-INVITE to remove video cannot be sent right now"; } return ret; }; @@ -1528,12 +1564,13 @@ void ClientConference::onParticipantsCleared() { void ClientConference::onConferenceCreated(BCTBX_UNUSED(const std::shared_ptr<Address> &addr)) { #ifdef HAVE_ADVANCED_IM const auto &conferenceId = getConferenceId(); - const ConferenceId newConferenceId(addr, conferenceId.getLocalAddress()); + const ConferenceId newConferenceId(addr, conferenceId.getLocalAddress(), getCore()->createConferenceIdParams()); if (!getCore()->getPrivate()->findExumedChatRoomFromPreviousConferenceId(newConferenceId)) { setConferenceId(newConferenceId); } setConferenceAddress(addr); - lInfo() << "Conference [" << conferenceId << "] has been created with address " << *getConferenceAddress(); + lInfo() << *this << ": Conference [" << conferenceId << "] has been created with address " + << *getConferenceAddress(); mFocus->setAddress(addr); mFocus->clearDevices(); @@ -1562,13 +1599,13 @@ void ClientConference::onConferenceTerminated(BCTBX_UNUSED(const std::shared_ptr #ifdef HAVE_ADVANCED_IM auto chatRoom = getChatRoom(); if (mConfParams->chatEnabled() && chatRoom) { - if (eventHandler) { - eventHandler->unsubscribe(); + if (mEventHandler) { + mEventHandler->unsubscribe(); } resetLastNotify(); // remove event handler from list event handler if used if (getCore()->getPrivate()->clientListEventHandler) { - getCore()->getPrivate()->clientListEventHandler->removeHandler(eventHandler); + getCore()->getPrivate()->clientListEventHandler->removeHandler(mEventHandler); } // No need to notify such event during the core startup. @@ -1583,6 +1620,7 @@ void ClientConference::onConferenceTerminated(BCTBX_UNUSED(const std::shared_ptr _linphone_chat_room_notify_conference_left(chatRoom->toC(), L_GET_C_BACK_PTR(event)); } } + mClientEktManager = nullptr; #endif // HAVE_ADVANCED_IM auto session = getMainSession(); @@ -1626,18 +1664,18 @@ AbstractChatRoom::SecurityLevel ClientConference::getSecurityLevelExcept(const std::shared_ptr<ParticipantDevice> &ignoredDevice) const { auto encryptionEngine = getCore()->getEncryptionEngine(); if (!encryptionEngine) { - lWarning() << "Asking participant security level but there is no encryption engine enabled"; + lWarning() << *this << ": Asking participant security level but there is no encryption engine enabled"; return AbstractChatRoom::SecurityLevel::ClearText; } if (!getCurrentParams()->getChatParams()->isEncrypted()) { - lDebug() << "Chatroom SecurityLevel = ClearText"; + lDebug() << *this << ": Chatroom SecurityLevel = ClearText"; return AbstractChatRoom::SecurityLevel::ClearText; } // Until participant list & self devices list is populated, don't assume chat room is safe but encrypted if (getParticipants().size() == 0 && getMe()->getDevices().size() == 0) { - lDebug() << "Chatroom SecurityLevel = Encrypted"; + lDebug() << *this << ": Chatroom SecurityLevel = Encrypted"; return AbstractChatRoom::SecurityLevel::Encrypted; } @@ -1661,7 +1699,7 @@ ClientConference::getSecurityLevelExcept(const std::shared_ptr<ParticipantDevice return AbstractChatRoom::SecurityLevel::Safe; } auto level = encryptionEngine->getSecurityLevel(allDevices); - lDebug() << "Chatroom SecurityLevel = " << level; + lDebug() << *this << ": Chatroom SecurityLevel = " << level; return level; } @@ -1673,7 +1711,7 @@ void ClientConference::onFirstNotifyReceived(BCTBX_UNUSED(const std::shared_ptr< // capabilities, this callback might be called when the conference is in the CreationPending state if (!((mState == ConferenceInterface::State::Created) || (supportsMedia() && (mState == ConferenceInterface::State::CreationPending)))) { - lWarning() << "First notify received in ClientConference that is not in the Created state [" + lWarning() << "First notify received in " << *this << " that is not in the Created state [" << Utils::toString(getState()) << "], ignoring it!"; return; } @@ -1742,12 +1780,14 @@ void ClientConference::onFullStateReceived() { if (supportsMedia()) { #ifdef HAVE_ADVANCED_IM - if ((mConfParams->getSecurityLevel() == ConferenceParamsInterface::SecurityLevel::EndToEnd) && - (mClientEktManager == nullptr)) { - shared_ptr<ClientConference> clientConference = dynamic_pointer_cast<ClientConference>(getSharedFromThis()); - mClientEktManager = make_shared<ClientEktManager>(MSEKTCipherType::MS_EKT_CIPHERTYPE_AESKW256, - MSCryptoSuite::MS_AEAD_AES_256_GCM); - mClientEktManager->init(clientConference); + if (mConfParams->getSecurityLevel() == ConferenceParamsInterface::SecurityLevel::EndToEnd) { + if (mClientEktManager == nullptr) { + shared_ptr<ClientConference> clientConference = + dynamic_pointer_cast<ClientConference>(getSharedFromThis()); + mClientEktManager = make_shared<ClientEktManager>(MSEKTCipherType::MS_EKT_CIPHERTYPE_AESKW256, + MSCryptoSuite::MS_AEAD_AES_256_GCM); + mClientEktManager->init(clientConference); + } mClientEktManager->subscribe(); } @@ -1915,7 +1955,7 @@ bool ClientConference::isSubscriptionUnderWay() const { underWay = getCore()->getPrivate()->clientListEventHandler->getInitialSubscriptionUnderWayFlag(getConferenceId()); } else { - underWay = eventHandler ? eventHandler->getInitialSubscriptionUnderWayFlag() : false; + underWay = mEventHandler ? mEventHandler->getInitialSubscriptionUnderWayFlag() : false; } #endif // HAVE_ADVANCED_IM @@ -1928,19 +1968,21 @@ bool ClientConference::isSubscriptionUnderWay() const { #endif // _MSC_VER void ClientConference::multipartNotifyReceived(const std::shared_ptr<Event> ¬ifyLev, const Content &content) { #ifdef HAVE_ADVANCED_IM - if (eventHandler) { - const auto initialSubscription = eventHandler->getInitialSubscriptionUnderWayFlag(); - eventHandler->multipartNotifyReceived(notifyLev, content); + if (mEventHandler) { + const auto initialSubscription = mEventHandler->getInitialSubscriptionUnderWayFlag(); + mEventHandler->multipartNotifyReceived(notifyLev, content); const auto &chatRoom = getChatRoom(); if (mConfParams->chatEnabled() && chatRoom && initialSubscription && - !eventHandler->getInitialSubscriptionUnderWayFlag()) { + !mEventHandler->getInitialSubscriptionUnderWayFlag()) { chatRoom->sendPendingMessages(); } return; } #endif // HAVE_ADVANCED_IM - lInfo() << "Unable to handle multi part NOTIFY because conference event package (RFC 4575) is disabled or the SDK " - "was not compiled with ENABLE_ADVANCED_IM flag set to on"; + lInfo() + << *this + << ": Unable to handle multi part NOTIFY because conference event package (RFC 4575) is disabled or the SDK " + "was not compiled with ENABLE_ADVANCED_IM flag set to on"; } #ifndef _MSC_VER #pragma GCC diagnostic pop @@ -1958,27 +2000,28 @@ void ClientConference::notifyReceived(const std::shared_ptr<Event> ¬ifyLev, c return; } } else { - if (eventHandler) { - const auto initialSubscription = eventHandler->getInitialSubscriptionUnderWayFlag(); - eventHandler->notifyReceived(notifyLev, content); + if (mEventHandler) { + const auto initialSubscription = mEventHandler->getInitialSubscriptionUnderWayFlag(); + mEventHandler->notifyReceived(notifyLev, content); const auto &chatRoom = getChatRoom(); if (mConfParams->chatEnabled() && chatRoom && initialSubscription && - !eventHandler->getInitialSubscriptionUnderWayFlag()) { + !mEventHandler->getInitialSubscriptionUnderWayFlag()) { chatRoom->sendPendingMessages(); } return; } } #endif // HAVE_ADVANCED_IM - lInfo() << "Unable to handle NOTIFY because conference event package (RFC 4575) is disabled or the SDK was not " + lInfo() << *this + << ": Unable to handle NOTIFY because conference event package (RFC 4575) is disabled or the SDK was not " "compiled with ENABLE_ADVANCED_IM flag set to on"; } #ifndef _MSC_VER #pragma GCC diagnostic pop #endif // _MSC_VER -int ClientConference::inviteAddresses(const std::list<std::shared_ptr<const Address>> &addresses, - BCTBX_UNUSED(const LinphoneCallParams *params)) { +int ClientConference::inviteAddresses(const std::list<std::shared_ptr<Address>> &addresses, + const LinphoneCallParams *params) { const auto &account = mConfParams->getAccount(); const auto organizer = account ? account->getAccountParams()->getIdentityAddress() : Address::create(linphone_core_get_identity(getCore()->getCCore())); @@ -1999,24 +2042,26 @@ int ClientConference::inviteAddresses(const std::list<std::shared_ptr<const Addr // The main session for the time being is the one used to create the conference. It will later replaced by the // actual session used to join the conference - auto session = - getCore()->createOrUpdateConferenceOnServer(mConfParams, organizer, invitees, getConferenceAddress(), ref); + auto session = getCore()->createOrUpdateConferenceOnServer(mConfParams, invitees, getConferenceAddress(), ref); if (!session) { lInfo() << "Aborting creation of conference " << *this << " because the call session cannot be established"; setState(ConferenceInterface::State::CreationFailed); return -1; } setMainSession(session); + if (params) { + mJoiningParams = L_GET_CPP_PTR_FROM_C_OBJECT(params)->clone(); + } return 0; } -bool ClientConference::dialOutAddresses(BCTBX_UNUSED(const std::list<std::shared_ptr<const Address>> &addressList)) { - lError() << "ClientConference::dialOutAddresses() not implemented"; +bool ClientConference::dialOutAddresses(BCTBX_UNUSED(const std::list<std::shared_ptr<Address>> &addressList)) { + lError() << *this << ": ClientConference::dialOutAddresses() not implemented"; return false; } bool ClientConference::finalizeParticipantAddition(BCTBX_UNUSED(std::shared_ptr<Call> call)) { - lError() << "ClientConference::finalizeParticipantAddition() not implemented"; + lError() << *this << ": ClientConference::finalizeParticipantAddition() not implemented"; return false; } @@ -2028,7 +2073,7 @@ int ClientConference::removeParticipant(const std::shared_ptr<Address> &addr) { case ConferenceInterface::State::Created: case ConferenceInterface::State::TerminationPending: { if (!findParticipant(addr)) { - lError() << "Conference: could not remove participant \'" << *addr + lError() << *this << ": could not remove participant \'" << *addr << "\': not in the participants list"; return -1; } @@ -2042,7 +2087,7 @@ int ClientConference::removeParticipant(const std::shared_ptr<Address> &addr) { referOp->unref(); if (res != 0) { - lError() << "Conference: could not remove participant \'" << *addr + lError() << *this << ": could not remove participant \'" << *addr << "\': REFER with BYE has failed"; return -1; } @@ -2072,7 +2117,7 @@ int ClientConference::removeParticipant(const std::shared_ptr<CallSession> &sess return removeParticipant(p) ? 0 : -1; } } else { - lError() << "Unable to remove participant " << *(p->getAddress()) << " because focus " + lError() << *this << ": Unable to remove participant " << *(p->getAddress()) << " because focus " << *(getMe()->getAddress()) << " is not admin"; } return -1; @@ -2097,7 +2142,7 @@ bool ClientConference::removeParticipant(const std::shared_ptr<Participant> &par return true; } } else { - lError() << "Unable to remove participant " << *participantAddress << " because focus " + lError() << *this << ": Unable to remove participant " << *participantAddress << " because focus " << *(getMe()->getAddress()) << " is not admin"; } return false; @@ -2105,57 +2150,57 @@ bool ClientConference::removeParticipant(const std::shared_ptr<Participant> &par int ClientConference::participantDeviceMediaCapabilityChanged( BCTBX_UNUSED(const std::shared_ptr<CallSession> &session)) { - lError() << "ClientConference::participantDeviceMediaCapabilityChanged() not implemented"; + lError() << *this << ": ClientConference::participantDeviceMediaCapabilityChanged() not implemented"; return -1; } int ClientConference::participantDeviceMediaCapabilityChanged(BCTBX_UNUSED(const std::shared_ptr<Address> &addr)) { - lError() << "ClientConference::participantDeviceMediaCapabilityChanged() not implemented"; + lError() << *this << ": ClientConference::participantDeviceMediaCapabilityChanged() not implemented"; return -1; } int ClientConference::participantDeviceMediaCapabilityChanged( BCTBX_UNUSED(const std::shared_ptr<Participant> &participant), BCTBX_UNUSED(const std::shared_ptr<ParticipantDevice> &device)) { - lError() << "ClientConference::participantDeviceMediaCapabilityChanged() not implemented"; + lError() << *this << ": ClientConference::participantDeviceMediaCapabilityChanged() not implemented"; return -1; } int ClientConference::participantDeviceSsrcChanged(BCTBX_UNUSED(const std::shared_ptr<CallSession> &session), BCTBX_UNUSED(const LinphoneStreamType type), BCTBX_UNUSED(uint32_t ssrc)) { - lError() << "ClientConference::participantDeviceSsrcChanged() not implemented"; + lError() << *this << ": ClientConference::participantDeviceSsrcChanged() not implemented"; return -1; } int ClientConference::participantDeviceSsrcChanged(BCTBX_UNUSED(const std::shared_ptr<CallSession> &session), BCTBX_UNUSED(uint32_t audioSsrc), BCTBX_UNUSED(uint32_t videoSsrc)) { - lError() << "ClientConference::participantDeviceSsrcChanged() not implemented"; + lError() << *this << ": ClientConference::participantDeviceSsrcChanged() not implemented"; return -1; } int ClientConference::participantDeviceAlerting(BCTBX_UNUSED(const std::shared_ptr<CallSession> &session)) { - lError() << "ClientConference::participantDeviceAlerting() not implemented"; + lError() << *this << ": ClientConference::participantDeviceAlerting() not implemented"; return -1; } int ClientConference::participantDeviceAlerting(BCTBX_UNUSED(const std::shared_ptr<Participant> &participant), BCTBX_UNUSED(const std::shared_ptr<ParticipantDevice> &device)) { - lError() << "ClientConference::participantDeviceAlerting() not implemented"; + lError() << *this << ": ClientConference::participantDeviceAlerting() not implemented"; return -1; } int ClientConference::participantDeviceJoined(BCTBX_UNUSED(const std::shared_ptr<CallSession> &session)) { - lError() << "ClientConference::participantDeviceJoined() not implemented"; + lError() << *this << ": ClientConference::participantDeviceJoined() not implemented"; return -1; } int ClientConference::participantDeviceJoined(BCTBX_UNUSED(const std::shared_ptr<Participant> &participant), BCTBX_UNUSED(const std::shared_ptr<ParticipantDevice> &device)) { - lError() << "ClientConference::participantDeviceJoined() not implemented"; + lError() << *this << ": ClientConference::participantDeviceJoined() not implemented"; return -1; } int ClientConference::participantDeviceLeft(BCTBX_UNUSED(const std::shared_ptr<CallSession> &session)) { - lError() << "ClientConference::participantDeviceLeft() not implemented"; + lError() << *this << ": ClientConference::participantDeviceLeft() not implemented"; return -1; } int ClientConference::participantDeviceLeft(BCTBX_UNUSED(const std::shared_ptr<Participant> &participant), BCTBX_UNUSED(const std::shared_ptr<ParticipantDevice> &device)) { - lError() << "ClientConference::participantDeviceLeft() not implemented"; + lError() << *this << ": ClientConference::participantDeviceLeft() not implemented"; return -1; } @@ -2234,6 +2279,7 @@ int ClientConference::terminate() { } else if (mState != ConferenceInterface::State::Deleted) { setState(ConferenceInterface::State::TerminationPending); if (!sessionCall) { + setState(ConferenceInterface::State::Terminated); setState(ConferenceInterface::State::Deleted); } } @@ -2248,7 +2294,7 @@ int ClientConference::terminate() { void ClientConference::finalizeCreation() { if (mFinalized) { - lDebug() << "Conference " << this << " has already been mFinalized"; + lDebug() << "Conference " << this << " has already been finalized"; return; } else { mFinalized = true; @@ -2349,19 +2395,21 @@ void ClientConference::leave() { } #ifdef HAVE_ADVANCED_IM } else if (mConfParams->chatEnabled()) { - if (eventHandler) { - eventHandler->unsubscribe(); + if (mEventHandler) { + mEventHandler->unsubscribe(); } shared_ptr<CallSession> session = mFocus->getSession(); - if (session) session->terminate(); - else { + if (session) { + session->terminate(); + } else if (mState != ConferenceInterface::State::CreationFailed) { + // No need to create a session if the creation already failed session = createSession(); session->startInvite(nullptr, "", nullptr); } - - const auto &chatRoom = getChatRoom(); - if (chatRoom) { - setState(ConferenceInterface::State::TerminationPending); + setState(ConferenceInterface::State::TerminationPending); + if (!session) { + setState(ConferenceInterface::State::Terminated); + setState(ConferenceInterface::State::Deleted); } #endif // HAVE_ADVANCED_IM } @@ -2433,7 +2481,7 @@ AudioStream *ClientConference::getAudioStream() { } bool ClientConference::hasBeenLeft() const { - return (mState != State::Created); + return (mState == State::TerminationPending) || (mState == State::Terminated) || (mState == State::Deleted); } void ClientConference::handleRefer(SalReferOp *op, @@ -2512,20 +2560,19 @@ void ClientConference::onCallSessionSetTerminated(const shared_ptr<CallSession> auto ref = getSharedFromThis(); getCore()->removeConferencePendingCreation(ref); if (remoteAddress == nullptr) { - lError() << "[Conference] [" << this - << "] The session to update the conference information did not succesfully establish hence it is " + lError() << *this + << " The session to update the conference information did not successfully establish hence it is " "likely that the request wasn't taken into account by the server"; setState(ConferenceInterface::State::CreationFailed); } else if (dynamic_pointer_cast<MediaSession>(session) && ((mState == ConferenceInterface::State::Instantiated) || (mState == ConferenceInterface::State::CreationPending)) && (session->getParams()->getPrivate()->getStartTime() < 0)) { - // TODO: same code as in the conference scheduler and core.cpp. Needs refactoring? auto conferenceAddress = remoteAddress; - lInfo() << "Conference [" << this << "] has been succesfully created: " << *conferenceAddress; + lInfo() << *this << " has been successfully created: " << *conferenceAddress; const auto &meAddress = getMe()->getAddress(); - ConferenceId conferenceId(conferenceAddress, meAddress); + ConferenceId conferenceId(conferenceAddress, meAddress, getCore()->createConferenceIdParams()); // Do not change the conference ID yet if exhuming a chatroom if (!isLocalExhume) { setConferenceId(conferenceId); @@ -2536,14 +2583,23 @@ void ClientConference::onCallSessionSetTerminated(const shared_ptr<CallSession> // will be always greater than 1 (the core holds it) getMe()->setConference(getSharedFromThis()); - lInfo() << "Automatically rejoining conference " << *remoteAddress; - auto new_params = linphone_core_create_call_params(getCore()->getCCore(), nullptr); - + lInfo() << "Automatically rejoining " << *this; + MediaSessionParams *dialoutParams = nullptr; + if (mJoiningParams) { + dialoutParams = mJoiningParams->clone(); + } else { + // No parameters were given to ClientConference::inviteAddresses() + dialoutParams = new MediaSessionParams(); + dialoutParams->initDefault(getCore(), LinphoneCallOutgoing); + // Copy conference capabilities as the application didn't specify any parameter to pass on to the call + dialoutParams->enableAudio(mConfParams->audioEnabled()); + dialoutParams->enableVideo(mConfParams->videoEnabled()); + dialoutParams->enableRealtimeText(false); + } // Participant with the focus call is admin - L_GET_CPP_PTR_FROM_C_OBJECT(new_params) - ->addCustomContactParameter(Conference::AdminParameter, Utils::toString(true)); + dialoutParams->addCustomContactParameter(Conference::AdminParameter, Utils::toString(true)); if (mConfParams->chatEnabled()) { - L_GET_CPP_PTR_FROM_C_OBJECT(new_params)->addCustomHeader("Require", "recipient-list-invite"); + dialoutParams->addCustomHeader("Require", "recipient-list-invite"); } std::list<Address> addressesList; @@ -2572,51 +2628,47 @@ void ClientConference::onCallSessionSetTerminated(const shared_ptr<CallSession> // For audio video conferences it is requires as the client has to initiate a media session. The chat // room code, instead, makes the assumption that the participant list is the body itself if (mediaSupported) { - L_GET_CPP_PTR_FROM_C_OBJECT(new_params)->addCustomContent(resourceList); + dialoutParams->addCustomContent(resourceList); } else { content = resourceList; } } if (!mediaSupported) { - L_GET_CPP_PTR_FROM_C_OBJECT(new_params)->addCustomContactParameter(Conference::TextParameter); + dialoutParams->addCustomContactParameter(Conference::TextParameter); if (!mConfParams->isGroup()) { - L_GET_CPP_PTR_FROM_C_OBJECT(new_params)->addCustomHeader("One-To-One-Chat-Room", "true"); + dialoutParams->addCustomHeader("One-To-One-Chat-Room", "true"); } if (mConfParams->getChatParams()->isEncrypted()) { - L_GET_CPP_PTR_FROM_C_OBJECT(new_params)->addCustomHeader("End-To-End-Encrypted", "true"); + dialoutParams->addCustomHeader("End-To-End-Encrypted", "true"); } if (mConfParams->getChatParams()->getEphemeralMode() == AbstractChatRoom::EphemeralMode::AdminManaged) { - L_GET_CPP_PTR_FROM_C_OBJECT(new_params)->addCustomHeader("Ephemerable", "true"); - L_GET_CPP_PTR_FROM_C_OBJECT(new_params) - ->addCustomHeader("Ephemeral-Life-Time", - to_string(mConfParams->getChatParams()->getEphemeralLifetime())); + dialoutParams->addCustomHeader("Ephemerable", "true"); + dialoutParams->addCustomHeader("Ephemeral-Life-Time", + to_string(mConfParams->getChatParams()->getEphemeralLifetime())); } } - linphone_call_params_enable_tone_indications(new_params, mediaSupported); + dialoutParams->getPrivate()->disableRinging(!mediaSupported); + dialoutParams->getPrivate()->enableToneIndications(mediaSupported); - const auto cCore = getCore()->getCCore(); - const LinphoneVideoActivationPolicy *pol = linphone_core_get_video_activation_policy(cCore); - bool initiate_video = !!linphone_video_activation_policy_get_automatically_initiate(pol); // If one of the pending calls has the video enabled, then force the activation of the video. Otherwise it // would be weird to be in a video call and once it is merged the participant has no video anymore bool forceVideoEnabled = false; for (const auto &call : mPendingCalls) { forceVideoEnabled |= call->getParams()->videoEnabled(); } - const auto setupSessionHasVideo = - dynamic_pointer_cast<MediaSession>(session)->getMediaParams()->videoEnabled(); - linphone_call_params_enable_video(new_params, - forceVideoEnabled || (setupSessionHasVideo && initiate_video)); - - linphone_call_params_enable_audio(new_params, mConfParams->audioEnabled()); - linphone_call_params_enable_realtime_text(new_params, FALSE); + if (forceVideoEnabled) { + lInfo() << "Forcing " << *mMe->getAddress() << " to enable video capabilities when joining " << *this + << " because at least one call that was merged had video capabilities on"; + dialoutParams->enableVideo(true); + } + const auto cCore = getCore()->getCCore(); const auto &subject = mConfParams->getUtf8Subject(); if (mediaSupported) { - LinphoneCall *call = linphone_core_invite_address_with_params_2(cCore, remoteAddress->toC(), new_params, - L_STRING_TO_C(subject), - content ? content->toC() : nullptr); + LinphoneCall *call = linphone_core_invite_address_with_params_2( + cCore, remoteAddress->toC(), L_GET_C_BACK_PTR(dialoutParams), L_STRING_TO_C(subject), + content ? content->toC() : nullptr); auto session = Call::toCpp(call)->getActiveSession(); setMainSession(session); session->addListener(getSharedFromThis()); @@ -2630,10 +2682,10 @@ void ClientConference::onCallSessionSetTerminated(const shared_ptr<CallSession> lError() << "Unable to find account used to join " << *this; setState(ConferenceInterface::State::CreationFailed); setMainSession(nullptr); - linphone_call_params_unref(new_params); + delete dialoutParams; return; } - auto session = mMe->createSession(getCore(), L_GET_CPP_PTR_FROM_C_OBJECT(new_params), TRUE); + auto session = mMe->createSession(getCore(), dialoutParams, TRUE); setMainSession(session); session->addListener(getSharedFromThis()); session->configure(LinphoneCallOutgoing, account, nullptr, from, remoteAddress); @@ -2642,7 +2694,7 @@ void ClientConference::onCallSessionSetTerminated(const shared_ptr<CallSession> session->startInvite(nullptr, subject, content); } } - linphone_call_params_unref(new_params); + delete dialoutParams; } } } @@ -2707,7 +2759,20 @@ void ClientConference::onCallSessionSetReleased(const shared_ptr<CallSession> &s void ClientConference::requestFullState() { #ifdef HAVE_ADVANCED_IM - eventHandler->requestFullState(); + mEventHandler->requestFullState(); +#endif // HAVE_ADVANCED_IM +} + +void ClientConference::subscribe(BCTBX_UNUSED(bool addToListEventHandler), BCTBX_UNUSED(bool unsubscribeFirst)) { +#ifdef HAVE_ADVANCED_IM + if (mEventHandler) { + if (unsubscribeFirst) { + mEventHandler->unsubscribe(); // Required for next subscribe to be sent + } + } else { + initializeHandlers(this, addToListEventHandler); + } + mEventHandler->subscribe(getConferenceId()); #endif // HAVE_ADVANCED_IM } diff --git a/src/conference/client-conference.h b/src/conference/client-conference.h index 0bae863f28046d79c866e91d5413765097721000..c0bd05f664acd18c5703718f28d2b6da70d21925 100644 --- a/src/conference/client-conference.h +++ b/src/conference/client-conference.h @@ -39,7 +39,6 @@ class LINPHONE_PUBLIC ClientConference : public Conference, public ConferenceLis public: ClientConference(const std::shared_ptr<Core> &core, - const std::shared_ptr<Address> &meAddr, std::shared_ptr<CallSessionListener> listener, const std::shared_ptr<const ConferenceParams> params); virtual ~ClientConference(); @@ -48,18 +47,18 @@ public: const ConferenceId conferenceId, const unsigned int lastNotifyId, bool hasBeenLeft) override; - void initWithFocus(const std::shared_ptr<Address> focusAddr, - const std::shared_ptr<CallSession> focusSession, + void initWithFocus(const std::shared_ptr<const Address> &focusAddr, + const std::shared_ptr<CallSession> &focusSession, SalCallOp *op = nullptr, ConferenceListener *confListener = nullptr); - virtual int inviteAddresses(const std::list<std::shared_ptr<const Address>> &addresses, + virtual int inviteAddresses(const std::list<std::shared_ptr<Address>> &addresses, const LinphoneCallParams *params) override; - virtual bool dialOutAddresses(const std::list<std::shared_ptr<const Address>> &addressList) override; + virtual bool dialOutAddresses(const std::list<std::shared_ptr<Address>> &addressList) override; virtual bool addParticipant(const std::shared_ptr<ParticipantInfo> &info) override; virtual bool addParticipants(const std::list<std::shared_ptr<Call>> &call) override; - virtual bool addParticipants(const std::list<std::shared_ptr<const Address>> &addresses) override; + virtual bool addParticipants(const std::list<std::shared_ptr<Address>> &addresses) override; virtual bool addParticipant(std::shared_ptr<Call> call) override; - virtual bool addParticipant(const std::shared_ptr<const Address> &participantAddress) override; + virtual bool addParticipant(const std::shared_ptr<Address> &participantAddress) override; virtual std::shared_ptr<ParticipantDevice> createParticipantDevice(std::shared_ptr<Participant> participant, std::shared_ptr<Call> call) override; virtual bool finalizeParticipantAddition(std::shared_ptr<Call> call) override; @@ -161,7 +160,7 @@ public: virtual void onEphemeralLifetimeChanged(const std::shared_ptr<ConferenceEphemeralMessageEvent> &event) override; #ifdef HAVE_ADVANCED_IM - std::shared_ptr<ClientConferenceEventHandler> eventHandler; + std::shared_ptr<ClientConferenceEventHandler> mEventHandler; #endif // HAVE_ADVANCED_IM void requestFullState(); @@ -176,7 +175,7 @@ public: AbstractChatRoom::SecurityLevel getSecurityLevelExcept(const std::shared_ptr<ParticipantDevice> &ignoredDevice) const; - void createFocus(const std::shared_ptr<Address> focusAddr, + void createFocus(const std::shared_ptr<const Address> &focusAddr, const std::shared_ptr<CallSession> focusSession = nullptr); virtual std::pair<bool, LinphoneMediaDirection> getMainStreamVideoDirection( @@ -188,6 +187,7 @@ public: CallSession::State state, const std::string &message) override; void setUtf8Subject(const std::string &subject) override; + void subscribe(bool addToListEventHandler, bool unsubscribeFirst = true); #ifdef HAVE_ADVANCED_IM std::shared_ptr<LinphonePrivate::ClientEktManager> getClientEktManager() const; @@ -199,7 +199,7 @@ protected: private: void acceptSession(const std::shared_ptr<CallSession> &session); - std::shared_ptr<CallSession> createSessionTo(const std::shared_ptr<Address> &sessionTo); + std::shared_ptr<CallSession> createSessionTo(const std::shared_ptr<const Address> &sessionTo); std::shared_ptr<CallSession> createSession(); virtual std::shared_ptr<CallSession> getMainSession() const override; virtual std::shared_ptr<ConferenceInfo> createConferenceInfo() const override; @@ -211,7 +211,7 @@ private: void onFocusCallStateChanged(CallSession::State state, const std::string &message); void onPendingCallStateChanged(std::shared_ptr<Call> call, CallSession::State callState); - std::list<Address> cleanAddressesList(const std::list<std::shared_ptr<const Address>> &addresses) const; + std::list<Address> cleanAddressesList(const std::list<std::shared_ptr<Address>> &addresses) const; virtual void configure(SalCallOp *op) override; @@ -236,6 +236,7 @@ private: std::shared_ptr<Participant> mFocus; std::list<std::shared_ptr<Call>> mPendingCalls; std::list<std::shared_ptr<Call>> mTransferingCalls; + MediaSessionParams *mJoiningParams = nullptr; uint32_t mDisplayedSpeaker = 0; uint32_t mLouderSpeaker = 0; diff --git a/src/conference/conference-context.cpp b/src/conference/conference-context.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8d8e289b9c8a15375da8214a820d555df4d9c079 --- /dev/null +++ b/src/conference/conference-context.cpp @@ -0,0 +1,101 @@ +/* + * 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 "conference/conference-context.h" + +#include "conference/conference-id.h" +#include "conference/conference-params.h" + +LINPHONE_BEGIN_NAMESPACE + +ConferenceContext::ConferenceContext(const std::shared_ptr<ConferenceParams> ¶ms, + const std::shared_ptr<const Address> &localAddress, + const std::shared_ptr<const Address> &remoteAddress, + const std::list<std::shared_ptr<Address>> &participants) { + mParams = params; + mParticipants = participants; + mLocalAddress = (localAddress) ? localAddress->getUriWithoutGruu() : Address(); + mRemoteAddress = (remoteAddress) ? remoteAddress->getUriWithoutGruu() : Address(); +} + +bool ConferenceContext::operator==(const ConferenceContext &other) const { + if (mLocalAddress.isValid() && + (mLocalAddress.toStringUriOnlyOrdered(false) != other.getLocalAddress().toStringUriOnlyOrdered(false))) + return false; + if (mRemoteAddress.isValid() && + (mRemoteAddress.toStringUriOnlyOrdered(false) != other.getRemoteAddress().toStringUriOnlyOrdered(false))) + return false; + + // Check parameters only if pointer provided as argument is not null + if (mParams) { + const auto &otherParams = other.getParams(); + if (mParams->audioEnabled() != otherParams->audioEnabled()) return false; + if (mParams->videoEnabled() != otherParams->videoEnabled()) return false; + if (mParams->chatEnabled() != otherParams->chatEnabled()) return false; + if (mParams->getSecurityLevel() != otherParams->getSecurityLevel()) return false; + + if (mParams->audioEnabled() || mParams->videoEnabled()) { + if (!mParams->getUtf8Subject().empty() && + (mParams->getUtf8Subject().compare(otherParams->getUtf8Subject()) != 0)) + return false; + if (mParams->localParticipantEnabled() != otherParams->localParticipantEnabled()) return false; + } + + if (mParams->chatEnabled()) { + if (mParams->getChatParams()->getBackend() != otherParams->getChatParams()->getBackend()) return false; + + if (mParams->isGroup() != otherParams->isGroup()) return false; + + if (mParams->isGroup() && + (otherParams->getChatParams()->getBackend() == LinphonePrivate::ChatParams::Backend::Basic)) + return false; + + if (mParams->getChatParams()->isEncrypted() != otherParams->getChatParams()->isEncrypted()) return false; + + // Subject doesn't make any sense for basic chat room and one to one chats + if (mParams->isGroup() && + (mParams->getChatParams()->getBackend() == LinphonePrivate::ChatParams::Backend::FlexisipChat) && + (!mParams->getUtf8Subject().empty() && mParams->getUtf8Subject() != otherParams->getUtf8Subject())) + return false; + } + } + + // Check participants only if list provided as argument is not empty + if (!mParticipants.empty()) { + const auto &otherParticipants = other.getParticipants(); + if (mParticipants.size() != otherParticipants.size()) return false; + for (const auto &participant : mParticipants) { + bool found = false; + for (const auto &otherParticipant : otherParticipants) { + if (participant->weakEqual(*otherParticipant)) { + found = true; + break; + } + } + if (!found) { + return false; + } + } + } + + return true; +} + +LINPHONE_END_NAMESPACE diff --git a/src/conference/conference-context.h b/src/conference/conference-context.h new file mode 100644 index 0000000000000000000000000000000000000000..72fa3825a6bf27e6b2bc696b844c49a52c5f03ee --- /dev/null +++ b/src/conference/conference-context.h @@ -0,0 +1,69 @@ +/* + * 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 _L_CONFERENCE_CONTEXT_H_ +#define _L_CONFERENCE_CONTEXT_H_ + +#include <list> +#include <memory> + +#include "address/address.h" +#include "linphone/utils/general.h" + +LINPHONE_BEGIN_NAMESPACE + +class ConferenceParams; + +class ConferenceContext { +public: + ConferenceContext() = delete; + ConferenceContext(const std::shared_ptr<ConferenceParams> ¶ms, + const std::shared_ptr<const Address> &localAddress, + const std::shared_ptr<const Address> &remoteAddress, + const std::list<std::shared_ptr<Address>> &participants); + ConferenceContext(ConferenceContext &&other) = delete; + ConferenceContext(const ConferenceContext &other) = delete; + virtual ~ConferenceContext() = default; + + inline const std::shared_ptr<const ConferenceParams> getParams() const { + return mParams; + } + inline const std::list<std::shared_ptr<Address>> &getParticipants() const { + return mParticipants; + } + inline const Address &getLocalAddress() const { + return mLocalAddress; + } + inline const Address &getRemoteAddress() const { + return mRemoteAddress; + } + + bool operator==(const ConferenceContext &other) const; + +private: + std::shared_ptr<const ConferenceParams> mParams; + std::list<std::shared_ptr<Address>> mParticipants; + Address mLocalAddress; + Address mRemoteAddress; +}; + +LINPHONE_END_NAMESPACE + +#endif // ifndef _L_CONFERENCE_CONTEXT_H_ diff --git a/src/conference/conference-id-params.cpp b/src/conference/conference-id-params.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b9525069ece88bc061cdecdce6faf3ed62f8529b --- /dev/null +++ b/src/conference/conference-id-params.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2010-2025 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 "conference-id-params.h" + +#include "core/core.h" +#include "linphone/core.h" + +// ============================================================================= + +using namespace std; + +LINPHONE_BEGIN_NAMESPACE + +ConferenceIdParams::ConferenceIdParams(const std::shared_ptr<const Core> &core) { + if (core) { + setKeepGruu(!!linphone_core_gruu_in_conference_address_enabled(core->getCCore())); + } +} + +bool ConferenceIdParams::getKeepGruu() const { + return mKeepGruu; +} + +void ConferenceIdParams::setKeepGruu(bool keepGruu) { + mKeepGruu = keepGruu; +} + +bool ConferenceIdParams::extractUriEnabled() const { + return mExtractUri; +} + +void ConferenceIdParams::enableExtractUri(bool extractUri) { + mExtractUri = extractUri; +} + +LINPHONE_END_NAMESPACE diff --git a/src/conference/conference-id-params.h b/src/conference/conference-id-params.h new file mode 100644 index 0000000000000000000000000000000000000000..f1c100ad315ab686ef4d2e395e1be8e14c944bae --- /dev/null +++ b/src/conference/conference-id-params.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2010-2025 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_CONFERENCE_ID_PARAMS_H_ +#define _L_CONFERENCE_ID_PARAMS_H_ + +#include <string> + +#include "linphone/utils/general.h" + +// ============================================================================= + +LINPHONE_BEGIN_NAMESPACE + +class Core; + +class LINPHONE_PUBLIC ConferenceIdParams { +public: + ConferenceIdParams(const std::shared_ptr<const Core> &core = nullptr); + ConferenceIdParams(const ConferenceIdParams &other) = default; + ConferenceIdParams(ConferenceIdParams &&other) = default; + ConferenceIdParams &operator=(const ConferenceIdParams &other) = default; + + virtual ~ConferenceIdParams() = default; + + bool getKeepGruu() const; + void setKeepGruu(bool keepGruu); + + bool extractUriEnabled() const; + void enableExtractUri(bool extractUri); + +private: + bool mKeepGruu = false; + bool mExtractUri = true; +}; + +LINPHONE_END_NAMESPACE + +#endif // ifndef _L_CONFERENCE_ID_PARAMS_H_ diff --git a/src/conference/conference-id.cpp b/src/conference/conference-id.cpp index 6a30bb3158e731aaf5c0defd34e0c06eb717694a..967238a351ba0c3f5204450a5952cd7a6dd5f7d3 100644 --- a/src/conference/conference-id.cpp +++ b/src/conference/conference-id.cpp @@ -20,6 +20,7 @@ #include "conference-id.h" #include "conference.h" + #include "logger/logger.h" // ============================================================================= @@ -28,26 +29,27 @@ using namespace std; LINPHONE_BEGIN_NAMESPACE +const std::string ConferenceId::IdentifierDelimiter = "##"; + ConferenceId::ConferenceId() { } -ConferenceId::ConferenceId(Address &&pAddress, Address &&lAddress) { - setPeerAddress(Address::create(pAddress)); - setLocalAddress(Address::create(lAddress)); +ConferenceId::ConferenceId(Address &&pAddress, Address &&lAddress, const ConferenceIdParams ¶ms) { + mParams = params; + mPeerAddress = processAddress(std::move(pAddress)); + mLocalAddress = processAddress(std::move(lAddress)); } ConferenceId::ConferenceId(const std::shared_ptr<const Address> &pAddress, - const std::shared_ptr<const Address> &lAddress) { - setPeerAddress(pAddress); - setLocalAddress(lAddress); -} - -ConferenceId::ConferenceId(const std::shared_ptr<Address> &pAddress, const std::shared_ptr<Address> &lAddress) { + const std::shared_ptr<const Address> &lAddress, + const ConferenceIdParams ¶ms) { + mParams = params; setPeerAddress(pAddress); setLocalAddress(lAddress); } ConferenceId::ConferenceId(const ConferenceId &other) { + mParams = other.mParams; setPeerAddress(other.mPeerAddress, true); setLocalAddress(other.mLocalAddress, true); mHash = other.mHash; @@ -55,6 +57,7 @@ ConferenceId::ConferenceId(const ConferenceId &other) { } ConferenceId &ConferenceId::operator=(const ConferenceId &other) { + mParams = other.mParams; setPeerAddress(other.mPeerAddress, true); setLocalAddress(other.mLocalAddress, true); mHash = other.mHash; @@ -76,6 +79,25 @@ bool ConferenceId::operator<(const ConferenceId &other) const { (*mPeerAddress == *(other.mPeerAddress) && *mLocalAddress < *(other.mLocalAddress)); } +std::shared_ptr<Address> ConferenceId::processAddress(const Address &addr) const { + if (addr.isValid()) { + if (mParams.extractUriEnabled()) { + if (mParams.getKeepGruu()) { + return Address::create(addr.getUri()); + } else { + return Address::create(addr.getUriWithoutGruu()); + } + } else { + auto processedAddress = Address::create(addr); + if (!mParams.getKeepGruu()) { + processedAddress->removeUriParam("gr"); + } + return processedAddress; + } + } + return Address::create(); +} + bool ConferenceId::canUpdateAddress(const std::shared_ptr<const Address> &addr, bool useLocal) const { // Local and peer addresses cannot be modified if the hash or weak hash have already been computed const auto newUri = (addr) ? addr->getUri() : Address(); @@ -85,19 +107,21 @@ bool ConferenceId::canUpdateAddress(const std::shared_ptr<const Address> &addr, } void ConferenceId::setPeerAddress(const std::shared_ptr<const Address> &addr, bool forceUpdate) { + if (!addr) return; if (!forceUpdate && !canUpdateAddress(addr, false)) { - lError() << "Cannot modify peer address if either its hash or its weak hash is defined"; + lError() << *this << ": Cannot modify peer address if either its hash or its weak hash is defined"; abort(); } - mPeerAddress = (addr) ? Address::create(addr->getUri()) : Address::create(); + mPeerAddress = processAddress(*addr); } void ConferenceId::setLocalAddress(const std::shared_ptr<const Address> &addr, bool forceUpdate) { + if (!addr) return; if (!forceUpdate && !canUpdateAddress(addr, true)) { - lInfo() << "Cannot modify local address if either its hash or its weak hash is defined"; + lInfo() << *this << ": Cannot modify local address if either its hash or its weak hash is defined"; abort(); } - mLocalAddress = (addr) ? Address::create(addr->getUri()) : Address::create(); + mLocalAddress = processAddress(*addr); } const std::shared_ptr<Address> &ConferenceId::getPeerAddress() const { @@ -121,6 +145,15 @@ size_t ConferenceId::getHash() const { return mHash; } +const std::string &ConferenceId::getIdentifier() const { + if (mIdentifier.empty()) { + const auto &pAddress = mPeerAddress ? reducedAddress(*mPeerAddress).toStringUriOnlyOrdered(false) : "sip:"; + const auto &lAddress = mLocalAddress ? reducedAddress(*mLocalAddress).toStringUriOnlyOrdered(false) : "sip:"; + mIdentifier += pAddress + ConferenceId::IdentifierDelimiter + lAddress; + } + return mIdentifier; +} + Address ConferenceId::reducedAddress(const Address &addr) { Address ret = addr.getUriWithoutGruu(); ret.removeUriParam(Conference::SecurityModeParameter); @@ -142,4 +175,15 @@ bool ConferenceId::weakEqual(const ConferenceId &other) const { reducedAddress(*mLocalAddress) == reducedAddress(*other.mLocalAddress); } +std::pair<std::shared_ptr<Address>, std::shared_ptr<Address>> +ConferenceId::parseIdentifier(const std::string &identifier) { + std::string peerAddressString = identifier.substr(0, identifier.find(ConferenceId::IdentifierDelimiter)); + std::string localAddressString = + identifier.substr(identifier.find(ConferenceId::IdentifierDelimiter) + ConferenceId::IdentifierDelimiter.size(), + identifier.size() - 1); + auto localAddress = Address::create(localAddressString); + auto peerAddress = Address::create(peerAddressString); + return std::make_pair(localAddress, peerAddress); +} + LINPHONE_END_NAMESPACE diff --git a/src/conference/conference-id.h b/src/conference/conference-id.h index 71b2cb3ecdbe52092604116cf44359f5a2f47904..cafa8ce33d669ca44e5dcb8b8e8c8f08ff699de5 100644 --- a/src/conference/conference-id.h +++ b/src/conference/conference-id.h @@ -22,6 +22,7 @@ #define _L_CONFERENCE_ID_H_ #include "address/address.h" +#include "conference-id-params.h" // ============================================================================= @@ -29,12 +30,16 @@ LINPHONE_BEGIN_NAMESPACE class LINPHONE_PUBLIC ConferenceId { public: + static const std::string IdentifierDelimiter; + static std::pair<std::shared_ptr<Address>, std::shared_ptr<Address>> parseIdentifier(const std::string &identifier); + ConferenceId(); // Caution: this optimized constructor does not care about extracting the URI part only. Use it for URI only // Address. - ConferenceId(Address &&peerAddress, Address &&localAddress); - ConferenceId(const std::shared_ptr<const Address> &peerAddress, const std::shared_ptr<const Address> &localAddress); - ConferenceId(const std::shared_ptr<Address> &peerAddress, const std::shared_ptr<Address> &localAddress); + ConferenceId(Address &&peerAddress, Address &&localAddress, const ConferenceIdParams ¶ms); + ConferenceId(const std::shared_ptr<const Address> &peerAddress, + const std::shared_ptr<const Address> &localAddress, + const ConferenceIdParams ¶ms); ConferenceId(const ConferenceId &other); ConferenceId(ConferenceId &&other) = default; @@ -57,6 +62,8 @@ public: void setPeerAddress(const std::shared_ptr<const Address> &addr, bool forceUpdate = false); void setLocalAddress(const std::shared_ptr<const Address> &addr, bool forceUpdate = false); + const std::string &getIdentifier() const; + bool isValid() const; size_t getHash() const; size_t getWeakHash() const; @@ -78,8 +85,11 @@ private: std::shared_ptr<Address> mLocalAddress; mutable size_t mHash = 0; mutable size_t mWeakHash = 0; + ConferenceIdParams mParams; + mutable std::string mIdentifier; bool canUpdateAddress(const std::shared_ptr<const Address> &addr, bool useLocal) const; + std::shared_ptr<Address> processAddress(const Address &addr) const; }; inline std::ostream &operator<<(std::ostream &os, const ConferenceId &conferenceId) { diff --git a/src/conference/conference-info.h b/src/conference/conference-info.h index a0eac30de39030f87fcefde42c220742a9e85f87..c442450e1d51f7061b3157e5f1e70bd8944e19d1 100644 --- a/src/conference/conference-info.h +++ b/src/conference/conference-info.h @@ -174,6 +174,12 @@ private: std::ostream &operator<<(std::ostream &lhs, ConferenceInfo::State s); +inline std::ostream &operator<<(std::ostream &str, const ConferenceInfo &conferenceInfo) { + const auto &uri = conferenceInfo.getUri(); + str << "ConferenceInfo [" << &conferenceInfo << "] (" << (uri ? uri->toString() : std::string("sip:")) << ")"; + return str; +} + LINPHONE_END_NAMESPACE #endif // ifndef _L_CONFERENCE_INFO_H_ diff --git a/src/conference/conference-interface.h b/src/conference/conference-interface.h index 363c032816399b30599f742e14dae9fbc106614a..633fab20e96b0a1b605ec615fc3240d9dbefdafe 100644 --- a/src/conference/conference-interface.h +++ b/src/conference/conference-interface.h @@ -148,7 +148,7 @@ public: <br> @param[in] participantAddress The address of the participant to add to this Conference. */ - virtual bool addParticipant(const std::shared_ptr<const Address> &participantAddress) = 0; + virtual bool addParticipant(const std::shared_ptr<Address> &participantAddress) = 0; /* * Same as function addParticipant(const std::shared_ptr<Address> &participantAddress), except that call to add is @@ -171,7 +171,7 @@ public: * @param[in] addresses * @return True if everything is OK, False otherwise */ - virtual bool addParticipants(const std::list<std::shared_ptr<const Address>> &addresses) = 0; + virtual bool addParticipants(const std::list<std::shared_ptr<Address>> &addresses) = 0; /* * Add local participant to this conference, this fonction is only available for local focus. Behavior is the same diff --git a/src/conference/conference-params-interface.h b/src/conference/conference-params-interface.h index 1d248b4e5b0d35c17d8ac780aaf0a0f535446637..d1ec03533d674e1d8ed404d0cf21c7436efcdc4a 100644 --- a/src/conference/conference-params-interface.h +++ b/src/conference/conference-params-interface.h @@ -58,7 +58,7 @@ public: *conferencing server * @param[in] The Address of the conference focus. **/ - virtual void setConferenceAddress(const std::shared_ptr<Address> conferenceAddress) = 0; + virtual void setConferenceAddress(const std::shared_ptr<Address> &conferenceAddress) = 0; /* * Set the subject of this conference in UTF8. If not focus, this operation is only available if the local @@ -67,13 +67,6 @@ public: */ virtual void setUtf8Subject(const std::string &subject) = 0; - /* - *Set participant representing myself in this Conference. - *If set this participant is added to the conference - * @param[in] participantAddress of the conference focus. - */ - virtual void setMe(const std::shared_ptr<Address> &participantAddress) = 0; - /* * Enable audio media type for a conference * @param enable If true, audio will be enabled during conference diff --git a/src/conference/conference-params.cpp b/src/conference/conference-params.cpp index a92c57226e64cae7a983e059da39f99872b19de1..4eb6b1b7d4c2625528507055bda01509d61e0f0b 100644 --- a/src/conference/conference-params.cpp +++ b/src/conference/conference-params.cpp @@ -70,13 +70,13 @@ ConferenceParams::ConferenceParams(const ConferenceParams &other) void ConferenceParams::setAudioVideoDefaults() { try { - auto core = getCore()->getCCore(); - if (core) { + auto cCore = getCore()->getCCore(); + if (cCore) { enableAudio(true); - const LinphoneVideoActivationPolicy *policy = linphone_core_get_video_activation_policy(core); + const LinphoneVideoActivationPolicy *policy = linphone_core_get_video_activation_policy(cCore); enableVideo(linphone_video_activation_policy_get_automatically_initiate(policy)); setParticipantListType( - static_cast<ParticipantListType>(linphone_core_get_conference_participant_list_type(core))); + static_cast<ParticipantListType>(linphone_core_get_conference_participant_list_type(cCore))); } } catch (const bad_weak_ptr &) { } @@ -113,12 +113,6 @@ void ConferenceParams::updateFromAccount( if (account) { auto accountParams = account->getAccountParams(); if (accountParams) { - auto identity = accountParams->getIdentityAddress(); - if (identity) { - setMe(identity); - } else { - setMe(nullptr); - } if (mUseDefaultFactoryAddress) { const auto &conferenceFactory = (audioEnabled() || videoEnabled()) ? accountParams->getAudioVideoConferenceFactoryAddress() @@ -155,8 +149,14 @@ void ConferenceParams::setUtf8Subject(const std::string &subject) { mUtf8Subject = subject; } -void ConferenceParams::setConferenceAddress(const std::shared_ptr<Address> conferenceAddress) { - mConferenceAddress = Address::create(conferenceAddress->getUri()); +void ConferenceParams::setConferenceAddress(const std::shared_ptr<Address> &conferenceAddress) { + auto cCore = getCore()->getCCore(); + bool keepGruu = !!linphone_core_gruu_in_conference_address_enabled(cCore); + if (keepGruu) { + mConferenceAddress = Address::create(conferenceAddress->getUri()); + } else { + mConferenceAddress = Address::create(conferenceAddress->getUriWithoutGruu()); + } }; bool ConferenceParams::isGroup() const { @@ -170,6 +170,13 @@ void ConferenceParams::setGroup(bool group) { } } +void ConferenceParams::setSecurityLevel(const SecurityLevel &level) { + mSecurityLevel = level; + if (mChatParams) { + mChatParams->updateSecurityParams((mSecurityLevel == SecurityLevel::EndToEnd)); + } +}; + void ConferenceParams::updateAccordingToCapabilities(AbstractChatRoom::CapabilitiesMask capabilities) { if (capabilities & AbstractChatRoom::CapabilitiesMask(AbstractChatRoom::Capabilities::Basic)) { mChatParams->setBackend(ChatParams::Backend::Basic); diff --git a/src/conference/conference-params.h b/src/conference/conference-params.h index 513ce0db7b968e04c14e419975b9007d53018d03..a28d1bf4cd484ffaf9802bbc22313bb8bd23a3be 100644 --- a/src/conference/conference-params.h +++ b/src/conference/conference-params.h @@ -108,7 +108,7 @@ public: return mAllowOneParticipantConference; } - virtual void setConferenceAddress(const std::shared_ptr<Address> conferenceAddress) override; + virtual void setConferenceAddress(const std::shared_ptr<Address> &conferenceAddress) override; std::shared_ptr<Address> getConferenceAddress() const { return mConferenceAddress; }; @@ -128,14 +128,6 @@ public: return mUtf8Subject; } const std::string &getSubject() const; - - virtual void setMe(const std::shared_ptr<Address> &participantAddress) override { - mMe = participantAddress ? participantAddress->clone()->toSharedPtr() : nullptr; - }; - std::shared_ptr<Address> getMe() const { - return mMe; - }; - void setAccount(const std::shared_ptr<Account> &a); std::shared_ptr<Account> getAccount() const; @@ -173,12 +165,7 @@ public: return mJoinMode; }; - virtual void setSecurityLevel(const SecurityLevel &level) override { - mSecurityLevel = level; - if (mChatParams) { - mChatParams->updateSecurityParams((mSecurityLevel != SecurityLevel::None)); - } - }; + virtual void setSecurityLevel(const SecurityLevel &level) override; const SecurityLevel &getSecurityLevel() const { return mSecurityLevel; }; diff --git a/src/conference/conference-scheduler.cpp b/src/conference/conference-scheduler.cpp index 000202786a43c853d245e32e77ab979d1562e6c4..fc3326854d55b4ec297d3b838f1994e2596596a3 100644 --- a/src/conference/conference-scheduler.cpp +++ b/src/conference/conference-scheduler.cpp @@ -123,8 +123,8 @@ void ConferenceScheduler::setInfo(const std::shared_ptr<ConferenceInfo> &info) { mAccount = getCore()->getDefaultAccount(); } - const auto creator = getAccount() ? getAccount()->getAccountParams()->getIdentityAddress() - : Address::create(linphone_core_get_identity(getCore()->getCCore())); + auto creator = getAccount() ? getAccount()->getAccountParams()->getIdentityAddress()->clone()->toSharedPtr() + : Address::create(linphone_core_get_identity(getCore()->getCCore())); if (!creator || !creator->isValid()) { lWarning() << "[Conference Scheduler] [" << this << "] Unable to determine the address of the user attempting to set the conference information!"; @@ -206,7 +206,7 @@ void ConferenceScheduler::setInfo(const std::shared_ptr<ConferenceInfo> &info) { mConferenceInfo = clone; - createOrUpdateConference(mConferenceInfo, creator); + createOrUpdateConference(mConferenceInfo); } void ConferenceScheduler::onChatMessageStateChanged(const shared_ptr<ChatMessage> &message, ChatMessage::State state) { @@ -279,25 +279,27 @@ void ConferenceScheduler::setConferenceAddress(const std::shared_ptr<Address> &c if (getState() == State::AllocationPending) { lInfo() << "[Conference Scheduler] [" << this - << "] Conference has been succesfully created: " << *conferenceAddress; + << "] Conference has been successfully created: " << *conferenceAddress; mConferenceInfo->setUri(conferenceAddress); } else { const auto &uri = mConferenceInfo->getUri(); // No need to update the conference address during an update lInfo() << "[Conference Scheduler] [" << this - << "] Conference has been succesfully updated: " << (uri ? uri->toString() : std::string("sip:")); + << "] Conference has been successfully updated: " << (uri ? uri->toString() : std::string("sip:")); } + bool error = false; #ifdef HAVE_DB_STORAGE auto &mainDb = getCore()->getPrivate()->mainDb; if (mainDb) { lInfo() << "[Conference Scheduler] [" << this << "] Conference address " << *conferenceAddress << " is known, inserting conference info in database"; - mainDb->insertConferenceInfo(mConferenceInfo); + error = (mainDb->insertConferenceInfo(mConferenceInfo) < 0); } #endif - setState(State::Ready); + auto newState = error ? State::Error : State::Ready; + setState(newState); } shared_ptr<ChatMessage> ConferenceScheduler::createInvitationChatMessage(shared_ptr<AbstractChatRoom> chatRoom, @@ -468,7 +470,7 @@ void ConferenceScheduler::sendInvitations(shared_ptr<ConferenceParams> conferenc // Sending the ICS once for each participant in a separated chat room each time. for (auto participant : mInvitationsToSend) { - list<std::shared_ptr<const Address>> chatRoomParticipantList; + list<std::shared_ptr<Address>> chatRoomParticipantList; chatRoomParticipantList.push_back(participant); list<std::shared_ptr<Address>> participantList; std::shared_ptr<Address> remoteAddress = nullptr; @@ -483,7 +485,7 @@ void ConferenceScheduler::sendInvitations(shared_ptr<ConferenceParams> conferenc if (!chatRoom) { lInfo() << "[Conference Scheduler] [" << this << "] Existing chat room between [" << *sender << "] and [" << *participant << "] wasn't found, creating it."; - chatRoom = getCore()->getPrivate()->createChatRoom(conferenceParams, sender, chatRoomParticipantList); + chatRoom = getCore()->getPrivate()->createChatRoom(conferenceParams, chatRoomParticipantList); } else { lInfo() << "[Conference Scheduler] [" << this << "] Found existing chat room [" << *chatRoom->getPeerAddress() << "] between [" << *sender << "] and [" << *participant diff --git a/src/conference/conference-scheduler.h b/src/conference/conference-scheduler.h index 02e99fa2b9e339e9642da7db7060129446e465cc..0b44d2f8b6e9d92c38d01e0c5a3cd80372697ab1 100644 --- a/src/conference/conference-scheduler.h +++ b/src/conference/conference-scheduler.h @@ -79,8 +79,7 @@ protected: bool cancel); void fillCancelList(const ConferenceInfo::participant_list_t &oldList, const ConferenceInfo::participant_list_t &newList); - virtual void createOrUpdateConference(const std::shared_ptr<ConferenceInfo> &conferenceInfo, - const std::shared_ptr<Address> &creator) = 0; + virtual void createOrUpdateConference(const std::shared_ptr<ConferenceInfo> &conferenceInfo) = 0; virtual void processResponse(const LinphoneErrorInfo *errorCode, const std::shared_ptr<Address> conferenceAddress) = 0; diff --git a/src/conference/conference.cpp b/src/conference/conference.cpp index 92d617e6b116f32913f18de1952fa054b15b3af8..939359a9564ee967202d3ec32403378135b45551 100644 --- a/src/conference/conference.cpp +++ b/src/conference/conference.cpp @@ -21,6 +21,7 @@ #include <bctoolbox/defs.h> #include "call/call.h" +#include "conference/conference-id-params.h" #include "conference/conference.h" #include "conference/notify-conference-listener.h" #include "conference/params/media-session-params-p.h" @@ -60,14 +61,11 @@ const std::string Conference::IsFocusParameter = "isfocus"; const std::string Conference::TextParameter = "text"; Conference::Conference(const shared_ptr<Core> &core, - const std::shared_ptr<Address> &myAddress, std::shared_ptr<CallSessionListener> callSessionListener, const std::shared_ptr<const ConferenceParams> params) - : CoreAccessor(core), mConferenceId(Address(), Address(*myAddress)) { + : CoreAccessor(core) { mCallSessionListener = callSessionListener; update(*params); - mConfParams->setMe(myAddress); - if (mConfParams->videoEnabled()) { // If video is enabled, then always enable audio capabilities mConfParams->enableAudio(true); @@ -100,6 +98,27 @@ Conference::~Conference() { } // ----------------------------------------------------------------------------- +void Conference::inititializeMe() { + const auto &core = getCore(); + auto account = mConfParams->getAccount(); + if (!account) { + account = core->getDefaultAccount(); + if (account) { + lInfo() << "Using default account " << *account << " for " << *this + << " because it was not set in the conference parameters"; + mConfParams->setAccount(account); + } else { + lInfo() << "Not setting account for " << *this + << " because it was not set in the conference parameters and the core has no default account"; + } + } + + if (account) { + auto meAddress = account->getAccountParams()->getIdentityAddress()->getUri(); + mConferenceId = ConferenceId(Address(), std::move(meAddress), core->createConferenceIdParams()); + mMe = Participant::create(getSharedFromThis(), mConferenceId.getLocalAddress()); + } +} void Conference::removeListener(std::shared_ptr<ConferenceListenerInterface> listener) { mConfListeners.remove(listener); @@ -175,6 +194,8 @@ std::shared_ptr<ParticipantDevice> Conference::createParticipantDevice(std::shar << " (address " << *dAddress << ") which is requesting to join " << *this; } device->setState(deviceState, false); + // By default send a NOTIFY to inform the other clients that has been added + device->setSendAddedNotify(true); return device; } else { lDebug() << "Participant with address " << *remoteAddress << " has already a device with session " << session; @@ -186,11 +207,36 @@ bool Conference::addParticipantDevice(std::shared_ptr<Call> call) { const std::shared_ptr<Address> &remoteAddress = call->getRemoteAddress(); auto p = findParticipant(remoteAddress); if (p) { - return (createParticipantDevice(p, call) != nullptr); + auto device = createParticipantDevice(p, call); + bool added = (device != nullptr); + if (added) { + notifyNewDevice(device); + } + return added; } + return false; } +void Conference::addParticipantDevice(BCTBX_UNUSED(const shared_ptr<Participant> &participant), + BCTBX_UNUSED(const shared_ptr<ParticipantDeviceIdentity> &deviceInfo)) { + lWarning() << __func__ << " not implemented"; +} + +void Conference::notifyNewDevice(const std::shared_ptr<ParticipantDevice> &device) { + if (device) { + const auto &p = device->getParticipant(); + if (p) { + time_t creationTime = time(nullptr); + if (device->getState() == ParticipantDevice::State::Joining) { + notifyParticipantDeviceAdded(creationTime, false, p, device); + } else { + notifyParticipantDeviceJoiningRequest(creationTime, false, p, device); + } + } + } +} + Address Conference::createParticipantAddressForResourceList(const std::shared_ptr<Participant> &p) { Address address = *p->getAddress(); address.setUriParam(ParticipantInfo::roleParameter, Participant::roleToText(p->getRole())); @@ -296,7 +342,8 @@ std::shared_ptr<Participant> Conference::createParticipant(std::shared_ptr<Call> participant->setAdmin(value); } - createParticipantDevice(participant, call); + auto device = createParticipantDevice(participant, call); + device->setSendAddedNotify(false); return participant; } @@ -316,11 +363,10 @@ bool Conference::addParticipant(std::shared_ptr<Call> call) { lWarning() << "Participant with address " << *remoteAddress << " is already part of " << *this; success = false; } - return success; } -bool Conference::addParticipant(const std::shared_ptr<const Address> &participantAddress) { +bool Conference::addParticipant(const std::shared_ptr<Address> &participantAddress) { shared_ptr<Participant> participant = findParticipant(participantAddress); if (participant) { lWarning() << "Not adding participant '" << *participantAddress << "' because it is already a participant of " @@ -334,16 +380,15 @@ bool Conference::addParticipant(const std::shared_ptr<const Address> &participan lInfo() << "Participant with address " << *participantAddress << " has been added to " << *this; time_t creationTime = time(nullptr); notifyParticipantAdded(creationTime, false, participant); - return true; } std::shared_ptr<CallSession> Conference::getMainSession() const { - return mMe->getSession(); + return mMe ? mMe->getSession() : nullptr; } -bool Conference::addParticipants(const std::list<std::shared_ptr<const Address>> &addresses) { - list<std::shared_ptr<const Address>> sortedAddresses(addresses); +bool Conference::addParticipants(const std::list<std::shared_ptr<Address>> &addresses) { + list<std::shared_ptr<Address>> sortedAddresses(addresses); sortedAddresses.sort([](const auto &addr1, const auto &addr2) { return *addr1 < *addr2; }); sortedAddresses.unique([](const auto &addr1, const auto &addr2) { return addr1->weakEqual(*addr2); }); @@ -692,14 +737,9 @@ void Conference::setConferenceAddress(const std::shared_ptr<Address> &conference return; } - if (linphone_core_conference_server_enabled(getCore()->getCCore())) { - mConfParams->setConferenceAddress(Address::create(conferenceAddress->getUriWithoutGruu())); - } else { - // Handle backward compatibility with release/5.3 - mConfParams->setConferenceAddress(conferenceAddress); - } + mConfParams->setConferenceAddress(conferenceAddress); setState(ConferenceInterface::State::CreationPending); - lInfo() << "Conference " << this << " has been given the address " << *conferenceAddress; + lInfo() << "Conference " << this << " has been given the address " << *mConfParams->getConferenceAddress(); } else { lDebug() << "Cannot set the conference address of the Conference in state " << state << " to " << *conferenceAddress; @@ -719,6 +759,14 @@ const list<shared_ptr<Participant>> &Conference::getParticipants() const { return mParticipants; } +std::list<std::shared_ptr<Address>> Conference::getParticipantAddresses() const { + list<std::shared_ptr<Address>> addresses; + for (auto &participant : mParticipants) { + addresses.push_back(participant->getAddress()); + } + return addresses; +} + list<shared_ptr<ParticipantDevice>> Conference::getParticipantDevices(bool includeMe) const { list<shared_ptr<ParticipantDevice>> devices; for (const auto &p : mParticipants) { @@ -1041,6 +1089,14 @@ std::map<ConferenceMediaCapabilities, bool> Conference::getMediaCapabilities() c // ----------------------------------------------------------------------------- bool Conference::isMe(const std::shared_ptr<const Address> &addr) const { + if (!mMe) { + // Cannot know if it is the me participant if it is not defined. + // This may happen when a server chat room is retrieved from the database. The me participant is not defined as + // it has already been created. The server is therefore a passive component whose task is to dispatch MESSAGE + // request or handle participants. + lDebug() << *this << ": Unable to known if the me participant is not defined"; + return false; + } if (!addr || !addr->isValid()) { lError() << *this << ": Unable to known if an invalid address is the me participant"; return false; @@ -1073,6 +1129,13 @@ const ConferenceId &Conference::getConferenceId() const { return mConferenceId; } +std::optional<std::reference_wrapper<const std::string>> Conference::getIdentifier() const { + if (mState == ConferenceInterface::State::Instantiated) { + return std::nullopt; + } + return mConferenceId.getIdentifier(); +} + void Conference::notifyFullState() { for (const auto &l : mConfListeners) { l->onFullStateReceived(); @@ -1335,35 +1398,39 @@ bool Conference::isTerminationState(ConferenceInterface::State state) { } void Conference::setState(ConferenceInterface::State state) { - ConferenceInterface::State previousState = getState(); - shared_ptr<Conference> ref = getSharedFromThis(); - // Change state if: - // - current state is not Deleted - // - current state is Deleted and trying to move to Instantiated state - if ((previousState != ConferenceInterface::State::Deleted) || - ((previousState == ConferenceInterface::State::Deleted) && - (state == ConferenceInterface::State::Instantiated))) { - if (mState != state) { - if (linphone_core_get_global_state(getCore()->getCCore()) == LinphoneGlobalStartup) { - lDebug() << "Switching conference [" << this << "] from state " << mState << " to " << state; - } else { - lInfo() << "Switching conference [" << this << "] from state " << mState << " to " << state; + try { + ConferenceInterface::State previousState = getState(); + shared_ptr<Conference> ref = getSharedFromThis(); + // Change state if: + // - current state is not Deleted + // - current state is Deleted and trying to move to Instantiated state + if ((previousState != ConferenceInterface::State::Deleted) || + ((previousState == ConferenceInterface::State::Deleted) && + (state == ConferenceInterface::State::Instantiated))) { + if (mState != state) { + if (linphone_core_get_global_state(getCore()->getCCore()) == LinphoneGlobalStartup) { + lDebug() << "Switching " << *this << " from state " << mState << " to " << state; + } else { + lInfo() << "Switching " << *this << " from state " << mState << " to " << state; + } + mState = state; + notifyStateChanged(mState); } - mState = state; - notifyStateChanged(mState); } - } - if (mState == ConferenceInterface::State::Terminated) { - onConferenceTerminated(getConferenceAddress()); - } else if (mState == ConferenceInterface::State::Deleted) { - // If core is in Global Shutdown state, then do not remove it from the map as it will be freed by Core::uninit() - if (linphone_core_get_global_state(getCore()->getCCore()) != LinphoneGlobalShutdown) { - getCore()->deleteConference(ref); - } + if (mState == ConferenceInterface::State::Terminated) { + onConferenceTerminated(getConferenceAddress()); + } else if (mState == ConferenceInterface::State::Deleted) { + // If core is in Global Shutdown state, then do not remove it from the map as it will be freed by + // Core::uninit() + if (linphone_core_get_global_state(getCore()->getCCore()) != LinphoneGlobalShutdown) { + getCore()->deleteConference(ref); + } #ifdef HAVE_ADVANCED_IM - setChatRoom(nullptr); + setChatRoom(nullptr); #endif // HAVE_ADVANCED_IM + } + } catch (const bad_weak_ptr &) { } } @@ -1797,7 +1864,7 @@ int Conference::stopRecording() { if (aci) { aci->stopRecording(); } else { - lError() << "ServerConference::stopRecording(): no audio mixer."; + lError() << "Conference::stopRecording(): no audio mixer."; return -1; } return 0; diff --git a/src/conference/conference.h b/src/conference/conference.h index e79425fe932f8fe5cec174c88ccbbda3a47b25f9..705a2b7f0bdc58a8a5b8d591cdb6f23dbb6e50b0 100644 --- a/src/conference/conference.h +++ b/src/conference/conference.h @@ -80,9 +80,9 @@ public: virtual ~Conference(); - virtual int inviteAddresses(const std::list<std::shared_ptr<const Address>> &addresses, + virtual int inviteAddresses(const std::list<std::shared_ptr<Address>> &addresses, const LinphoneCallParams *params) = 0; - virtual bool dialOutAddresses(const std::list<std::shared_ptr<const Address>> &addressList) = 0; + virtual bool dialOutAddresses(const std::list<std::shared_ptr<Address>> &addressList) = 0; virtual bool finalizeParticipantAddition(std::shared_ptr<Call> call) = 0; std::shared_ptr<Participant> getActiveParticipant() const; @@ -114,12 +114,15 @@ public: void addInvitedParticipant(const std::shared_ptr<Participant> &participant); bool addParticipant(std::shared_ptr<Call> call) override; bool addParticipant(const std::shared_ptr<ParticipantInfo> &info) override; - bool addParticipant(const std::shared_ptr<const Address> &participantAddress) override; - bool addParticipants(const std::list<std::shared_ptr<const Address>> &addresses) override; + bool addParticipant(const std::shared_ptr<Address> &participantAddress) override; + bool addParticipants(const std::list<std::shared_ptr<Address>> &addresses) override; virtual bool addParticipantDevice(std::shared_ptr<Call> call); + virtual void addParticipantDevice(const std::shared_ptr<Participant> &participant, + const std::shared_ptr<ParticipantDeviceIdentity> &deviceInfo); int getParticipantCount() const override; const std::list<std::shared_ptr<Participant>> &getParticipants() const override; + std::list<std::shared_ptr<Address>> getParticipantAddresses() const; std::list<std::shared_ptr<ParticipantDevice>> getParticipantDevices(bool includeMe = true) const override; std::shared_ptr<Participant> getScreenSharingParticipant() const; std::shared_ptr<ParticipantDevice> getScreenSharingDevice() const; @@ -180,6 +183,7 @@ public: void addListener(std::shared_ptr<ConferenceListenerInterface> listener) override; const ConferenceId &getConferenceId() const override; + std::optional<std::reference_wrapper<const std::string>> getIdentifier() const; inline unsigned int getLastNotify() const { return mLastNotify; }; @@ -349,9 +353,10 @@ public: const std::shared_ptr<LinphonePrivate::Address> &referAddr, const std::string method) = 0; + void resetLastNotify(); + protected: explicit Conference(const std::shared_ptr<Core> &core, - const std::shared_ptr<Address> &myAddress, std::shared_ptr<CallSessionListener> callSessionListener, const std::shared_ptr<const ConferenceParams> params); @@ -420,11 +425,13 @@ protected: std::list<std::shared_ptr<Participant>> getFullParticipantList() const; void fillParticipantAttributes(std::shared_ptr<Participant> &p) const; + void notifyNewDevice(const std::shared_ptr<ParticipantDevice> &device); + virtual void configure(SalCallOp *op) = 0; + void inititializeMe(); void incrementLastNotify(); void setLastNotify(unsigned int lastNotify); - void resetLastNotify(); void setConferenceId(const ConferenceId &conferenceId); diff --git a/src/conference/db-conference-scheduler.cpp b/src/conference/db-conference-scheduler.cpp index cc77bc8d1cc25843bf9a4e0e23428b44b22bf7ff..b8df1b126420daba775e76617a2d736aaee5ba0d 100644 --- a/src/conference/db-conference-scheduler.cpp +++ b/src/conference/db-conference-scheduler.cpp @@ -38,13 +38,15 @@ DBConferenceScheduler::DBConferenceScheduler(const shared_ptr<Core> &core, const } void DBConferenceScheduler::createOrUpdateConference( - BCTBX_UNUSED(const std::shared_ptr<ConferenceInfo> &conferenceInfo), const std::shared_ptr<Address> &creator) { + BCTBX_UNUSED(const std::shared_ptr<ConferenceInfo> &conferenceInfo)) { std::shared_ptr<Address> conferenceAddress = nullptr; if (getState() == State::AllocationPending) { if (mConferenceInfo->getDateTime() <= 0) { // Set start time only if a conference is going to be created mConferenceInfo->setDateTime(ms_time(NULL)); } + const auto &account = getAccount() ? getAccount() : getCore()->getDefaultAccount(); + const auto &creator = account->getAccountParams()->getIdentityAddress(); conferenceAddress = creator->clone()->toSharedPtr(); char confId[LinphonePrivate::ServerConference::sConfIdLength]; belle_sip_random_token(confId, sizeof(confId)); diff --git a/src/conference/db-conference-scheduler.h b/src/conference/db-conference-scheduler.h index 436bbc2f1474da786040f0a22bf247019f9e7f10..7ec081992ffca2b437a76b4a7c7e1325727a9997 100644 --- a/src/conference/db-conference-scheduler.h +++ b/src/conference/db-conference-scheduler.h @@ -32,8 +32,7 @@ public: DBConferenceScheduler(const std::shared_ptr<Core> &core, const std::shared_ptr<Account> &account = nullptr); virtual ~DBConferenceScheduler() = default; - virtual void createOrUpdateConference(const std::shared_ptr<ConferenceInfo> &conferenceInfo, - const std::shared_ptr<Address> &creator) override; + virtual void createOrUpdateConference(const std::shared_ptr<ConferenceInfo> &conferenceInfo) override; virtual void processResponse(const LinphoneErrorInfo *errorInfo, const std::shared_ptr<Address> conferenceAddress) override; diff --git a/src/conference/encryption/client-ekt-manager.cpp b/src/conference/encryption/client-ekt-manager.cpp index dc462d8dc20dae678959fe7a4fc67ac84cfeb5f1..7c015e47904fde1fe5dbefba85a1b89874fc8f7d 100644 --- a/src/conference/encryption/client-ekt-manager.cpp +++ b/src/conference/encryption/client-ekt-manager.cpp @@ -109,11 +109,11 @@ void ClientEktManager::EktContext::fillMSParametersSet(MSEKTParametersSet *param params->ekt_cipher_type = mCipherType; params->ekt_srtp_crypto_suite = mCryptoSuite; if (mCipherType == MS_EKT_CIPHERTYPE_AESKW128) { - std::copy(mEkt.data(), mEkt.data() + 16, params->ekt_key_value); + std::copy_n(mEkt.data(), 16, params->ekt_key_value); } else { - std::copy(mEkt.data(), mEkt.data() + 32, params->ekt_key_value); + std::copy_n(mEkt.data(), 32, params->ekt_key_value); } - std::copy(mCSpi.data(), mCSpi.data() + 14, params->ekt_master_salt); + std::copy_n(mCSpi.data(), 14, params->ekt_master_salt); params->ekt_spi = mSSpi; params->ekt_ttl = 0; } @@ -166,9 +166,7 @@ ClientEktManager::~ClientEktManager() { } void ClientEktManager::onNetworkReachable(bool sipNetworkReachable, BCTBX_UNUSED(bool mediaNetworkReachable)) { - if (sipNetworkReachable) { - subscribe(); - } else { + if (!sipNetworkReachable) { if (mEventSubscribe) { mEventSubscribe->terminate(); mEventSubscribe = nullptr; @@ -180,14 +178,6 @@ void ClientEktManager::onNetworkReachable(bool sipNetworkReachable, BCTBX_UNUSED } } -void ClientEktManager::onAccountRegistrationStateChanged(BCTBX_UNUSED(std::shared_ptr<Account> account), - LinphoneRegistrationState state, - BCTBX_UNUSED(const std::string &message)) { - if (state == LinphoneRegistrationOk) { - subscribe(); - } -} - void ClientEktManager::onStateChanged(ConferenceInterface::State newState) { switch (newState) { case ConferenceInterface::State::None: @@ -413,8 +403,16 @@ void ClientEktManager::notifyReceived(const Content &content) { } void ClientEktManager::sendPublish(const shared_ptr<EktInfo> &ei) { - auto core = mClientConf.lock()->getCore(); - string xmlBody = core->createXmlFromEktInfo(ei); + auto sharedClientConf = mClientConf.lock(); + if (!sharedClientConf) { + lWarning() << __func__ << " : Ignoring the attempt to send an EKT PUBLISH from a null ClientConference"; + return; + } + + auto core = sharedClientConf->getCore(); + auto account = sharedClientConf->getAccount(); + + string xmlBody = core->createXmlFromEktInfo(ei, account); shared_ptr<Content> content = make_shared<Content>(); ContentType contentType; @@ -443,7 +441,7 @@ void ClientEktManager::publishCipheredEkt(const shared_ptr<EktInfo> &ei, } auto ciphers = make_shared<Dictionary>(); shared_ptr<Buffer> buffer = nullptr; - for (auto cipher : cipherTexts) { + for (const auto &cipher : cipherTexts) { buffer = make_shared<Buffer>(cipher.second); ciphers->setProperty(cipher.first, buffer); } diff --git a/src/conference/encryption/client-ekt-manager.h b/src/conference/encryption/client-ekt-manager.h index f653505c9cfafb2b12c9024f36fd88cb21913312..8c83e371fc0cabd52e2396eada786f40d02485aa 100644 --- a/src/conference/encryption/client-ekt-manager.h +++ b/src/conference/encryption/client-ekt-manager.h @@ -90,10 +90,6 @@ public: ~ClientEktManager() override; void onNetworkReachable(bool sipNetworkReachable, BCTBX_UNUSED(bool mediaNetworkReachable)) override; - void onAccountRegistrationStateChanged(BCTBX_UNUSED(std::shared_ptr<Account> account), - LinphoneRegistrationState state, - BCTBX_UNUSED(const std::string &message)) override; - void onStateChanged(ConferenceInterface::State newState) override; static void onPublishStateChangedCb(LinphoneEvent *lev, LinphonePublishState state); diff --git a/src/conference/handlers/client-conference-event-handler.cpp b/src/conference/handlers/client-conference-event-handler.cpp index 4ef965175858ccbdd705a650e19a460c9e38e99f..87fe6d794d5b025ac4d2a7961d5b80c42a56cb5e 100644 --- a/src/conference/handlers/client-conference-event-handler.cpp +++ b/src/conference/handlers/client-conference-event-handler.cpp @@ -62,7 +62,12 @@ ClientConferenceEventHandler::ClientConferenceEventHandler(const std::shared_ptr : CoreAccessor(core) { conf = clientConference; confListener = listener; - getCore()->getPrivate()->registerListener(this); + try { + getCore()->getPrivate()->registerListener(this); + } catch (const bad_weak_ptr &) { + lError() << "ClientConferenceEventHandler [" << this + << "]: Unable to register listener as the core has already been destroyed"; + } } ClientConferenceEventHandler::~ClientConferenceEventHandler() { @@ -145,634 +150,661 @@ void ClientConferenceEventHandler::conferenceInfoNotifyReceived(const string &xm return; } - const auto &core = getCore(); - auto chatRoom = core->findChatRoom(getConferenceId()); - - const auto &conferenceAddress = getConference()->getConferenceAddress(); - std::shared_ptr<Address> entityAddress = Address::create(confInfo->getEntity()); - if (!conferenceAddress || (*entityAddress != *conferenceAddress)) { - lError() << "Unable to process received NOTIFY because the entity address " << *entityAddress - << " doesn't match the conference address " - << (conferenceAddress ? conferenceAddress->toString() : std::string("sip:")); - return; - } + try { + bool isFullState = (confInfo->getState() == StateType::full); + const auto conference = getConference(); + if (waitingFullState && !isFullState) { + // No need to do any processing if the client is waiting a full state + lError() << "Unable to process received NOTIFY because " << *conference << " is waiting a full state"; + return; + } else { + waitingFullState = false; + } - bool isFullState = confInfo->getState() == StateType::full; + const auto &core = getCore(); + const auto &chatRoom = conference->getChatRoom(); + const auto &conferenceAddress = conference->getConferenceAddress(); + std::shared_ptr<Address> entityAddress = Address::create(confInfo->getEntity()); + auto prunedEntityAddress = entityAddress ? entityAddress->getUriWithoutGruu() : Address(); + auto prunedConferenceAddress = conferenceAddress ? conferenceAddress->getUriWithoutGruu() : Address(); + + if (!conferenceAddress || (prunedEntityAddress != prunedConferenceAddress)) { + lError() << "Unable to process received NOTIFY for conference " << *conference + << " because the entity address " << prunedEntityAddress + << " doesn't match the conference address " << prunedConferenceAddress + << " or the conference address is not valid"; + return; + } - if (isFullState) { - setInitialSubscriptionUnderWayFlag(false); - } - bool synchronizing = fullStateRequested; - if (isFullState && fullStateRequested) { - fullStateRequested = false; - } + if (isFullState) { + setInitialSubscriptionUnderWayFlag(false); + } - if (waitingFullState && !isFullState) { - lError() << "Unable to process received NOTIFY because " << *getConference() << " is waiting a full state"; - return; - } else { - waitingFullState = false; - } + // The client is synchronizing when it requested a full state as there was a discrepancy between its last notify + // ID and the one from the server or the server sent a full state to catch up a big number of missed events + const auto currentNotifyVersion = getLastNotify(); + bool synchronizing = fullStateRequested || (isFullState && (currentNotifyVersion != 0)); + if (isFullState && fullStateRequested) { + fullStateRequested = false; + } - auto &confDescription = confInfo->getConferenceDescription(); + auto &confDescription = confInfo->getConferenceDescription(); - // 1. Compute event time. - time_t creationTime = time(nullptr); - { - if (confDescription.present()) { - auto &freeText = confDescription->getFreeText(); - if (freeText.present()) creationTime = static_cast<time_t>(Utils::stoll(freeText.get())); + // 1. Compute event time. + time_t creationTime = time(nullptr); + { + if (confDescription.present()) { + auto &freeText = confDescription->getFreeText(); + if (freeText.present()) creationTime = static_cast<time_t>(Utils::stoll(freeText.get())); + } } - } - // 2. Update last notify. - { - const auto previousLastNotify = getLastNotify(); - auto &version = confInfo->getVersion(); - if (version.present()) { - unsigned int notifyVersion = version.get(); - synchronizing |= isFullState && (previousLastNotify >= notifyVersion); - if (!isFullState && (previousLastNotify >= notifyVersion)) { - lWarning() << "Ignoring conference notify for: " << getConferenceId() - << ", notify version received is: " << notifyVersion - << ", should be stricly more than last notify id of conference: " << previousLastNotify; - requestFullState(); - return; - } - getConference()->setLastNotify(version.get()); - if (chatRoom) { - // Update last notify ID in the DB just in case the notify does not generate any further event - core->getPrivate()->mainDb->updateNotifyId(chatRoom, getLastNotify()); + // 2. Update last notify. + { + auto &version = confInfo->getVersion(); + if (version.present()) { + unsigned int notifyVersion = version.get(); + if (!isFullState && (currentNotifyVersion >= notifyVersion)) { + lWarning() << "Ignoring conference notify for " << *conference << " (" << getConferenceId() + << "): notify version received (" << notifyVersion + << ") should be stricly larger than the current notify version (" << currentNotifyVersion + << ")"; + requestFullState(); + return; + } + conference->setLastNotify(version.get()); + if (chatRoom) { + // Update last notify ID in the DB just in case the notify does not generate any further event + core->getPrivate()->mainDb->updateNotifyId(chatRoom, getLastNotify()); + } } } - } - // 3. Notify ephemeral settings, media, subject and keywords. - if (confDescription.present()) { - auto &subject = confDescription.get().getSubject(); - if (subject.present() && !subject.get().empty()) { - if (getConference()->getUtf8Subject() != subject.get()) { - getConference()->Conference::setUtf8Subject(subject.get()); - if (!isFullState) { - // Subject must be stored in the system locale - getConference()->notifySubjectChanged(creationTime, isFullState, subject.get()); + // 3. Notify ephemeral settings, media, subject and keywords. + if (confDescription.present()) { + auto &subject = confDescription.get().getSubject(); + if (subject.present() && !subject.get().empty()) { + if (conference->getUtf8Subject() != subject.get()) { + conference->Conference::setUtf8Subject(subject.get()); + if (!isFullState) { + // Subject must be stored in the system locale + conference->notifySubjectChanged(creationTime, isFullState, subject.get()); + } } } - } - auto &keywords = confDescription.get().getKeywords(); - if (keywords.present() && !keywords.get().empty()) { - KeywordsType xmlKeywords = keywords.get(); - confListener->onConferenceKeywordsChanged(vector<string>(xmlKeywords.begin(), xmlKeywords.end())); - } + auto &keywords = confDescription.get().getKeywords(); + if (keywords.present() && !keywords.get().empty()) { + KeywordsType xmlKeywords = keywords.get(); + confListener->onConferenceKeywordsChanged(vector<string>(xmlKeywords.begin(), xmlKeywords.end())); + } - const auto &availableMedia = confDescription.get().getAvailableMedia(); - // Take into consideration available media only if the conference is not a chat room - if (availableMedia.present() && !chatRoom) { - for (auto &mediaEntry : availableMedia.get().getEntry()) { - const std::string mediaType = mediaEntry.getType(); - const LinphoneMediaDirection mediaDirection = - XmlUtils::mediaStatusToMediaDirection(mediaEntry.getStatus().get()); - const bool enabled = (mediaDirection == LinphoneMediaDirectionSendRecv); - if (mediaType.compare("audio") == 0) { - getConference()->getCurrentParams()->enableAudio(enabled); - } else if (mediaType.compare("video") == 0) { - getConference()->getCurrentParams()->enableVideo(enabled); - } else if (mediaType.compare("text") == 0) { - // Do not allow to turn off chat capabilities in order to preserve backward compatibility - getConference()->getCurrentParams()->enableChat(enabled || - getConference()->getCurrentParams()->chatEnabled()); - } else { - lError() << "Unrecognized media type " << mediaType; + const auto &availableMedia = confDescription.get().getAvailableMedia(); + // Take into consideration available media only if the conference supports media. + // A client that has not received an ICS will join the conference with an audio and/or video stream. If the + // conference server accept any of the offered media stream, the client will assume that such a capability + // is supported by the conference. The NOTIFY full state will only refine and set the final set of + // conference capabilities. For instance, if the client joined the conference without offering a video + // stream, the conference video capability will be known only upon reception of the NOTIFY full state. + if (availableMedia.present() && conference->supportsMedia()) { + for (auto &mediaEntry : availableMedia.get().getEntry()) { + const std::string mediaType = mediaEntry.getType(); + const LinphoneMediaDirection mediaDirection = + XmlUtils::mediaStatusToMediaDirection(mediaEntry.getStatus().get()); + const bool enabled = (mediaDirection == LinphoneMediaDirectionSendRecv); + if (mediaType.compare("audio") == 0) { + conference->getCurrentParams()->enableAudio(enabled); + } else if (mediaType.compare("video") == 0) { + conference->getCurrentParams()->enableVideo(enabled); + } else if (mediaType.compare("text") == 0) { + // Do not allow to turn off chat capabilities in order to preserve backward compatibility + conference->getCurrentParams()->enableChat(enabled || + conference->getCurrentParams()->chatEnabled()); + } else { + lError() << "Unrecognized media type " << mediaType; + } + } + if (!isFullState) { + conference->notifyAvailableMediaChanged(creationTime, isFullState, + conference->getMediaCapabilities()); } } - if (!isFullState) { - getConference()->notifyAvailableMediaChanged(creationTime, isFullState, - getConference()->getMediaCapabilities()); - } - } - auto &anySequence(confDescription.get().getAny()); + auto &anySequence(confDescription.get().getAny()); - for (const auto &anyElement : anySequence) { - auto name = xsd::cxx::xml::transcode<char>(anyElement.getLocalName()); - auto nodeName = xsd::cxx::xml::transcode<char>(anyElement.getNodeName()); - auto nodeValue = xsd::cxx::xml::transcode<char>(anyElement.getNodeValue()); - if (nodeName == "linphone-cie:ephemeral") { - Ephemeral ephemeral{anyElement}; - auto ephemeralLifetime = ephemeral.getLifetime(); - auto ephemeralMode = ephemeral.getMode(); + for (const auto &anyElement : anySequence) { + auto name = xsd::cxx::xml::transcode<char>(anyElement.getLocalName()); + auto nodeName = xsd::cxx::xml::transcode<char>(anyElement.getNodeName()); + auto nodeValue = xsd::cxx::xml::transcode<char>(anyElement.getNodeValue()); + if (nodeName == "linphone-cie:ephemeral") { + Ephemeral ephemeral{anyElement}; + auto ephemeralLifetime = ephemeral.getLifetime(); + auto ephemeralMode = ephemeral.getMode(); - std::shared_ptr<LinphonePrivate::ClientChatRoom> cgcr = nullptr; - if (chatRoom && (chatRoom->getConference() == getConference())) { - cgcr = dynamic_pointer_cast<LinphonePrivate::ClientChatRoom>(chatRoom); - } - if (cgcr) { - if (ephemeralMode.empty() || (ephemeralMode.compare("admin-managed") == 0)) { - cgcr->getCurrentParams()->getChatParams()->setEphemeralMode( - AbstractChatRoom::EphemeralMode::AdminManaged); - if (!ephemeralLifetime.empty()) { - const auto lifetime = std::stol(ephemeralLifetime); - cgcr->getCurrentParams()->getChatParams()->setEphemeralLifetime(lifetime); - cgcr->enableEphemeral((lifetime != 0), false); - if (!isFullState) { - getConference()->notifyEphemeralLifetimeChanged(creationTime, isFullState, lifetime); - - getConference()->notifyEphemeralMessageEnabled(creationTime, isFullState, - (lifetime != 0)); + std::shared_ptr<LinphonePrivate::ClientChatRoom> cgcr = nullptr; + if (chatRoom) { + cgcr = dynamic_pointer_cast<LinphonePrivate::ClientChatRoom>(chatRoom); + } + if (cgcr) { + if (ephemeralMode.empty() || (ephemeralMode.compare("admin-managed") == 0)) { + cgcr->getCurrentParams()->getChatParams()->setEphemeralMode( + AbstractChatRoom::EphemeralMode::AdminManaged); + if (!ephemeralLifetime.empty()) { + const auto lifetime = std::stol(ephemeralLifetime); + cgcr->getCurrentParams()->getChatParams()->setEphemeralLifetime(lifetime); + cgcr->enableEphemeral((lifetime != 0), false); + if (!isFullState) { + conference->notifyEphemeralLifetimeChanged(creationTime, isFullState, lifetime); + + conference->notifyEphemeralMessageEnabled(creationTime, isFullState, + (lifetime != 0)); + } } + } else if (ephemeralMode.compare("device-managed") == 0) { + cgcr->getCurrentParams()->getChatParams()->setEphemeralMode( + AbstractChatRoom::EphemeralMode::DeviceManaged); } - } else if (ephemeralMode.compare("device-managed") == 0) { - cgcr->getCurrentParams()->getChatParams()->setEphemeralMode( - AbstractChatRoom::EphemeralMode::DeviceManaged); } + } else if (nodeName == "linphone-cie:conference-times") { + ConferenceTimes conferenceTimes{anyElement}; + if (conferenceTimes.getStart().present()) { + auto startTime = conferenceTimes.getStart().get(); + conference->getCurrentParams()->setStartTime(dateTimeToTimeT(startTime)); + } + if (conferenceTimes.getEnd().present()) { + auto endTime = conferenceTimes.getEnd().get(); + conference->getCurrentParams()->setEndTime(dateTimeToTimeT(endTime)); + } + } else if (nodeName == "linphone-cie:crypto-security-level") { + CryptoSecurityLevel cryptoSecurityLevel{anyElement}; + auto securityLevelString = cryptoSecurityLevel.getLevel(); + auto securityLevel = ConferenceParams::getSecurityLevelFromAttribute(securityLevelString); + conference->getCurrentParams()->setSecurityLevel(securityLevel); } - } else if (nodeName == "linphone-cie:conference-times") { - ConferenceTimes conferenceTimes{anyElement}; - if (conferenceTimes.getStart().present()) { - auto startTime = conferenceTimes.getStart().get(); - getConference()->getCurrentParams()->setStartTime(dateTimeToTimeT(startTime)); - } - if (conferenceTimes.getEnd().present()) { - auto endTime = conferenceTimes.getEnd().get(); - getConference()->getCurrentParams()->setEndTime(dateTimeToTimeT(endTime)); - } - } else if (nodeName == "linphone-cie:crypto-security-level") { - CryptoSecurityLevel cryptoSecurityLevel{anyElement}; - auto securityLevelString = cryptoSecurityLevel.getLevel(); - auto securityLevel = ConferenceParams::getSecurityLevelFromAttribute(securityLevelString); - getConference()->getCurrentParams()->setSecurityLevel(securityLevel); } } - } - auto &confState = confInfo->getConferenceState(); - if (confState.present()) { - auto &anySequence(confState.get().getAny()); + auto &confState = confInfo->getConferenceState(); + if (confState.present()) { + auto &anySequence(confState.get().getAny()); - for (const auto &anyElement : anySequence) { - auto name = xsd::cxx::xml::transcode<char>(anyElement.getLocalName()); - auto nodeName = xsd::cxx::xml::transcode<char>(anyElement.getNodeName()); - auto nodeValue = xsd::cxx::xml::transcode<char>(anyElement.getNodeValue()); + for (const auto &anyElement : anySequence) { + auto name = xsd::cxx::xml::transcode<char>(anyElement.getLocalName()); + auto nodeName = xsd::cxx::xml::transcode<char>(anyElement.getNodeName()); + auto nodeValue = xsd::cxx::xml::transcode<char>(anyElement.getNodeValue()); + } } - } - auto oldParticipants = getConference()->getParticipants(); - auto oldMeDevices = getConference()->getMe()->getDevices(); - if (isFullState) { - confListener->onParticipantsCleared(); - } + auto oldParticipants = conference->getParticipants(); + auto oldMeDevices = conference->getMe()->getDevices(); + if (isFullState) { + confListener->onParticipantsCleared(); + } - auto &users = confInfo->getUsers(); - if (!users.present()) return; - - // 4. Notify changes on users. - for (auto &user : users->getUser()) { - std::shared_ptr<Address> address = core->interpretUrl(user.getEntity().get(), false); - StateType state = user.getState(); - - bool isMe = getConference()->isMe(address); - - shared_ptr<Participant> participant = nullptr; - if (isMe) participant = getConference()->getMe(); - else participant = getConference()->findParticipant(address); - - const auto &pIt = - std::find_if(oldParticipants.cbegin(), oldParticipants.cend(), [&address](const auto ¤tParticipant) { - return (*address == *currentParticipant->getAddress()); - }); - - auto &roles = user.getRoles(); - if (state == StateType::deleted) { - if (isMe) { - lInfo() << "Participant " << *address << " requested to be deleted is me."; - continue; - } else if (participant) { - getConference()->mParticipants.remove(participant); - lInfo() << "Participant " << *participant << " is successfully removed - " << *getConference() - << " has " << getConference()->getParticipantCount() << " participants"; - if (!isFullState) { - getConference()->notifyParticipantRemoved(creationTime, isFullState, participant); + auto &users = confInfo->getUsers(); + if (!users.present()) return; + + auto account = conference->getAccount(); + auto accountContactAddress = account ? account->getContactAddress() : nullptr; + + // 4. Notify changes on users. + for (auto &user : users->getUser()) { + std::shared_ptr<Address> address = core->interpretUrl(user.getEntity().get(), false); + StateType state = user.getState(); + + bool isMe = conference->isMe(address); + + shared_ptr<Participant> participant = nullptr; + if (isMe) participant = conference->getMe(); + else participant = conference->findParticipant(address); + + const auto &pIt = std::find_if( + oldParticipants.cbegin(), oldParticipants.cend(), + [&address](const auto ¤tParticipant) { return (*address == *currentParticipant->getAddress()); }); + + auto &roles = user.getRoles(); + if (state == StateType::deleted) { + if (isMe) { + lInfo() << "Participant " << *address << " requested to be deleted is me."; + continue; + } else if (participant) { + conference->mParticipants.remove(participant); + lInfo() << "Participant " << *participant << " is successfully removed - " << *conference << " has " + << conference->getParticipantCount() << " participants"; + if (!isFullState) { + conference->notifyParticipantRemoved(creationTime, isFullState, participant); + + // TODO FIXME: Remove later when devices for friends will be notified through presence + lInfo() << "[Friend] Removing device with address [" << address->asStringUriOnly() << "]"; + core->getPrivate()->mainDb->removeDevice(address); + } - // TODO FIXME: Remove later when devices for friends will be notified through presence - lInfo() << "[Friend] Removing device with address [" << address->asStringUriOnly() << "]"; - getCore()->getPrivate()->mainDb->removeDevice(address); + continue; + } else { + lWarning() << "Participant " << *address << " removed but not in the list of participants!"; } - - continue; - } else { - lWarning() << "Participant " << *address << " removed but not in the list of participants!"; - } - } else if (state == StateType::full) { - if (isMe) { - lInfo() << "Participant " << *address << " requested to be added is me."; - fillParticipantAttributes(participant, roles, state, isFullState, false); - } else if (participant) { - lWarning() << "Participant " << *participant << " added but already in the list of participants!"; - } else { - participant = Participant::create(getConference(), address); - fillParticipantAttributes(participant, roles, state, isFullState, false); - - getConference()->mParticipants.push_back(participant); - lInfo() << "Participant " << *participant << " is successfully added - " << *getConference() << " has " - << getConference()->getParticipantCount() << " participants"; - if (!isFullState || (!oldParticipants.empty() && (pIt == oldParticipants.cend()) && !isMe)) { - getConference()->notifyParticipantAdded(creationTime, isFullState, participant); + } else if (state == StateType::full) { + if (isMe) { + lInfo() << "Participant " << *address << " requested to be added is me."; + fillParticipantAttributes(participant, roles, state, isFullState, false); + } else if (participant) { + lWarning() << "Participant " << *participant << " added but already in the list of participants!"; + } else { + participant = Participant::create(conference, address); + fillParticipantAttributes(participant, roles, state, isFullState, false); + + conference->mParticipants.push_back(participant); + lInfo() << "Participant " << *participant << " is successfully added - " << *conference << " has " + << conference->getParticipantCount() << " participants"; + if (!isFullState || (!oldParticipants.empty() && (pIt == oldParticipants.cend()) && !isMe)) { + conference->notifyParticipantAdded(creationTime, isFullState, participant); + } } } - } - if (!participant) { - // Try to get participant again as it may have been added or removed earlier on - if (isMe) participant = getConference()->getMe(); - else participant = getConference()->findParticipant(address); - } + if (!participant) { + // Try to get participant again as it may have been added or removed earlier on + if (isMe) participant = conference->getMe(); + else participant = conference->findParticipant(address); + } - if (!participant) { - lDebug() << "Participant " << *address - << " is not in the list of participants however it is trying to change the list of devices or " - "change role! Resubscribing to " - << *getConference() << " to clear things up."; - requestFullState(); - } else { + if (!participant) { + lDebug() << "Participant " << *address + << " is not in the list of participants however it is trying to change the list of devices or " + "change role! Resubscribing to " + << *conference << " to clear things up."; + requestFullState(); + } else { - fillParticipantAttributes(participant, roles, state, isFullState, true); - for (const auto &endpoint : user.getEndpoint()) { - if (!endpoint.getEntity().present()) continue; + fillParticipantAttributes(participant, roles, state, isFullState, true); + for (const auto &endpoint : user.getEndpoint()) { + if (!endpoint.getEntity().present()) continue; - std::shared_ptr<Address> gruu = Address::create(endpoint.getEntity().get()); - StateType state = endpoint.getState(); + std::shared_ptr<Address> gruu = Address::create(endpoint.getEntity().get()); + StateType state = endpoint.getState(); - shared_ptr<ParticipantDevice> device = nullptr; - if (state == StateType::full) { - device = participant->addDevice(gruu); - } else { - if (endpoint.getCallInfo().present()) { - const auto &callInfo = endpoint.getCallInfo().get(); - if (callInfo.getSip().present()) { - const auto &sip = callInfo.getSip().get(); - device = participant->findDeviceByCallId(sip.getCallId()); - if (device) { - device->setAddress(gruu); + shared_ptr<ParticipantDevice> device = nullptr; + if (state == StateType::full) { + device = participant->addDevice(gruu); + } else { + if (endpoint.getCallInfo().present()) { + const auto &callInfo = endpoint.getCallInfo().get(); + if (callInfo.getSip().present()) { + const auto &sip = callInfo.getSip().get(); + device = participant->findDeviceByCallId(sip.getCallId()); + if (device) { + device->setAddress(gruu); + } } } + if (!device) { + device = participant->findDevice(gruu); + } } - if (!device) { - device = participant->findDevice(gruu); - } - } - const auto previousDeviceState = - device ? device->getState() : ParticipantDevice::State::ScheduledForJoining; + const auto previousDeviceState = + device ? device->getState() : ParticipantDevice::State::ScheduledForJoining; - if (state == StateType::deleted) { + if (state == StateType::deleted) { - participant->removeDevice(gruu); + participant->removeDevice(gruu); - if (device) { - if (endpoint.getDisconnectionInfo().present()) { - const auto &disconnectionInfo = endpoint.getDisconnectionInfo().get(); - if (disconnectionInfo.getWhen().present()) { - auto disconnectionTime = disconnectionInfo.getWhen().get(); - device->setTimeOfDisconnection(dateTimeToTimeT(disconnectionTime)); - } + if (device) { + if (endpoint.getDisconnectionInfo().present()) { + const auto &disconnectionInfo = endpoint.getDisconnectionInfo().get(); + if (disconnectionInfo.getWhen().present()) { + auto disconnectionTime = disconnectionInfo.getWhen().get(); + device->setTimeOfDisconnection(dateTimeToTimeT(disconnectionTime)); + } - if (disconnectionInfo.getReason().present()) { - device->setDisconnectionReason(disconnectionInfo.getReason().get()); + if (disconnectionInfo.getReason().present()) { + device->setDisconnectionReason(disconnectionInfo.getReason().get()); + } } - } - if (endpoint.getDisconnectionMethod().present()) { - const auto &disconnectionMethod = endpoint.getDisconnectionMethod().get(); - switch (disconnectionMethod) { - case DisconnectionType::booted: - device->setDisconnectionMethod(ParticipantDevice::DisconnectionMethod::Booted); - break; - case DisconnectionType::departed: - device->setDisconnectionMethod(ParticipantDevice::DisconnectionMethod::Departed); - break; - case DisconnectionType::busy: - device->setDisconnectionMethod(ParticipantDevice::DisconnectionMethod::Busy); - break; - case DisconnectionType::failed: - device->setDisconnectionMethod(ParticipantDevice::DisconnectionMethod::Failed); - break; + if (endpoint.getDisconnectionMethod().present()) { + const auto &disconnectionMethod = endpoint.getDisconnectionMethod().get(); + switch (disconnectionMethod) { + case DisconnectionType::booted: + device->setDisconnectionMethod(ParticipantDevice::DisconnectionMethod::Booted); + break; + case DisconnectionType::departed: + device->setDisconnectionMethod( + ParticipantDevice::DisconnectionMethod::Departed); + break; + case DisconnectionType::busy: + device->setDisconnectionMethod(ParticipantDevice::DisconnectionMethod::Busy); + break; + case DisconnectionType::failed: + device->setDisconnectionMethod(ParticipantDevice::DisconnectionMethod::Failed); + break; + } } - } - - // Set participant device state to left in case the application regularly checks its state - device->setState(ParticipantDevice::State::Left); - if (!isFullState && participant) { - getConference()->notifyParticipantDeviceRemoved(creationTime, isFullState, participant, - device); - } - } - } else if (device) { - /* - auto & deviceAnySequence (endpoint.get().getAny()); - for (auto anyElementIt = deviceAnySequence.begin(); anyElementIt != - deviceAnySequence.end (); ++anyElementIt) { const xercesc_3_1::DOMElement& anyElement - (*anyElementIt); string name (xsd::cxx::xml::transcode<char>(anyElement.getLocalName())); string - nodeName (xsd::cxx::xml::transcode<char>(anyElement.getNodeName())); string nodeValue - (xsd::cxx::xml::transcode<char>(anyElement.getNodeValue())); if - (nodeName.compare("linphone-cie:service-description") == 0) { const auto service = - ServiceDescription(anyElement); const auto serviceId = service.getServiceId(); const auto - serviceVersion = Utils::Version(service.getVersion()); if (serviceId.compare("ephemeral") == 0) { - device->enableAdminModeSupport((serviceVersion > - Utils::Version(1,1))); - } - } - } - */ - - bool isScreenSharing = false; - std::set<LinphoneStreamType> mediaCapabilityChanged; - bool thumbnailTagFound = false; - for (const auto &media : endpoint.getMedia()) { - const std::string mediaType = media.getType().get(); - LinphoneStreamType streamType = LinphoneStreamTypeUnknown; - if (mediaType.compare("audio") == 0) { - streamType = LinphoneStreamTypeAudio; - } else if (mediaType.compare("video") == 0) { - streamType = LinphoneStreamTypeVideo; - } else if (mediaType.compare("text") == 0) { - streamType = LinphoneStreamTypeText; - } else { - lError() << "Unrecognized media type " << mediaType; - } + // Set participant device state to left in case the application regularly checks its state + device->setState(ParticipantDevice::State::Left); - std::string content; - auto &mediaAnySequence(media.getAny()); - for (const auto &anyElement : mediaAnySequence) { - auto name = xsd::cxx::xml::transcode<char>(anyElement.getLocalName()); - auto nodeName = xsd::cxx::xml::transcode<char>(anyElement.getNodeName()); - auto nodeValue = xsd::cxx::xml::transcode<char>(anyElement.getNodeValue()); - if (nodeName == "linphone-cie:stream-data") { - StreamData streamData{anyElement}; - content = streamData.getStreamContent(); + if (!isFullState && participant) { + conference->notifyParticipantDeviceRemoved(creationTime, isFullState, participant, + device); } } + } else if (device) { + /* + auto & deviceAnySequence (endpoint.get().getAny()); + for (auto anyElementIt = deviceAnySequence.begin(); anyElementIt != + deviceAnySequence.end (); ++anyElementIt) { const xercesc_3_1::DOMElement& anyElement + (*anyElementIt); string name (xsd::cxx::xml::transcode<char>(anyElement.getLocalName())); + string nodeName (xsd::cxx::xml::transcode<char>(anyElement.getNodeName())); string nodeValue + (xsd::cxx::xml::transcode<char>(anyElement.getNodeValue())); if + (nodeName.compare("linphone-cie:service-description") == 0) { const auto service = + ServiceDescription(anyElement); const auto serviceId = service.getServiceId(); const auto + serviceVersion = Utils::Version(service.getVersion()); if (serviceId.compare("ephemeral") == + 0) { device->enableAdminModeSupport((serviceVersion > Utils::Version(1,1))); + } + } + } + */ + + bool isScreenSharing = false; + std::set<LinphoneStreamType> mediaCapabilityChanged; + bool thumbnailTagFound = false; + for (const auto &media : endpoint.getMedia()) { + const std::string mediaType = media.getType().get(); + LinphoneStreamType streamType = LinphoneStreamTypeUnknown; + if (mediaType.compare("audio") == 0) { + streamType = LinphoneStreamTypeAudio; + } else if (mediaType.compare("video") == 0) { + streamType = LinphoneStreamTypeVideo; + } else if (mediaType.compare("text") == 0) { + streamType = LinphoneStreamTypeText; + } else { + lError() << "Unrecognized media type " << mediaType; + } - isScreenSharing |= (streamType == LinphoneStreamTypeVideo) && (content.compare("slides") == 0); - LinphoneMediaDirection mediaDirection = - XmlUtils::mediaStatusToMediaDirection(media.getStatus().get()); - uint32_t ssrc = 0; - if (media.getSrcId() && (mediaDirection != LinphoneMediaDirectionInactive)) { - const std::string srcId = media.getSrcId().get(); - ssrc = (uint32_t)std::stoul(srcId); - } - std::string label; - if (media.getLabel()) { - label = media.getLabel().get(); - } - bool isThumbnailStream = - (streamType == LinphoneStreamTypeVideo) && (content.compare("thumbnail") == 0); - if (isThumbnailStream) { - thumbnailTagFound = true; - device->setThumbnailStreamSsrc(ssrc); - if (!label.empty()) { - device->setThumbnailStreamLabel(label); + std::string content; + auto &mediaAnySequence(media.getAny()); + for (const auto &anyElement : mediaAnySequence) { + auto name = xsd::cxx::xml::transcode<char>(anyElement.getLocalName()); + auto nodeName = xsd::cxx::xml::transcode<char>(anyElement.getNodeName()); + auto nodeValue = xsd::cxx::xml::transcode<char>(anyElement.getNodeValue()); + if (nodeName == "linphone-cie:stream-data") { + StreamData streamData{anyElement}; + content = streamData.getStreamContent(); + } } - if (device->setThumbnailStreamCapability(mediaDirection)) { - mediaCapabilityChanged.insert(LinphoneStreamTypeVideo); + + isScreenSharing |= + (streamType == LinphoneStreamTypeVideo) && (content.compare("slides") == 0); + LinphoneMediaDirection mediaDirection = + XmlUtils::mediaStatusToMediaDirection(media.getStatus().get()); + uint32_t ssrc = 0; + if (media.getSrcId() && (mediaDirection != LinphoneMediaDirectionInactive)) { + const std::string srcId = media.getSrcId().get(); + ssrc = (uint32_t)std::stoul(srcId); } - } else { - device->setSsrc(streamType, ssrc); - if (!label.empty()) { - device->setStreamLabel(label, streamType); + std::string label; + if (media.getLabel()) { + label = media.getLabel().get(); } - if (device->setStreamCapability(mediaDirection, streamType)) { - mediaCapabilityChanged.insert(streamType); + bool isThumbnailStream = + (streamType == LinphoneStreamTypeVideo) && (content.compare("thumbnail") == 0); + if (isThumbnailStream) { + thumbnailTagFound = true; + device->setThumbnailStreamSsrc(ssrc); + if (!label.empty()) { + device->setThumbnailStreamLabel(label); + } + if (device->setThumbnailStreamCapability(mediaDirection)) { + mediaCapabilityChanged.insert(LinphoneStreamTypeVideo); + } + } else { + device->setSsrc(streamType, ssrc); + if (!label.empty()) { + device->setStreamLabel(label, streamType); + } + if (device->setStreamCapability(mediaDirection, streamType)) { + mediaCapabilityChanged.insert(streamType); + } } } - } - const auto &mainSession = getConference()->getMainSession(); - if (!thumbnailTagFound) { - lInfo() << "It seems that we are dealing with a legacy conference server that doesn't provide " - "device's thumbnail informations."; - const auto &remoteAddress = mainSession ? mainSession->getRemoteAddress() : nullptr; - bool thumbnailEnabled = false; - if (isMe && remoteAddress && remoteAddress->uriEqual(*device->getAddress())) { - const auto &ms = dynamic_pointer_cast<MediaSession>(mainSession); - if (ms) { - const auto ¶ms = ms->getMediaParams(); - thumbnailEnabled = params->cameraEnabled(); + const auto &mainSession = conference->getMainSession(); + if (!thumbnailTagFound) { + lInfo() + << "It seems that we are dealing with a legacy conference server that doesn't provide " + "device's thumbnail informations."; + const auto &remoteAddress = mainSession ? mainSession->getRemoteAddress() : nullptr; + bool thumbnailEnabled = false; + if (isMe && remoteAddress && remoteAddress->uriEqual(*device->getAddress())) { + const auto &ms = dynamic_pointer_cast<MediaSession>(mainSession); + if (ms) { + const auto ¶ms = ms->getMediaParams(); + thumbnailEnabled = params->cameraEnabled(); + } + } else { + const auto &deviceCapability = device->getStreamCapability(LinphoneStreamTypeVideo); + thumbnailEnabled = ((deviceCapability == LinphoneMediaDirectionSendOnly) || + (deviceCapability == LinphoneMediaDirectionSendRecv)); + } + device->setThumbnailStreamLabel(device->getStreamLabel(LinphoneStreamTypeVideo)); + if (device->setThumbnailStreamCapability(thumbnailEnabled + ? LinphoneMediaDirectionSendOnly + : LinphoneMediaDirectionInactive)) { + mediaCapabilityChanged.insert(LinphoneStreamTypeVideo); } - } else { - const auto &deviceCapability = device->getStreamCapability(LinphoneStreamTypeVideo); - thumbnailEnabled = ((deviceCapability == LinphoneMediaDirectionSendOnly) || - (deviceCapability == LinphoneMediaDirectionSendRecv)); } - device->setThumbnailStreamLabel(device->getStreamLabel(LinphoneStreamTypeVideo)); - if (device->setThumbnailStreamCapability(thumbnailEnabled ? LinphoneMediaDirectionSendOnly - : LinphoneMediaDirectionInactive)) { - mediaCapabilityChanged.insert(LinphoneStreamTypeVideo); + conference->setCachedScreenSharingDevice(); + const auto screenSharingChanged = device->enableScreenSharing(isScreenSharing); + if (!isFullState && (state != StateType::full) && screenSharingChanged) { + conference->notifyParticipantDeviceScreenSharingChanged(creationTime, isFullState, + participant, device); } - } - getConference()->setCachedScreenSharingDevice(); - const auto screenSharingChanged = device->enableScreenSharing(isScreenSharing); - if (!isFullState && (state != StateType::full) && screenSharingChanged) { - getConference()->notifyParticipantDeviceScreenSharingChanged(creationTime, isFullState, - participant, device); - } - 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.empty()) { - getConference()->notifyParticipantDeviceMediaAvailabilityChanged(creationTime, isFullState, - participant, device); - } + 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.empty()) { + conference->notifyParticipantDeviceMediaAvailabilityChanged(creationTime, isFullState, + participant, device); + } - if (!mediaCapabilityChanged.empty()) { - getConference()->notifyParticipantDeviceMediaCapabilityChanged(creationTime, isFullState, - participant, device); - } - } - getConference()->resetCachedScreenSharingDevice(); - - if (endpoint.getJoiningMethod().present()) { - const auto &joiningMethod = endpoint.getJoiningMethod().get(); - switch (joiningMethod) { - case JoiningType::dialed_in: - device->setJoiningMethod(ParticipantDevice::JoiningMethod::DialedIn); - break; - case JoiningType::dialed_out: - device->setJoiningMethod(ParticipantDevice::JoiningMethod::DialedOut); - break; - case JoiningType::focus_owner: - device->setJoiningMethod(ParticipantDevice::JoiningMethod::FocusOwner); - break; + if (!mediaCapabilityChanged.empty()) { + conference->notifyParticipantDeviceMediaCapabilityChanged(creationTime, isFullState, + participant, device); + } } - } + conference->resetCachedScreenSharingDevice(); - if (endpoint.getJoiningInfo().present()) { - const auto &joiningInfo = endpoint.getJoiningInfo().get(); - if (joiningInfo.getWhen().present()) { - auto joiningTime = joiningInfo.getWhen().get(); - device->setTimeOfJoining(dateTimeToTimeT(joiningTime)); + if (endpoint.getJoiningMethod().present()) { + const auto &joiningMethod = endpoint.getJoiningMethod().get(); + switch (joiningMethod) { + case JoiningType::dialed_in: + device->setJoiningMethod(ParticipantDevice::JoiningMethod::DialedIn); + break; + case JoiningType::dialed_out: + device->setJoiningMethod(ParticipantDevice::JoiningMethod::DialedOut); + break; + case JoiningType::focus_owner: + device->setJoiningMethod(ParticipantDevice::JoiningMethod::FocusOwner); + break; + } } - } - // Set state after setting the joining time - if (endpoint.getStatus().present()) { - const auto &status = endpoint.getStatus().get(); - ParticipantDevice::State deviceState = ParticipantDevice::State::Joining; - switch (status) { - case EndpointStatusType::dialing_in: - case EndpointStatusType::dialing_out: - deviceState = ParticipantDevice::State::Joining; - break; - case EndpointStatusType::alerting: - deviceState = ParticipantDevice::State::Alerting; - break; - case EndpointStatusType::pending: - deviceState = ParticipantDevice::State::ScheduledForJoining; - break; - case EndpointStatusType::connected: - deviceState = ParticipantDevice::State::Present; - break; - case EndpointStatusType::on_hold: - // If the joining time is not defined and the client receives a NOTIFY that a - // participant is on hold it means that it is requesting to join the conference. - // The joining time is indeed defined the first time the participant state moves to the - // Present state - deviceState = (device->getTimeOfJoining() == (time_t)-1) - ? ParticipantDevice::State::RequestingToJoin - : ParticipantDevice::State::OnHold; - break; - case EndpointStatusType::disconnecting: - deviceState = ParticipantDevice::State::Leaving; - break; - case EndpointStatusType::disconnected: - deviceState = ParticipantDevice::State::Left; - break; - case EndpointStatusType::muted_via_focus: - deviceState = ParticipantDevice::State::MutedByFocus; - break; + if (endpoint.getJoiningInfo().present()) { + const auto &joiningInfo = endpoint.getJoiningInfo().get(); + if (joiningInfo.getWhen().present()) { + auto joiningTime = joiningInfo.getWhen().get(); + device->setTimeOfJoining(dateTimeToTimeT(joiningTime)); + } } - device->setState(deviceState, !isFullState); - } - if (endpoint.getCallInfo().present()) { - const auto &callInfo = endpoint.getCallInfo().get(); - if (callInfo.getSip().present()) { - const auto &sip = callInfo.getSip().get(); - device->setCallId(sip.getCallId()); - device->setFromTag(sip.getFromTag()); - device->setToTag(sip.getToTag()); + // Set state after setting the joining time + if (endpoint.getStatus().present()) { + const auto &status = endpoint.getStatus().get(); + ParticipantDevice::State deviceState = ParticipantDevice::State::Joining; + switch (status) { + case EndpointStatusType::dialing_in: + case EndpointStatusType::dialing_out: + deviceState = ParticipantDevice::State::Joining; + break; + case EndpointStatusType::alerting: + deviceState = ParticipantDevice::State::Alerting; + break; + case EndpointStatusType::pending: + deviceState = ParticipantDevice::State::ScheduledForJoining; + break; + case EndpointStatusType::connected: + deviceState = ParticipantDevice::State::Present; + break; + case EndpointStatusType::on_hold: + // If the joining time is not defined and the client receives a NOTIFY that a + // participant is on hold it means that it is requesting to join the conference. + // The joining time is indeed defined the first time the participant state moves to + // the Present state + deviceState = (device->getTimeOfJoining() == (time_t)-1) + ? ParticipantDevice::State::RequestingToJoin + : ParticipantDevice::State::OnHold; + break; + case EndpointStatusType::disconnecting: + deviceState = ParticipantDevice::State::Leaving; + break; + case EndpointStatusType::disconnected: + deviceState = ParticipantDevice::State::Left; + break; + case EndpointStatusType::muted_via_focus: + deviceState = ParticipantDevice::State::MutedByFocus; + break; + } + device->setState(deviceState, !isFullState); } - } - const string &name = endpoint.getDisplayText().present() ? endpoint.getDisplayText().get() : ""; + if (endpoint.getCallInfo().present()) { + const auto &callInfo = endpoint.getCallInfo().get(); + if (callInfo.getSip().present()) { + const auto &sip = callInfo.getSip().get(); + device->setCallId(sip.getCallId()); + device->setFromTag(sip.getFromTag()); + device->setToTag(sip.getToTag()); + } + } - if (!name.empty()) device->setName(name); - // TODO FIXME: Remove later when devices for friends will be notified through presence - lInfo() << "[Friend] Inserting new device with name [" << name << "] and address [" - << gruu->asStringUriOnly() << "]"; - getCore()->getPrivate()->mainDb->insertDevice(gruu, name); + const string &name = endpoint.getDisplayText().present() ? endpoint.getDisplayText().get() : ""; - // For chat rooms, the session is handled by the participant - if (isMe && mainSession && - (getConference()->getCurrentParams()->audioEnabled() || - getConference()->getCurrentParams()->videoEnabled())) - device->setSession(mainSession); + if (!name.empty()) device->setName(name); + // TODO FIXME: Remove later when devices for friends will be notified through presence + lInfo() << "[Friend] Inserting new device with name [" << name << "] and address [" + << gruu->asStringUriOnly() << "]"; + core->getPrivate()->mainDb->insertDevice(gruu, name); - if (state == StateType::full) { - lInfo() << "Participant device " << *gruu << " has been successfully added"; - bool sendNotify = (!oldParticipants.empty() && (pIt == oldParticipants.cend())) && !isMe; - if (pIt != oldParticipants.cend()) { - const auto &oldDevices = (*pIt)->getDevices(); - const auto &dIt = - std::find_if(oldDevices.cbegin(), oldDevices.cend(), [&gruu](const auto &oldDevice) { - return (*gruu == *oldDevice->getAddress()); - }); - sendNotify = (dIt == oldDevices.cend()) && !isMe; - } - if (!isFullState || sendNotify) { - if (device->getState() == ParticipantDevice::State::RequestingToJoin) { - getConference()->notifyParticipantDeviceJoiningRequest(creationTime, isFullState, - participant, device); + // For chat rooms, the session is handled by the participant + if (isMe && mainSession && conference->supportsMedia()) { + if (accountContactAddress) { + if (*gruu == *accountContactAddress) { + device->setSession(mainSession); + } } else { - getConference()->notifyParticipantDeviceAdded(creationTime, isFullState, participant, - device); + lError() << "The account " << account << " linked to conference " << *conference + << " has no contact address, therefore the core is not able to assign the " + "main session to any participant device"; } } + + if (state == StateType::full) { + lInfo() << "Participant device " << *gruu << " has been successfully added"; + bool sendNotify = (!oldParticipants.empty() && (pIt == oldParticipants.cend())) && !isMe; + if (pIt != oldParticipants.cend()) { + const auto &oldDevices = (*pIt)->getDevices(); + const auto &dIt = std::find_if( + oldDevices.cbegin(), oldDevices.cend(), + [&gruu](const auto &oldDevice) { return (*gruu == *oldDevice->getAddress()); }); + sendNotify = (dIt == oldDevices.cend()) && !isMe; + } + if (!isFullState || sendNotify) { + if (device->getState() == ParticipantDevice::State::RequestingToJoin) { + conference->notifyParticipantDeviceJoiningRequest(creationTime, isFullState, + participant, device); + } else { + conference->notifyParticipantDeviceAdded(creationTime, isFullState, participant, + device); + } + } + } else { + lInfo() << "Participant device " << *gruu << " has been successfully updated"; + } } else { - lInfo() << "Participant device " << *gruu << " has been successfully updated"; + lDebug() << "Unable to update media direction of device " << *gruu + << " because it has not been found in " << *conference + << ". Resubscribing to it to clear things up."; + requestFullState(); } - } else { - lDebug() << "Unable to update media direction of device " << *gruu - << " because it has not been found in " << *getConference() - << ". Resubscribing to it to clear things up."; - requestFullState(); } } } - } - if (isFullState) { - auto currentParticipants = getConference()->getParticipants(); - auto currentMeDevices = getConference()->getMe()->getDevices(); - // Send participant and participant device removed notifys if the full state has less participants than the - // current chat room or conference - for (const auto &p : oldParticipants) { - const auto &pIt = std::find_if(currentParticipants.cbegin(), currentParticipants.cend(), - [&p](const auto ¤tParticipant) { - return (*p->getAddress() == *currentParticipant->getAddress()); - }); - for (const auto &d : p->getDevices()) { - bool deviceFound = false; + if (isFullState) { + auto currentParticipants = conference->getParticipants(); + auto currentMeDevices = conference->getMe()->getDevices(); + // Send participant and participant device removed notifys if the full state has less participants than + // the current chat room or conference + for (const auto &p : oldParticipants) { + const auto &pIt = std::find_if(currentParticipants.cbegin(), currentParticipants.cend(), + [&p](const auto ¤tParticipant) { + return (*p->getAddress() == *currentParticipant->getAddress()); + }); + for (const auto &d : p->getDevices()) { + bool deviceFound = false; + if (pIt == currentParticipants.cend()) { + deviceFound = false; + } else { + const auto ¤tDevices = (*pIt)->getDevices(); + const auto &dIt = std::find_if(currentDevices.cbegin(), currentDevices.cend(), + [&d](const auto ¤tDevice) { + return (*d->getAddress() == *currentDevice->getAddress()); + }); + deviceFound = (dIt != currentDevices.cend()); + } + if (!deviceFound) { + lInfo() << "Device " << *d->getAddress() << " is no longer a member of " << *conference; + conference->notifyParticipantDeviceRemoved(creationTime, isFullState, p, d); + } + } if (pIt == currentParticipants.cend()) { - deviceFound = false; - } else { - const auto ¤tDevices = (*pIt)->getDevices(); - const auto &dIt = - std::find_if(currentDevices.cbegin(), currentDevices.cend(), [&d](const auto ¤tDevice) { - return (*d->getAddress() == *currentDevice->getAddress()); - }); - deviceFound = (dIt != currentDevices.cend()); + lInfo() << "Participant " << *p->getAddress() << " is no longer a member of " << *conference; + conference->notifyParticipantRemoved(creationTime, isFullState, p); } + } + for (const auto &d : oldMeDevices) { + const auto &dIt = + std::find_if(currentMeDevices.cbegin(), currentMeDevices.cend(), [&d](const auto ¤tDevice) { + return (*d->getAddress() == *currentDevice->getAddress()); + }); + bool deviceFound = (dIt != currentMeDevices.cend()); if (!deviceFound) { - lInfo() << "Device " << *d->getAddress() << " is no longer a member of " << *getConference(); - getConference()->notifyParticipantDeviceRemoved(creationTime, isFullState, p, d); + lInfo() << "Device " << *d->getAddress() << " is no longer a member of " << *conference; + conference->notifyParticipantDeviceRemoved(creationTime, isFullState, conference->getMe(), d); } } - if (pIt == currentParticipants.cend()) { - lInfo() << "Participant " << *p->getAddress() << " is no longer a member of " << *getConference(); - getConference()->notifyParticipantRemoved(creationTime, isFullState, p); - } - } - for (const auto &d : oldMeDevices) { - const auto &dIt = - std::find_if(currentMeDevices.cbegin(), currentMeDevices.cend(), [&d](const auto ¤tDevice) { - return (*d->getAddress() == *currentDevice->getAddress()); - }); - bool deviceFound = (dIt != currentMeDevices.cend()); - if (!deviceFound) { - lInfo() << "Device " << *d->getAddress() << " is no longer a member of " << *getConference(); - getConference()->notifyParticipantDeviceRemoved(creationTime, isFullState, getConference()->getMe(), d); + conference->notifyFullState(); + if (!synchronizing) { + confListener->onFirstNotifyReceived(getConferenceId().getPeerAddress()); } } - getConference()->notifyFullState(); - if (!synchronizing) { - confListener->onFirstNotifyReceived(getConferenceId().getPeerAddress()); - } + } catch (const bad_weak_ptr &) { + // Core is destroyed + lError() << "ClientConferenceEventHandler [" << this + << "]: Unable to deal with incoming NOTIFY message as the core has already been destroyed"; } } void ClientConferenceEventHandler::requestFullState() { auto conference = getConference(); - lInfo() << "Requesting full state for conference " - << (conference->getConferenceAddress() ? conference->getConferenceAddress()->toString() - : std::string("sip:")); + lInfo() << "Requesting full state for " << *conference; unsubscribe(); - conference->setLastNotify(0); + conference->resetLastNotify(); subscribe(getConferenceId()); fullStateRequested = true; } @@ -796,24 +828,31 @@ bool ClientConferenceEventHandler::subscribe() { if (!localAddress) return false; // Unknown local address auto conference = getConference(); + if (!conference) return false; // Conference has not been set auto account = conference->getAccount(); if (!account || (account->getState() != LinphoneRegistrationOk)) { return false; } - const auto &conferenceAddress = getConference()->getConferenceAddress(); - if (!conferenceAddress) return false; // Unknown peer address - ev = dynamic_pointer_cast<EventSubscribe>( - (new EventSubscribe(getCore(), conferenceAddress, account, "conference", 600))->toSharedPtr()); - ev->getOp()->setFromAddress(localAddress->getImpl()); - setInitialSubscriptionUnderWayFlag(true); - const string &lastNotifyStr = Utils::toString(getLastNotify()); - ev->addCustomHeader("Last-Notify-Version", lastNotifyStr.c_str()); - ev->setInternal(true); - ev->setProperty("event-handler-private", this); - lInfo() << *localAddress << " is subscribing to chat room or conference: " << *conferenceAddress - << " with last notify: " << lastNotifyStr; - return (ev->send(nullptr) == 0); + const auto &subscribeToHeader = conference->getConferenceAddress(); + if (!subscribeToHeader) return false; // Unknown peer address + try { + ev = dynamic_pointer_cast<EventSubscribe>( + (new EventSubscribe(getCore(), subscribeToHeader, account, "conference", 600))->toSharedPtr()); + ev->getOp()->setFromAddress(localAddress->getImpl()); + setInitialSubscriptionUnderWayFlag(true); + const string &lastNotifyStr = Utils::toString(getLastNotify()); + ev->addCustomHeader("Last-Notify-Version", lastNotifyStr.c_str()); + ev->setInternal(true); + ev->setProperty("event-handler-private", this); + lInfo() << *localAddress << " is subscribing to chat room or conference: " << *subscribeToHeader + << " with last notify: " << lastNotifyStr; + return (ev->send(nullptr) == 0); + } catch (const bad_weak_ptr &) { + lError() << "ClientConferenceEventHandler [" << this << "]: Unable to send subscribe to " << *conference + << " as the core has already been destroyed"; + } + return false; } // ----------------------------------------------------------------------------- @@ -821,8 +860,8 @@ bool ClientConferenceEventHandler::subscribe() { void ClientConferenceEventHandler::unsubscribePrivate() { if (ev) { /* The following tricky code is to break a cycle. Indeed linphone_event_terminate() will change the event's - * state, which will be notified to the core, that will call us immediately in invalidateSubscription(), which - * resets 'ev' while we still have to unref it.*/ + * state, which will be notified to the core, that will call us immediately in invalidateSubscription(), + * which resets 'ev' while we still have to unref it.*/ shared_ptr<EventSubscribe> tmpEv = ev; ev = nullptr; tmpEv->terminate(); @@ -832,7 +871,10 @@ void ClientConferenceEventHandler::unsubscribePrivate() { void ClientConferenceEventHandler::onNetworkReachable(bool sipNetworkReachable, BCTBX_UNUSED(bool mediaNetworkReachable)) { if (sipNetworkReachable) { - subscribe(getConferenceId()); + auto conference = getConference(); + if (conference && conference->isChatOnly()) { + subscribe(getConferenceId()); + } } else { unsubscribePrivate(); } @@ -845,7 +887,11 @@ void ClientConferenceEventHandler::onAccountRegistrationStateChanged(std::shared const auto &localAddress = conferenceId.getLocalAddress(); const auto ¶ms = account->getAccountParams(); const auto &address = params->getIdentityAddress(); - if (localAddress && address->weakEqual(*localAddress) && (state == LinphoneRegistrationOk)) subscribe(conferenceId); + auto conference = getConference(); + bool isChatOnly = conference && conference->isChatOnly(); + if (localAddress && address->weakEqual(*localAddress) && (state == LinphoneRegistrationOk) && isChatOnly) { + subscribe(conferenceId); + } } void ClientConferenceEventHandler::onEnteringBackground() { @@ -853,15 +899,19 @@ void ClientConferenceEventHandler::onEnteringBackground() { } void ClientConferenceEventHandler::onEnteringForeground() { - subscribe(); + auto conference = getConference(); + bool isChatOnly = conference && conference->isChatOnly(); + if (isChatOnly) { + subscribe(); + } } void ClientConferenceEventHandler::invalidateSubscription() { if (ev) { if ((ev->getState() == LinphoneSubscriptionError) && (getConference()->getState() == ConferenceInterface::State::CreationPending)) { - // The conference received an answer to its SUBSCRIBE and the server is not supporting the conference event - // package + // The conference received an answer to its SUBSCRIBE and the server is not supporting the conference + // event package getConference()->setState(ConferenceInterface::State::Created); } ev = nullptr; @@ -924,7 +974,11 @@ void ClientConferenceEventHandler::multipartNotifyReceived(const Content &conten } shared_ptr<Conference> ClientConferenceEventHandler::getConference() const { - return conf.lock(); + try { + return shared_ptr<Conference>(conf); + } catch (const bad_weak_ptr &) { + return nullptr; + } }; const ConferenceId &ClientConferenceEventHandler::getConferenceId() const { diff --git a/src/conference/handlers/client-conference-list-event-handler.cpp b/src/conference/handlers/client-conference-list-event-handler.cpp index 2250a2e07e4bba5ee60bcd10c9a65acad9bdd37d..282e60b43e4da3f36ee175b724a37aef02c3d31f 100644 --- a/src/conference/handlers/client-conference-list-event-handler.cpp +++ b/src/conference/handlers/client-conference-list-event-handler.cpp @@ -82,12 +82,12 @@ bool ClientConferenceListEventHandler::subscribe(const shared_ptr<Account> &acco if (account->getState() != LinphoneRegistrationOk) return false; const auto &accountParams = account->getAccountParams(); - std::shared_ptr<Address> identityAddress = accountParams->getIdentityAddress(); + auto identityAddress = accountParams->getIdentityAddress(); const auto &factoryUri = accountParams->getConferenceFactoryAddress(); if (!factoryUri || !factoryUri->isValid()) { - lError() << "Couldn't send chat room list subscription for account " << account << " (" << *identityAddress - << ") because there's no conference factory uri"; + lError() << "Couldn't send chat room list subscription for " << *account + << " because there's no conference factory uri"; return false; } @@ -102,7 +102,7 @@ bool ClientConferenceListEventHandler::subscribe(const shared_ptr<Account> &acco const std::shared_ptr<ClientConferenceEventHandler> handler(handlerWkPtr); const ConferenceId &conferenceId = handler->getConferenceId(); if (identityAddress->weakEqual(*conferenceId.getLocalAddress())) { - shared_ptr<AbstractChatRoom> cr = getCore()->findChatRoom(conferenceId); + shared_ptr<AbstractChatRoom> cr = getCore()->findChatRoom(conferenceId, false); if (!cr) { lError() << "Couldn't add chat room " << conferenceId << " in the chat room list subscription because chat room couldn't be found"; @@ -174,7 +174,7 @@ void ClientConferenceListEventHandler::unsubscribe(const std::shared_ptr<Account } const auto &accountParams = account->getAccountParams(); - std::shared_ptr<Address> identityAddress = accountParams->getIdentityAddress(); + auto identityAddress = accountParams->getIdentityAddress(); for (const auto &[key, handlerWkPtr] : handlers) { try { const std::shared_ptr<ClientConferenceEventHandler> handler(handlerWkPtr); @@ -203,6 +203,8 @@ void ClientConferenceListEventHandler::notifyReceived(std::shared_ptr<Event> not auto it = std::find_if(levs.begin(), levs.end(), [&from](const auto &lev) { return (*Address::create(lev->getOp()->getFrom()) == *from); }); + auto core = getCore(); + const auto conferenceIdParams = core->createConferenceIdParams(); const auto levFound = (it != levs.end()); if (notifyContent->getContentType() == ContentType::ConferenceInfo) { // Simple notify received directly from a chat-room @@ -217,7 +219,7 @@ void ClientConferenceListEventHandler::notifyReceived(std::shared_ptr<Event> not } std::shared_ptr<Address> entityAddress = Address::create(confInfo->getEntity().c_str()); - ConferenceId id(entityAddress, from); + ConferenceId id(entityAddress, from, conferenceIdParams); auto handler = findHandler(id); if (!handler) return; @@ -245,7 +247,7 @@ void ClientConferenceListEventHandler::notifyReceived(std::shared_ptr<Event> not if (it == addresses.cend()) continue; std::shared_ptr<Address> peer = it->second; - ConferenceId id(peer, from); + ConferenceId id(peer, from, conferenceIdParams); auto handler = findHandler(id); if (!handler) continue; diff --git a/src/conference/handlers/server-conference-event-handler.cpp b/src/conference/handlers/server-conference-event-handler.cpp index b91629465a24e1be0a30db8925efc255c0f37cfc..434f6af58c75370865b78f8e9a5062a9e54845cf 100644 --- a/src/conference/handlers/server-conference-event-handler.cpp +++ b/src/conference/handlers/server-conference-event-handler.cpp @@ -136,7 +136,7 @@ std::shared_ptr<Content> ServerConferenceEventHandler::createNotifyFullState(con } std::shared_ptr<Address> conferenceAddress = conf->getConferenceAddress(); - ConferenceId conferenceId(conferenceAddress, conferenceAddress); + ConferenceId conferenceId(conferenceAddress, conferenceAddress, conf->getCore()->createConferenceIdParams()); // Enquire whether this conference belongs to a server group chat room std::shared_ptr<AbstractChatRoom> chatRoom = conf->getChatRoom(); const bool oneToOne = chatRoom ? !!!chatRoom->getCurrentParams()->isGroup() : false; @@ -210,7 +210,7 @@ std::shared_ptr<Content> ServerConferenceEventHandler::createNotifyFullState(con } ConferenceParamsInterface::SecurityLevel securityLevel = conferenceParams->getSecurityLevel(); - if (chatRoom) { + if (conf->isChatOnly()) { securityLevel = (chatRoom->getCurrentParams()->getChatParams()->isEncrypted() ? ConferenceParamsInterface::SecurityLevel::EndToEnd : ConferenceParamsInterface::SecurityLevel::None); @@ -502,8 +502,10 @@ std::shared_ptr<Content> ServerConferenceEventHandler::createNotifyMultipart(int return nullptr; } - list<shared_ptr<EventLog>> events = conf->getCore()->getPrivate()->mainDb->getConferenceNotifiedEvents( - ConferenceId(conf->getConferenceAddress(), conf->getConferenceAddress()), static_cast<unsigned int>(notifyId)); + auto core = conf->getCore(); + list<shared_ptr<EventLog>> events = core->getPrivate()->mainDb->getConferenceNotifiedEvents( + ConferenceId(conf->getConferenceAddress(), conf->getConferenceAddress(), core->createConferenceIdParams()), + static_cast<unsigned int>(notifyId)); list<shared_ptr<Content>> contents; for (const auto &eventLog : events) { @@ -615,7 +617,6 @@ string ServerConferenceEventHandler::createNotifyParticipantAdded(const std::sha confInfo.setUsers(users); UserType user = UserType(); UserRolesType roles; - UserType::EndpointSequence endpoints; shared_ptr<Participant> participant = conf->isMe(pAddress) ? conf->getMe() : conf->findParticipant(pAddress); if (participant) { @@ -711,7 +712,6 @@ string ServerConferenceEventHandler::createNotifyParticipantDeviceAdded(const st confInfo.setUsers(users); UserType user = UserType(); - UserType::EndpointSequence endpoints; user.setEntity(pAddress->asStringUriOnly()); user.setState(StateType::partial); @@ -971,7 +971,7 @@ string ServerConferenceEventHandler::createNotifyEphemeralMode(const EventLog::T confDescr.setKeywords(keywords); } - ConferenceId conferenceId(conferenceAddress, conferenceAddress); + ConferenceId conferenceId(conferenceAddress, conferenceAddress, conf->getCore()->createConferenceIdParams()); // Enquire whether this conference belongs to a server group chat room std::shared_ptr<AbstractChatRoom> chatRoom = conf->getChatRoom(); const ModeType mode = @@ -1016,7 +1016,7 @@ string ServerConferenceEventHandler::createNotifyEphemeralLifetime(const long &l } } - ConferenceId conferenceId(conferenceAddress, conferenceAddress); + ConferenceId conferenceId(conferenceAddress, conferenceAddress, conf->getCore()->createConferenceIdParams()); // Enquire whether this conference belongs to a server group chat room std::shared_ptr<AbstractChatRoom> chatRoom = conf->getChatRoom(); shared_ptr<Core> core = conf->getCore(); @@ -1131,14 +1131,10 @@ LinphoneStatus ServerConferenceEventHandler::subscribeReceived(const shared_ptr< const auto &participantAddress = ev->getFrom(); unsigned int lastNotify = conf->getLastNotify(); - const auto &conferenceAddress = conf->getConferenceAddress(); - const std::string conferenceAddressString = - conferenceAddress ? conferenceAddress->asStringUriOnly() : std::string("sip:"); - shared_ptr<Participant> participant = getConferenceParticipant(participantAddress); if (!participant) { - lError() << "Declining SUBSCRIBE because participant " << *participantAddress - << " cannot be found in conference [" << conferenceAddressString << "]"; + lError() << "Declining SUBSCRIBE because participant " << *participantAddress << " cannot be found in " + << *conf; ev->deny(LinphoneReasonDeclined); return -1; } @@ -1148,8 +1144,8 @@ LinphoneStatus ServerConferenceEventHandler::subscribeReceived(const shared_ptr< const auto deviceState = device ? device->getState() : ParticipantDevice::State::ScheduledForJoining; if (!device || ((deviceState != ParticipantDevice::State::Present) && (deviceState != ParticipantDevice::State::Joining))) { - lError() << "Received SUBSCRIBE for conference [" << conferenceAddressString << "], device sending subscribe [" - << *contactAddr << "] is not known, no NOTIFY sent"; + lError() << "Received SUBSCRIBE for " << *conf << ", device sending subscribe [" << *contactAddr + << "] is not known, no NOTIFY sent"; ev->deny(LinphoneReasonDeclined); return -1; } @@ -1167,27 +1163,16 @@ LinphoneStatus ServerConferenceEventHandler::subscribeReceived(const shared_ptr< if ((evLastNotify == 0) || (deviceState == ParticipantDevice::State::Joining)) { const auto &needToSyncDevice = device->isChangingSubscribeEvent(); if (needToSyncDevice) { - lInfo() << "Participant " << *dAddress << " is already part of " << *getConference() + lInfo() << "Participant " << *dAddress << " is already part of " << *conf << " hence send full state to be sure the client and the server are on the same page"; - } else { - // conf->setLastNotify(lastNotify + 1); } - lInfo() << "Sending initial notify of conference [" << conferenceAddressString << "] to: " << *dAddress + lInfo() << "Sending initial notify of " << *conf << " to: " << *dAddress << " with last notify version set to " << conf->getLastNotify(); notifyFullState(createNotifyFullState(ev), device); - // Do not notify everybody that a particiant has been added if it was already part of the conference. It may - // mean that the client and the server wanted to synchronize to each other - if (!needToSyncDevice) { - // Notify everybody that a participant device has been added and its capabilities after receiving the - // SUBSCRIBE - // const auto notify = createNotifyParticipantDeviceDataChanged(pAddress, dAddress); - // notifyAllExceptDevice(makeContent(notify), device); - } - // As a SUBSCRIBE has been received, clear the flag in case the device unsubscribes again device->clearChangingSubscribeEvent(); } else if (evLastNotify < lastNotify) { - lInfo() << "Sending all missed notify [" << evLastNotify << "-" << lastNotify << "] for conference [" - << conferenceAddressString << "] to: " << *pAddress; + lInfo() << "Sending all missed notify [" << evLastNotify << "-" << lastNotify << "] for " << *conf + << " to: " << *pAddress; const int fullStateTrigger = linphone_config_get_int(linphone_core_get_config(conf->getCore()->getCCore()), "misc", "full_state_trigger_due_to_missing_updates", 10); @@ -1195,16 +1180,16 @@ LinphoneStatus ServerConferenceEventHandler::subscribeReceived(const shared_ptr< // FIXME: Temporary workaround until chatrooms and conference will be one single class with different // capabilities. Every subscribe sent for a conference will be answered by a notify full state as events are // not stored in the database - const auto &conference = conf->getCore()->findConference(conf->getConferenceId()); - if ((conference && !conference->getCurrentParams()->chatEnabled()) || forceFullState) { + const auto &conference = conf->getCore()->findConference(conf->getConferenceId(), false); + if ((conference && !conference->isChatOnly()) || forceFullState) { notifyFullState(createNotifyFullState(ev), device); } else { notifyParticipantDevice(createNotifyMultipart(static_cast<int>(evLastNotify)), device); } } else if (evLastNotify > lastNotify) { - lWarning() << "Last notify received by client [" << evLastNotify << "] for conference [" - << conferenceAddressString << "] should not be higher than last notify sent by server [" - << lastNotify << "] - sending a notify full state in an attempt to recover from this situation"; + lWarning() << "Last notify received by client [" << evLastNotify << "] for " << *conf + << " should not be higher than last notify sent by server [" << lastNotify + << "] - sending a notify full state in an attempt to recover from this situation"; notifyFullState(createNotifyFullState(ev), device); } else { notifyParticipantDevice(Content::create(), device); @@ -1225,8 +1210,7 @@ void ServerConferenceEventHandler::subscriptionStateChanged(const shared_ptr<Eve shared_ptr<ParticipantDevice> device = participant->findDevice(contactAddr); if (!device) return; if (ev == device->getConferenceSubscribeEvent()) { - lInfo() << "End of subscription for device [" << *device->getAddress() << "] of conference [" - << *conf->getConferenceAddress() << "]"; + lInfo() << "End of subscription for device [" << *device->getAddress() << "] of " << *conf; device->setConferenceSubscribeEvent(nullptr); } } @@ -1407,11 +1391,13 @@ void ServerConferenceEventHandler::onParticipantDeviceAdded( if (conf) { auto participant = device->getParticipant(); const auto &pAddress = participant->getAddress(); - // If the ssrc is not 0, send a NOTIFY to the participant being added in order to give him its own SSRC - if ((device->getSsrc(LinphoneStreamTypeAudio) != 0) || (device->getSsrc(LinphoneStreamTypeVideo) != 0)) { - notifyAll(makeContent(createNotifyParticipantDeviceAdded(pAddress, dAddress))); - } else { - notifyAllExceptDevice(makeContent(createNotifyParticipantDeviceAdded(pAddress, dAddress)), device); + if (device->addedNotifySent()) { + // If the ssrc is not 0, send a NOTIFY to the participant being added in order to give him its own SSRC + if ((device->getSsrc(LinphoneStreamTypeAudio) != 0) || (device->getSsrc(LinphoneStreamTypeVideo) != 0)) { + notifyAll(makeContent(createNotifyParticipantDeviceAdded(pAddress, dAddress))); + } else { + notifyAllExceptDevice(makeContent(createNotifyParticipantDeviceAdded(pAddress, dAddress)), device); + } } // Enquire whether this conference belongs to a server group chat room std::shared_ptr<AbstractChatRoom> chatRoom = conf->getChatRoom(); diff --git a/src/conference/handlers/server-conference-list-event-handler.cpp b/src/conference/handlers/server-conference-list-event-handler.cpp index ccf439bc2b54caae40746fd8b5f2099c8b8cd2bc..b6d35d948aa53a0fb942a6acb27d41ac476120fb 100644 --- a/src/conference/handlers/server-conference-list-event-handler.cpp +++ b/src/conference/handlers/server-conference-list-event-handler.cpp @@ -115,16 +115,17 @@ void ServerConferenceListEventHandler::subscribeReceived(const std::shared_ptr<E return; } + auto core = getCore(); for (const auto &l : rl->getList()) { for (const auto &entry : l.getEntry()) { std::shared_ptr<Address> addr = Address::create(entry.getUri()); string notifyIdStr = addr->getUriParamValue("Last-Notify"); addr->removeUriParam("Last-Notify"); - ConferenceId conferenceId(addr, addr); + ConferenceId conferenceId(addr, addr, core->createConferenceIdParams()); std::shared_ptr<ServerConferenceEventHandler> handler = findHandler(conferenceId); if (!handler) continue; - shared_ptr<AbstractChatRoom> chatRoom = ev->getCore()->findChatRoom(conferenceId); + shared_ptr<AbstractChatRoom> chatRoom = core->findChatRoom(conferenceId, false); if (!chatRoom) { lError() << "Received subscribe for unknown chat room: " << conferenceId; continue; @@ -132,15 +133,15 @@ void ServerConferenceListEventHandler::subscribeReceived(const std::shared_ptr<E shared_ptr<Participant> participant = chatRoom->findParticipant(participantAddr); if (!participant) { - lError() << "Received subscribe for unknown participant: " << participantAddr - << " for chat room: " << conferenceId; + lError() << "Received subscribe for unknown participant [" << *participantAddr + << "] in chat room: " << conferenceId; continue; } shared_ptr<ParticipantDevice> device = participant->findDevice(deviceAddr); if (!device || (device->getState() != ParticipantDevice::State::Present && device->getState() != ParticipantDevice::State::Joining)) { - lError() << "Received subscribe for unknown device: " << deviceAddr - << " for participant: " << participantAddr << " for chat room: " << conferenceId; + lError() << "Received subscribe for unknown device [" << *deviceAddr << "] of participant [" + << *participantAddr << "] in chat room: " << conferenceId; continue; } device->setConferenceSubscribeEvent((subscriptionState == LinphoneSubscriptionIncomingReceived) ? ev @@ -191,8 +192,7 @@ void ServerConferenceListEventHandler::subscribeReceived(const std::shared_ptr<E cbs->notifyResponseCb = notifyResponseCb; ev->addCallbacks(cbs); auto multipart = Content::create(ContentManager::contentListToMultipart(contentsAsPtr)); - if (linphone_core_content_encoding_supported(getCore()->getCCore(), "deflate")) - multipart->setContentEncoding("deflate"); + if (linphone_core_content_encoding_supported(core->getCCore(), "deflate")) multipart->setContentEncoding("deflate"); ev->notify(multipart); } diff --git a/src/conference/notify-conference-listener.cpp b/src/conference/notify-conference-listener.cpp index 57cfe38f62e970054dad105234eb3516386dea7f..445b4c9f0873c46850dbe076dfb3083b729a0c41 100644 --- a/src/conference/notify-conference-listener.cpp +++ b/src/conference/notify-conference-listener.cpp @@ -128,7 +128,7 @@ void NotifyConferenceListener::onStateChanged(ConferenceInterface::State newStat } void NotifyConferenceListener::onActiveSpeakerParticipantDevice(const std::shared_ptr<ParticipantDevice> &device) { - _linphone_conference_notify_active_speaker_participant_device(conf->toC(), device->toC()); + _linphone_conference_notify_active_speaker_participant_device(conf->toC(), device ? device->toC() : nullptr); } void NotifyConferenceListener::onFullStateReceived() { diff --git a/src/conference/params/media-session-params-p.h b/src/conference/params/media-session-params-p.h index 4f9824b7d4b7b0acb697b31920c997a75ccf704a..8637b55d840e1f42aa66f3dd3b18037c7cdb2198 100644 --- a/src/conference/params/media-session-params-p.h +++ b/src/conference/params/media-session-params-p.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2022 Belledonne Communications SARL. + * Copyright (c) 2010-2025 Belledonne Communications SARL. * * This file is part of Liblinphone * (see https://gitlab.linphone.org/BC/public/liblinphone). @@ -82,7 +82,10 @@ public: } bool getUpdateCallWhenIceCompleted() const; - void enableToneIndications(const bool enable); + void disableRinging(bool disable); + bool ringingDisabled() const; + + void enableToneIndications(bool enable); bool toneIndicationsEnabled() const; void setUpdateCallWhenIceCompleted(bool value) { @@ -160,6 +163,7 @@ public: bool recordAware = false; SalMediaRecord recordState = SalMediaRecordNone; + bool mRingingDisabled = false; bool toneIndications = true; private: diff --git a/src/conference/params/media-session-params.cpp b/src/conference/params/media-session-params.cpp index 396708576f1819c681995d5e8591f5fe093d5326..c629dd97ac16688acc41d55f1098e09a1b170138 100644 --- a/src/conference/params/media-session-params.cpp +++ b/src/conference/params/media-session-params.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2022 Belledonne Communications SARL. + * Copyright (c) 2010-2025 Belledonne Communications SARL. * * This file is part of Liblinphone * (see https://gitlab.linphone.org/BC/public/liblinphone). @@ -40,6 +40,7 @@ void MediaSessionParamsPrivate::clone(const MediaSessionParamsPrivate *src) { audioBandwidthLimit = src->audioBandwidthLimit; audioDirection = src->audioDirection; audioMulticastEnabled = src->audioMulticastEnabled; + mRingingDisabled = src->mRingingDisabled; toneIndications = src->toneIndications; usedAudioCodec = src->usedAudioCodec; cameraEnabled = src->cameraEnabled; @@ -212,11 +213,19 @@ bool MediaSessionParamsPrivate::getUpdateCallWhenIceCompleted() const { return updateCallWhenIceCompleted; } +bool MediaSessionParamsPrivate::ringingDisabled() const { + return mRingingDisabled; +} + +void MediaSessionParamsPrivate::disableRinging(bool disable) { + mRingingDisabled = disable; +} + bool MediaSessionParamsPrivate::toneIndicationsEnabled() const { return toneIndications; } -void MediaSessionParamsPrivate::enableToneIndications(const bool enable) { +void MediaSessionParamsPrivate::enableToneIndications(bool enable) { toneIndications = enable; } @@ -295,7 +304,8 @@ void MediaSessionParams::initDefault(const std::shared_ptr<Core> &core, Linphone d->audioDirection = LinphoneMediaDirectionSendRecv; d->earlyMediaSendingEnabled = !!linphone_config_get_int(linphone_core_get_config(cCore), "misc", "real_early_media", false); - d->enableToneIndications(linphone_core_call_tone_indications_enabled(cCore)); + d->disableRinging(!!linphone_core_call_ringing_disabled(cCore)); + d->enableToneIndications(!!linphone_core_call_tone_indications_enabled(cCore)); d->audioMulticastEnabled = !!linphone_core_audio_multicast_enabled(cCore); d->videoMulticastEnabled = !!linphone_core_video_multicast_enabled(cCore); d->updateCallWhenIceCompleted = diff --git a/src/conference/participant-device.cpp b/src/conference/participant-device.cpp index 5749301510081bfbeadcea31e91e0cf7d649cd0f..7dc17a40b6f708edc7341603d39530fa7b9dab6f 100644 --- a/src/conference/participant-device.cpp +++ b/src/conference/participant-device.cpp @@ -91,7 +91,8 @@ std::shared_ptr<Address> ParticipantDevice::getAddress() const { void ParticipantDevice::setAddress(const std::shared_ptr<Address> &address) { if (mGruu) { - lInfo() << "Changing address of device [" << this << "] from " << *mGruu << " to " << *address; + auto conference = getConference(); + lInfo() << "Changing address of " << *this << " in " << *conference << " from " << *mGruu << " to " << *address; } mGruu = Address::create(address->getUri()); if (address->hasParam("+org.linphone.specs")) { @@ -102,12 +103,11 @@ void ParticipantDevice::setAddress(const std::shared_ptr<Address> &address) { std::shared_ptr<Participant> ParticipantDevice::getParticipant() const { if (mParticipant.expired()) { - lWarning() << "The participant owning device " << this << " (address " << *mGruu - << ") has already been deleted"; + lWarning() << "The participant owning " << *this << " has already been deleted"; } shared_ptr<Participant> participant = mParticipant.lock(); if (!participant) { - lWarning() << "Unable to get the participant owning the device " << this << " (address " << *mGruu << ")"; + lWarning() << "Unable to get the participant owning the " << *this; return nullptr; } return participant; @@ -134,7 +134,8 @@ void ParticipantDevice::setConferenceSubscribeEvent(const shared_ptr<EventSubscr AbstractChatRoom::SecurityLevel ParticipantDevice::getSecurityLevel() const { auto encryptionEngine = getCore()->getEncryptionEngine(); if (encryptionEngine) return encryptionEngine->getSecurityLevel(mGruu->asStringUriOnly()); - lWarning() << "Asking device security level but there is no encryption engine enabled"; + lWarning() << "Asking security level of " << *this << " in " << *getConference() + << " but there is no encryption engine enabled"; return AbstractChatRoom::SecurityLevel::ClearText; } @@ -185,14 +186,11 @@ bool ParticipantDevice::setSsrc(const LinphoneStreamType type, uint32_t newSsrc) if (changed) { if (conference) { - lInfo() << "Setting " << std::string(linphone_stream_type_to_string(type)) << " ssrc of participant device " - << this << " (address " << *mGruu << ") in conference " - << (conference->getConferenceAddress() ? conference->getConferenceAddress()->toString() - : std::string("sip:")) - << " to " << newSsrc; + lInfo() << "Setting " << std::string(linphone_stream_type_to_string(type)) << " ssrc of " << *this << " in " + << *conference << " to " << newSsrc; } else { - lInfo() << "Setting " << std::string(linphone_stream_type_to_string(type)) << " ssrc of participant device " - << this << " (address " << *mGruu << ") to " << newSsrc; + lInfo() << "Setting " << std::string(linphone_stream_type_to_string(type)) << " ssrc of " << *this << " to " + << newSsrc; } } @@ -217,14 +215,9 @@ bool ParticipantDevice::setThumbnailStreamSsrc(uint32_t newSsrc) { auto conference = getConference(); if (changed) { if (conference) { - lInfo() << "Setting thumbnail stream ssrc of participant device " << this << " (address " << *mGruu - << ") in conference " - << (conference->getConferenceAddress() ? conference->getConferenceAddress()->toString() - : std::string("sip:")) - << " to " << newSsrc; + lInfo() << "Setting thumbnail stream ssrc of " << *this << " in " << *conference << " to " << newSsrc; } else { - lInfo() << "Setting thumbnail stream ssrc of participant device " << this << " (address " << *mGruu - << ") to " << newSsrc; + lInfo() << "Setting thumbnail stream ssrc of " << *this << " to " << newSsrc; } } @@ -310,8 +303,7 @@ void ParticipantDevice::setState(State newState, bool notify) { } if (getCore() != nullptr && linphone_core_get_global_state(getCore()->getCCore()) != LinphoneGlobalStartup) { // When creating participant device from database - lInfo() << "Moving participant device " << this << " (address " << *mGruu << ") from state " << mState - << " to " << newState; + lInfo() << "Moving " << *this << " from state " << mState << " to " << newState; } mState = newState; _linphone_participant_device_notify_state_changed(toC(), (LinphoneParticipantDeviceState)newState); @@ -326,8 +318,7 @@ void ParticipantDevice::setState(State newState, bool notify) { bool ParticipantDevice::enableScreenSharing(bool enabled) { bool changed = (screenSharingEnabled() != enabled); if (changed) { - lInfo() << "Participant device " << this << " (address " << *mGruu << ") " - << std::string(enabled ? "starts" : "stops") << " sharing its screen"; + lInfo() << *this << " " << std::string(enabled ? "starts" : "stops") << " sharing its screen"; mIsScreenSharing = enabled; if (mSession && mSession->getPrivate()->isInConference()) { if (enabled) { @@ -378,6 +369,7 @@ void ParticipantDevice::setCapabilityDescriptor(const std::string &capabilities) } void ParticipantDevice::setSession(std::shared_ptr<CallSession> session) { + lInfo() << "Assigning session " << session << " to " << *this << " in " << *getConference(); mSession = session; } @@ -393,12 +385,8 @@ bool ParticipantDevice::setStreamLabel(const std::string &streamLabel, const Lin const bool idxFound = (streams.find(type) != streams.cend()); if (!idxFound || (streams[type].label != streamLabel)) { auto conference = getConference(); - const auto conferenceAddress = - ((conference && conference->getConferenceAddress()) ? conference->getConferenceAddress()->toString() - : std::string("sip:")); - lInfo() << "Setting label of " << std::string(linphone_stream_type_to_string(type)) - << " stream of participant device " << this << " (address " << *mGruu << ") in conference " - << conferenceAddress << " to " << streamLabel; + lInfo() << "Setting label of " << std::string(linphone_stream_type_to_string(type)) << " stream of " << *this + << " in " << *conference << " to " << streamLabel; streams[type].label = streamLabel; return true; } @@ -412,11 +400,8 @@ const std::string &ParticipantDevice::getThumbnailStreamLabel() const { bool ParticipantDevice::setThumbnailStreamLabel(const std::string &streamLabel) { if (thumbnailStream.label != streamLabel) { auto conference = getConference(); - lInfo() << "Setting label of the thumbnail stream of participant device " << this << " (address " << *mGruu - << ") in conference " - << (conference->getConferenceAddress() ? conference->getConferenceAddress()->toString() - : std::string("sip:")) - << " to " << streamLabel; + lInfo() << "Setting label of the thumbnail stream of " << *this << " in " << *conference << " to " + << streamLabel; thumbnailStream.label = streamLabel; return true; } @@ -813,15 +798,13 @@ void *ParticipantDevice::createWindowId() { : getStreamLabel(LinphoneStreamTypeVideo); // Empty label is used only for main stream which is handled by the call. if (label.empty() || !mGruu) { - lError() << "Unable to create a window ID for device [" << this << " - address " - << (mGruu ? mGruu->toString() : std::string("sip:")) << " because no label is associated to it"; + lError() << "Unable to create a window ID for " << *this << " because no label is associated to it"; } else { windowId = static_pointer_cast<MediaSession>(session)->createNativeVideoWindowId( label, conference->isMe(mGruu), true); } } else { - lError() << "Unable to create a window ID for device [" << this << " - address " - << (mGruu ? mGruu->toString() : std::string("sip:")) << " because no session is linked to this device"; + lError() << "Unable to create a window ID for " << *this << " because no session is linked to this device"; } #endif return windowId; @@ -842,8 +825,7 @@ void ParticipantDevice::setWindowId(void *newWindowId) { : getStreamLabel(LinphoneStreamTypeVideo); // Empty label is used only for main stream which is handled by the call. if (label.empty() || !mGruu) { - lError() << "Unable to set a window ID for device [" << this << " - address " - << (mGruu ? mGruu->toString() : std::string("sip:")) << " because no label is associated to it"; + lError() << "Unable to set a window ID for device " << *this << " because no label is associated to it"; } else { const auto isMe = conference->isMe(mGruu); if (isMe) { @@ -853,8 +835,7 @@ void ParticipantDevice::setWindowId(void *newWindowId) { } } } else { - lError() << "Unable to set a window ID for device [" << this << " - address " - << (mGruu ? mGruu->toString() : std::string("sip:")) << " because no session is linked to this device"; + lError() << "Unable to set a window ID for device " << *this << " because no session is linked to this device"; } #endif } @@ -866,6 +847,14 @@ void *ParticipantDevice::getWindowId() const { return mWindowId; } +void ParticipantDevice::setSendAddedNotify(bool sendNotify) { + mSendAddedNotify = sendNotify; +} + +bool ParticipantDevice::addedNotifySent() const { + return mSendAddedNotify; +} + void ParticipantDevice::setIsSpeaking(bool isSpeaking) { mIsSpeaking = isSpeaking; } diff --git a/src/conference/participant-device.h b/src/conference/participant-device.h index 634fdb37bbe6852b6c6d598f6640252ee64b1004..3b3d5de3a7867dcaf2a5280236ca721d957b8137 100644 --- a/src/conference/participant-device.h +++ b/src/conference/participant-device.h @@ -217,6 +217,9 @@ public: static bool isLeavingState(const ParticipantDevice::State &state); + void setSendAddedNotify(bool sendNotify); + bool addedNotifySent() const; + protected: std::shared_ptr<Conference> getConference() const; @@ -241,6 +244,7 @@ private: bool mIsMuted = false; bool mIsSpeaking = false; bool mIsScreenSharing = false; + bool mSendAddedNotify = true; // The following boolean tells if the device is changing the SUBSCRIBE event. In fact, it may happen that the client // and server get out of sychronization and the recovery process kicks in. The client unsubscribes and send a new @@ -269,6 +273,14 @@ private: L_DISABLE_COPY(ParticipantDevice); }; +inline std::ostream &operator<<(std::ostream &os, const ParticipantDevice &device) { + auto address = device.getAddress(); + auto addressStr = address ? address->toString() : std::string("sip:"); + return os << "ParticipantDevice [" << &device << "] (" << addressStr << ")"; + ; + return os; +} + std::ostream &operator<<(std::ostream &stream, ParticipantDevice::State state); class ParticipantDeviceCbs : public bellesip::HybridObject<LinphoneParticipantDeviceCbs, ParticipantDeviceCbs>, diff --git a/src/conference/participant.cpp b/src/conference/participant.cpp index 925918a467d8d1695a9469a9895c3a7fda35fd5a..65bf87278a9ecce8550a79c82c0fbb5f70fc0c80 100644 --- a/src/conference/participant.cpp +++ b/src/conference/participant.cpp @@ -40,7 +40,7 @@ Participant::Participant(const std::shared_ptr<Conference> conference, const std } Participant::Participant(const std::shared_ptr<Conference> conference, - const std::shared_ptr<Address> &address, + const std::shared_ptr<const Address> &address, std::shared_ptr<CallSession> callSession) : Participant(conference, address) { session = callSession; @@ -52,8 +52,7 @@ Participant::Participant(std::shared_ptr<Address> address) : mAddress(address) { } Participant::~Participant() { - lDebug() << "Destroying participant [" << this - << "] (address: " << (mAddress ? mAddress->toString() : std::string("sip:")) << ")"; + lDebug() << "Destroying " << *this; } void Participant::configure(const std::shared_ptr<Conference> conference, @@ -110,7 +109,8 @@ std::shared_ptr<ParticipantDevice> Participant::addDevice(const std::shared_ptr< } else if (gruu->isValid()) { newDevice = addDevice(gruu, name); } else { - lError() << "Attempting to add a device that has neither call session associated nor a valid address"; + lError() << "Attempting to add " << *device << " to " << *this + << " that has neither call session associated nor a valid address"; return nullptr; } @@ -131,11 +131,11 @@ std::shared_ptr<ParticipantDevice> Participant::addDevice(const std::shared_ptr< shared_ptr<ParticipantDevice> device = findDevice(session, false); if (device) return device; if (getCore() && (linphone_core_get_global_state(getCore()->getCCore()) == LinphoneGlobalOn)) { - lInfo() << "Add device " << (name.empty() ? "<no-name>" : name) << " with session " << session - << " to participant " << *getAddress(); + lInfo() << "Add device " << (name.empty() ? "<no-name>" : name) << " with session " << session << " to " + << *this; } else { - lDebug() << "Add device " << (name.empty() ? "<no-name>" : name) << " with session " << session - << " to participant " << getAddress()->toString(); + lDebug() << "Add device " << (name.empty() ? "<no-name>" : name) << " with session " << session << " to " + << *this; } device = ParticipantDevice::create(getSharedFromThis(), session, name); devices.push_back(device); @@ -150,13 +150,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 - << " to participant " << *getAddress(); + lInfo() << "Add device " << (name.empty() ? "<no-name>" : name) << " with address " << *gruu << " to " + << *this; } } else { if (bctbx_log_level_enabled(BCTBX_LOG_DOMAIN, BCTBX_LOG_DEBUG)) { - lDebug() << "Add device " << (name.empty() ? "<no-name>" : name) << " with address " << *gruu - << " to participant " << *getAddress(); + lDebug() << "Add device " << (name.empty() ? "<no-name>" : name) << " with address " << *gruu << " to " + << *this; } } device = ParticipantDevice::create(getSharedFromThis(), gruu, name); @@ -179,8 +179,7 @@ Participant::findDevice(const LinphoneStreamType type, const std::string &label, } } if (logFailure) { - lInfo() << "Unable to find device with label " << label << " among those belonging to participant " - << *getAddress(); + lInfo() << "Unable to find device with label " << label << " among those belonging to " << *this; } return nullptr; } @@ -190,8 +189,7 @@ shared_ptr<ParticipantDevice> Participant::findDeviceByCallId(const std::string if (device->getCallId() == callId) return device; } if (logFailure) { - lInfo() << "Unable to find device with call id " << callId << " among those belonging to participant " - << *getAddress(); + lInfo() << "Unable to find device with call id " << callId << " among those belonging to " << *this; } return nullptr; } @@ -212,8 +210,7 @@ shared_ptr<ParticipantDevice> Participant::findDevice(const std::shared_ptr<cons } if (logFailure) { - lInfo() << "Unable to find device with address " << *gruu << " among those belonging to participant " - << *getAddress(); + lInfo() << "Unable to find device with address " << *gruu << " among those belonging to " << *this; } return nullptr; } @@ -228,7 +225,7 @@ shared_ptr<ParticipantDevice> Participant::findDevice(const shared_ptr<const Cal } if (logFailure) { - lInfo() << "Unable to find device with call session " << session; + lInfo() << "Unable to find device with call session " << session << " among those belonging to " << *this; } return nullptr; } @@ -268,7 +265,7 @@ AbstractChatRoom::SecurityLevel Participant::getSecurityLevelExcept(const std::shared_ptr<ParticipantDevice> &ignoredDevice) const { auto encryptionEngine = getCore()->getEncryptionEngine(); if (!encryptionEngine) { - lWarning() << "Asking participant security level but there is no encryption engine enabled"; + lWarning() << *this << ": Asking participant security level but there is no encryption engine enabled"; return AbstractChatRoom::SecurityLevel::ClearText; } @@ -295,8 +292,7 @@ std::shared_ptr<Core> Participant::getCore() const { std::shared_ptr<Conference> Participant::getConference() const { if (mConference.expired()) { - lDebug() << "The conference owning participant " << this << " (address " << *getAddress() - << ") has already been deleted"; + lDebug() << "The conference owning participant " << *this << " has already been deleted"; } return mConference.lock(); } @@ -342,7 +338,7 @@ int Participant::getSequenceNumber() const { }; void Participant::setRole(Participant::Role role) { - lInfo() << "Changing role of participant " << *getAddress() << " from " << Participant::roleToText(mRole) << " to " + lInfo() << "Changing role of " << *this << " from " << Participant::roleToText(mRole) << " to " << Participant::roleToText(role); mRole = role; } diff --git a/src/conference/participant.h b/src/conference/participant.h index 5c07b8df3fea0ff675797eb70f56ff5a25d21942..beb3b72c8ac9bc5c0621f82412db16b8e5fe0bd8 100644 --- a/src/conference/participant.h +++ b/src/conference/participant.h @@ -79,7 +79,7 @@ public: static Participant::Role textToRole(const std::string &str); explicit Participant(const std::shared_ptr<Conference> conference, - const std::shared_ptr<Address> &address, + const std::shared_ptr<const Address> &address, std::shared_ptr<CallSession> callSession); explicit Participant(const std::shared_ptr<Conference> conference, const std::shared_ptr<const Address> &address); // acquires the address, that must be a simple URI without 'gr' parameter. @@ -183,7 +183,10 @@ private: }; inline std::ostream &operator<<(std::ostream &os, const Participant &participant) { - return os << participant.getAddress()->toString(); + auto address = participant.getAddress(); + auto addressStr = address ? address->toString() : std::string("sip:"); + return os << "Participant [" << &participant << "] (" << addressStr << ")"; + ; return os; } diff --git a/src/conference/server-conference.cpp b/src/conference/server-conference.cpp index 02bc9ba4102aa5b3e32d49922b3ed475efd7a62b..55ffe1121db3fb42c338dd29afe134dcb9c6aa24 100644 --- a/src/conference/server-conference.cpp +++ b/src/conference/server-conference.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2022 Belledonne Communications SARL. + * Copyright (c) 2010-2025 Belledonne Communications SARL. * * This file is part of Liblinphone * (see https://gitlab.linphone.org/BC/public/liblinphone). @@ -57,24 +57,32 @@ LINPHONE_BEGIN_NAMESPACE constexpr int SERVER_CONFIGURATION_FAILED = 1; ServerConference::ServerConference(const shared_ptr<Core> &core, - const std::shared_ptr<Address> &myAddress, std::shared_ptr<CallSessionListener> listener, const std::shared_ptr<ConferenceParams> params) - : Conference(core, myAddress, listener, params) { + : Conference(core, listener, params) { } ServerConference::~ServerConference() { - if ((mState != ConferenceInterface::State::Terminated) && (mState != ConferenceInterface::State::Deleted)) { + if ((mState != ConferenceInterface::State::TerminationPending) && + (mState != ConferenceInterface::State::Terminated) && (mState != ConferenceInterface::State::Deleted)) { terminate(); } + if (mState == ConferenceInterface::State::TerminationPending) { + setState(ConferenceInterface::State::Terminated); + } + if (mState == ConferenceInterface::State::Terminated) { + setState(ConferenceInterface::State::Deleted); + } cleanup(); } -void ServerConference::initFromDb(BCTBX_UNUSED(const std::shared_ptr<Participant> &me), +void ServerConference::initFromDb(const std::shared_ptr<Participant> &me, const ConferenceId conferenceId, const unsigned int lastNotifyId, BCTBX_UNUSED(bool hasBeenLeft)) { - mMe = Participant::create(getSharedFromThis(), mConfParams->getMe()); + if (me) { + mMe = me->clone()->toSharedPtr(); + } setLastNotify(lastNotifyId); mConferenceId = conferenceId; getCore()->getPrivate()->registerListener(this); @@ -97,27 +105,29 @@ void ServerConference::init(SalCallOp *op, ConferenceListener *confListener) { // Set last notify to 1 in order to ensure that the 1st notify to client conference is correctly processed // Remote conference sets last notify to 0 in its constructor setLastNotify(1); - mMe = Participant::create(getSharedFromThis(), mConfParams->getMe()); + + inititializeMe(); setOrganizer(op ? Address::create(op->getFrom()) : mMe->getAddress()); + const auto &core = getCore(); createEventHandler(confListener); if (mConfParams->chatEnabled()) { mConfParams->enableLocalParticipant(false); - getCore()->getPrivate()->registerListener(this); + core->getPrivate()->registerListener(this); #ifdef HAVE_ADVANCED_IM auto chatRoom = - dynamic_pointer_cast<ServerChatRoom>((new ServerChatRoom(getCore(), getSharedFromThis()))->toSharedPtr()); + dynamic_pointer_cast<ServerChatRoom>((new ServerChatRoom(core, getSharedFromThis()))->toSharedPtr()); setChatRoom(chatRoom); #endif // HAVE_ADVANCED_IM } setState(ConferenceInterface::State::Instantiated); - LinphoneCore *lc = getCore()->getCCore(); + LinphoneCore *lc = core->getCCore(); if (op) { configure(op); } else { // Update proxy contact address to add conference ID // Do not use organizer address directly as it may lack some parameter like gruu - auto account = getCore()->lookupKnownAccount(mOrganizer, true); + auto account = core->lookupKnownAccount(mOrganizer, true); char *contactAddressStr = nullptr; if (account && account->getOp()) { contactAddressStr = sal_address_as_string(account->getOp()->getContactAddress()); @@ -146,14 +156,17 @@ void ServerConference::init(SalCallOp *op, ConferenceListener *confListener) { #endif // HAVE_ADVANCED_IM if (!eventLogEnabled) { - setConferenceId(ConferenceId(contactAddress, contactAddress)); + setConferenceId(ConferenceId(contactAddress, contactAddress, core->createConferenceIdParams())); } #ifdef HAVE_DB_STORAGE const auto &conferenceInfo = createOrGetConferenceInfo(); if (conferenceInfo) { - auto &mainDb = getCore()->getPrivate()->mainDb; + auto &mainDb = core->getPrivate()->mainDb; if (mainDb) { + lInfo() << "Inserting new conference information to database in order to be able to recreate the " + "conference " + << *getConferenceAddress() << " in case of restart"; mainDb->insertConferenceInfo(conferenceInfo); } } @@ -173,12 +186,12 @@ void ServerConference::createEventHandler(BCTBX_UNUSED(ConferenceListener *confL bool eventLogEnabled = !!linphone_config_get_bool(linphone_core_get_config(lc), "misc", "conference_event_log_enabled", TRUE); if (eventLogEnabled) { - eventHandler = std::make_shared<ServerConferenceEventHandler>(getSharedFromThis(), confListener); + mEventHandler = std::make_shared<ServerConferenceEventHandler>(getSharedFromThis(), confListener); const auto chatEnabled = mConfParams->chatEnabled(); if (chatEnabled && getCore()->getPrivate()->serverListEventHandler && getConferenceId().isValid()) { - getCore()->getPrivate()->serverListEventHandler->addHandler(eventHandler); + getCore()->getPrivate()->serverListEventHandler->addHandler(mEventHandler); } - addListener(eventHandler); + addListener(mEventHandler); } else { #endif // HAVE_ADVANCED_IM lInfo() << "Unable to add listener to local conference as conference event package (RFC 4575) is disabled or " @@ -444,8 +457,8 @@ void ServerConference::configure(SalCallOp *op) { } } } - mConfParams->setSecurityLevel(securityLevel); + if (mMixerSession) { mMixerSession->setSecurityLevel(mConfParams->getSecurityLevel()); } @@ -515,6 +528,7 @@ void ServerConference::configure(SalCallOp *op) { msp->initDefault(getCore(), LinphoneCallIncoming); msp->enableAudio(audioEnabled); msp->enableVideo(videoEnabled); + msp->getPrivate()->disableRinging(true); msp->getPrivate()->enableToneIndications(false); msp->getPrivate()->setConferenceCreation(true); msp->getPrivate()->setInConference(true); @@ -532,7 +546,7 @@ void ServerConference::configure(SalCallOp *op) { if (createdConference) { const auto &conferenceAddress = info->getUri(); - setConferenceId(ConferenceId(conferenceAddress, conferenceAddress)); + setConferenceId(ConferenceId(conferenceAddress, conferenceAddress, getCore()->createConferenceIdParams())); setConferenceAddress(conferenceAddress); } @@ -584,9 +598,10 @@ void ServerConference::confirmJoining(BCTBX_UNUSED(SalCallOp *op)) { serverGroupChatRoom = dynamic_pointer_cast<ServerChatRoom>(chatRoom); } + auto conferenceAddress = getConferenceAddress(); std::shared_ptr<Address> contactAddr = Address::create(op->getRemoteContact()); if (contactAddr->getUriParamValue("gr").empty()) { - lError() << "Conference " << *getConferenceAddress() + lError() << "Conference " << *conferenceAddress << ": Declining INVITE because the contact does not have a 'gr' uri parameter [" << *contactAddr << "]"; op->decline(SalReasonDeclined, ""); @@ -599,10 +614,11 @@ void ServerConference::confirmJoining(BCTBX_UNUSED(SalCallOp *op)) { shared_ptr<CallSession> deviceSession; auto joiningPendingAfterCreation = serverGroupChatRoom ? serverGroupChatRoom->isJoiningPendingAfterCreation() : false; + auto from = Address::create(op->getFrom()); if (joiningPendingAfterCreation) { // Check if the participant is already there, this INVITE may come from an unknown device of an already present // participant - participant = addParticipantToList(Address::create(op->getFrom())); + participant = addParticipantToList(from); participant->setAdmin(true); device = participant->addDevice(gruu); deviceSession = device->getSession(); @@ -620,17 +636,17 @@ void ServerConference::confirmJoining(BCTBX_UNUSED(SalCallOp *op)) { } } else { // INVITE coming from an invited participant - participant = findInvitedParticipant(Address::create(op->getFrom())); + participant = findInvitedParticipant(from); if (!participant) { - lError() << "Conference " << *getConferenceAddress() - << ": Declining INVITE coming from someone that is not a participant"; + lError() << "Conference " << *conferenceAddress << ": Declining INVITE coming from [" << *from + << "] that is not in the list of invited participants"; op->decline(SalReasonDeclined, ""); return; } // In protocol < 1.1, one to one chatroom can be resurected by a participant, but the participant actually never // leaves from server's standpoint. if (getCurrentParams()->isGroup() && op->isContentInRemote(ContentType::ResourceLists)) { - lError() << "Conference " << *getConferenceAddress() + lError() << "Conference " << *conferenceAddress << "Receiving ressource list body while not in creation step."; op->decline(SalReasonNotAcceptable); return; @@ -638,7 +654,7 @@ void ServerConference::confirmJoining(BCTBX_UNUSED(SalCallOp *op)) { device = participant->addDevice(gruu); if (!getCurrentParams()->isGroup()) { if (device->getState() == ParticipantDevice::State::Left) { - lInfo() << "Conference " << *getConferenceAddress() << " " << *gruu + lInfo() << "Conference [" << *conferenceAddress << "] - " << *gruu << " is reconnected to the one to one chatroom."; setParticipantDeviceState(device, ParticipantDevice::State::Joining); } @@ -833,6 +849,10 @@ void ServerConference::confirmCreation() { const_cast<CallSessionParamsPrivate *>(L_GET_PRIVATE(session->getParams()))->setInConference(true); const auto &actualConferenceAddress = getConferenceAddress(); + if (!actualConferenceAddress) { + lError() << "Something went wrong while creating " << *this + << " because the conference address is not yet known"; + } session->getPrivate()->setConferenceId(actualConferenceAddress->getUriParamValue(Conference::ConfIdParameter)); #ifdef HAVE_ADVANCED_IM @@ -846,8 +866,7 @@ void ServerConference::confirmCreation() { #ifdef HAVE_DB_STORAGE // Method startIncomingNotification can move the conference to the CreationFailed state if the organizer // doesn't have any of the codecs the server supports - if ((mConfParams->audioEnabled() || mConfParams->videoEnabled()) && - (getState() != ConferenceInterface::State::CreationFailed)) { + if (supportsMedia() && (getState() != ConferenceInterface::State::CreationFailed)) { // Store into DB after the start incoming notification in order to have a valid conference address being the // contact address of the call auto &mainDb = getCore()->getPrivate()->mainDb; @@ -943,7 +962,7 @@ void ServerConference::finalizeCreation() { } #endif // HAVE_ADVANCED_IM const std::shared_ptr<Address> &conferenceAddress = getConferenceAddress(); - setConferenceId(ConferenceId(conferenceAddress, conferenceAddress)); + setConferenceId(ConferenceId(conferenceAddress, conferenceAddress, getCore()->createConferenceIdParams())); std::shared_ptr<ConferenceInfo> info = nullptr; #ifdef HAVE_DB_STORAGE auto &mainDb = getCore()->getPrivate()->mainDb; @@ -955,7 +974,7 @@ void ServerConference::finalizeCreation() { if (createdConference) { lInfo() << "Conference [" << this << "] with address " << *conferenceAddress << " has already been created therefore no need to carry out the redirection to its address"; - } else { + } else if (mMe) { shared_ptr<CallSession> session = mMe->getSession(); if (session) { if (mConfParams->getJoiningMode() == ConferenceParams::JoiningMode::DialOut) { @@ -994,7 +1013,7 @@ void ServerConference::finalizeCreation() { #ifdef HAVE_ADVANCED_IM if (chatRoom) { auto serverGroupChatRoom = dynamic_pointer_cast<ServerChatRoom>(chatRoom); - getCore()->getPrivate()->serverListEventHandler->addHandler(eventHandler); + getCore()->getPrivate()->serverListEventHandler->addHandler(mEventHandler); serverGroupChatRoom->setJoiningPendingAfterCreation(true); getCore()->getPrivate()->insertChatRoomWithDb(chatRoom); } @@ -1032,8 +1051,8 @@ void ServerConference::subscribeReceived(const shared_ptr<EventSubscribe> &event inviteDevice(device); } - if (eventHandler) { - if (eventHandler->subscribeReceived(event) == 0) { + if (mEventHandler) { + if (mEventHandler->subscribeReceived(event) == 0) { if (device && chatEnabled) { vector<string> acceptedContents = vector<string>(); const auto message = (belle_sip_message_t *)event->getOp()->getRecvCustomHeaders(); @@ -1078,8 +1097,8 @@ void ServerConference::subscribeReceived(const shared_ptr<EventSubscribe> &event #endif // _MSC_VER void ServerConference::subscriptionStateChanged(shared_ptr<EventSubscribe> event, LinphoneSubscriptionState state) { #ifdef HAVE_ADVANCED_IM - if (eventHandler) { - eventHandler->subscriptionStateChanged(event, state); + if (mEventHandler) { + mEventHandler->subscriptionStateChanged(event, state); } else { #endif // HAVE_ADVANCED_IM lInfo() << "Unable to handle subscription state change because conference event package (RFC 4575) is disabled " @@ -1224,7 +1243,7 @@ shared_ptr<ConferenceAvailableMediaEvent> ServerConference::notifyAvailableMedia return Conference::notifyAvailableMediaChanged(creationTime, isFullState, mediaCapabilities); } -int ServerConference::inviteAddresses(const list<std::shared_ptr<const Address>> &addresses, +int ServerConference::inviteAddresses(const std::list<std::shared_ptr<Address>> &addresses, const LinphoneCallParams *params) { const auto &coreCurrentCall = getCore()->getCurrentCall(); @@ -1291,6 +1310,7 @@ int ServerConference::inviteAddresses(const list<std::shared_ptr<const Address>> linphone_call_params_enable_audio(new_params, audioEnabled); linphone_call_params_enable_video(new_params, videoEnabled); } + linphone_call_params_disable_ringing(new_params, supportsMedia()); linphone_call_params_enable_tone_indications(new_params, !supportsMedia()); linphone_call_params_set_in_conference(new_params, TRUE); linphone_call_params_set_start_time(new_params, mConfParams->getStartTime()); @@ -1319,14 +1339,13 @@ int ServerConference::inviteAddresses(const list<std::shared_ptr<const Address>> Call::toCpp(linphone_core_invite_address_with_params_2( lc, address->toC(), new_params, L_STRING_TO_C(mConfParams->getUtf8Subject()), NULL)) ->getSharedFromThis(); - session = call->getActiveSession(); - session->addListener(getSharedFromThis()); - - tryAddMeDevice(); if (!call) { lError() << "ServerConference::inviteAddresses(): could not invite participant"; } else { + tryAddMeDevice(); + session = call->getActiveSession(); + session->addListener(getSharedFromThis()); addParticipant(call); auto participant = findParticipant(address); participant->setPreserveSession(false); @@ -1369,7 +1388,7 @@ int ServerConference::inviteAddresses(const list<std::shared_ptr<const Address>> return 0; } -bool ServerConference::dialOutAddresses(const std::list<std::shared_ptr<const Address>> &addressList) { +bool ServerConference::dialOutAddresses(const std::list<std::shared_ptr<Address>> &addressList) { auto new_params = linphone_core_create_call_params(getCore()->getCCore(), nullptr); linphone_call_params_enable_audio(new_params, mConfParams->audioEnabled()); linphone_call_params_enable_video(new_params, mConfParams->videoEnabled()); @@ -1435,7 +1454,6 @@ bool ServerConference::dialOutAddresses(const std::list<std::shared_ptr<const Ad return success; } -// returns true if a new session has been created, false if there is already an existing and valid one. shared_ptr<CallSession> ServerConference::makeSession(const std::shared_ptr<ParticipantDevice> &device, BCTBX_UNUSED(const MediaSessionParams *csp)) { shared_ptr<CallSession> session = device->getSession(); @@ -1471,6 +1489,7 @@ shared_ptr<CallSession> ServerConference::makeSession(const std::shared_ptr<Part } } + currentParams->getPrivate()->disableRinging(!supportsMedia()); currentParams->getPrivate()->enableToneIndications(supportsMedia()); currentParams->getPrivate()->setInConference(TRUE); session = participant->createSession(*this, currentParams, true); @@ -1506,6 +1525,7 @@ void ServerConference::byeDevice(const std::shared_ptr<ParticipantDevice> &devic to_string(getCurrentParams()->getChatParams()->getEphemeralLifetime())); } } + csp.getPrivate()->disableRinging(!supportsMedia()); csp.getPrivate()->enableToneIndications(supportsMedia()); csp.getPrivate()->setInConference(TRUE); const string &confId = conferenceAddress->getUriParamValue(Conference::ConfIdParameter); @@ -1746,7 +1766,7 @@ bool ServerConference::addParticipants(const std::list<std::shared_ptr<Call>> &c return success; } -bool ServerConference::addParticipants(const std::list<std::shared_ptr<const Address>> &addresses) { +bool ServerConference::addParticipants(const std::list<std::shared_ptr<Address>> &addresses) { return Conference::addParticipants(addresses); } @@ -1779,7 +1799,7 @@ bool ServerConference::addParticipant(std::shared_ptr<Call> call) { lError() << "Unable to add " << *call << " because " << *this << " will start at " << startTime << " and now it is " << now; LinphoneErrorInfo *ei = linphone_error_info_new(); - linphone_error_info_set(ei, NULL, LinphoneReasonUnknown, 403, "Conference not started yet", + linphone_error_info_set(ei, NULL, LinphoneReasonForbidden, 403, "Conference not started yet", "Conference not started yet"); call->terminate(ei); linphone_error_info_unref(ei); @@ -1790,7 +1810,7 @@ bool ServerConference::addParticipant(std::shared_ptr<Call> call) { lError() << "Unable to add " << *call << " because " << *this << " is already terminated at " << endTime << " and now it is " << now; LinphoneErrorInfo *ei = linphone_error_info_new(); - linphone_error_info_set(ei, NULL, LinphoneReasonUnknown, 403, "Conference already terminated", + linphone_error_info_set(ei, NULL, LinphoneReasonForbidden, 403, "Conference already terminated", "Conference already terminated"); call->terminate(ei); linphone_error_info_unref(ei); @@ -2000,7 +2020,7 @@ bool ServerConference::addParticipant(std::shared_ptr<Call> call) { // If no resource list is provided in the INVITE, there is not need to call participants if ((initialState == ConferenceInterface::State::CreationPending) && dialout && !isEmpty) { - list<std::shared_ptr<const Address>> addresses; + std::list<std::shared_ptr<Address>> addresses; for (auto &participant : mInvitedParticipants) { const auto &addr = participant->getAddress(); // Do not invite organizer as it is already dialing in @@ -2018,7 +2038,7 @@ bool ServerConference::addParticipant(std::shared_ptr<Call> call) { return false; } -bool ServerConference::addParticipant(const std::shared_ptr<const Address> &participantAddress) { +bool ServerConference::addParticipant(const std::shared_ptr<Address> &participantAddress) { auto participantInfo = Factory::get()->createParticipantInfo(participantAddress); // Participants invited after the start of a conference through the address can only listen to it participantInfo->setRole(Participant::Role::Listener); @@ -2044,7 +2064,7 @@ bool ServerConference::addParticipant(const std::shared_ptr<ParticipantInfo> &in mInvitedParticipants.push_back(participant); } - std::list<std::shared_ptr<const Address>> addressesList{participantAddress}; + std::list<std::shared_ptr<Address>> addressesList{participantAddress}; return dialOutAddresses(addressesList); } } else { @@ -2112,6 +2132,10 @@ bool ServerConference::addParticipant(const std::shared_ptr<ParticipantInfo> &in return false; } +bool ServerConference::addParticipantDevice(std::shared_ptr<Call> call) { + return Conference::addParticipantDevice(call); +} + void ServerConference::addParticipantDevice(BCTBX_UNUSED(const shared_ptr<Participant> &participant), BCTBX_UNUSED(const shared_ptr<ParticipantDeviceIdentity> &deviceInfo)) { #ifdef HAVE_ADVANCED_IM @@ -2197,36 +2221,12 @@ std::shared_ptr<ParticipantDevice> ServerConference::createParticipantDevice(std return device; } -void ServerConference::notifyNewDevice(const std::shared_ptr<ParticipantDevice> &device) { - if (device) { - const auto &p = device->getParticipant(); - if (p) { - time_t creationTime = time(nullptr); - if (device->getState() == ParticipantDevice::State::Joining) { - notifyParticipantDeviceAdded(creationTime, false, p, device); - } else { - notifyParticipantDeviceJoiningRequest(creationTime, false, p, device); - } - } - } -} - -bool ServerConference::addParticipantDevice(std::shared_ptr<Call> call) { - auto success = (Conference::addParticipantDevice(call)); - if (success) { - auto session = call->getActiveSession(); - notifyNewDevice(findParticipantDevice(session)); - } - return success; -} - bool ServerConference::addParticipantAndDevice(std::shared_ptr<Call> call) { auto success = Conference::addParticipant(call); if (success) { auto session = call->getActiveSession(); - // Not really useful to notify the new device as it is already done by the method Conference::addParticipant. - // Nonetheless this allow to call a few callback related to the participant device addition and state on the - // server side + // Just notify the application that a new device has been added but do not send out the XML + // In fact the list of endpoints has already been sent out by onParticipantAdded callback notifyNewDevice(findParticipantDevice(session)); } else { // If the participant has already been added, at least try to add the device @@ -2273,7 +2273,7 @@ int ServerConference::removeParticipant(const std::shared_ptr<CallSession> &sess } else { if (!sessionHasEnded) { lError() << "Trying to remove participant " << *session->getRemoteAddress() << " with session " << session - << " which is not part of conference " << *getConferenceAddress(); + << " which is not part of " << *this; } return -1; } @@ -2420,35 +2420,34 @@ bool ServerConference::removeParticipant(const std::shared_ptr<Participant> &par } } else { success = Conference::removeParticipant(participant); - } - #ifdef HAVE_ADVANCED_IM - // Add event to DB only for server group chat rooms - const auto &chatRoom = getChatRoom(); - if (mConfParams->chatEnabled() && chatRoom) { - auto serverGroupChatRoom = dynamic_pointer_cast<ServerChatRoom>(chatRoom); - unique_ptr<MainDb> &mainDb = getCore()->getPrivate()->mainDb; - time_t creationTime = time(nullptr); - shared_ptr<ConferenceParticipantEvent> event = notifyParticipantRemoved(creationTime, false, participant); - mainDb->addConferenceParticipantEventToDb(event); - - serverGroupChatRoom->removeQueuedParticipantMessages(participant); - - // Remove participant from the database immediately because it has no devices associated. - // In case of registration in the future, the devices will attempt to subscribe and the conference server will - // reply 603 Decline - if (participantHasNoDevices) { - lInfo() - << "Conference " << *getConferenceAddress() << ": Participant '" << *participant->getAddress() - << "' is immediately removed because there has been an explicit request to do it and it has no devices " - "associated to it, unsubscribing"; - serverGroupChatRoom->unSubscribeRegistrationForParticipant(participant->getAddress()); - mainDb->deleteChatRoomParticipant(serverGroupChatRoom, participant->getAddress()); - } + // Add event to DB only for server group chat rooms + const auto &chatRoom = getChatRoom(); + if (mConfParams->chatEnabled() && chatRoom) { + auto serverGroupChatRoom = dynamic_pointer_cast<ServerChatRoom>(chatRoom); + unique_ptr<MainDb> &mainDb = getCore()->getPrivate()->mainDb; + time_t creationTime = time(nullptr); + shared_ptr<ConferenceParticipantEvent> event = notifyParticipantRemoved(creationTime, false, participant); + mainDb->addConferenceParticipantEventToDb(event); - if (!hasAdminLeft()) chooseAnotherAdminIfNoneInConference(); - } + serverGroupChatRoom->removeQueuedParticipantMessages(participant); + + // Remove participant from the database immediately because it has no devices associated. + // In case of registration in the future, the devices will attempt to subscribe and the conference server + // will reply 603 Decline + if (participantHasNoDevices) { + lInfo() << "Conference " << *getConferenceAddress() << ": Participant '" << *participant->getAddress() + << "' is immediately removed because there has been an explicit request to do it and it has no " + "devices " + "associated to it, unsubscribing"; + serverGroupChatRoom->unSubscribeRegistrationForParticipant(participant->getAddress()); + mainDb->deleteChatRoomParticipant(serverGroupChatRoom, participant->getAddress()); + } + + if (!hasAdminLeft()) chooseAnotherAdminIfNoneInConference(); + } #endif // HAVE_ADVANCED_IM + } return success; } @@ -2517,10 +2516,9 @@ void ServerConference::setUtf8Subject(const std::string &subject) { const auto previousSubject = getUtf8Subject(); Conference::setUtf8Subject(subject); if (subject.compare(previousSubject) != 0) { - const auto &chatRoom = getChatRoom(); time_t creationTime = time(nullptr); auto event = notifySubjectChanged(creationTime, false, getUtf8Subject()); - if (mConfParams->chatEnabled() && chatRoom) { + if (isChatOnly()) { getCore()->getPrivate()->mainDb->addEvent(event); } } @@ -2532,9 +2530,8 @@ void ServerConference::cleanup() { } try { #ifdef HAVE_ADVANCED_IM - const auto chatEnabled = mConfParams->chatEnabled(); - if (chatEnabled && getCore()->getPrivate()->serverListEventHandler) { - getCore()->getPrivate()->serverListEventHandler->removeHandler(eventHandler); + if (isChatOnly() && getCore()->getPrivate()->serverListEventHandler) { + getCore()->getPrivate()->serverListEventHandler->removeHandler(mEventHandler); } #endif // HAVE_ADVANCED_IM getCore()->getPrivate()->unregisterListener(this); @@ -2542,8 +2539,8 @@ void ServerConference::cleanup() { // Unable to unregister listener here. Core is destroyed and the listener doesn't exist. } #ifdef HAVE_ADVANCED_IM - if (eventHandler) { - eventHandler.reset(); + if (mEventHandler) { + mEventHandler.reset(); } #endif // HAVE_ADVANCED_IM } @@ -2876,17 +2873,20 @@ int ServerConference::terminate() { const auto zeroDevices = (noDevices == 0); if (zeroDevices #ifdef HAVE_ADVANCED_IM - || !eventHandler + || !mEventHandler #endif // HAVE_ADVANCED_IM ) { setState(ConferenceInterface::State::Terminated); } - // The server deletes the conference info once the conference is terminated. - // It avoids having a growing database on the server side - if (conferenceAddress && - (isConferenceEnded() || (linphone_core_get_global_state(getCore()->getCCore()) == LinphoneGlobalOn))) { - getCore()->getPrivate()->deleteConferenceInfo(conferenceAddress); + try { + // The server deletes the conference info once the conference is terminated. + // It avoids having a growing database on the server side + if (conferenceAddress && + (isConferenceEnded() || (linphone_core_get_global_state(getCore()->getCCore()) == LinphoneGlobalOn))) { + getCore()->getPrivate()->deleteConferenceInfo(conferenceAddress); + } + } catch (const bad_weak_ptr &) { } } else { setChatRoom(nullptr); @@ -3065,7 +3065,7 @@ void ServerConference::onCallSessionStateChanged(const std::shared_ptr<CallSessi lInfo() << *remoteAddress << " is not allowed to change the participant list of conference [" << this << " - " << *getConferenceAddress() << "]"; LinphoneErrorInfo *ei = linphone_error_info_new(); - linphone_error_info_set(ei, NULL, LinphoneReasonUnknown, 403, + linphone_error_info_set(ei, NULL, LinphoneReasonForbidden, 403, "Participant not authorized to change the participant list", "Participant not authorized to change the participant list"); ms->decline(ei); diff --git a/src/conference/server-conference.h b/src/conference/server-conference.h index 055ff10c43a8120212325f3fa2e732f16986c491..2f147b6b2e0af7fdd980845cf4e4094204ab13b6 100644 --- a/src/conference/server-conference.h +++ b/src/conference/server-conference.h @@ -47,14 +47,13 @@ public: static constexpr int sConfIdLength = 32; ServerConference(const std::shared_ptr<Core> &core, - const std::shared_ptr<Address> &myAddress, std::shared_ptr<CallSessionListener> listener, const std::shared_ptr<ConferenceParams> params); virtual ~ServerConference(); - virtual int inviteAddresses(const std::list<std::shared_ptr<const Address>> &addresses, + virtual int inviteAddresses(const std::list<std::shared_ptr<Address>> &addresses, const LinphoneCallParams *params) override; - virtual bool dialOutAddresses(const std::list<std::shared_ptr<const Address>> &addressList) override; + virtual bool dialOutAddresses(const std::list<std::shared_ptr<Address>> &addressList) override; void inviteDevice(const std::shared_ptr<ParticipantDevice> &device); void byeDevice(const std::shared_ptr<ParticipantDevice> &device); @@ -64,10 +63,10 @@ public: int getParticipantCount() const override; virtual bool addParticipants(const std::list<std::shared_ptr<Call>> &call) override; - virtual bool addParticipants(const std::list<std::shared_ptr<const Address>> &addresses) override; + virtual bool addParticipants(const std::list<std::shared_ptr<Address>> &addresses) override; virtual bool addParticipant(std::shared_ptr<Call> call) override; virtual bool addParticipant(const std::shared_ptr<ParticipantInfo> &info) override; - virtual bool addParticipant(const std::shared_ptr<const Address> &participantAddress) override; + virtual bool addParticipant(const std::shared_ptr<Address> &participantAddress) override; virtual bool finalizeParticipantAddition(std::shared_ptr<Call> call) override; virtual std::shared_ptr<ParticipantDevice> createParticipantDevice(std::shared_ptr<Participant> participant, std::shared_ptr<Call> call) override; @@ -199,10 +198,6 @@ public: virtual void setParticipantAdminStatus(const std::shared_ptr<Participant> &participant, bool isAdmin) override; - // TODO: move to private once server group chat room class has been deleted -#ifdef HAVE_ADVANCED_IM - std::shared_ptr<ServerConferenceEventHandler> eventHandler; -#endif // HAVE_ADVANCED_IM void moveDeviceToPresent(const std::shared_ptr<CallSession> &session); void moveDeviceToPresent(const std::shared_ptr<ParticipantDevice> &device); void setParticipantDeviceState(const std::shared_ptr<ParticipantDevice> &device, @@ -254,14 +249,16 @@ protected: private: L_DISABLE_COPY(ServerConference); +#ifdef HAVE_ADVANCED_IM + std::shared_ptr<ServerConferenceEventHandler> mEventHandler; +#endif // HAVE_ADVANCED_IM std::unique_ptr<MixerSession> mMixerSession; bool mIsIn = false; bool initializeParticipants(const std::shared_ptr<Participant> &initiator, SalCallOp *op); void addParticipantDevice(const std::shared_ptr<Participant> &participant, - const std::shared_ptr<ParticipantDeviceIdentity> &deviceInfo); + const std::shared_ptr<ParticipantDeviceIdentity> &deviceInfo) override; bool addParticipantAndDevice(std::shared_ptr<Call> call); - void notifyNewDevice(const std::shared_ptr<ParticipantDevice> &device); bool validateNewParameters(const ConferenceParams &newConfParams) const; std::shared_ptr<CallSession> makeSession(const std::shared_ptr<ParticipantDevice> &device, const MediaSessionParams *csp); diff --git a/src/conference/session/call-session.cpp b/src/conference/session/call-session.cpp index bbee10955dbf8a085c311d9cbff006ec79c59c1a..7c5052d43c0a6342c5f209213ca1f2608b0dc640 100644 --- a/src/conference/session/call-session.cpp +++ b/src/conference/session/call-session.cpp @@ -114,32 +114,31 @@ void CallSessionPrivate::setState(CallSession::State newState, const string &mes messageState = message; } + auto core = q->getCore(); switch (newState) { case CallSession::State::IncomingReceived: { if (op) { - auto call = q->getCore()->getCallByCallId(op->getCallId()); + auto call = core->getCallByCallId(op->getCallId()); // If there is an active call with the same call ID as the session, then this session may belong to // a conference if (call) { const std::shared_ptr<Address> to = Address::create(op->getTo()); // Server conference if (to->hasUriParam(Conference::ConfIdParameter)) { - auto lc = q->getCore()->getCCore(); - shared_ptr<Conference> conference = - L_GET_CPP_PTR_FROM_C_OBJECT(lc)->findConference(ConferenceId(to, to)); + shared_ptr<Conference> conference = core->findConference( + ConferenceId(to, to, q->getCore()->createConferenceIdParams()), false); if (conference) { // The call is for a conference stored in the core ref->addListener(conference); } } else if (op->getRemoteContactAddress()) { - std::shared_ptr<Address> remoteContactAddress = Address::create(); - remoteContactAddress->setImpl(op->getRemoteContactAddress()); - if (remoteContactAddress->hasParam(Conference::IsFocusParameter)) { + if (sal_address_has_param(op->getRemoteContactAddress(), + Conference::IsFocusParameter.c_str())) { const auto &conferenceInfo = Utils::createConferenceInfoFromOp(op, true); if (conferenceInfo->getUri()->isValid()) { #ifdef HAVE_DB_STORAGE - auto &mainDb = q->getCore()->getPrivate()->mainDb; + auto &mainDb = core->getPrivate()->mainDb; if (mainDb) { lInfo() << "Inserting conference information to database related to conference " << *conferenceInfo->getUri(); @@ -190,7 +189,7 @@ void CallSessionPrivate::setState(CallSession::State newState, const string &mes log->setStatus(LinphoneCallSuccess); log->setConnectedTime(ms_time(nullptr)); if ((q->sdpFoundInRemoteBody() || q->sdpFoundInLocalBody()) && reportEvents()) { - q->getCore()->reportConferenceCallEvent(EventLog::Type::ConferenceCallConnected, log, nullptr); + core->reportConferenceCallEvent(EventLog::Type::ConferenceCallConnected, log, nullptr); } break; default: @@ -975,40 +974,63 @@ void CallSessionPrivate::setContactOp() { if (contactAddress && contactAddress->isValid()) { q->updateContactAddress(*contactAddress); if (isInConference()) { + // Make the best effort to find the conference linked to the call session on the server side. + // Try with both the To or From headers and the guessed contact address + auto core = q->getCore(); + const auto conferenceIdParams = core->createConferenceIdParams(); std::shared_ptr<Conference> conference = - q->getCore()->findConference(ConferenceId(contactAddress, contactAddress)); - + core->findConference(ConferenceId(contactAddress, contactAddress, conferenceIdParams), false); + // Find server conference based on From or To header as the tchat conference server may give an address to + // the conference that doesn't match the identity address or contact address of any of the accounts held by + // the core. For example, flexisip tchat servers based on SDK 5.3, will create chatroom with username + // chatroom-XXXXX which doesn't match any of the account held by the core so the To or From header have the + // chatroom address auto guessedConferenceAddress = Address::create((direction == LinphoneCallIncoming) ? op->getTo() : op->getFrom()); + std::shared_ptr<Conference> guessedConference = core->findConference( + ConferenceId(guessedConferenceAddress, guessedConferenceAddress, conferenceIdParams), false); + std::shared_ptr<Address> conferenceAddress; + std::shared_ptr<Conference> conferenceFound; if (conference) { - // Try to change conference address in order to add GRUU to it - // Note that this operation may fail if the conference was previously created on the server - conference->setConferenceAddress(contactAddress); + // The URI returned by getFixedAddress matches a conference + conferenceAddress = conference->getConferenceAddress()->clone()->toSharedPtr(); + conferenceFound = conference; + } else if (guessedConference) { + // The conference is actually a chatroom in which its conference ID doesn't match the guessed account. + // For example, if the chatroom peer address is sip:chatroom-xyz@sip.example.org and the account is + // sip:focus@sip.example.org + conferenceAddress = guessedConference->getConferenceAddress()->clone()->toSharedPtr(); + conferenceFound = guessedConference; + lInfo() << "The guessed contact address " << *contactAddress + << " doesn't match the actual chatroom conference address " << *conferenceAddress; } else { #ifdef HAVE_DB_STORAGE auto &mainDb = q->getCore()->getPrivate()->mainDb; const auto &confInfo = mainDb->getConferenceInfoFromURI(guessedConferenceAddress); if (confInfo) { - const auto &conferenceAddress = confInfo->getUri(); - if (conferenceAddress && conferenceAddress->isValid()) { - // The conference may have already been terminated when setting the contact address. - // This happens when an admin cancel a conference by sending an INVITE with an empty resource - // list Add parameters stored in the conference information URI to the contact address - if (contactAddress && contactAddress->isValid()) { - contactAddress->merge(*conferenceAddress); - } else { - contactAddress = conferenceAddress; - } - } + // The conference may have already been terminated when setting the contact address. + // This happens when an admin cancel a conference by sending an INVITE with an empty resource + // list Add parameters stored in the conference information URI to the contact address + conferenceAddress = confInfo->getUri()->clone()->toSharedPtr(); } #endif // HAVE_DB_STORAGE } + if (conferenceAddress && conferenceAddress->isValid()) { + if (contactAddress && contactAddress->isValid()) { + // Copy all parameters of the guessed contact address into the conference address. Here, it is + // interesting to pass the GRUU parameter on + lInfo() << "Copying all parameters of the guessed contact address " << *contactAddress + << " to found conference address " << *conferenceAddress; + conferenceAddress->merge(*contactAddress); + } + contactAddress = conferenceAddress; + } } - lInfo() << "Setting contact address for session " << q << " to " << *contactAddress; + lInfo() << "Setting contact address for " << *q << " to " << *contactAddress; op->setContactAddress(contactAddress->getImpl()); } else { - lWarning() << "Unable to set contact address for session " << q << " to " + lWarning() << "Unable to set contact address for " << *q << " to " << ((contactAddress) ? contactAddress->toString() : std::string("sip:")) << " as it is not valid"; } } @@ -1045,8 +1067,7 @@ bool CallSessionPrivate::reportEvents() const { const auto clientConferenceGuessedAddress = (remoteContactAddress && remoteContactAddress->hasUriParam("conf-id")) ? remoteContactAddress : remoteAddress; const auto &peerAddress = isInConference() ? serverConferenceGuessedAddress : clientConferenceGuessedAddress; - const auto conference = - q->getCore()->searchConference(nullptr, nullptr, peerAddress, std::list<std::shared_ptr<Address>>()); + const auto conference = q->getCore()->searchConference(nullptr, nullptr, peerAddress, {}); ConferenceInterface::State conferenceState = (conference) ? conference->getState() : ConferenceInterface::State::Instantiated; bool conferenceCreated = !((conferenceState == ConferenceInterface::State::Instantiated) || @@ -1121,7 +1142,7 @@ std::shared_ptr<Address> CallSessionPrivate::getFixedContact() const { if (addr && (account->getOp() || (account->getDependency() != nullptr) || linphone_core_conference_server_enabled(q->getCore()->getCCore()))) { /* If using a account, use the contact address as guessed with the REGISTERs */ - lInfo() << "Contact " << *addr << " has been fixed using account"; + lInfo() << "Contact " << *addr << " has been fixed using account " << *account; result = addr->clone()->toSharedPtr(); return result; } @@ -1270,7 +1291,7 @@ void CallSessionPrivate::repairIfBroken() { break; case CallSession::State::OutgoingEarlyMedia: case CallSession::State::OutgoingRinging: - if (op->getRemoteTag() != nullptr) { + if (op->getRemoteTag() != nullptr && strlen(op->getRemoteTag()) > 0) { repairByNewInvite(true); broken = false; } else { diff --git a/src/conference/session/media-session-p.h b/src/conference/session/media-session-p.h index 9d008423af4cdec695555b85b92ead2ecf140962..4359d65ce8eabeda26374c1147b80bb6fdf712a1 100644 --- a/src/conference/session/media-session-p.h +++ b/src/conference/session/media-session-p.h @@ -54,6 +54,7 @@ public: static const std::string ThumbnailVideoContentAttribute; static const std::string ScreenSharingContentAttribute; + static bool isMainStreamContent(const std::string &content); static int resumeAfterFailedTransfer(void *userData, unsigned int); static void stunAuthRequestedCb(void *userData, const char *realm, diff --git a/src/conference/session/media-session.cpp b/src/conference/session/media-session.cpp index 0329e51fc6d346d9cb2c1f8af3021da96dcef972..bd13dde2c8bf077e6d3ae86acd0d96d83ebca67a 100644 --- a/src/conference/session/media-session.cpp +++ b/src/conference/session/media-session.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2024 Belledonne Communications SARL. + * Copyright (c) 2010-2025 Belledonne Communications SARL. * * This file is part of Liblinphone * (see https://gitlab.linphone.org/BC/public/liblinphone). @@ -165,7 +165,8 @@ bool MediaSessionPrivate::tryEnterConference() { L_Q(); q->updateContactAddressInOp(); const auto updatedContactAddress = q->getContactAddress(); - ConferenceId serverConferenceId = ConferenceId(updatedContactAddress, updatedContactAddress); + ConferenceId serverConferenceId = + ConferenceId(updatedContactAddress, updatedContactAddress, q->getCore()->createConferenceIdParams()); shared_ptr<Conference> conference = q->getCore()->findConference(serverConferenceId, false); // If the call conference ID is not an empty string but no conference is linked to the call means that it was added // to the conference after the INVITE session was started but before its completition @@ -238,9 +239,8 @@ void MediaSessionPrivate::accepted() { case CallSession::State::OutgoingRinging: case CallSession::State::OutgoingEarlyMedia: case CallSession::State::Connected: - if (q->getCore()->getCCore()->sip_conf.sdp_200_ack || q->getCore()->getCCore()->sal->mediaDisabled()) { - if (!q->getCore()->getCCore()->sal->mediaDisabled()) - lInfo() << "Initializing local media description according to remote offer in 200Ok"; + if (q->getCore()->getCCore()->sip_conf.sdp_200_ack && !q->getCore()->getCCore()->sal->mediaDisabled()) { + lInfo() << "Initializing local media description according to remote offer in 200Ok"; if (!localIsOfferer) { // We were waiting for an incoming offer. Now prepare the local media description according to // remote offer. @@ -271,7 +271,7 @@ void MediaSessionPrivate::accepted() { /* Reset the internal call update flag, so it doesn't risk to be copied and used in further re-INVITEs */ getParams()->getPrivate()->setInternalCallUpdate(false); std::shared_ptr<SalMediaDescription> lmd = op->getLocalMediaDescription(); - if (lmd) { + if (lmd || q->getCore()->getCCore()->sal->mediaDisabled()) { std::shared_ptr<SalMediaDescription> rmd = op->getRemoteMediaDescription(); std::shared_ptr<SalMediaDescription> cmd = op->getFinalMediaDescription(); std::shared_ptr<SalMediaDescription> md = cmd; @@ -315,7 +315,7 @@ void MediaSessionPrivate::accepted() { // 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 (isPausedByRemoteAllowed() && !localDesc->hasDir(SalStreamInactive) && + if (isPausedByRemoteAllowed() && localDesc && !localDesc->hasDir(SalStreamInactive) && (md->hasDir(SalStreamRecvOnly) || md->hasDir(SalStreamInactive) || md->isEmpty())) { nextState = CallSession::State::PausedByRemote; nextStateMsg = "Call paused by remote"; @@ -867,7 +867,8 @@ void MediaSessionPrivate::updating(bool isUpdate) { const auto videoEnabled = getParams()->videoEnabled(); const auto remoteContactAddress = q->getRemoteContactAddress(); const auto localAddress = q->getLocalAddress(); - const auto conference = q->getCore()->findConference(ConferenceId(localAddress, localAddress), false); + const auto conference = q->getCore()->findConference( + ConferenceId(localAddress, localAddress, q->getCore()->createConferenceIdParams()), false); // If the media session is in a conference, the remote media description is empty and audio video capabilities // are disabled, then just call end the update if ((((!!linphone_core_conference_server_enabled(q->getCore()->getCCore())) && conference) || @@ -929,7 +930,7 @@ MediaSessionParams *MediaSessionPrivate::createMediaSessionParams() { MediaSessionParams *msp = new MediaSessionParams(*getParams()); auto videoDir = computeNewVideoDirection(q->getCore()->getCCore()->video_policy->accept_media_direction); - const auto &conference = q->getCore()->findConference(q->getSharedFromThis()); + const auto &conference = q->getCore()->findConference(q->getSharedFromThis(), false); if (conference) { videoDir = conference->verifyVideoDirection(q->getSharedFromThis(), videoDir); } @@ -1054,7 +1055,7 @@ void MediaSessionPrivate::setState(CallSession::State newState, const string &me if ((newState != state) && (newState != CallSession::State::StreamsRunning)) q->cancelDtmfs(); CallSessionPrivate::setState(newState, message); q->notifyCallSessionStateChangedForReporting(); - std::shared_ptr<SalMediaDescription> rmd = nullptr; + std::shared_ptr<SalMediaDescription> rmd; switch (newState) { case CallSession::State::UpdatedByRemote: // Handle specifically the case of an incoming ICE-concluded reINVITE @@ -1163,7 +1164,8 @@ void MediaSessionPrivate::initializeParamsAccordingToIncomingCallParams() { CallSessionPrivate::initializeParamsAccordingToIncomingCallParams(); const auto remoteContactAddress = q->getRemoteContactAddress(); const auto localAddress = q->getLocalAddress(); - const auto conference = q->getCore()->findConference(ConferenceId(localAddress, localAddress), false); + const auto conference = q->getCore()->findConference( + ConferenceId(localAddress, localAddress, q->getCore()->createConferenceIdParams()), false); std::shared_ptr<SalMediaDescription> md = op->getRemoteMediaDescription(); if (md) { /* It is implicit to receive an INVITE without SDP, in this case WE choose the media parameters according to @@ -1176,6 +1178,7 @@ void MediaSessionPrivate::initializeParamsAccordingToIncomingCallParams() { << "]: disabling audio and video in our call params because the remote party didn't send a valid SDP"; getParams()->enableAudio(false); getParams()->enableVideo(false); + getParams()->getPrivate()->disableRinging(true); getParams()->getPrivate()->enableToneIndications(false); } } @@ -1340,7 +1343,8 @@ string MediaSessionPrivate::getLocalIpFromSignaling() const { std::string MediaSessionPrivate::getLocalIpFromMedia() const { string guessedIpAddress; - if (op && ((state == CallSession::State::Idle) || (state == CallSession::State::IncomingReceived) || (state == CallSession::State::IncomingEarlyMedia) || (state == CallSession::State::UpdatedByRemote))) { + if (op && ((state == CallSession::State::Idle) || (state == CallSession::State::IncomingReceived) || + (state == CallSession::State::IncomingEarlyMedia) || (state == CallSession::State::UpdatedByRemote))) { auto remoteDesc = op->getRemoteMediaDescription(); string remoteAddr; if (remoteDesc) { @@ -1855,19 +1859,22 @@ void MediaSessionPrivate::fillLocalStreamDescription(SalStreamDescription &strea cfg.rtcp_cname = getMe()->getAddress()->toString(); const auto conference = q->getCore()->findConference(q->getSharedFromThis(), false); - if ((type == SalAudio) && isInConference()) { - bool rtpVolumesAllowed = - !!linphone_config_get_int(linphone_core_get_config(q->getCore()->getCCore()), "rtp", "use_volumes", 1); - if (rtpVolumesAllowed) { - cfg.mixer_to_client_extension_id = RTP_EXTENSION_MIXER_TO_CLIENT_AUDIO_LEVEL; - cfg.client_to_mixer_extension_id = RTP_EXTENSION_CLIENT_TO_MIXER_AUDIO_LEVEL; - } else { - lInfo() << "RTP client-to-mixer and mixer-to-client volumes disabled by configuration."; - } - } else if ((type == SalVideo) && conference) { - validateVideoStreamDirection(cfg); + if (conference || (q->getRemoteContactAddress() != nullptr && + q->getRemoteContactAddress()->hasParam(Conference::IsFocusParameter))) { + if (type == SalAudio) { + bool rtpVolumesAllowed = !!linphone_config_get_int(linphone_core_get_config(q->getCore()->getCCore()), + "rtp", "use_volumes", 1); + if (rtpVolumesAllowed) { + cfg.mixer_to_client_extension_id = RTP_EXTENSION_MIXER_TO_CLIENT_AUDIO_LEVEL; + cfg.client_to_mixer_extension_id = RTP_EXTENSION_CLIENT_TO_MIXER_AUDIO_LEVEL; + } else { + lInfo() << "RTP client-to-mixer and mixer-to-client volumes disabled by configuration."; + } + } else if (type == SalVideo) { + validateVideoStreamDirection(cfg); - cfg.frame_marking_extension_id = RTP_EXTENSION_FRAME_MARKING; + cfg.frame_marking_extension_id = RTP_EXTENSION_FRAME_MARKING; + } } if (getParams()->rtpBundleEnabled()) addStreamToBundle(md, stream, cfg, mid); @@ -1898,9 +1905,9 @@ std::list<unsigned int> MediaSessionPrivate::getProtectedStreamNumbers(std::shar // Protected streams are the first audio stream and the first 2 video streams as they handle local participant // medias if (conference) { - auto firstAudioStream = md->findFirstStreamIdxOfType(SalAudio); - if (firstAudioStream > -1) { - streamNumbers.push_back(static_cast<unsigned int>(firstAudioStream)); + auto audioStream = md->findIdxBestStream(SalAudio); + if (audioStream > -1) { + streamNumbers.push_back(static_cast<unsigned int>(audioStream)); } const std::string activeSpeakerAttribute(MediaSessionPrivate::ActiveSpeakerVideoContentAttribute); @@ -1988,14 +1995,24 @@ SalStreamDescription &MediaSessionPrivate::addStreamToMd(std::shared_ptr<SalMedi // If a stream at the index requested in the the function argument has already been allocated and it is // active, then it must be replaced. if ((stream.getDirection() != SalStreamInactive) && oldMd) { + const auto ¤tStreamLabel = stream.getLabel(); + bool currentStreamLabelEmpty = currentStreamLabel.empty(); const auto oldMdSize = oldMd->streams.size(); int idxOldMd = -1; + // Search in the previous media description a free stream with the same label for (size_t mdStreamIdx = 0; mdStreamIdx < oldMdSize; mdStreamIdx++) { const auto &protectedIdx = (std::find(protectedStreamNumbersOldMd.cbegin(), protectedStreamNumbersOldMd.cend(), mdStreamIdx) != protectedStreamNumbersOldMd.cend()); auto oldStream = oldMd->getStreamAtIdx(static_cast<unsigned int>(mdStreamIdx)); - if (conference && !protectedIdx && (oldStream.getLabel() == stream.getLabel())) { + const auto &oldStreamLabel = oldStream.getLabel(); + bool oldStreamLabelEmpty = oldStreamLabel.empty(); + // Select index if either the labels match or the new and old stream have no labels and the index is + // not the same. In fact it may happen that a faulty core sends an SDP with multiple streams without + // label while in a conference causing a fatal error. + if (conference && !protectedIdx && + ((oldStreamLabel == currentStreamLabel) || (oldStreamLabelEmpty && currentStreamLabelEmpty && + (static_cast<int>(mdStreamIdx) != streamIdx)))) { idxOldMd = static_cast<int>(mdStreamIdx); break; } @@ -2010,6 +2027,10 @@ SalStreamDescription &MediaSessionPrivate::addStreamToMd(std::shared_ptr<SalMedi md->streams[static_cast<size_t>(freeSlot)] = stream; } } else { + if (idxOldMd == streamIdx) { + lFatal() << "Unable to find available free stream description index:\n- Index in previous SDP: " + << idxOldMd << "\n- Guessed index: " << streamIdx; + } auto &streamToFill = addStreamToMd(md, idxOldMd, oldMd); streamToFill = stream; } @@ -2053,7 +2074,7 @@ void MediaSessionPrivate::addConferenceLocalParticipantStreams(bool add, lWarning() << "In an effort to ensure a satisfactory video conferencing quality, MediaSession [" << q << "] (local address " << *q->getLocalAddress() << " remote address " << *q->getRemoteAddress() - << " in conference [" << conference << "] (address: " << *conference->getConferenceAddress() + << ") in " << *conference << ") is not sending the thumbnail of the local participant because RTP bundle has been disabled"; return; } @@ -2064,7 +2085,7 @@ void MediaSessionPrivate::addConferenceLocalParticipantStreams(bool add, bool isInLocalConference = getParams()->getPrivate()->getInConference(); const auto &participantDevice = isInLocalConference ? conference->findParticipantDevice(q->getSharedFromThis()) - : conference->getMe()->findDevice(q->getSharedFromThis()); + : conference->getMe()->findDevice(q->getSharedFromThis(), true); if (participantDevice) { const auto &deviceState = participantDevice->getState(); std::string content; @@ -2148,14 +2169,22 @@ void MediaSessionPrivate::addConferenceLocalParticipantStreams(bool add, } } else { lInfo() << "Don't put " << std::string(sal_stream_type_to_string(type)) - << " stream for device in conference with address " << *participantDevice->getAddress() - << " on local offer for CallSession [" << q << "]"; + << " stream for device in " << *conference << " with address " + << *participantDevice->getAddress() << " on local offer for " << *q; cfg.dir = SalStreamInactive; } PayloadTypeHandler::clearPayloadList(l); newStream.addActualConfiguration(cfg); newStream.setSupportedEncryptions(encs); fillRtpParameters(newStream); + } else { + lWarning() << "Do not add thumbnail stream for the local participant " + << *participantDevice->getAddress() << " of " << *conference << " because: "; + lWarning() << "- no stream has been found with content " << content << " and label " << deviceLabel + << ": " << foundStreamIdx; + lWarning() << "- the core is an offerer and the device is not in the Joining, Present or OnHold " + "state: isOfferer " + << localIsOfferer << " participant device state " << Utils::toString(deviceState); } } } @@ -2173,23 +2202,27 @@ void MediaSessionPrivate::addConferenceParticipantStreams(std::shared_ptr<SalMed const auto ¤tConfParams = conference->getCurrentParams(); bool isVideoConferenceEnabled = currentConfParams->videoEnabled(); bool isVideoStream = (type == SalVideo); + bool isConferenceServer = linphone_core_conference_server_enabled(q->getCore()->getCCore()); + const auto &remoteContactAddress = q->getRemoteContactAddress(); // Add additional video streams if required if ((isVideoStream && isVideoConferenceEnabled) || (type == SalAudio)) { bool isInLocalConference = getParams()->getPrivate()->getInConference(); const auto ¶meters = isInLocalConference ? getRemoteParams() : getParams(); if (!parameters) { - lInfo() << "Not adding streams of type " << std::string(sal_stream_type_to_string(type)) - << " because the layout is not known yet"; + lInfo() << "MediaSession [" << q << "] (local address " << *q->getLocalAddress() << " remote address " + << *q->getRemoteAddress() << "] in " << *conference << " is not adding streams of type " + << std::string(sal_stream_type_to_string(type)) << " because the layout is not known yet"; return; } const auto &confLayout = parameters->getConferenceVideoLayout(); bool isConferenceLayoutActiveSpeaker = (confLayout == ConferenceLayout::ActiveSpeaker); - if (localIsOfferer && !linphone_core_conference_server_enabled(q->getCore()->getCCore())) { + if (localIsOfferer && !isConferenceServer) { bool request = true; if (isVideoStream) { request = conference->areThumbnailsRequested(false); } + if (request) { const std::string participantContent( (type == SalAudio) @@ -2198,7 +2231,7 @@ void MediaSessionPrivate::addConferenceParticipantStreams(std::shared_ptr<SalMed : "")); const std::string bundleNameStreamPrefix((isVideoStream) ? "vs" : "as"); const auto participantDeviceAddress = - (isInLocalConference) ? q->getRemoteContactAddress() : q->getContactAddress(); + (isInLocalConference) ? remoteContactAddress : q->getContactAddress(); for (const auto &p : conference->getParticipants()) { for (const auto &dev : p->getDevices()) { const auto &devAddress = dev->getAddress(); @@ -2221,6 +2254,11 @@ void MediaSessionPrivate::addConferenceParticipantStreams(std::shared_ptr<SalMed const auto &foundStreamIdx = devLabel.empty() ? -1 : oldMd->findIdxStreamWithContent(participantContent, devLabel); + lInfo() << "MediaSession [" << q << "] (local address " << *q->getLocalAddress() + << " remote address " << *q->getRemoteAddress() << "] in " << *conference + << " is adding a stream of type " + << std::string(sal_stream_type_to_string(type)) << " for participant device " + << *devAddress; SalStreamDescription &newParticipantStream = addStreamToMd(md, foundStreamIdx, oldMd); if (isConferenceLayoutActiveSpeaker || (type == SalAudio)) { newParticipantStream.setContent(participantContent); @@ -2244,75 +2282,71 @@ void MediaSessionPrivate::addConferenceParticipantStreams(std::shared_ptr<SalMed } else { if (getParams()->rtpBundleEnabled()) { lWarning() << "MediaSession [" << q << "] (local address " << *q->getLocalAddress() - << " remote address " << *q->getRemoteAddress() << " in conference [" << conference - << "] (address: " << *conference->getConferenceAddress() - << ") is not requesting other participants' camera stream because their number " + << " remote address " << *q->getRemoteAddress() << "] in " << *conference + << " is not requesting other participants' camera stream because their number " << conference->getParticipantCount() << " is bigger than the maximum allowed for this core " << linphone_core_get_conference_max_thumbnails(q->getCore()->getCCore()); } else { lWarning() << "In an effort to ensure a satisfactory video conferencing quality, MediaSession [" << q << "] (local address " << *q->getLocalAddress() << " remote address " - << *q->getRemoteAddress() << " in conference [" << conference - << "] (address: " << *conference->getConferenceAddress() - << ") is not requesting other participants' camera stream because RTP bundle has " + << *q->getRemoteAddress() << "] in " << *conference + << " is not requesting other participants' camera stream because RTP bundle has " "been disabled"; } } - } else { - // The conference server is a passive core, therefore he must not add any stream to the SDP - // Do not attempt to modify already existing streams - const auto &refMd = - (linphone_core_conference_server_enabled(q->getCore()->getCCore()) && localIsOfferer) - ? oldMd - : op->getRemoteMediaDescription(); - - // By default, every participant has a main stream and a thumbnail to send its video stream to the - // conference server - const unsigned int protectedStreams = (type == SalVideo) ? 2 : 1; - unsigned int nbStream = 0; - + } else if (isConferenceServer || + (remoteContactAddress && remoteContactAddress->hasParam(Conference::IsFocusParameter))) { + // The conference server is a passive element, therefore it should always look at the client offer to + // know the participant stream order Clients may also enter this if branch if they receive an INVITE by + // a conference server. This is the case at the beginning of dial out conference for the initial INVITE + // and the ICE reINVITE, should ICE be enabled In the case of clients, it is important to verify that + // they are still in a conference. As they end up executing this code when they receive an offer from + // the server, it is important to make sure that the isfocus parameter is found in the remote contact + // address. In fact, if the server remove a participant from a conference, the client conference is + // still alive (i.e. its pointer is not null). In such a scenario, the conference will not add any + // content to the streams (including the main one) and the client will associate it to a thumbnail + // stream when the conference is in the Grid layout + const auto &refMd = (localIsOfferer) ? oldMd : op->getRemoteMediaDescription(); + // Other participants thumbnails are send by the server to the client + const auto thumbnailDirection = isConferenceServer ? SalStreamRecvOnly : SalStreamSendOnly; auto beginIt = refMd->streams.cbegin(); for (auto sIt = beginIt; sIt != refMd->streams.end(); sIt++) { const auto &s = *sIt; - if (s.getType() == type) { - nbStream++; - if (nbStream > protectedStreams) { - - const auto idx = std::distance(refMd->streams.cbegin(), sIt); - const std::string contentAttrValue = s.getContent(); - const std::string participantsAttrValue = s.getLabel(); - - std::shared_ptr<ParticipantDevice> dev = nullptr; - if (!participantsAttrValue.empty()) { - dev = conference->findParticipantDeviceByLabel(sal_stream_type_to_linphone(type), - participantsAttrValue); - if (!dev && conference->getMe()) { - // It might be me - dev = conference->getMe()->findDevice(sal_stream_type_to_linphone(type), - participantsAttrValue, false); - } + const std::string contentAttrValue = s.getContent(); + if ((s.getType() == type) && (s.getDirection() == thumbnailDirection) && + !MediaSessionPrivate::isMainStreamContent(contentAttrValue)) { + const auto idx = std::distance(refMd->streams.cbegin(), sIt); + const std::string participantsAttrValue = s.getLabel(); + + std::shared_ptr<ParticipantDevice> dev; + if (!participantsAttrValue.empty()) { + dev = conference->findParticipantDeviceByLabel(sal_stream_type_to_linphone(type), + participantsAttrValue); + if (!dev && conference->getMe()) { + // It might be me + dev = conference->getMe()->findDevice(sal_stream_type_to_linphone(type), + participantsAttrValue, false); } + } - SalStreamDescription &newStream = addStreamToMd(md, static_cast<int>(idx), oldMd); - if (dev) { - newStream.setContent(contentAttrValue); - fillConferenceParticipantStream(newStream, oldMd, md, dev, pth, encs, type, s.getMid()); - } else { - const auto &s = refMd->streams[static_cast<size_t>(idx)]; - SalStreamConfiguration cfg; - cfg.dir = SalStreamInactive; - newStream.disable(); - newStream.type = s.type; - newStream.rtp_port = 0; - newStream.rtcp_port = 0; - newStream.addActualConfiguration(cfg); - lWarning() << *q - << ": New stream added as disabled and inactive because no device has been " - "found with label " - << participantsAttrValue << " in conference " - << *conference->getConferenceAddress(); - } + SalStreamDescription &newStream = addStreamToMd(md, static_cast<int>(idx), oldMd); + if (dev) { + newStream.setContent(contentAttrValue); + fillConferenceParticipantStream(newStream, oldMd, md, dev, pth, encs, type, s.getMid()); + } else { + const auto &s = refMd->streams[static_cast<size_t>(idx)]; + SalStreamConfiguration cfg; + cfg.dir = SalStreamInactive; + newStream.disable(); + newStream.type = s.type; + newStream.rtp_port = 0; + newStream.rtcp_port = 0; + newStream.addActualConfiguration(cfg); + lWarning() << *q + << ": New stream added as disabled and inactive because no device has been " + "found with label " + << participantsAttrValue << " in " << *conference; } } } @@ -2321,6 +2355,13 @@ void MediaSessionPrivate::addConferenceParticipantStreams(std::shared_ptr<SalMed } } +bool MediaSessionPrivate::isMainStreamContent(const std::string &content) { + // The main stream has always a content whereas participant stream may not have a content + return ((content == MediaSessionPrivate::ScreenSharingContentAttribute) || + (content == MediaSessionPrivate::ActiveSpeakerVideoContentAttribute) || + (content == MediaSessionPrivate::GridVideoContentAttribute)); +} + void MediaSessionPrivate::validateVideoStreamDirection(SalStreamConfiguration &cfg) const { L_Q(); // Check core parameter to eventually change the media direction @@ -2493,7 +2534,7 @@ void MediaSessionPrivate::makeLocalMediaDescription(bool localIsOfferer, bool isAudioConferenceEnabled = false; ConferenceLayout confLayout = ConferenceLayout::ActiveSpeaker; auto deviceState = ParticipantDevice::State::ScheduledForJoining; - std::shared_ptr<ParticipantDevice> participantDevice = nullptr; + std::shared_ptr<ParticipantDevice> participantDevice; if (conference) { const auto ¤tConfParams = conference->getCurrentParams(); const auto &conferenceState = conference->getState(); @@ -3945,10 +3986,15 @@ void MediaSessionPrivate::updateCurrentParams() const { getCurrentParams()->getPrivate()->setUpdateCallWhenIceCompleted(isUpdateSentWhenIceCompleted()); bool deviceIsScreenSharing = false; if (conference) { - const auto meDevices = conference->getMe()->getDevices(); - const auto &participantDevice = ((meDevices.size() == 0) || isInConference()) - ? conference->findParticipantDevice(q->getSharedFromThis()) - : meDevices.front(); + std::shared_ptr<ParticipantDevice> participantDevice; + if (isInConference()) { + participantDevice = conference->findParticipantDevice(q->getSharedFromThis()); + } else { + const auto meDevices = conference->getMe()->getDevices(); + if (meDevices.size() > 0) { + participantDevice = meDevices.front(); + } + } if (participantDevice) { deviceIsScreenSharing = participantDevice->screenSharingEnabled(); } @@ -4237,7 +4283,7 @@ void MediaSessionPrivate::stunAuthRequestedCb(const char *realm, const char **ha1) { L_Q(); /* Get the username from the nat policy or the proxy config */ - std::shared_ptr<Account> stunAccount = nullptr; + std::shared_ptr<Account> stunAccount; const auto &account = getDestAccount(); if (account) stunAccount = account; else { @@ -4397,6 +4443,10 @@ void MediaSession::setNatPolicy(const std::shared_ptr<NatPolicy> &pol) { d->natPolicy = pol; } +bool MediaSession::ringingDisabled() const { + return getMediaParams()->getPrivate()->ringingDisabled(); +} + bool MediaSession::toneIndicationsEnabled() const { return getMediaParams()->getPrivate()->toneIndicationsEnabled(); } @@ -4527,9 +4577,15 @@ bool MediaSession::initiateOutgoing(const string &subject, const std::shared_ptr bool defer = CallSession::initiateOutgoing(subject, content); if (!d->op) d->createOp(); - if (!(getCore()->getCCore()->sip_conf.sdp_200_ack || d->op->getSal()->mediaDisabled())) { + + if (d->op->getSal()->mediaDisabled()) { + // Set localIsOfferer here, as it is normally done by makeLocalMediaDescription. + // But without media we do not call it. + d->localIsOfferer = true; + lInfo() << *this << ": Setting us as local offerer as the core is configured with media disabled"; + } else if (!getCore()->getCCore()->sip_conf.sdp_200_ack) { d->makeLocalMediaDescription(true, isCapabilityNegotiationEnabled(), false); - lInfo() << "Created local media description."; + lInfo() << *this << ": Created local media description"; } if (d->natPolicy && d->natPolicy->iceEnabled()) { @@ -4659,7 +4715,7 @@ LinphoneStatus MediaSession::resume() { const auto res = d->op->update(subject.c_str(), false); - if (getCore()->getCCore()->sip_conf.sdp_200_ack || d->op->getSal()->mediaDisabled()) { + if (getCore()->getCCore()->sip_conf.sdp_200_ack && !d->op->getSal()->mediaDisabled()) { /* We are NOT offering, set local media description after sending the call so that we are ready to * process the remote offer when it will arrive. */ d->op->setLocalMediaDescription(d->localDesc); @@ -4753,27 +4809,26 @@ void MediaSession::sendVfuRequest() { } else lInfo() << "vfu request using sip disabled from config [sip,vfu_with_info]"; } -// Try to search the local conference by first looking at the contact address and if it is unsuccesfull to the to -// address as a client may try to be calling a conference URI directly Typically, the seach using the contact address +// Try to search the local conference by first looking at the contact address and if it is unsuccessfull to the to +// address as a client may try to be calling a conference URI directly Typically, the search using the contact address // will succeed when a client creates a conference. const std::shared_ptr<Conference> MediaSession::getLocalConference() const { L_D(); - ConferenceId serverConferenceId; - shared_ptr<Conference> conference = nullptr; - + shared_ptr<Conference> conference; auto log = getLog(); const auto conferenceInfo = (log) ? log->getConferenceInfo() : nullptr; + auto conferenceIdParams = getCore()->createConferenceIdParams(); if (conferenceInfo) { auto conferenceAddress = conferenceInfo->getUri(); - serverConferenceId = ConferenceId(conferenceAddress, conferenceAddress); + serverConferenceId = ConferenceId(conferenceAddress, conferenceAddress, conferenceIdParams); conference = getCore()->findConference(serverConferenceId, false); } if (!conference) { auto contactAddress = getContactAddress(); if (contactAddress) { updateContactAddress(*contactAddress); - serverConferenceId = ConferenceId(contactAddress, contactAddress); + serverConferenceId = ConferenceId(contactAddress, contactAddress, conferenceIdParams); conference = getCore()->findConference(serverConferenceId, false); } } @@ -4781,7 +4836,7 @@ const std::shared_ptr<Conference> MediaSession::getLocalConference() const { const auto to = Address::create(d->op->getTo()); // Local conference if (to->hasUriParam(Conference::ConfIdParameter)) { - serverConferenceId = ConferenceId(to, to); + serverConferenceId = ConferenceId(to, to, conferenceIdParams); conference = getCore()->findConference(serverConferenceId, false); } } @@ -4972,11 +5027,12 @@ LinphoneStatus MediaSession::update(const MediaSessionParams *msp, bool addCapabilityNegotiationAttributesToLocalMd = isCapabilityNegotiationEnabled() && !isCapabilityNegotiationUpdate; bool isCapabilityNegotiationReInvite = isCapabilityNegotiationEnabled() && isCapabilityNegotiationUpdate; - bool isOfferer = isCapabilityNegotiationUpdate || - !(getCore()->getCCore()->sip_conf.sdp_200_ack || d->op->getSal()->mediaDisabled()); + bool isOfferer = isCapabilityNegotiationUpdate || !getCore()->getCCore()->sip_conf.sdp_200_ack || + d->op->getSal()->mediaDisabled(); d->localIsOfferer = isOfferer; - d->makeLocalMediaDescription(d->localIsOfferer, addCapabilityNegotiationAttributesToLocalMd, - isCapabilityNegotiationReInvite); + if (!d->op->getSal()->mediaDisabled()) + d->makeLocalMediaDescription(d->localIsOfferer, addCapabilityNegotiationAttributesToLocalMd, + isCapabilityNegotiationReInvite); const auto &localDesc = d->localDesc; const auto &contentList = msp->getCustomContents(); @@ -4996,14 +5052,20 @@ LinphoneStatus MediaSession::update(const MediaSessionParams *msp, d->state = newState; } - // We may running this code after ICE candidates have been gathered or ICE released task completed, - // therefore the local description must be updated to include ICE candidates for every stream - const auto currentLocalDesc = d->localDesc; - d->localDesc = localDesc; - d->updateLocalMediaDescriptionFromIce(isOfferer); const auto &mediaDisabled = d->op->getSal()->mediaDisabled(); - if (isOfferer && !mediaDisabled) { - d->op->setLocalMediaDescription(d->localDesc); + const auto currentLocalDesc = d->localDesc; + + if (!mediaDisabled) { + // We may running this code after ICE candidates have been gathered or ICE released task completed, + // therefore the local description must be updated to include ICE candidates for every stream + d->localDesc = localDesc; + d->updateLocalMediaDescriptionFromIce(isOfferer); + + if (isOfferer) { + d->op->setLocalMediaDescription(d->localDesc); + } else { + d->op->setLocalMediaDescription(nullptr); + } } else { d->op->setLocalMediaDescription(nullptr); } @@ -5017,7 +5079,7 @@ LinphoneStatus MediaSession::update(const MediaSessionParams *msp, LinphoneStatus res = d->startUpdate(method, subject); d->localDesc = currentLocalDesc; - if (!isOfferer || mediaDisabled) { + if (!isOfferer && !mediaDisabled) { /* We are NOT offering, set local media description after sending the call so that we are ready to * process the remote offer when it will arrive. */ d->op->setLocalMediaDescription(d->localDesc); @@ -5544,8 +5606,8 @@ const MediaSessionParams *MediaSession::getRemoteParams() const { shared_ptr<CallStats> MediaSession::getStats(LinphoneStreamType type) const { L_D(); if (type == LinphoneStreamTypeUnknown) return nullptr; - shared_ptr<CallStats> stats = nullptr; - shared_ptr<CallStats> statsCopy = nullptr; + shared_ptr<CallStats> stats; + shared_ptr<CallStats> statsCopy; Stream *s = d->getStream(type); if (s && (stats = s->getStats())) { statsCopy = stats->clone()->toSharedPtr(); @@ -5603,11 +5665,15 @@ void MediaSession::setAuthenticationTokenVerified(bool value) { if (!value) { char *peerDeviceId = nullptr; auto encryptionEngine = getCore()->getEncryptionEngine(); - if (encryptionEngine) { // inform lime that zrtp no longuer guaranty the trust + if (encryptionEngine) { // inform lime that zrtp no longer guarantees the trust const SalAddress *remoteAddress = d->getOp()->getRemoteContactAddress(); - peerDeviceId = sal_address_as_string_uri_only(remoteAddress); - encryptionEngine->authenticationRejected(peerDeviceId); - ms_free(peerDeviceId); + if (!remoteAddress) { + lError() << __func__ << " : MediaSession [" << this << "] Failed to retrieve remote address"; + } else { + peerDeviceId = sal_address_as_string_uri_only(remoteAddress); + encryptionEngine->authenticationRejected(peerDeviceId); + ms_free(peerDeviceId); + } } } else { d->getStreamsGroup().setZrtpCacheMismatch(false); diff --git a/src/conference/session/media-session.h b/src/conference/session/media-session.h index 39fc0874172b07f1e38132b782b3b3a0bcc5192f..9573630291154a02d858e8acffbfd3b3bdc32abe 100644 --- a/src/conference/session/media-session.h +++ b/src/conference/session/media-session.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2022 Belledonne Communications SARL. + * Copyright (c) 2010-2025 Belledonne Communications SARL. * * This file is part of Liblinphone * (see https://gitlab.linphone.org/BC/public/liblinphone). @@ -67,6 +67,7 @@ public: void cancelDtmfs(); void setNatPolicy(const std::shared_ptr<NatPolicy> &pol); void setSubject(const std::string &subject); + bool ringingDisabled() const; bool toneIndicationsEnabled() const; void configure(LinphoneCallDir direction, const std::shared_ptr<Account> &account, diff --git a/src/conference/session/ms2-stream.cpp b/src/conference/session/ms2-stream.cpp index cc4ef15b306a805dd3239f4023963d3ebaedbc3a..9554f57862ba4035fa23fb1b39ea1b0625e2fce1 100644 --- a/src/conference/session/ms2-stream.cpp +++ b/src/conference/session/ms2-stream.cpp @@ -646,13 +646,36 @@ void MS2Stream::getRtpDestination(const OfferAnswerContext ¶ms, RtpAddressIn ? params.resultMediaDescription->getStreamAtIdx(static_cast<unsigned int>(mBundleOwner->getIndex())) : params.getResultStreamDescription(); - info->rtpAddr = stream.rtp_addr.empty() == false ? stream.rtp_addr : params.resultMediaDescription->addr; - bool isMulticast = !!ms_is_multicast(info->rtpAddr.c_str()); - info->rtpPort = stream.rtp_port; - info->rtcpAddr = stream.rtcp_addr.empty() == false ? stream.rtcp_addr : info->rtpAddr; - info->rtcpPort = (linphone_core_rtcp_enabled(getCCore()) && !isMulticast) - ? (stream.rtcp_port ? stream.rtcp_port : stream.rtp_port + 1) - : 0; + const auto &remoteStream = + (mRtpBundle && !mOwnsBundle && mBundleOwner) + ? params.remoteMediaDescription->getStreamAtIdx(static_cast<unsigned int>(mBundleOwner->getIndex())) + : params.getRemoteStreamDescription(); + + // Work-around for WebRTC as it does not send remote candidates when it should. + // So in this case, re-use the IP and port already used by ICE. + if (!params.localIsOfferer && getIceService().isActive() && getIceService().hasCompleted() && + remoteStream.ice_remote_candidates.empty()) { + const auto *session = + (mRtpBundle && !mOwnsBundle && mBundleOwner) ? mBundleOwner->mSessions.rtp_session : mSessions.rtp_session; + + char rtpAddr[64] = {}; + bctbx_sockaddr_to_ip_address((struct sockaddr *)&session->rtp.gs.rem_addr, session->rtp.gs.rem_addrlen, rtpAddr, + sizeof(rtpAddr), &info->rtpPort); + info->rtpAddr = string(rtpAddr); + + char rtcpAddr[64] = {}; + bctbx_sockaddr_to_ip_address((struct sockaddr *)&session->rtcp.gs.rem_addr, session->rtcp.gs.rem_addrlen, + rtcpAddr, sizeof(rtcpAddr), &info->rtcpPort); + info->rtcpAddr = string(rtcpAddr); + } else { + info->rtpAddr = stream.rtp_addr.empty() == false ? stream.rtp_addr : params.resultMediaDescription->addr; + bool isMulticast = !!ms_is_multicast(info->rtpAddr.c_str()); + info->rtpPort = stream.rtp_port; + info->rtcpAddr = stream.rtcp_addr.empty() == false ? stream.rtcp_addr : info->rtpAddr; + info->rtcpPort = (linphone_core_rtcp_enabled(getCCore()) && !isMulticast) + ? (stream.rtcp_port ? stream.rtcp_port : stream.rtp_port + 1) + : 0; + } } /* @@ -1105,14 +1128,14 @@ void MS2Stream::updateDestinations(const OfferAnswerContext ¶ms) { return; } - std::string rtpAddr = - (resultStreamDesc.rtp_addr.empty() == false) ? resultStreamDesc.rtp_addr : params.resultMediaDescription->addr; - std::string rtcpAddr = (resultStreamDesc.rtcp_addr.empty() == false) ? resultStreamDesc.rtcp_addr - : params.resultMediaDescription->addr; - lInfo() << "Change audio stream destination: RTP=" << rtpAddr << ":" << resultStreamDesc.rtp_port - << " RTCP=" << rtcpAddr << ":" << resultStreamDesc.rtcp_port; - rtp_session_set_remote_addr_full(mSessions.rtp_session, rtpAddr.c_str(), resultStreamDesc.rtp_port, - rtcpAddr.c_str(), resultStreamDesc.rtcp_port); + RtpAddressInfo info; + getRtpDestination(params, &info); + + lInfo() << "Change audio stream destination: RTP=" << info.rtpAddr << ":" << info.rtpPort + << " RTCP=" << info.rtcpAddr << ":" << info.rtcpPort; + + rtp_session_set_remote_addr_full(mSessions.rtp_session, info.rtpAddr.c_str(), info.rtpPort, info.rtcpAddr.c_str(), + info.rtcpPort); } bool MS2Stream::updateRtpProfile(const OfferAnswerContext ¶ms) { diff --git a/src/conference/session/tone-manager.cpp b/src/conference/session/tone-manager.cpp index 41c6d6782cde0fd89a07a5fb8ec44551c308ffc9..271d3251a332a2a8b0f974b83adf8d8a953768bc 100644 --- a/src/conference/session/tone-manager.cpp +++ b/src/conference/session/tone-manager.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2022 Belledonne Communications SARL. + * Copyright (c) 2010-2025 Belledonne Communications SARL. * * This file is part of Liblinphone * (see https://gitlab.linphone.org/BC/public/liblinphone). @@ -40,14 +40,11 @@ ToneManager::ToneManager(Core &core) : mCore(core) { mStats = {0, 0, 0, 0, 0, 0}; /* Assign default tones */ - setTone(LinphoneToneBusy, NULL); - setTone(LinphoneToneCallEnd, NULL); - setTone(LinphoneToneCallLost, NULL); - setTone(LinphoneToneCallNotAnswered, NULL); - setTone(LinphoneToneCallLost, NULL); -} - -ToneManager::~ToneManager() { + setTone(LinphoneToneBusy, nullptr); + setTone(LinphoneToneCallEnd, nullptr); + setTone(LinphoneToneCallLost, nullptr); + setTone(LinphoneToneCallNotAnswered, nullptr); + setTone(LinphoneToneCallLost, nullptr); } std::shared_ptr<AudioDevice> ToneManager::getOutputDevice(const shared_ptr<CallSession> &session) const { @@ -109,7 +106,7 @@ void ToneManager::startRingbackTone() { if (lc->sound_conf.remote_ring) { ms_snd_card_set_stream_type(ringCard, MS_SND_CARD_STREAM_DTMF); - mRingStream = ring_start(lc->factory, lc->sound_conf.remote_ring, 2000, (lc->use_files) ? NULL : ringCard); + mRingStream = ring_start(lc->factory, lc->sound_conf.remote_ring, 2000, (lc->use_files) ? nullptr : ringCard); } } @@ -121,19 +118,20 @@ void ToneManager::stopRingbackTone() { void ToneManager::startRingtone() { LinphoneCore *lc = getCore().getCCore(); - lInfo() << "[ToneManager] " << __func__; - if (linphone_config_get_int(lc->config, "sound", "disable_ringing", 0) == 1) { + if (linphone_core_call_ringing_disabled(lc)) { lWarning() << "[ToneManager] Ringing was disabled in configuration (disable_ringing item in [sound] section is " "set to 1)"; return; } - if (!getPlatformHelpers(lc)->isRingingAllowed()) { - lWarning() << "[ToneManager] Platform Helper says ringing isn't allowed by platform, do not ring..."; + if (!getPlatformHelpers(lc)->isPlayingSoundAllowed()) { + lWarning() << "[ToneManager] Platform Helper says playing sound isn't allowed by platform, do not ring..."; return; } + lInfo() << "[ToneManager] " << __func__; + mStats.number_of_startRingtone++; MSSndCard *ringcard = lc->sound_conf.lsd_card ? lc->sound_conf.lsd_card : lc->sound_conf.ring_sndcard; if (ringcard) { @@ -152,6 +150,11 @@ void ToneManager::stopRingtone() { } void ToneManager::startNamedTone(LinphoneToneID toneId) { + auto call = getCore().getCurrentCall(); + if (!call) return; + shared_ptr<MediaSession> mediaSession = dynamic_pointer_cast<MediaSession>(call->getActiveSession()); + if (!mediaSession->toneIndicationsEnabled()) return; + lInfo() << "[ToneManager] " << __func__; mStats.number_of_startNamedTone++; LinphoneToneDescription *tone = getTone(toneId); @@ -168,9 +171,9 @@ void ToneManager::stopTone() { LinphoneCore *lc = getCore().getCCore(); mStats.number_of_stopTone++; MSFilter *f = getAudioResource(LocalPlayer, lc->sound_conf.play_sndcard, false); - if (f != NULL) ms_filter_call_method_noarg(f, MS_PLAYER_CLOSE); // MS_PLAYER is used while being in call - f = getAudioResource(ToneGenerator, NULL, FALSE); - if (f != NULL) ms_filter_call_method_noarg(f, MS_DTMF_GEN_STOP); + if (f != nullptr) ms_filter_call_method_noarg(f, MS_PLAYER_CLOSE); // MS_PLAYER is used while being in call + f = getAudioResource(ToneGenerator, nullptr, false); + if (f != nullptr) ms_filter_call_method_noarg(f, MS_DTMF_GEN_STOP); } // --------------------------------------------------- @@ -184,7 +187,7 @@ void ToneManager::playDtmf(char dtmf, int duration) { MSSndCard *card = linphone_core_in_call(lc) ? lc->sound_conf.play_sndcard : lc->sound_conf.ring_sndcard; MSFilter *f = getAudioResource(ToneGenerator, card, true); - if (f == NULL) { + if (f == nullptr) { lError() << "[ToneManager] No dtmf generator at this time !"; return; } @@ -198,8 +201,8 @@ void ToneManager::playDtmf(char dtmf, int duration) { void ToneManager::stopDtmf() { lInfo() << "[ToneManager] " << __func__; - MSFilter *f = getAudioResource(ToneGenerator, NULL, false); - if (f != NULL) { + MSFilter *f = getAudioResource(ToneGenerator, nullptr, false); + if (f != nullptr) { ms_filter_call_method_noarg(f, MS_DTMF_GEN_STOP); } } @@ -248,7 +251,7 @@ LinphoneStatus ToneManager::playFile(const char *audiofile) { lInfo() << "[ToneManager] " << __func__ << " requested play file " << std::string(audiofile) << " is not found. Using sound resource from platform helper " << ((fileString.empty()) ? std::string("<unknown>") : fileString); - if (fileString != "") audiofileToUse = fileString.c_str(); + if (!fileString.empty()) audiofileToUse = fileString.c_str(); } if (ms_filter_call_method(f, MS_PLAYER_OPEN, (void *)audiofileToUse) != 0) { @@ -263,7 +266,7 @@ void ToneManager::playTone(const MSDtmfGenCustomTone &tone) { lInfo() << "[ToneManager] " << __func__ << " playing DTMF tone " << std::string(tone.tone_name); LinphoneCore *lc = getCore().getCCore(); shared_ptr<CallSession> session; - MSSndCard *card = NULL; + MSSndCard *card = nullptr; std::shared_ptr<LinphonePrivate::Call> call = getCore().getCurrentCall(); if (call) session = call->getActiveSession(); @@ -276,12 +279,12 @@ void ToneManager::playTone(const MSDtmfGenCustomTone &tone) { } // If card is null, use the default playcard - if (card == NULL) { + if (card == nullptr) { card = lc->sound_conf.play_sndcard; } MSFilter *f = getAudioResource(ToneGenerator, card, true); - if (f == NULL) { + if (f == nullptr) { lError() << "[ToneManager] No tone generator at this time !"; return; } @@ -329,9 +332,9 @@ void ToneManager::destroyRingStream() { MSFilter *ToneManager::getAudioResource(AudioResourceType rtype, MSSndCard *card, bool create) { LinphoneCore *lc = getCore().getCCore(); LinphoneCall *call = linphone_core_get_current_call(lc); - AudioStream *stream = NULL; - RingStream *ringstream; - MSFilter *audioResource = NULL; + AudioStream *stream = nullptr; + RingStream *ringStream; + MSFilter *audioResource = nullptr; float tmp; if (call) { stream = reinterpret_cast<AudioStream *>(linphone_call_get_stream(call, LinphoneStreamTypeAudio)); @@ -347,35 +350,35 @@ MSFilter *ToneManager::getAudioResource(AudioResourceType rtype, MSSndCard *card ring_stop(mRingStream); mRingStream = nullptr; } - if (mRingStream == NULL) { + if (mRingStream == nullptr) { #if TARGET_OS_IPHONE tmp = 0.007f; #else tmp = 0.1f; #endif float amp = linphone_config_get_float(lc->config, "sound", "dtmf_player_amp", tmp); - MSSndCard *ringcard = NULL; + MSSndCard *ringcard = nullptr; if (!lc->use_files) { ringcard = lc->sound_conf.lsd_card ? lc->sound_conf.lsd_card : card ? card : lc->sound_conf.ring_sndcard; - if (ringcard == NULL) return NULL; + if (ringcard == nullptr) return nullptr; ms_snd_card_set_stream_type(ringcard, MS_SND_CARD_STREAM_DTMF); } - if (!create) return NULL; + if (!create) return nullptr; - ringstream = mRingStream = ring_start( - lc->factory, NULL, 0, ringcard); // passing a NULL ringcard if core if lc->use_files is enabled + ringStream = mRingStream = ring_start( + lc->factory, nullptr, 0, ringcard); // passing a nullptr ringcard if core if lc->use_files is enabled ms_filter_call_method(mRingStream->gendtmf, MS_DTMF_GEN_SET_DEFAULT_AMPLITUDE, &); /* A ring stream was started in the purpose of playing a tone. Make sure it is destroyed once the tone is * finished. */ scheduleRingStreamDestruction(); } else { - ringstream = mRingStream; + ringStream = mRingStream; } - if (rtype == ToneGenerator) audioResource = ringstream->gendtmf; - if (rtype == LocalPlayer) audioResource = ringstream->source; + if (rtype == ToneGenerator) audioResource = ringStream->gendtmf; + if (rtype == LocalPlayer) audioResource = ringStream->source; } return audioResource; } @@ -395,21 +398,21 @@ void ToneManager::freeAudioResources() { LinphoneToneDescription *ToneManager::getTone(LinphoneToneID id) { const bctbx_list_t *elem; - for (elem = getCore().getCCore()->tones; elem != NULL; elem = elem->next) { - LinphoneToneDescription *tone = (LinphoneToneDescription *)elem->data; + for (elem = getCore().getCCore()->tones; elem != nullptr; elem = elem->next) { + auto tone = (LinphoneToneDescription *)elem->data; if (tone->toneid == id) return tone; } - return NULL; + return nullptr; } -void ToneManager::setTone(LinphoneToneID id, const char *audiofile) { +void ToneManager::setTone(LinphoneToneID id, const char *audioFile) { LinphoneCore *lc = getCore().getCCore(); LinphoneToneDescription *tone = getTone(id); if (tone) { lc->tones = bctbx_list_remove(lc->tones, tone); linphone_tone_description_destroy(tone); } - tone = linphone_tone_description_new(id, audiofile); + tone = linphone_tone_description_new(id, audioFile); lc->tones = bctbx_list_append(lc->tones, tone); } @@ -510,6 +513,7 @@ bool ToneManager::shouldPlayWaitingTone(const std::shared_ptr<CallSession> &sess void ToneManager::notifyIncomingCall(const std::shared_ptr<CallSession> &session) { shared_ptr<Call> currentCall = getCore().getCurrentCall(); LinphoneCore *lc = getCore().getCCore(); + shared_ptr<MediaSession> mediaSession = dynamic_pointer_cast<MediaSession>(session); if (mSessionRinging && mSessionRinging != session) { // Already a session in ringing state. @@ -518,31 +522,33 @@ void ToneManager::notifyIncomingCall(const std::shared_ptr<CallSession> &session if (shouldPlayWaitingTone(session)) { /* already in a call or in a local conference. A tone indication will be used. */ - if (linphone_core_tone_indications_enabled(lc)) { + if (mediaSession->toneIndicationsEnabled()) { startNamedTone(LinphoneToneCallWaiting); - /* Setup the way this incoming call notification has to be stopped */ + /* Set up the way this incoming call notification has to be stopped */ mSessionRingingStopFunction = [this]() { this->stopTone(); }; } } else { - /* Not already in a call or conference, so play the normal ringtone. */ - /* - * For ios, only one write sound card is allowed. To ensure this, free audio resources before a new incoming - * call. It may be a call-end tone from a previous call, if a new call arrives closely after ending the current - * one. - */ - freeAudioResources(); - if (linphone_core_is_native_ringing_enabled(lc)) { - lInfo() << "Native (ie platform dependant) ringing is enabled, so not ringing from liblinphone."; - return; - } - if (linphone_core_callkit_enabled(lc)) { - lInfo() << "Callkit mode is enabled, will not play ring tone from liblinphone."; - return; - } + if (mediaSession && !mediaSession->ringingDisabled()) { + /* Not already in a call or conference, so play the normal ringtone. */ + /* + * For ios, only one write sound card is allowed. To ensure this, free audio resources before a new incoming + * call. It may be a call-end tone from a previous call, if a new call arrives closely after ending the + * current one. + */ + freeAudioResources(); + if (linphone_core_is_native_ringing_enabled(lc)) { + lInfo() << "Native (ie platform dependant) ringing is enabled, so not ringing from liblinphone."; + return; + } + if (linphone_core_callkit_enabled(lc)) { + lInfo() << "Callkit mode is enabled, will not play ring tone from liblinphone."; + return; + } - startRingtone(); - /* Setup the way this incoming call notification has to be stopped */ - mSessionRingingStopFunction = [this]() { this->stopRingtone(); }; + startRingtone(); + /* Set up the way this incoming call notification has to be stopped */ + mSessionRingingStopFunction = [this]() { this->stopRingtone(); }; + } } mSessionRinging = session; } @@ -561,8 +567,12 @@ void ToneManager::notifyOutgoingCallRinging(const std::shared_ptr<CallSession> & } void ToneManager::notifyToneIndication(LinphoneReason reason) { - if (!linphone_core_tone_indications_enabled(getCore().getCCore())) return; + auto call = getCore().getCurrentCall(); + if (!call) return; + shared_ptr<MediaSession> mediaSession = dynamic_pointer_cast<MediaSession>(call->getActiveSession()); + if (!mediaSession->toneIndicationsEnabled()) return; + LinphoneCore *lc = getCore().getCCore(); lInfo() << "[ToneManager] " << __func__ << " reason " << std::string(linphone_reason_to_string(reason)); switch (reason) { @@ -571,7 +581,12 @@ void ToneManager::notifyToneIndication(LinphoneReason reason) { startNamedTone(LinphoneToneCallEnd); break; case LinphoneReasonNotAnswered: - startNamedTone(LinphoneToneCallNotAnswered); + if (!getPlatformHelpers(lc)->isPlayingSoundAllowed()) { + lWarning() << "[ToneManager] Platform Helper says playsing sound isn't allowed by platform, do not " + "play call not answered tone"; + } else { + startNamedTone(LinphoneToneCallNotAnswered); + } break; case LinphoneReasonBusy: startNamedTone(LinphoneToneBusy); @@ -600,7 +615,7 @@ bool ToneManager::inCallOrConference() const { */ shared_ptr<CallSession> ToneManager::lookupRingingSession() const { - for (auto call : getCore().getCalls()) { + for (const auto &call : getCore().getCalls()) { auto session = call->getActiveSession(); if (session->getState() == CallSession::State::IncomingReceived) { return session; @@ -671,7 +686,7 @@ void ToneManager::updateRingingSessions(const std::shared_ptr<CallSession> &call void ToneManager::prepareForNextState(const std::shared_ptr<CallSession> &callSession, CallSession::State nextState) { shared_ptr<MediaSession> session = dynamic_pointer_cast<MediaSession>(callSession); if (!session) return; - if (!session->toneIndicationsEnabled()) return; + if (!session->toneIndicationsEnabled() && session->ringingDisabled()) return; updateRingingSessions(callSession, nextState); @@ -690,7 +705,6 @@ void ToneManager::prepareForNextState(const std::shared_ptr<CallSession> &callSe void ToneManager::notifyState(const std::shared_ptr<CallSession> &callSession, CallSession::State state) { shared_ptr<MediaSession> session = dynamic_pointer_cast<MediaSession>(callSession); if (!session) return; - if (!session->toneIndicationsEnabled()) return; updateRingingSessions(callSession, state); diff --git a/src/conference/session/tone-manager.h b/src/conference/session/tone-manager.h index b2ed222adfad0ac10c3927ea19fef055798c5024..e49ae9e9f7581f9a37ffde5f3f8794b68fa63f9d 100644 --- a/src/conference/session/tone-manager.h +++ b/src/conference/session/tone-manager.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2022 Belledonne Communications SARL. + * Copyright (c) 2010-2025 Belledonne Communications SARL. * * This file is part of Liblinphone * (see https://gitlab.linphone.org/BC/public/liblinphone). @@ -38,7 +38,7 @@ class ToneManager { public: ToneManager(Core &core); ToneManager(const ToneManager &other) = delete; - ~ToneManager(); + ~ToneManager() = default; /* * The CallSession's state change notification are sufficient to trigger rings and tones. diff --git a/src/conference/sip-conference-scheduler.cpp b/src/conference/sip-conference-scheduler.cpp index 0d676f662b7d8152b1e7fcad6956fcc8cc43eddb..6204f47ed6b16e24f54c9b3825405efe08397d45 100644 --- a/src/conference/sip-conference-scheduler.cpp +++ b/src/conference/sip-conference-scheduler.cpp @@ -36,8 +36,7 @@ SIPConferenceScheduler::SIPConferenceScheduler(const shared_ptr<Core> &core, con : ConferenceScheduler(core, account) { } -void SIPConferenceScheduler::createOrUpdateConference(const std::shared_ptr<ConferenceInfo> &conferenceInfo, - const std::shared_ptr<Address> &creator) { +void SIPConferenceScheduler::createOrUpdateConference(const std::shared_ptr<ConferenceInfo> &conferenceInfo) { shared_ptr<LinphonePrivate::ConferenceParams> conferenceParams = ConferenceParams::create(getCore()); conferenceParams->setAccount(getAccount()); @@ -48,6 +47,14 @@ void SIPConferenceScheduler::createOrUpdateConference(const std::shared_ptr<Conf conferenceParams->setSecurityLevel(mConferenceInfo->getSecurityLevel()); const auto &startTime = conferenceInfo->getDateTime(); + + if (startTime < 0) { + lError() << "[Conference Scheduler] [" << this << "] unable to schedule a conference with start time " + << startTime; + setState(State::Error); + return; + } + conferenceParams->setStartTime(startTime); const auto &duration = conferenceInfo->getDuration(); if (duration > 0) { @@ -62,7 +69,7 @@ void SIPConferenceScheduler::createOrUpdateConference(const std::shared_ptr<Conf auto ref = getSharedFromThis(); const auto &conferenceAddress = conferenceInfo->getUri(); - mSession = getCore()->createOrUpdateConferenceOnServer(conferenceParams, creator, invitees, conferenceAddress, + mSession = getCore()->createOrUpdateConferenceOnServer(conferenceParams, invitees, conferenceAddress, dynamic_pointer_cast<SIPConferenceScheduler>(ref)); if (mSession == nullptr) { lError() << "[Conference Scheduler] [" << this << "] createConferenceOnServer returned a null session!"; @@ -89,7 +96,7 @@ void SIPConferenceScheduler::onCallSessionSetTerminated(const std::shared_ptr<Ca << "] The session to update the conference information of conference " << (conferenceAddress && conferenceAddress->isValid() ? conferenceAddress->toString() : std::string("sip:")) - << " did not succesfully establish hence it is likely that the request wasn't taken into account by " + << " did not successfully establish hence it is likely that the request wasn't taken into account by " "the server"; setState(State::Error); } else if (getState() != State::Error) { @@ -99,41 +106,6 @@ void SIPConferenceScheduler::onCallSessionSetTerminated(const std::shared_ptr<Ca mainDb->insertConferenceInfo(mConferenceInfo); #endif // HAVE_DB_STORAGE - // Do not try to call impromptu conference if a participant updates its informations - if ((getState() == State::AllocationPending) && (session->getParams()->getPrivate()->getStartTime() < 0)) { - lInfo() << "Automatically rejoining conference " << *remoteAddress; - auto new_params = linphone_core_create_call_params(getCore()->getCCore(), nullptr); - // Participant with the focus call is admin - L_GET_CPP_PTR_FROM_C_OBJECT(new_params)->addCustomContactParameter("admin", Utils::toString(true)); - std::list<Address> addressesList; - for (const auto &participantInfo : mConferenceInfo->getParticipants()) { - addressesList.push_back(Conference::createParticipantAddressForResourceList(participantInfo)); - } - addressesList.sort([](const auto &addr1, const auto &addr2) { return addr1 < addr2; }); - addressesList.unique([](const auto &addr1, const auto &addr2) { return addr1.weakEqual(addr2); }); - - if (!addressesList.empty()) { - auto content = Content::create(); - content->setBodyFromUtf8(Utils::getResourceLists(addressesList)); - content->setContentType(ContentType::ResourceLists); - content->setContentDisposition(ContentDisposition::RecipientList); - if (linphone_core_content_encoding_supported(getCore()->getCCore(), "deflate")) { - content->setContentEncoding("deflate"); - } - - L_GET_CPP_PTR_FROM_C_OBJECT(new_params)->addCustomContent(content); - } - const 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); - - linphone_core_invite_address_with_params_2(getCore()->getCCore(), remoteAddress->toC(), new_params, - L_STRING_TO_C(mConferenceInfo->getUtf8Subject()), NULL); - linphone_call_params_unref(new_params); - } - auto conferenceAddress = remoteAddress; setConferenceAddress(conferenceAddress); } diff --git a/src/conference/sip-conference-scheduler.h b/src/conference/sip-conference-scheduler.h index 03303ce1ccbed1963f15121e7afd752eb0bc50d7..49297c005df577326f515a96f5b539c50841e092 100644 --- a/src/conference/sip-conference-scheduler.h +++ b/src/conference/sip-conference-scheduler.h @@ -38,8 +38,7 @@ public: virtual void onCallSessionStateChanged(const std::shared_ptr<CallSession> &session, CallSession::State state, const std::string &message) override; - virtual void createOrUpdateConference(const std::shared_ptr<ConferenceInfo> &conferenceInfo, - const std::shared_ptr<Address> &creator) override; + virtual void createOrUpdateConference(const std::shared_ptr<ConferenceInfo> &conferenceInfo) override; virtual void processResponse(const LinphoneErrorInfo *errorInfo, const std::shared_ptr<Address> conferenceAddress) override; inline std::shared_ptr<CallSession> getSession() { diff --git a/src/content/content.cpp b/src/content/content.cpp index c8a75e84f7cec49d22604e6686a4c3e7e7116312..99b0c302696c66624ae651c8178abfacb5ab8d20 100644 --- a/src/content/content.cpp +++ b/src/content/content.cpp @@ -281,6 +281,14 @@ void Content::setFilePath(const std::string &path) { mCache.filePath = path; } +void Content::setRelatedChatMessageId(const string &messageId) { + mMessageId = messageId; +} + +const string &Content::getRelatedChatMessageId() const { + return mMessageId; +} + void Content::addHeader(const string &headerName, const string &headerValue) { removeHeader(headerName); Header header = Header(headerName, headerValue); @@ -442,6 +450,7 @@ const string Content::exportPlainFileFromEncryptedFile(const string &filePath) c plainPathTest = cacheDir + std::to_string(index) + "_" + basename; } plainPath = plainPathTest; + lInfo() << "[Content] Using file [" << plainPath << "]"; } bctbx_free(basename); diff --git a/src/content/content.h b/src/content/content.h index c31085053dead9325448693dad64222e332f6ba0..fb508573909fe40417fed7e0d47bc3ca9cfa912a 100644 --- a/src/content/content.h +++ b/src/content/content.h @@ -110,6 +110,9 @@ public: std::list<Header>::const_iterator findHeader(const std::string &headerName) const; const std::string &getCustomHeader(const std::string &headerName) const; + void setRelatedChatMessageId(const std::string &messageId); + const std::string &getRelatedChatMessageId() const; + void setUserData(const Variant &userData); Variant getUserData() const; @@ -142,6 +145,7 @@ private: void *mCryptoContext = nullptr; // Used to encrypt file for RCS file transfer. bool mIsDirty = false; SalBodyHandler *mBodyHandler = nullptr; + std::string mMessageId; struct Cache { std::string name; diff --git a/src/core/core-chat-room.cpp b/src/core/core-chat-room.cpp index efc1c21039fc82be833bb5f11c8511bf121a96d3..f81cbf1108128def367969c781956d4b69a746c0 100644 --- a/src/core/core-chat-room.cpp +++ b/src/core/core-chat-room.cpp @@ -30,6 +30,7 @@ #include "chat/chat-room/abstract-chat-room.h" #include "chat/chat-room/basic-chat-room.h" #include "chat/chat-room/chat-room.h" +#include "conference/conference-context.h" #include "conference/participant-info.h" #include "conference/participant.h" #include "conference/server-conference.h" @@ -111,9 +112,10 @@ CorePrivate::getIdentityAddressWithGruu(const std::shared_ptr<const Address> &id #pragma GCC diagnostic ignored "-Wunused-parameter" #endif // _MSC_VER // Base server group chat room creator -shared_ptr<AbstractChatRoom> CorePrivate::createServerChatRoom(const std::shared_ptr<Address> &conferenceFactoryUri, - SalCallOp *op, - const std::shared_ptr<ConferenceParams> ¶ms) { +shared_ptr<AbstractChatRoom> +CorePrivate::createServerChatRoom(BCTBX_UNUSED(const std::shared_ptr<const Address> &conferenceFactoryUri), + SalCallOp *op, + const std::shared_ptr<ConferenceParams> ¶ms) { #ifdef HAVE_ADVANCED_IM L_Q(); if (!params || !params->isValid()) { @@ -133,8 +135,7 @@ shared_ptr<AbstractChatRoom> CorePrivate::createServerChatRoom(const std::shared } auto conference = dynamic_pointer_cast<ServerConference>( - (new ServerConference(q->getSharedFromThis(), conferenceFactoryUri, nullptr, newConferenceParameters)) - ->toSharedPtr()); + (new ServerConference(q->getSharedFromThis(), nullptr, newConferenceParameters))->toSharedPtr()); conference->init(op, conference.get()); return conference->getChatRoom(); #else @@ -151,10 +152,11 @@ shared_ptr<AbstractChatRoom> CorePrivate::createServerChatRoom(const std::shared #pragma GCC diagnostic ignored "-Wunused-parameter" #endif // _MSC_VER // Base client group chat room creator -shared_ptr<AbstractChatRoom> CorePrivate::createClientChatRoom(const std::shared_ptr<Address> &conferenceFactoryUri, - const ConferenceId &conferenceId, - SalCallOp *op, - const std::shared_ptr<ConferenceParams> ¶ms) { +shared_ptr<AbstractChatRoom> +CorePrivate::createClientChatRoom(const std::shared_ptr<const Address> &conferenceFactoryUri, + const ConferenceId &conferenceId, + SalCallOp *op, + const std::shared_ptr<ConferenceParams> ¶ms) { #ifdef HAVE_ADVANCED_IM L_Q(); if (!params || !params->isValid()) { @@ -176,12 +178,13 @@ shared_ptr<AbstractChatRoom> CorePrivate::createClientChatRoom(const std::shared } auto conference = dynamic_pointer_cast<ClientConference>( - (new ClientConference(q->getSharedFromThis(), conferenceId.getLocalAddress(), nullptr, newConferenceParameters)) - ->toSharedPtr()); + (new ClientConference(q->getSharedFromThis(), nullptr, newConferenceParameters))->toSharedPtr()); conference->initWithFocus(conferenceFactoryUri, nullptr, op, conference.get()); if (conferenceId.isValid()) { conference->setConferenceId(conferenceId); } + lInfo() << *conference << " with id " << conferenceId + << " and chat only capabilities has been successfully created"; return conference->getChatRoom(); #else lWarning() << "Advanced IM such as group chat is disabled!"; @@ -237,7 +240,9 @@ shared_ptr<AbstractChatRoom> CorePrivate::createClientChatRoom(const string &sub params->getChatParams()->setEphemeralMode(AbstractChatRoom::EphemeralMode::DeviceManaged); params->getChatParams()->setBackend(ChatParams::Backend::FlexisipChat); - return createClientChatRoom(conferenceFactoryUri, ConferenceId(nullptr, defaultLocalAddress), nullptr, params); + return createClientChatRoom(conferenceFactoryUri, + ConferenceId(nullptr, defaultLocalAddress, q->createConferenceIdParams()), nullptr, + params); } shared_ptr<AbstractChatRoom> CorePrivate::createBasicChatRoom(const ConferenceId &conferenceId, @@ -265,78 +270,38 @@ shared_ptr<AbstractChatRoom> CorePrivate::createBasicChatRoom(const ConferenceId return chatRoom; } -shared_ptr<AbstractChatRoom> -CorePrivate::searchChatRoom(const shared_ptr<ConferenceParams> ¶ms, +std::shared_ptr<AbstractChatRoom> +CorePrivate::searchChatRoom(const std::shared_ptr<ConferenceParams> ¶ms, const std::shared_ptr<const Address> &localAddress, const std::shared_ptr<const Address> &remoteAddress, const std::list<std::shared_ptr<Address>> &participants) const { L_Q(); - for (const auto &chatRoom : q->getRawChatRoomList()) { - if (params) { - const auto &chatRoomParams = chatRoom->getCurrentParams(); - - if (params->audioEnabled() != chatRoomParams->audioEnabled()) continue; - if (params->videoEnabled() != chatRoomParams->videoEnabled()) continue; - - if (params->getChatParams()->getBackend() != chatRoomParams->getChatParams()->getBackend()) continue; - - if (params->isGroup() != chatRoomParams->isGroup()) continue; - - if (params->isGroup() && - (chatRoomParams->getChatParams()->getBackend() == LinphonePrivate::ChatParams::Backend::Basic)) - continue; - - if (params->getChatParams()->isEncrypted() != chatRoomParams->getChatParams()->isEncrypted()) continue; - - // Subject doesn't make any sense for basic chat room - if ((params->getChatParams()->getBackend() == LinphonePrivate::ChatParams::Backend::FlexisipChat) && - (!params->getUtf8Subject().empty() && params->getUtf8Subject() != chatRoom->getSubjectUtf8())) - continue; - } - - std::shared_ptr<Address> curLocalAddress = chatRoom->getLocalAddress(); - const auto curLocalAddressWithoutGruu = curLocalAddress->getUriWithoutGruu(); - const auto localAddressWithoutGruu = - (localAddress && localAddress->isValid()) ? localAddress->getUriWithoutGruu() : Address(); - if (localAddressWithoutGruu.isValid() && (localAddressWithoutGruu != curLocalAddressWithoutGruu)) continue; - - std::shared_ptr<Address> curRemoteAddress = chatRoom->getPeerAddress(); - const auto curRemoteAddressWithoutGruu = curRemoteAddress->getUriWithoutGruu(); - const auto remoteAddressWithoutGruu = - (remoteAddress && remoteAddress->isValid()) ? remoteAddress->getUriWithoutGruu() : Address(); - if (remoteAddressWithoutGruu.isValid() && (remoteAddressWithoutGruu != curRemoteAddressWithoutGruu)) continue; - - const auto &chatRoomParticipants = chatRoom->getParticipants(); - const auto expectedParticipantNb = participants.size(); - if ((expectedParticipantNb > 0) && (chatRoomParticipants.size() != expectedParticipantNb)) continue; - bool allFound = true; - for (const auto &participant : participants) { - bool found = false; - for (const auto &p : chatRoomParticipants) { - if (participant->weakEqual(*(p->getAddress()))) { - found = true; - break; - } - } - if (!found) { - allFound = false; - break; - } - } - if (!allFound) continue; + ConferenceContext referenceConferenceContext(params, localAddress, remoteAddress, participants); + const auto &chatRooms = q->getRawChatRoomList(); + const auto it = std::find_if(chatRooms.begin(), chatRooms.end(), [&](const auto &chatRoom) { + ConferenceContext conferenceContext(chatRoom->getCurrentParams(), chatRoom->getLocalAddress(), + chatRoom->getPeerAddress(), chatRoom->getParticipantAddresses()); + return (referenceConferenceContext == conferenceContext); + }); + + std::shared_ptr<AbstractChatRoom> chatRoom; + if (it != chatRooms.cend()) { + chatRoom = *it; + } + return chatRoom; +} - return chatRoom; +std::shared_ptr<AbstractChatRoom> CorePrivate::searchChatRoom(const std::string identifier) const { + auto [localAddress, peerAddress] = ConferenceId::parseIdentifier(identifier); + if (!localAddress || !localAddress->isValid() || !peerAddress || !peerAddress->isValid()) { + return nullptr; } - return nullptr; + return searchChatRoom(nullptr, localAddress, peerAddress, {}); } -shared_ptr<AbstractChatRoom> -CorePrivate::createChatRoom(const shared_ptr<ConferenceParams> ¶ms, - const std::shared_ptr<const Address> &localAddr, - const std::list<std::shared_ptr<const Address>> &participants) { -#ifdef HAVE_ADVANCED_IM +shared_ptr<AbstractChatRoom> CorePrivate::createChatRoom(const shared_ptr<ConferenceParams> ¶ms, + const std::list<std::shared_ptr<Address>> &participants) { L_Q(); -#endif if (!params) { lWarning() << "Trying to create chat room with null parameters"; return nullptr; @@ -354,29 +319,35 @@ CorePrivate::createChatRoom(const shared_ptr<ConferenceParams> ¶ms, lWarning() << "Tying to create chat room with unavailable backend"; return nullptr; } + + auto account = params->getAccount(); + if (!account) { + account = q->getDefaultAccount(); + } + const std::shared_ptr<const Address> localAddr = + account ? account->getAccountParams()->getIdentityAddress() : getDefaultLocalAddress(nullptr, false); + shared_ptr<AbstractChatRoom> chatRoom; if (params->getChatParams()->getBackend() == ChatParams::Backend::FlexisipChat) { #ifdef HAVE_ADVANCED_IM - const auto &conferenceFactoryUri = Core::getConferenceFactoryAddress(q->getSharedFromThis(), localAddr); + const auto &conferenceFactoryUri = account->getAccountParams()->getConferenceFactoryAddress(); if (!conferenceFactoryUri || !conferenceFactoryUri->isValid()) { - lWarning() << "Not creating group chat room: no conference factory uri for local address [" << localAddr + lWarning() << "Not creating group chat room: no conference factory uri for local address [" << *localAddr << "]"; return nullptr; } - ConferenceId conferenceId = ConferenceId(nullptr, localAddr); if (!params->isGroup() && participants.size() > 0) { - // Prevent multiple 1-1 conference based chat room with same local/remote addresses - chatRoom = q->findOneToOneChatRoom(localAddr, participants.front(), false, true, - params->getChatParams()->isEncrypted()); - if (chatRoom != nullptr) { - lWarning() << "Found already existing 1-1 chat room that matches, using this one " << chatRoom; + chatRoom = searchChatRoom(params, localAddr, nullptr, participants); + if (chatRoom) { + lWarning() << "Found already existing 1-1 chat room that matches the given parameters, using this one " + << *chatRoom; return chatRoom; } } + ConferenceId conferenceId(nullptr, localAddr, q->createConferenceIdParams()); chatRoom = createClientChatRoom(conferenceFactoryUri, conferenceId, nullptr, params); - if (!chatRoom) { lWarning() << "Cannot create createClientChatRoom with subject [" << params->getSubject() << "]"; return nullptr; @@ -397,31 +368,23 @@ CorePrivate::createChatRoom(const shared_ptr<ConferenceParams> ¶ms, } std::shared_ptr<const Address> remoteAddr = participants.front(); - std::list<std::shared_ptr<Address>> emptyList; - chatRoom = searchChatRoom(params, localAddr, remoteAddr, emptyList); + chatRoom = searchChatRoom(params, localAddr, remoteAddr, {}); if (chatRoom == nullptr) { - chatRoom = createBasicChatRoom(ConferenceId(remoteAddr, localAddr), params); + std::shared_ptr<const Address> remoteAddr = participants.front(); + chatRoom = createBasicChatRoom(ConferenceId(remoteAddr, localAddr, q->createConferenceIdParams()), params); insertChatRoom(chatRoom); insertChatRoomWithDb(chatRoom); } else { - lInfo() << "Found an existing BasicChatRoom with this participant, using it instead of creating a new one"; + lInfo() << "Found an existing BasicChatRoom with " << *remoteAddr + << ", using it instead of creating a new one"; } } return chatRoom; } -shared_ptr<AbstractChatRoom> -CorePrivate::createChatRoom(const shared_ptr<ConferenceParams> ¶ms, - const std::list<std::shared_ptr<const Address>> &participants) { - auto defaultLocalAddress = - getDefaultLocalAddress(nullptr, params->getChatParams()->getBackend() == ChatParams::Backend::FlexisipChat); - return createChatRoom(params, defaultLocalAddress, participants); -} - -shared_ptr<AbstractChatRoom> -CorePrivate::createChatRoom(const std::string &subject, const std::list<std::shared_ptr<const Address>> &participants) { +shared_ptr<AbstractChatRoom> CorePrivate::createChatRoom(const std::string &subject, + const std::list<std::shared_ptr<Address>> &participants) { L_Q(); - shared_ptr<ConferenceParams> params = ConferenceParams::create(q->getSharedFromThis()); params->setChatDefaults(); if (participants.size() > 1) { @@ -430,21 +393,19 @@ CorePrivate::createChatRoom(const std::string &subject, const std::list<std::sha } else { params->getChatParams()->setBackend(ChatParams::Backend::Basic); } - auto defaultLocalAddress = - getDefaultLocalAddress(nullptr, params->getChatParams()->getBackend() == ChatParams::Backend::FlexisipChat); + params->setAccount(q->getDefaultAccount()); params->setUtf8Subject(subject); - return createChatRoom(params, defaultLocalAddress, participants); + return createChatRoom(params, participants); } shared_ptr<AbstractChatRoom> CorePrivate::createChatRoom(const shared_ptr<ConferenceParams> ¶ms, - const std::shared_ptr<Address> &localAddr, - const std::shared_ptr<const Address> &participant) { - const std::list<std::shared_ptr<const Address>> &participants{participant}; - return createChatRoom(params, localAddr, participants); + const std::shared_ptr<Address> &participant) { + const std::list<std::shared_ptr<Address>> participants{participant}; + return createChatRoom(params, participants); } // Assume basic chat room creation -shared_ptr<AbstractChatRoom> CorePrivate::createChatRoom(const std::shared_ptr<const Address> &participant) { +shared_ptr<AbstractChatRoom> CorePrivate::createChatRoom(const std::shared_ptr<Address> &participant) { return createChatRoom("", {participant}); } @@ -685,8 +646,8 @@ static bool compare_chat_room(const shared_ptr<AbstractChatRoom> &first, const s return first->getLastUpdateTime() > second->getLastUpdateTime(); } -std::shared_ptr<Address> Core::getConferenceFactoryAddress(const shared_ptr<Core> &core, - const std::shared_ptr<const Address> &localAddress) { +std::shared_ptr<const Address> Core::getConferenceFactoryAddress(const shared_ptr<Core> &core, + const std::shared_ptr<const Address> &localAddress) { auto account = core->lookupKnownAccount(localAddress, true); if (!account) { // lWarning() << "No account found for local address: [" << *localAddress << "]"; @@ -694,8 +655,8 @@ std::shared_ptr<Address> Core::getConferenceFactoryAddress(const shared_ptr<Core } else return getConferenceFactoryAddress(core, account); } -std::shared_ptr<Address> Core::getConferenceFactoryAddress(BCTBX_UNUSED(const shared_ptr<Core> &core), - const std::shared_ptr<Account> &account) { +std::shared_ptr<const Address> Core::getConferenceFactoryAddress(BCTBX_UNUSED(const shared_ptr<Core> &core), + const std::shared_ptr<Account> &account) { const auto ¶ms = account->getAccountParams(); if (params) { const auto &uri = params->getConferenceFactoryAddress(); @@ -739,7 +700,7 @@ void Core::updateChatRoomList() const { bool hideChatRoomsFromRemovedProxyConfig = !!linphone_config_get_int(config, "misc", "hide_chat_rooms_from_removed_proxies", 1); - list<shared_ptr<Address>> localAddresses; + list<shared_ptr<const Address>> localAddresses; if (hideChatRoomsFromRemovedProxyConfig) { for (const auto &account : getAccounts()) { auto localAddress = account->getAccountParams()->getIdentityAddress(); @@ -791,9 +752,7 @@ const bctbx_list_t *Core::getChatRoomsCList() const { shared_ptr<AbstractChatRoom> Core::findChatRoom(const ConferenceId &conferenceId, bool logIfNotFound) const { L_D(); - std::list<std::shared_ptr<Address>> emptyList; - auto chatRoom = - d->searchChatRoom(nullptr, conferenceId.getLocalAddress(), conferenceId.getPeerAddress(), emptyList); + auto chatRoom = d->searchChatRoom(nullptr, conferenceId.getLocalAddress(), conferenceId.getPeerAddress(), {}); if (chatRoom) { lDebug() << "Found chat room in RAM for conference ID " << conferenceId << "."; return chatRoom; @@ -820,77 +779,30 @@ list<shared_ptr<AbstractChatRoom>> Core::findChatRooms(const std::shared_ptr<Add return output; } -shared_ptr<AbstractChatRoom> Core::findOneToOneChatRoom(const std::shared_ptr<const Address> &localAddress, - const std::shared_ptr<const Address> &participantAddress, - bool basicOnly, - bool conferenceOnly, - bool encrypted) const { - bool includeBasic = true; - bool includeConference = true; - if (basicOnly) { - includeConference = false; - } else if (conferenceOnly) { - includeBasic = false; - } - for (const auto &chatRoom : getRawChatRoomList(includeBasic, includeConference)) { - const std::shared_ptr<Address> &curLocalAddress = chatRoom->getLocalAddress(); - const auto &chatRoomParams = chatRoom->getCurrentParams(); - - // We are looking for a one to one chatroom - // Do not return a group chat room that everyone except one person has left - if (chatRoomParams->isGroup()) continue; - - if (encrypted != chatRoomParams->getChatParams()->isEncrypted()) continue; - - const auto chatBackend = chatRoomParams->getChatParams()->getBackend(); - // One to one client group chat room - // The only participant's address must match the participantAddress argument - if (!basicOnly && (chatBackend == LinphonePrivate::ChatParams::Backend::FlexisipChat) && - !chatRoom->getParticipants().empty() && localAddress->weakEqual(*curLocalAddress) && - participantAddress->weakEqual(*chatRoom->getParticipants().front()->getAddress())) - return chatRoom; - - // One to one basic chat room (addresses without gruu) - // The peer address must match the participantAddress argument - if (!conferenceOnly && (chatBackend == LinphonePrivate::ChatParams::Backend::Basic) && - localAddress->weakEqual(*curLocalAddress) && participantAddress->weakEqual(*chatRoom->getPeerAddress())) - return chatRoom; - } - return nullptr; -} - shared_ptr<AbstractChatRoom> Core::getOrCreateBasicChatRoom(const ConferenceId &conferenceId) { L_D(); - - shared_ptr<AbstractChatRoom> chatRoom = findChatRoom(conferenceId); + ChatRoom::CapabilitiesMask capabilities({ChatRoom::Capabilities::Basic, ChatRoom::Capabilities::OneToOne}); + auto params = ConferenceParams::fromCapabilities(capabilities, getSharedFromThis()); + shared_ptr<AbstractChatRoom> chatRoom = + d->searchChatRoom(params, conferenceId.getLocalAddress(), conferenceId.getPeerAddress(), {}); if (chatRoom) { return chatRoom; } - ChatRoom::CapabilitiesMask capabilities({ChatRoom::Capabilities::Basic, ChatRoom::Capabilities::OneToOne}); - auto params = ConferenceParams::fromCapabilities(capabilities, getSharedFromThis()); chatRoom = d->createBasicChatRoom(conferenceId, params); d->insertChatRoom(chatRoom); d->insertChatRoomWithDb(chatRoom); - + lInfo() << "Basic chat room [" << chatRoom << "] with id " << conferenceId << " has been successfully created"; return chatRoom; } shared_ptr<AbstractChatRoom> Core::getOrCreateBasicChatRoom(const std::shared_ptr<const Address> &localAddress, const std::shared_ptr<const Address> &peerAddress) { L_D(); - - shared_ptr<AbstractChatRoom> chatRoom = findOneToOneChatRoom(localAddress, peerAddress, true, false, false); - if (chatRoom) return chatRoom; - - ChatRoom::CapabilitiesMask capabilities({ChatRoom::Capabilities::Basic, ChatRoom::Capabilities::OneToOne}); - chatRoom = d->createBasicChatRoom(ConferenceId(peerAddress, (localAddress && localAddress->isValid() - ? localAddress - : d->getDefaultLocalAddress(peerAddress, false))), - ConferenceParams::fromCapabilities(capabilities, getSharedFromThis())); - d->insertChatRoom(chatRoom); - d->insertChatRoomWithDb(chatRoom); - - return chatRoom; + auto chatRoomLocalAddress = + (localAddress && localAddress->isValid()) ? localAddress : d->getDefaultLocalAddress(peerAddress, false); + auto conferenceIdParams = createConferenceIdParams(); + ConferenceId conferenceId(peerAddress, chatRoomLocalAddress, conferenceIdParams); + return getOrCreateBasicChatRoom(conferenceId); } shared_ptr<AbstractChatRoom> Core::getOrCreateBasicChatRoomFromUri(const std::string &localAddressUri, @@ -912,16 +824,17 @@ void Core::deleteChatRoom(const shared_ptr<AbstractChatRoom> &chatRoom) { auto core = chatRoom->getCore(); const ConferenceId &conferenceId = chatRoom->getConferenceId(); - lInfo() << "Trying to delete chat room with conference ID " << conferenceId << "."; + lInfo() << "Trying to delete chat room [" << chatRoom << "] with conference ID " << conferenceId << "."; - auto chatRoomInCoreMap = core->findChatRoom(conferenceId); + auto chatRoomInCoreMap = core->findChatRoom(conferenceId, false); if (chatRoomInCoreMap) { CorePrivate *d = core->getPrivate(); d->mConferenceById.erase(conferenceId); d->mChatRoomsById.erase(conferenceId); if (d->mainDb->isInitialized()) d->mainDb->deleteChatRoom(conferenceId); } else { - lError() << "Unable to delete chat room with conference ID " << conferenceId << " because it cannot be found."; + lError() << "Unable to delete chat room [" << chatRoom << "] with conference ID " << conferenceId + << " because it cannot be found."; } } @@ -951,30 +864,36 @@ void CorePrivate::stopChatMessagesAggregationTimer() { chatMessagesAggregationTimer = nullptr; } - for (const auto &chatRoom : q->getChatRooms()) { + for (const auto &chatRoom : q->getRawChatRoomList()) { chatRoom->notifyAggregatedChatMessages(); } chatMessagesAggregationBackgroundTask.stop(); } -bool Core::isCurrentlyAggregatingChatMessages() { +bool Core::isCurrentlyAggregatingChatMessages() const { L_D(); return d->chatMessagesAggregationTimer != nullptr; } -LinphoneReason -Core::handleChatMessagesAggregation(shared_ptr<AbstractChatRoom> chatRoom, SalOp *op, const SalMessage *sal_msg) { - L_D(); +bool Core::canAggregateChatMessages() const { LinphoneCore *cCore = getCCore(); - bool chatMessagesAggregationEnabled = !!linphone_core_get_chat_messages_aggregation_enabled(cCore); int chatMessagesAggregationDelay = linphone_config_get_int(linphone_core_get_config(cCore), "sip", "chat_messages_aggregation_delay", 0); lDebug() << "Chat messages aggregation enabled? " << chatMessagesAggregationEnabled << " with delay " << chatMessagesAggregationDelay; - if (chatMessagesAggregationEnabled && chatMessagesAggregationDelay > 0) { + return (chatMessagesAggregationEnabled && chatMessagesAggregationDelay > 0); +} + +LinphoneReason +Core::handleChatMessagesAggregation(shared_ptr<AbstractChatRoom> chatRoom, SalOp *op, const SalMessage *sal_msg) { + L_D(); + if (canAggregateChatMessages()) { + LinphoneCore *cCore = getCCore(); + int chatMessagesAggregationDelay = + linphone_config_get_int(linphone_core_get_config(cCore), "sip", "chat_messages_aggregation_delay", 0); if (!d->chatMessagesAggregationTimer) { d->chatMessagesAggregationTimer = cCore->sal->createTimer( [d]() -> bool { @@ -997,18 +916,18 @@ LinphoneReason Core::onSipMessageReceived(SalOp *op, const SalMessage *sal_msg) LinphoneCore *cCore = getCCore(); LinphoneReason reason = LinphoneReasonNotAcceptable; - std::shared_ptr<Address> peerAddress; - std::shared_ptr<Address> localAddress; + Address peerAddress; + Address localAddress; if (linphone_core_conference_server_enabled(cCore)) { - localAddress = peerAddress = Address::create(op->getTo()); + localAddress = peerAddress = Address(op->getToAddress()); } else { - peerAddress = Address::create(op->getFrom()); - localAddress = Address::create(op->getTo()); + peerAddress = Address(op->getFromAddress()); + localAddress = Address(op->getToAddress()); } - ConferenceId conferenceId{peerAddress, localAddress}; - shared_ptr<AbstractChatRoom> chatRoom = findChatRoom(conferenceId); + ConferenceId conferenceId(std::move(peerAddress), std::move(localAddress), createConferenceIdParams()); + shared_ptr<AbstractChatRoom> chatRoom = findChatRoom(conferenceId, false); if (chatRoom) { reason = handleChatMessagesAggregation(chatRoom, op, sal_msg); } else if (!linphone_core_conference_server_enabled(cCore)) { diff --git a/src/core/core-p.h b/src/core/core-p.h index a835db4649da69f567a0751e655a6a51f9255078..05264da6cd65415e4e6aaebc7cc9f827a1232760 100644 --- a/src/core/core-p.h +++ b/src/core/core-p.h @@ -116,10 +116,10 @@ public: void reloadRemoteContactDirectories(); // Base - std::shared_ptr<AbstractChatRoom> createServerChatRoom(const std::shared_ptr<Address> &conferenceFactoryUri, + std::shared_ptr<AbstractChatRoom> createServerChatRoom(const std::shared_ptr<const Address> &conferenceFactoryUri, SalCallOp *op, const std::shared_ptr<ConferenceParams> ¶ms); - std::shared_ptr<AbstractChatRoom> createClientChatRoom(const std::shared_ptr<Address> &conferenceFactoryUri, + std::shared_ptr<AbstractChatRoom> createClientChatRoom(const std::shared_ptr<const Address> &conferenceFactoryUri, const ConferenceId &conferenceId, SalCallOp *op, const std::shared_ptr<ConferenceParams> ¶ms); @@ -132,17 +132,14 @@ public: std::shared_ptr<AbstractChatRoom> createClientChatRoom(const std::string &subject, bool fallback, bool encrypted); std::shared_ptr<AbstractChatRoom> createChatRoom(const std::shared_ptr<ConferenceParams> ¶ms, - const std::shared_ptr<const Address> &localAddr, - const std::list<std::shared_ptr<const Address>> &participants); - std::shared_ptr<AbstractChatRoom> createChatRoom(const std::shared_ptr<ConferenceParams> ¶ms, - const std::list<std::shared_ptr<const Address>> &participants); + const std::list<std::shared_ptr<Address>> &participants); std::shared_ptr<AbstractChatRoom> createChatRoom(const std::string &subject, - const std::list<std::shared_ptr<const Address>> &participants); + const std::list<std::shared_ptr<Address>> &participants); std::shared_ptr<AbstractChatRoom> createChatRoom(const std::shared_ptr<ConferenceParams> ¶ms, - const std::shared_ptr<Address> &localAddr, - const std::shared_ptr<const Address> &participant); - std::shared_ptr<AbstractChatRoom> createChatRoom(const std::shared_ptr<const Address> &participant); + const std::shared_ptr<Address> &participant); + std::shared_ptr<AbstractChatRoom> createChatRoom(const std::shared_ptr<Address> &participant); + std::shared_ptr<AbstractChatRoom> searchChatRoom(const std::string identifier) const; std::shared_ptr<AbstractChatRoom> searchChatRoom(const std::shared_ptr<ConferenceParams> ¶ms, const std::shared_ptr<const Address> &localAddr, const std::shared_ptr<const Address> &remoteAddr, diff --git a/src/core/core.cpp b/src/core/core.cpp index 0b1e3fe5ebe744850eca40a41eac1e77f05fbb08..cc7ac2dac21f0071e4be9a47951de28f3f34fb97 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2024 Belledonne Communications SARL. + * Copyright (c) 2010-2025 Belledonne Communications SARL. * * This file is part of Liblinphone * (see https://gitlab.linphone.org/BC/public/liblinphone). @@ -54,6 +54,8 @@ #include "chat/encryption/lime-x3dh-encryption-engine.h" #endif // HAVE_LIME_X3DH #include "chat/encryption/lime-x3dh-server-engine.h" +#include "conference/conference-context.h" +#include "conference/conference-id-params.h" #include "conference/conference.h" #include "conference/handlers/client-conference-list-event-handler.h" #include "conference/handlers/server-conference-list-event-handler.h" @@ -229,6 +231,8 @@ void CorePrivate::init() { linphone_core_set_friends_database_path(lc, friendsDbPath.c_str()); } else lWarning() << "Friends database explicitely not requested"; } + } else { + lInfo() << "The Core was explicitely requested not to use any database"; } createConferenceCleanupTimer(); @@ -246,6 +250,10 @@ bool CorePrivate::listenerAlreadyRegistered(CoreListener *listener) const { } void CorePrivate::registerListener(CoreListener *listener) { + if (!listener) { + lError() << __func__ << " : Ignoring attempt to register a null listener"; + return; + } if (listenerAlreadyRegistered(listener)) return; listeners.push_back(listener); } @@ -341,7 +349,7 @@ void CorePrivate::deleteConferenceInfo(const std::shared_ptr<Address> &conferenc #ifdef HAVE_DB_STORAGE mainDb->deleteConferenceInfo(conferenceAddress); #endif // HAVE_DB_STORAGE - auto chatRoom = searchChatRoom(nullptr, nullptr, conferenceAddress, std::list<std::shared_ptr<Address>>()); + auto chatRoom = searchChatRoom(nullptr, nullptr, conferenceAddress, {}); if (chatRoom) { chatRoom->deleteFromDb(); } @@ -360,7 +368,7 @@ void CorePrivate::createConferenceCleanupTimer() { const auto conferenceIsEnded = conference->isConferenceEnded(); const auto conferenceHasNoParticipantDevices = (conference->getParticipantDevices(false).size() == 0); if (conferenceIsEnded && conferenceHasNoParticipantDevices) { - lInfo() << "Automatically terminating conference " << *conference->getConferenceAddress() + lInfo() << "Automatically terminating " << *conference << " because it is ended and no devices are left"; conference->terminate(); } @@ -956,7 +964,6 @@ void Core::enableLimeX3dh(bool enable) { } string dbAccess = getX3dhDbPath(); lInfo() << "Using [" << dbAccess << "] as lime database path"; - belle_http_provider_t *prov = linphone_core_get_http_provider(getCCore()); /* Both Lime and MainDb use soci as database engine. * However, the initialisation of backends must be done manually on platforms where soci is statically * linked. Lime does not do it. Doing it twice is risky - soci unloads the previously loaded backend. A @@ -964,11 +971,15 @@ void Core::enableLimeX3dh(bool enable) { * AbstractChatRoom::registerBackend() will do the job only once. */ AbstractDb::registerBackend(AbstractDb::Sqlite3); - LimeX3dhEncryptionEngine *engine = new LimeX3dhEncryptionEngine(dbAccess, prov, getSharedFromThis()); - setEncryptionEngine(engine); - d->registerListener(engine); - if (!hasSpec(Core::limeSpec)) { - addSpec(Core::limeSpec); + LimeX3dhEncryptionEngine *engine = new LimeX3dhEncryptionEngine(dbAccess, getSharedFromThis()); + if (engine->getEngineType() == EncryptionEngine::EngineType::LimeX3dh) { + setEncryptionEngine(engine); + d->registerListener(engine); + if (!hasSpec(Core::limeSpec)) { + addSpec(Core::limeSpec); + } + } else { // LimeX3DHEncryptionEngine creation failed + delete engine; } #else lWarning() << "Lime X3DH support is not available"; @@ -1679,7 +1690,7 @@ std::shared_ptr<ChatMessage> Core::findChatMessageFromCallId(const std::string & } void Core::handleIncomingMessageWaitingIndication(std::shared_ptr<Event> event, const Content *content) { - shared_ptr<Address> accountAddr = nullptr; + shared_ptr<const Address> accountAddr = nullptr; if (!content) { lWarning() << "MWI NOTIFY without body, doing nothing"; @@ -1882,7 +1893,8 @@ std::shared_ptr<Conference> Core::findConference(const std::shared_ptr<const Cal L_D(); for (const auto &[id, conference] : d->mConferenceById) { - if (session == conference->getMainSession()) { + const auto &conferenceSession = conference->getMainSession(); + if (conferenceSession && (session == conferenceSession)) { return conference; } } @@ -1908,7 +1920,7 @@ std::shared_ptr<Conference> Core::findConference(const ConferenceId &conferenceI L_D(); try { auto conference = d->mConferenceById.at(conferenceId); - lInfo() << "Found conference " << conference << " in RAM with conference ID " << conferenceId << "."; + lInfo() << "Found " << *conference << " in RAM with conference ID " << conferenceId << "."; return conference; } catch (const out_of_range &) { if (logIfNotFound) { @@ -1925,7 +1937,7 @@ void Core::insertConference(const shared_ptr<Conference> conference) { const ConferenceId &conferenceId = conference->getConferenceId(); if (!conferenceId.isValid()) { - lInfo() << "Attempting to insert conference " << conference << " with invalid conference ID " << conferenceId; + lInfo() << "Attempting to insert " << *conference << " with invalid conference ID " << conferenceId; return; } @@ -1934,12 +1946,12 @@ void Core::insertConference(const shared_ptr<Conference> conference) { if (!isStartup) { if (conference->getCurrentParams()->chatEnabled()) { // Handling of chat room exhume - const auto &chatRoom = findChatRoom(conferenceId); + const auto &chatRoom = findChatRoom(conferenceId, false); if (chatRoom) { conf = chatRoom->getConference(); } } else { - conf = findConference(conferenceId); + conf = findConference(conferenceId, false); } // When starting the LinphoneCore, it may happen to have 2 audio video conferences or chat room that have the // same conference ID apart from the GRUU which is not taken into the account for the comparison. In such a @@ -1948,7 +1960,7 @@ void Core::insertConference(const shared_ptr<Conference> conference) { L_ASSERT(conf == nullptr || conf == conference); } if ((conf == nullptr) || (conf != conference)) { - lInfo() << "Insert conference " << conference << " in RAM with conference ID " << conferenceId << "."; + lInfo() << "Insert " << *conference << " in RAM with conference ID " << conferenceId << "."; d->mConferenceById.insert_or_assign(conferenceId, conference); } } @@ -1957,64 +1969,33 @@ void Core::deleteConference(const ConferenceId &conferenceId) { L_D(); auto it = d->mConferenceById.find(conferenceId); if (it != d->mConferenceById.cend()) { - lInfo() << "Delete conference in RAM with conference ID " << conferenceId << "."; + lInfo() << "Delete " << *(it->second) << " in RAM with conference ID " << conferenceId << "."; d->mConferenceById.erase(it); } } void Core::deleteConference(const shared_ptr<const Conference> &conference) { - L_D(); const ConferenceId &conferenceId = conference->getConferenceId(); - auto it = d->mConferenceById.find(conferenceId); - if (it != d->mConferenceById.cend()) { - lInfo() << "Delete conference in RAM with conference ID " << conferenceId << "."; - d->mConferenceById.erase(it); - } + deleteConference(conferenceId); } -shared_ptr<Conference> Core::searchConference(const shared_ptr<ConferenceParams> ¶ms, - const std::shared_ptr<const Address> &localAddress, - const std::shared_ptr<const Address> &remoteAddress, - const std::list<std::shared_ptr<Address>> &participants) const { +std::shared_ptr<Conference> Core::searchConference(const std::shared_ptr<ConferenceParams> ¶ms, + const std::shared_ptr<const Address> &localAddress, + const std::shared_ptr<const Address> &remoteAddress, + const std::list<std::shared_ptr<Address>> &participants) const { L_D(); - auto localAddressUri = (localAddress) ? localAddress->getUriWithoutGruu() : Address(); - auto remoteAddressUri = (remoteAddress) ? remoteAddress->getUriWithoutGruu() : Address(); - const auto it = std::find_if(d->mConferenceById.begin(), d->mConferenceById.end(), [&](const auto &p) { - // p is of type std::pair<ConferenceId, std::shared_ptr<Conference> - const auto &conference = p.second; - const ConferenceId &conferenceId = conference->getConferenceId(); - auto curLocalAddress = - (conferenceId.getLocalAddress()) ? conferenceId.getLocalAddress()->getUriWithoutGruu() : Address(); - if (localAddressUri.isValid() && (localAddressUri != curLocalAddress)) return false; - - auto curPeerAddress = - (conferenceId.getPeerAddress()) ? conferenceId.getPeerAddress()->getUriWithoutGruu() : Address(); - if (remoteAddressUri.isValid() && (remoteAddressUri != curPeerAddress)) return false; - - // Check parameters only if pointer provided as argument is not null - if (params) { - const auto &conferenceParams = conference->getCurrentParams(); - if (!params->getUtf8Subject().empty() && - (params->getUtf8Subject().compare(conferenceParams->getUtf8Subject()) != 0)) - return false; - if (params->chatEnabled() != conferenceParams->chatEnabled()) return false; - if (params->audioEnabled() != conferenceParams->audioEnabled()) return false; - if (params->videoEnabled() != conferenceParams->videoEnabled()) return false; - if (params->localParticipantEnabled() != conferenceParams->localParticipantEnabled()) return false; - } - - // Check participants only if list provided as argument is not empty - bool participantListMatch = true; - if (participants.empty() == false) { - const std::list<std::shared_ptr<Participant>> &confParticipants = conference->getParticipants(); - participantListMatch = - equal(participants.cbegin(), participants.cend(), confParticipants.cbegin(), confParticipants.cend(), - [](const auto &p1, const auto &p2) { return (p1->weakEqual(*p2->getAddress())); }); - } - return participantListMatch; - }); - - shared_ptr<Conference> conference = nullptr; + ConferenceContext referenceConferenceContext(params, localAddress, remoteAddress, participants); + const auto it = std::find_if( + d->mConferenceById.begin(), d->mConferenceById.end(), [&referenceConferenceContext](const auto &p) { + // p is of type std::pair<ConferenceId, std::shared_ptr<Conference> + const auto &conference = p.second; + const ConferenceId &conferenceId = conference->getConferenceId(); + ConferenceContext conferenceContext(conference->getCurrentParams(), conferenceId.getLocalAddress(), + conferenceId.getPeerAddress(), conference->getParticipantAddresses()); + return (referenceConferenceContext == conferenceContext); + }); + + std::shared_ptr<Conference> conference; if (it != d->mConferenceById.cend()) { conference = it->second; } @@ -2022,6 +2003,14 @@ shared_ptr<Conference> Core::searchConference(const shared_ptr<ConferenceParams> return conference; } +std::shared_ptr<Conference> Core::searchConference(const std::string identifier) const { + auto [localAddress, peerAddress] = ConferenceId::parseIdentifier(identifier); + if (!localAddress || !localAddress->isValid() || !peerAddress || !peerAddress->isValid()) { + return nullptr; + } + return searchConference(nullptr, localAddress, peerAddress, {}); +} + shared_ptr<Conference> Core::searchConference(const std::shared_ptr<const Address> &conferenceAddress) const { L_D(); @@ -2064,7 +2053,6 @@ void Core::addConferenceScheduler(const std::shared_ptr<ConferenceScheduler> &sc } shared_ptr<CallSession> Core::createOrUpdateConferenceOnServer(const std::shared_ptr<ConferenceParams> &confParams, - const std::shared_ptr<const Address> &localAddr, const std::list<Address> &participants, const std::shared_ptr<Address> &confAddr, std::shared_ptr<CallSessionListener> listener) { @@ -2076,7 +2064,10 @@ shared_ptr<CallSession> Core::createOrUpdateConferenceOnServer(const std::shared MediaSessionParams params; params.initDefault(getSharedFromThis(), LinphoneCallOutgoing); - const auto &account = confParams->getAccount(); + auto account = confParams->getAccount(); + if (!account) { + account = getDefaultAccount(); + } params.setAccount(account); std::shared_ptr<Address> conferenceFactoryUri; @@ -2084,14 +2075,15 @@ shared_ptr<CallSession> Core::createOrUpdateConferenceOnServer(const std::shared if (confAddr) { conferenceFactoryUri = confAddr; } else { - std::shared_ptr<Address> conferenceFactoryUriRef; + std::shared_ptr<const Address> conferenceFactoryUriRef; if (mediaEnabled) { - conferenceFactoryUriRef = Core::getAudioVideoConferenceFactoryAddress(getSharedFromThis(), localAddr); + conferenceFactoryUriRef = Core::getAudioVideoConferenceFactoryAddress(getSharedFromThis(), account); } else { - conferenceFactoryUriRef = Core::getConferenceFactoryAddress(getSharedFromThis(), localAddr); + conferenceFactoryUriRef = account->getAccountParams()->getConferenceFactoryAddress(); } if (!conferenceFactoryUriRef || !conferenceFactoryUriRef->isValid()) { - lWarning() << "Not creating conference: no conference factory uri for local address [" << *localAddr << "]"; + lWarning() << "Not creating conference: no conference factory uri for local address [" + << *account->getAccountParams()->getIdentityAddress() << "]"; return nullptr; } conferenceFactoryUri = conferenceFactoryUriRef->clone()->toSharedPtr(); @@ -2133,8 +2125,10 @@ shared_ptr<CallSession> Core::createOrUpdateConferenceOnServer(const std::shared params.getPrivate()->setEndTime(confParams->getEndTime()); params.getPrivate()->setDescription(confParams->getDescription()); params.getPrivate()->setConferenceCreation(true); + params.getPrivate()->disableRinging(true); params.getPrivate()->enableToneIndications(false); + const auto &localAddr = account->getAccountParams()->getIdentityAddress(); auto participant = Participant::create(nullptr, localAddr); auto session = participant->createSession(getSharedFromThis(), ¶ms, true); session->addListener(listener); @@ -2180,7 +2174,7 @@ const std::list<LinphoneMediaEncryption> Core::getSupportedMediaEncryptions() co return encEnumList; } -std::shared_ptr<Address> +std::shared_ptr<const Address> Core::getAudioVideoConferenceFactoryAddress(const std::shared_ptr<Core> &core, const std::shared_ptr<const Address> &localAddress) { auto account = core->lookupKnownAccount(localAddress, true); @@ -2190,8 +2184,8 @@ Core::getAudioVideoConferenceFactoryAddress(const std::shared_ptr<Core> &core, } else return getAudioVideoConferenceFactoryAddress(core, account); } -std::shared_ptr<Address> Core::getAudioVideoConferenceFactoryAddress(const std::shared_ptr<Core> &core, - const std::shared_ptr<Account> &account) { +std::shared_ptr<const Address> Core::getAudioVideoConferenceFactoryAddress(const std::shared_ptr<Core> &core, + const std::shared_ptr<Account> &account) { auto address = account->getAccountParams()->getAudioVideoConferenceFactoryAddress(); if (address == nullptr) { const auto &conferenceFactoryUri = getConferenceFactoryAddress(core, account); @@ -2947,7 +2941,8 @@ bool Core::refreshTokens(const std::shared_ptr<AuthInfo> &ai) { return; } } - lError() << "Token refreshing failed."; + lError() << "Token refreshing failed, invoking authentication_requested..."; + linphone_core_notify_authentication_requested(getCCore(), ai->toC(), LinphoneAuthBearer); }); } catch (const std::exception &e) { lError() << "Cannot refresh access token: " << e.what(); @@ -2982,8 +2977,7 @@ shared_ptr<EktInfo> Core::createEktInfoFromXml(const std::string &xmlBody) const return ei; } - auto &sSpi = crypto->getSspi(); - if (sSpi) { + if (const auto &sSpi = crypto->getSspi()) { ei->setSSpi(static_cast<uint16_t>(sSpi)); } else { lError() << "Core::createEktInfoFromXml : Missing sSPI"; @@ -3007,11 +3001,20 @@ shared_ptr<EktInfo> Core::createEktInfoFromXml(const std::string &xmlBody) const return ei; } -string Core::createXmlFromEktInfo(const shared_ptr<const EktInfo> &ei) const { - auto account = getDefaultAccount(); - auto addr = account->getContactAddress(); +string Core::createXmlFromEktInfo(const shared_ptr<const EktInfo> &ei, const shared_ptr<const Account> &account) const { + stringstream xmlBody; + shared_ptr<Address> addr = nullptr; + if (account) { + addr = account->getContactAddress(); + } else if (getDefaultAccount()) { + lWarning() << __func__ << " : The account passed as an argument is null, using the default account"; + addr = getDefaultAccount()->getContactAddress(); + } else { + lError() << __func__ << " : No valid account found, return an empty XML body"; + return xmlBody.str(); + } - CryptoType crypto = CryptoType(ei->getSSpi(), addr->asStringUriOnly()); + auto crypto = CryptoType(ei->getSSpi(), addr->asStringUriOnly()); if (ei->getFrom()) crypto.setFrom(ei->getFrom()->asStringUriOnly()); @@ -3026,12 +3029,11 @@ string Core::createXmlFromEktInfo(const shared_ptr<const EktInfo> &ei) const { for (const auto &cipher : cipherMap) { vector<uint8_t> cipherVec(ei->getCiphers()->getBuffer(cipher.first)->getContent().begin(), ei->getCiphers()->getBuffer(cipher.first)->getContent().end()); - EncryptedektType ekt = EncryptedektType(bctoolbox::encodeBase64(cipherVec), cipher.first); + auto ekt = EncryptedektType(bctoolbox::encodeBase64(cipherVec), cipher.first); crypto.getCiphers()->getEncryptedekt().push_back(ekt); } } - stringstream xmlBody; Xsd::XmlSchema::NamespaceInfomap map; map[""].name = "linphone:xml:ns:ekt-linphone-extension"; serializeCrypto(xmlBody, crypto, map); @@ -3039,6 +3041,10 @@ string Core::createXmlFromEktInfo(const shared_ptr<const EktInfo> &ei) const { } #endif // HAVE_ADVANCED_IM +ConferenceIdParams Core::createConferenceIdParams() const { + return ConferenceIdParams(getSharedFromThis()); +} + void Core::addFriendList(const shared_ptr<FriendList> &list) { L_D(); diff --git a/src/core/core.h b/src/core/core.h index d552aa783a4e1c0040b703d947b7f720f9e7880a..4792c874402105b582bd3abab3a3e8068aa7f930 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2023 Belledonne Communications SARL. + * Copyright (c) 2010-2025 Belledonne Communications SARL. * * This file is part of Liblinphone * (see https://gitlab.linphone.org/BC/public/liblinphone). @@ -36,6 +36,7 @@ #include "conference/encryption/ekt-info.h" #endif // HAVE_ADVANCED_IM #include "event-log/event-log.h" +#include "event/event-publish.h" #include "friend/friend-list.h" #include "linphone/enums/c-enums.h" #include "linphone/types.h" @@ -62,6 +63,7 @@ class CallLog; class CallSession; class Conference; class ConferenceId; +class ConferenceIdParams; class ConferenceInfo; class Participant; class CallSessionListener; @@ -123,15 +125,15 @@ public: // Return a new Core instance. Entry point of Linphone. static std::shared_ptr<Core> create(LinphoneCore *cCore); - static std::shared_ptr<Address> getConferenceFactoryAddress(const std::shared_ptr<Core> &core, - const std::shared_ptr<const Address> &localAddress); - static std::shared_ptr<Address> getConferenceFactoryAddress(const std::shared_ptr<Core> &core, - const std::shared_ptr<Account> &account); - static std::shared_ptr<Address> + static std::shared_ptr<const Address> + getConferenceFactoryAddress(const std::shared_ptr<Core> &core, const std::shared_ptr<const Address> &localAddress); + static std::shared_ptr<const Address> getConferenceFactoryAddress(const std::shared_ptr<Core> &core, + const std::shared_ptr<Account> &account); + static std::shared_ptr<const Address> getAudioVideoConferenceFactoryAddress(const std::shared_ptr<Core> &core, const std::shared_ptr<const Address> &localAddress); - static std::shared_ptr<Address> getAudioVideoConferenceFactoryAddress(const std::shared_ptr<Core> &core, - const std::shared_ptr<Account> &account); + static std::shared_ptr<const Address> + getAudioVideoConferenceFactoryAddress(const std::shared_ptr<Core> &core, const std::shared_ptr<Account> &account); // --------------------------------------------------------------------------- // Application lifecycle. @@ -190,12 +192,6 @@ public: std::shared_ptr<AbstractChatRoom> findChatRoom(const ConferenceId &conferenceId, bool logIfNotFound = true) const; std::list<std::shared_ptr<AbstractChatRoom>> findChatRooms(const std::shared_ptr<Address> &peerAddress) const; - std::shared_ptr<AbstractChatRoom> findOneToOneChatRoom(const std::shared_ptr<const Address> &localAddress, - const std::shared_ptr<const Address> &participantAddress, - bool basicOnly, - bool conferenceOnly, - bool encrypted) const; - std::shared_ptr<AbstractChatRoom> createClientChatRoom(const std::string &subject, bool fallback = true); std::shared_ptr<AbstractChatRoom> createClientChatRoom(const std::string &subject, LinphoneChatRoomCapabilitiesMask capabilities, @@ -241,6 +237,7 @@ public: // Conference. // --------------------------------------------------------------------------- + ConferenceIdParams createConferenceIdParams() const; void setConferenceCleanupPeriod(long seconds); long getConferenceCleanupPeriod() const; void setAccountDeletionTimeout(unsigned int seconds); @@ -257,6 +254,7 @@ public: const std::shared_ptr<const Address> &remoteAddress, const std::list<std::shared_ptr<Address>> &participants) const; std::shared_ptr<Conference> searchConference(const std::shared_ptr<const Address> &conferenceAddress) const; + std::shared_ptr<Conference> searchConference(const std::string identifier) const; // --------------------------------------------------------------------------- // Paths. @@ -367,7 +365,6 @@ public: const std::list<LinphoneMediaEncryption> getSupportedMediaEncryptions() const; std::shared_ptr<CallSession> createOrUpdateConferenceOnServer(const std::shared_ptr<ConferenceParams> &confParams, - const std::shared_ptr<const Address> &localAddr, const std::list<Address> &participants, const std::shared_ptr<Address> &confAddr, std::shared_ptr<CallSessionListener> listener); @@ -378,7 +375,8 @@ public: void removeConferenceScheduler(const std::shared_ptr<ConferenceScheduler> &scheduler); void addConferenceScheduler(const std::shared_ptr<ConferenceScheduler> &scheduler); - bool isCurrentlyAggregatingChatMessages(); + bool canAggregateChatMessages() const; + bool isCurrentlyAggregatingChatMessages() const; // --------------------------------------------------------------------------- // Signal informations // --------------------------------------------------------------------------- @@ -409,7 +407,8 @@ public: // --------------------------------------------------------------------------- #ifdef HAVE_ADVANCED_IM std::shared_ptr<EktInfo> createEktInfoFromXml(const std::string &xmlBody) const; - std::string createXmlFromEktInfo(const std::shared_ptr<const EktInfo> &ei) const; + std::string createXmlFromEktInfo(const std::shared_ptr<const EktInfo> &ei, + const std::shared_ptr<const Account> &account) const; #endif // HAVE_ADVANCED_IM // --------------------------------------------------------------------------- diff --git a/src/core/platform-helpers/android-platform-helpers.cpp b/src/core/platform-helpers/android-platform-helpers.cpp index f2d1a37019dfb779fcaab37559681c6d34a33c2b..3629a46957f90949a23d2f2a9691be5d81e68ab3 100644 --- a/src/core/platform-helpers/android-platform-helpers.cpp +++ b/src/core/platform-helpers/android-platform-helpers.cpp @@ -88,7 +88,7 @@ public: void onRecordingStarted() const override; void onRecordingPaused() const override; - bool isRingingAllowed() const override; + bool isPlayingSoundAllowed() const override; void stopRinging() const override; void setDeviceRotation(int orientation) const override; @@ -145,7 +145,7 @@ private: jmethodID mStopAutoIterateId = nullptr; jmethodID mSetAudioManagerCommunicationMode = nullptr; jmethodID mSetAudioManagerNormalMode = nullptr; - jmethodID mIsRingingAllowed = nullptr; + jmethodID mIsPlayingSoundAllowed = nullptr; jmethodID mStopRingingId = nullptr; jmethodID mDestroyPlatformHelperId = nullptr; @@ -235,7 +235,7 @@ AndroidPlatformHelpers::AndroidPlatformHelpers(std::shared_ptr<LinphonePrivate:: mStopAutoIterateId = getMethodId(env, klass, "stopAutoIterate", "()V"); mSetAudioManagerCommunicationMode = getMethodId(env, klass, "setAudioManagerInCommunicationMode", "()V"); mSetAudioManagerNormalMode = getMethodId(env, klass, "setAudioManagerInNormalMode", "()V"); - mIsRingingAllowed = getMethodId(env, klass, "isRingingAllowed", "()Z"); + mIsPlayingSoundAllowed = getMethodId(env, klass, "isPlayingSoundAllowed", "()Z"); mStopRingingId = getMethodId(env, klass, "stopRinging", "()V"); mDestroyPlatformHelperId = getMethodId(env, klass, "destroy", "()V"); @@ -594,11 +594,11 @@ void AndroidPlatformHelpers::onRecordingStarted() const { void AndroidPlatformHelpers::onRecordingPaused() const { } -bool AndroidPlatformHelpers::isRingingAllowed() const { +bool AndroidPlatformHelpers::isPlayingSoundAllowed() const { JNIEnv *env = ms_get_jni_env(); if (env) { if (mJavaHelper) { - return env->CallBooleanMethod(mJavaHelper, mIsRingingAllowed); + return env->CallBooleanMethod(mJavaHelper, mIsPlayingSoundAllowed); } } return false; diff --git a/src/core/platform-helpers/ios-platform-helpers.mm b/src/core/platform-helpers/ios-platform-helpers.mm index e508f8af0fb1e8b739a828b554ec28919ebfc62f..0fcd4dc014dc28db36ed359afd5f9d30d2af2f1b 100644 --- a/src/core/platform-helpers/ios-platform-helpers.mm +++ b/src/core/platform-helpers/ios-platform-helpers.mm @@ -33,6 +33,7 @@ #include <AVFoundation/AVAudioSession.h> #include "core/core.h" +#include "core/core-p.h" #include "linphone/core.h" #include "linphone/utils/general.h" #include "linphone/utils/utils.h" @@ -53,10 +54,14 @@ using namespace std; LINPHONE_BEGIN_NAMESPACE -class IosPlatformHelpers : public MacPlatformHelpers { +class IosPlatformHelpers : public MacPlatformHelpers, public CoreListener { public: IosPlatformHelpers (std::shared_ptr<LinphonePrivate::Core> core, void *systemContext); ~IosPlatformHelpers () { + try { + L_GET_PRIVATE(getCore())->unregisterListener(this); + } catch (...) { + } [mAppDelegate dealloc]; } @@ -110,7 +115,8 @@ private: void kickOffConnectivity(); void bgTaskTimeout (); static void sBgTaskTimeout (void *data); - + void onGlobalStateChanged (LinphoneGlobalState state) override; + long int mCpuLockTaskId; int mCpuLockCount; SCNetworkReachabilityRef reachabilityRef = NULL; @@ -133,6 +139,7 @@ IosPlatformHelpers::IosPlatformHelpers (std::shared_ptr<LinphonePrivate::Core> c if (mUseAppDelgate) { mAppDelegate = [[IosAppDelegate alloc] initWithCore:core]; } + L_GET_PRIVATE(core)->registerListener(this); ms_message("IosPlatformHelpers is fully initialised"); } @@ -152,6 +159,16 @@ void IosPlatformHelpers::stop () { ms_message("IosPlatformHelpers is fully stopped"); } +// Make sure that we register for push notification token after the core is GlobalOn +// in order to avoid push token being erased by remote provisioning +void IosPlatformHelpers::onGlobalStateChanged (LinphoneGlobalState state) { + if (state == LinphoneGlobalOn) { + if (mUseAppDelgate && linphone_core_is_push_notification_enabled(getCore()->getCCore())) { + [mAppDelegate registerForPush]; + } + } +} + void IosPlatformHelpers::didRegisterForRemotePush(void *token) { [mAppDelegate didRegisterForRemotePush:(NSData *)token]; } @@ -255,9 +272,6 @@ void IosPlatformHelpers::onLinphoneCoreStart(bool monitoringEnabled) { if (monitoringEnabled) { startNetworkMonitoring(); } - if (mUseAppDelgate && linphone_core_is_push_notification_enabled(getCore()->getCCore())) { - [mAppDelegate registerForPush]; - } if (mUseAppDelgate && linphone_core_is_auto_iterate_enabled(getCore()->getCCore())) { enableAutoIterate(TRUE); } else { diff --git a/src/core/platform-helpers/platform-helpers.h b/src/core/platform-helpers/platform-helpers.h index c3b2c47aaf29036baaf328c467502b793fea52ec..937bb5b35f7008649541219867d929f46b6a74ed 100644 --- a/src/core/platform-helpers/platform-helpers.h +++ b/src/core/platform-helpers/platform-helpers.h @@ -118,7 +118,7 @@ public: virtual void onRecordingStarted() const = 0; virtual void onRecordingPaused() const = 0; - virtual bool isRingingAllowed() const = 0; + virtual bool isPlayingSoundAllowed() const = 0; virtual void stopRinging() const = 0; virtual void setDeviceRotation(int orientation) const = 0; @@ -191,19 +191,19 @@ public: void restorePreviousAudioRoute() override; void start(BCTBX_UNUSED(std::shared_ptr<LinphonePrivate::Core> core)) override{}; - void stop(void) override{}; + void stop(void) override {}; void didRegisterForRemotePush(BCTBX_UNUSED(void *token)) override{}; void didRegisterForRemotePushWithStringifiedToken(BCTBX_UNUSED(const char *tokenStr)) override{}; void setPushAndAppDelegateDispatchQueue(BCTBX_UNUSED(void *dispatch_queue)) override{}; void enableAutoIterate(BCTBX_UNUSED(bool autoIterateEnabled)) override{}; - void onRecordingStarted() const override{}; - void onRecordingPaused() const override{}; - bool isRingingAllowed() const override { + void onRecordingStarted() const override {}; + void onRecordingPaused() const override {}; + bool isPlayingSoundAllowed() const override { return true; }; - void stopRinging() const override{}; + void stopRinging() const override {}; void setDeviceRotation(BCTBX_UNUSED(int orientation)) const override{}; protected: diff --git a/src/db/main-db-event-key.cpp b/src/db/main-db-event-key.cpp index 525b93fda81846d859adfe7bbae99d6b7c342c00..37dccaf04efaa36263b200d217b855d828bb8095 100644 --- a/src/db/main-db-event-key.cpp +++ b/src/db/main-db-event-key.cpp @@ -31,7 +31,7 @@ LINPHONE_BEGIN_NAMESPACE // ----------------------------------------------------------------------------- -MainDbEventKey::MainDbEventKey() : MainDbKey(){}; +MainDbEventKey::MainDbEventKey() : MainDbKey() {}; MainDbEventKey::MainDbEventKey(const shared_ptr<Core> &core, long long storageId) : MainDbKey(core, storageId) { } @@ -43,7 +43,15 @@ MainDbEventKey::~MainDbEventKey() { void MainDbEventKey::resetStorageId() { L_D(); - if (isValid()) d->core.lock()->getPrivate()->mainDb->getPrivate()->storageIdToEvent.erase(d->storageId); + if (isValid()) { + auto core = d->core.lock(); + if (core) { + auto &db = core->getPrivate()->mainDb; + if (db) { + db->getPrivate()->storageIdToEvent.erase(d->storageId); + } + } + } d->storageId = -1; } diff --git a/src/db/main-db-p.h b/src/db/main-db-p.h index fd273bb7550f6841587d884a6a22a55b27a50f00..d15f4a434c8864359536dec90c9ea5d2ace6e614 100644 --- a/src/db/main-db-p.h +++ b/src/db/main-db-p.h @@ -56,7 +56,9 @@ private: // --------------------------------------------------------------------------- // Low level API. // --------------------------------------------------------------------------- + long long insertSipAddress(const Address &address); long long insertSipAddress(const std::shared_ptr<Address> &address); + long long insertSipAddress(const std::string &sipAddress, const std::string &displayName); void insertContent(long long chatMessageId, const Content &content); long long insertContentType(const std::string &contentType); long long @@ -105,6 +107,7 @@ private: long long insertOrUpdateFriendList(const std::shared_ptr<FriendList> &list); long long insertOrUpdateDevice(const std::shared_ptr<Address> &addressWithGruu, const std::string &displayName); + long long selectSipAddressId(const Address &address, const bool caseSensitive) const; long long selectSipAddressId(const std::string &sipAddress, const bool caseSensitive) const; long long selectSipAddressId(const std::shared_ptr<Address> &address, const bool caseSensitive) const; std::string selectSipAddressFromId(long long sipAddressId) const; @@ -257,9 +260,9 @@ private: // --------------------------------------------------------------------------- #ifdef HAVE_DB_STORAGE - void importLegacyFriends(DbSession &inDbSession); - void importLegacyHistory(DbSession &inDbSession); - void importLegacyCallLogs(DbSession &inDbSession); + bool importLegacyFriends(DbSession &inDbSession); + bool importLegacyHistory(DbSession &inDbSession); + bool importLegacyCallLogs(DbSession &inDbSession); #endif // --------------------------------------------------------------------------- diff --git a/src/db/main-db.cpp b/src/db/main-db.cpp index a6e6c122758723b6e82c705a4a923ecc3756027c..f978e0d5c59a0100e037858d851c3373db210138 100644 --- a/src/db/main-db.cpp +++ b/src/db/main-db.cpp @@ -289,14 +289,14 @@ buildSqlEventFilter(const list<MainDb::Filter> &filters, MainDb::FilterMask mask shared_ptr<AbstractChatRoom> MainDbPrivate::findChatRoom(const ConferenceId &conferenceId) const { L_Q(); - shared_ptr<AbstractChatRoom> chatRoom = q->getCore()->findChatRoom(conferenceId); + shared_ptr<AbstractChatRoom> chatRoom = q->getCore()->findChatRoom(conferenceId, false); if (!chatRoom) lError() << "Unable to find chat room: " << conferenceId << "."; return chatRoom; } shared_ptr<Conference> MainDbPrivate::findConference(const ConferenceId &conferenceId) const { L_Q(); - shared_ptr<Conference> conference = q->getCore()->findConference(conferenceId); + shared_ptr<Conference> conference = q->getCore()->findConference(conferenceId, false); if (!conference) lError() << "Unable to find audio video conference: " << conferenceId << "."; return conference; } @@ -304,15 +304,29 @@ shared_ptr<Conference> MainDbPrivate::findConference(const ConferenceId &confere // Low level API. // ----------------------------------------------------------------------------- -long long MainDbPrivate::insertSipAddress(BCTBX_UNUSED(const std::shared_ptr<Address> &address)) { -#ifdef HAVE_DB_STORAGE - if (!address) { +long long MainDbPrivate::insertSipAddress(const Address &address) { + if (!address.isValid()) { + return -1; + } + // This is a hack, because all addresses don't print their parameters in the same order. + const string sipAddress = address.toStringUriOnlyOrdered(); + const string displayName = address.getDisplayName(); + return insertSipAddress(sipAddress, displayName); +} + +long long MainDbPrivate::insertSipAddress(const std::shared_ptr<Address> &address) { + if (!address || !address->isValid()) { return -1; } // This is a hack, because all addresses don't print their parameters in the same order. const string sipAddress = address->toStringUriOnlyOrdered(); const string displayName = address->getDisplayName(); + return insertSipAddress(sipAddress, displayName); +} +long long MainDbPrivate::insertSipAddress(BCTBX_UNUSED(const std::string &sipAddress), + BCTBX_UNUSED(const std::string &displayName)) { +#ifdef HAVE_DB_STORAGE long long sipAddressId = selectSipAddressId(sipAddress, true); if (sipAddressId < 0) { lInfo() << "Insert new sip address in database: `" << sipAddress << "`."; @@ -418,7 +432,8 @@ long long MainDbPrivate::insertChatRoom(const shared_ptr<AbstractChatRoom> &chat L_Q(); if (q->isInitialized()) { const ConferenceId &conferenceId = chatRoom->getConferenceId(); - const long long &peerSipAddressId = insertSipAddress(conferenceId.getPeerAddress()); + const auto &peerAddress = conferenceId.getPeerAddress(); + const long long &peerSipAddressId = insertSipAddress(peerAddress); const long long &localSipAddressId = insertSipAddress(conferenceId.getLocalAddress()); long long chatRoomId = selectChatRoomId(peerSipAddressId, localSipAddressId); @@ -448,7 +463,8 @@ long long MainDbPrivate::insertChatRoom(const shared_ptr<AbstractChatRoom> &chat const string &subject = chatRoomParams->getUtf8Subject(); int ephemeralEnabled = chatRoom->ephemeralEnabled() ? 1 : 0; long ephemeralLifeTime = chatRoom->getEphemeralLifetime(); - const long long &dbConferenceInfoId = selectConferenceInfoId(peerSipAddressId); + const long long peerSipAddressNoGruuId = selectSipAddressId(peerAddress->getUriWithoutGruu(), true); + const long long &dbConferenceInfoId = selectConferenceInfoId(peerSipAddressNoGruuId); const long long conferenceInfoId = (dbConferenceInfoId <= 0) ? 0 : dbConferenceInfoId; *dbSession.getBackendSession() << "INSERT INTO chat_room (" " peer_sip_address_id, local_sip_address_id, creation_time," @@ -603,7 +619,7 @@ long long MainDbPrivate::insertConferenceInfo(const std::shared_ptr<ConferenceIn if (!organizerAddressOk || !conferenceUriOk) { const auto organizerAddressString = organizerAddressOk ? organizerAddress->toString() : std::string("sip:"); const auto conferenceUriString = conferenceUriOk ? conferenceUri->toString() : std::string("sip:"); - lError() << "Trying to insert a Conference Info without a valid organizer iSP address ( " + lError() << "Trying to insert a Conference Info without a valid organizer SIP address ( " << organizerAddressString << ") or URI ( " << conferenceUriString << ")!"; return -1; } @@ -628,7 +644,7 @@ long long MainDbPrivate::insertConferenceInfo(const std::shared_ptr<ConferenceIn ConferenceInfo::participant_list_t dbParticipantList; if (conferenceInfoId >= 0) { // The conference info is already stored in DB, but still update it some information might have changed - lInfo() << "Update conferenceInfo in database: " << conferenceInfoId << "."; + lInfo() << "Update " << *conferenceInfo << " in database: id " << conferenceInfoId << "."; if (oldConferenceInfo) { dbParticipantList = oldConferenceInfo->getParticipants(); } @@ -651,8 +667,6 @@ long long MainDbPrivate::insertConferenceInfo(const std::shared_ptr<ConferenceIn soci::use(subject), soci::use(description), soci::use(state), soci::use(sequence), soci::use(uid), soci::use(security_level), soci::use(conferenceInfoId); } else { - lInfo() << "Insert new conference info in database."; - *dbSession.getBackendSession() << "INSERT INTO conference_info (audio, video, chat, ccmp_uri, organizer_sip_address_id, " "uri_sip_address_id, start_time, duration, subject, description, state, ics_sequence, ics_uid, " @@ -665,6 +679,7 @@ long long MainDbPrivate::insertConferenceInfo(const std::shared_ptr<ConferenceIn soci::use(uid), soci::use(security_level); conferenceInfoId = dbSession.getLastInsertId(); + lInfo() << "Insert new " << *conferenceInfo << " in database: id " << conferenceInfoId << "."; } const bool isOrganizerAParticipant = conferenceInfo->hasParticipant(organizer); @@ -1022,7 +1037,7 @@ long long MainDbPrivate::insertOrUpdateDevice(const std::shared_ptr<Address> &ad deviceAddressId = insertSipAddress(addressWithGruu); } - auto withoutGruu = Address::create(addressWithGruu->getUriWithoutGruu()); + auto withoutGruu = addressWithGruu->getUriWithoutGruu(); long long sipAaddressId = selectSipAddressId(withoutGruu, false); if (sipAaddressId <= 0) { sipAaddressId = insertSipAddress(withoutGruu); @@ -1057,6 +1072,16 @@ long long MainDbPrivate::insertOrUpdateDevice(const std::shared_ptr<Address> &ad // ----------------------------------------------------------------------------- +long long MainDbPrivate::selectSipAddressId(const Address &address, const bool caseSensitive) const { +#ifdef HAVE_DB_STORAGE + // This is a hack, because all addresses don't print their parameters in the same order. + const string sipAddress = address.toStringUriOnlyOrdered(); + return selectSipAddressId(sipAddress, caseSensitive); +#else + return -1; +#endif +} + long long MainDbPrivate::selectSipAddressId(const std::shared_ptr<Address> &address, const bool caseSensitive) const { #ifdef HAVE_DB_STORAGE // This is a hack, because all addresses don't print their parameters in the same order. @@ -1159,6 +1184,7 @@ long long MainDbPrivate::selectChatRoomId(const ConferenceId &conferenceId) cons ConferenceId MainDbPrivate::selectConferenceId(const long long chatRoomId) const { #ifdef HAVE_DB_STORAGE + L_Q(); string peerSipAddress; string localSipAddress; @@ -1166,7 +1192,8 @@ ConferenceId MainDbPrivate::selectConferenceId(const long long chatRoomId) const soci::session *session = dbSession.getBackendSession(); *session << query, soci::use(chatRoomId), soci::into(peerSipAddress), soci::into(localSipAddress); - ConferenceId conferenceId = ConferenceId(Address::create(peerSipAddress), Address::create(localSipAddress)); + ConferenceId conferenceId(Address(peerSipAddress), Address(localSipAddress), + q->getCore()->createConferenceIdParams()); if (conferenceId.isValid()) { cache(conferenceId, chatRoomId); @@ -2041,7 +2068,7 @@ void MainDbPrivate::setChatMessageParticipantState(const shared_ptr<EventLog> &e const EventLogPrivate *dEventLog = eventLog->getPrivate(); MainDbKeyPrivate *dEventKey = static_cast<MainDbKey &>(dEventLog->dbKey).getPrivate(); const long long &eventId = dEventKey->storageId; - auto participantAddressWithoutGruu = Address::create(participantAddress->getUriWithoutGruu()); + auto participantAddressWithoutGruu = participantAddress->getUriWithoutGruu(); long long participantSipAddressId = selectSipAddressId(participantAddressWithoutGruu, true); long long nbEntries; *dbSession.getBackendSession() << "SELECT count(*) FROM chat_message_participant WHERE event_id = :eventId AND " @@ -2198,8 +2225,8 @@ shared_ptr<ConferenceInfo> MainDbPrivate::selectConferenceInfo(const soci::row & conferenceInfo->setUri(uri); const auto &conferenceUri = conferenceInfo->getUri(); if (conferenceUri && conferenceUri->isValid()) { - const auto &uri = Address::create(conferenceUri->getUriWithoutGruu()); - const auto &uriStringOrdered = uri->toStringUriOnlyOrdered(); + const auto &uri = conferenceUri->getUriWithoutGruu(); + const auto &uriStringOrdered = uri.toStringUriOnlyOrdered(); if (uriStringOrdered != uriString) { // Update conference address to ensure that a conference info can be successfully searched by its address const long long &uriSipAddressId = insertSipAddress(uri); @@ -3172,16 +3199,16 @@ static inline bool checkLegacyCallLogsTableExists(soci::session &session) { #endif #ifdef HAVE_DB_STORAGE -void MainDbPrivate::importLegacyFriends(DbSession &inDbSession) { +bool MainDbPrivate::importLegacyFriends(DbSession &inDbSession) { L_Q(); + bool ret; if (!q->isInitialized()) { lWarning() << "Unable to import legacy friend because the database has not been initialized"; - return; + return false; } L_DB_TRANSACTION_C(q) { if (getModuleVersion("legacy-friends-import") >= makeVersion(1, 0, 0)) return; - updateModuleVersion("legacy-friends-import", ModuleVersionLegacyFriendsImport); soci::session *inSession = inDbSession.getBackendSession(); if (!checkLegacyFriendsTableExists(*inSession)) return; @@ -3249,8 +3276,14 @@ void MainDbPrivate::importLegacyFriends(DbSession &inDbSession) { soci::use(vCard), soci::use(vCardEtag), soci::use(vCardSyncUri); } tr.commit(); + + // Only update the module version once the import has been done. + updateModuleVersion("legacy-friends-import", ModuleVersionLegacyFriendsImport); + lInfo() << "Successful import of legacy friends."; + ret = true; }; + return ret; } #ifdef HAVE_XML2 @@ -3296,17 +3329,17 @@ static string extractLegacyFileContentType(const string &xml) { return ""; } -void MainDbPrivate::importLegacyHistory(DbSession &inDbSession) { +bool MainDbPrivate::importLegacyHistory(DbSession &inDbSession) { L_Q(); + bool ret = false; if (!q->isInitialized()) { lWarning() << "Unable to import legacy history because the database has not been initialized"; - return; + return false; } L_DB_TRANSACTION_C(q) { if (getModuleVersion("legacy-history-import") >= makeVersion(1, 0, 0)) return; - updateModuleVersion("legacy-history-import", ModuleVersionLegacyHistoryImport); soci::session *inSession = inDbSession.getBackendSession(); if (!checkLegacyHistoryTableExists(*inSession)) return; @@ -3381,9 +3414,9 @@ void MainDbPrivate::importLegacyHistory(DbSession &inDbSession) { *session << "INSERT INTO event (type, creation_time) VALUES (:type, :creationTime)", soci::use(eventType), soci::use(creationTime.first, creationTime.second); const long long &eventId = dbSession.getLastInsertId(); - const auto localAddress = Address::create(message.get<string>(LegacyMessageColLocalAddress)); + const auto localAddress = Address(message.get<string>(LegacyMessageColLocalAddress)); const long long &localSipAddressId = insertSipAddress(localAddress); - const auto remoteAddress = Address::create(message.get<string>(LegacyMessageColRemoteAddress)); + const auto remoteAddress = Address(message.get<string>(LegacyMessageColRemoteAddress)); const long long &remoteSipAddressId = insertSipAddress(remoteAddress); const long long &chatRoomId = insertOrUpdateImportedBasicChatRoom(remoteSipAddressId, localSipAddressId, time); @@ -3432,21 +3465,27 @@ void MainDbPrivate::importLegacyHistory(DbSession &inDbSession) { "GROUP BY conference_event.chat_room_id),0))"; // if there are no messages, the first is NULL. So put // a 0 to the ID tr.commit(); + + // Only update the module version once the import has been done. + updateModuleVersion("legacy-history-import", ModuleVersionLegacyHistoryImport); + lInfo() << "Successful import of legacy messages."; + ret = true; }; + return ret; } -void MainDbPrivate::importLegacyCallLogs(DbSession &inDbSession) { +bool MainDbPrivate::importLegacyCallLogs(DbSession &inDbSession) { L_Q(); + bool ret = false; if (!q->isInitialized()) { lWarning() << "Unable to import legacy call logs because the database has not been initialized"; - return; + return false; } L_DB_TRANSACTION_C(q) { if (getModuleVersion("legacy-call-logs-import") >= makeVersion(1, 0, 0)) return; - updateModuleVersion("legacy-call-logs-import", ModuleVersionLegacyCallLogsImport); soci::session *inSession = inDbSession.getBackendSession(); if (!checkLegacyCallLogsTableExists(*inSession)) return; @@ -3486,8 +3525,14 @@ void MainDbPrivate::importLegacyCallLogs(DbSession &inDbSession) { } tr.commit(); + + // Only update the module version once the import has been done. + updateModuleVersion("legacy-call-logs-import", ModuleVersionLegacyCallLogsImport); + lInfo() << "Successful import of legacy call logs."; + ret = true; }; + return ret; } #endif @@ -4294,10 +4339,10 @@ void MainDb::migrateConferenceInfos() { for (const auto &row : rows) { const long long &dbConferenceInfoId = d->dbSession.resolveId(row, 0); const std::string uriString = row.get<string>(1); - std::shared_ptr<Address> uri = Address::create(uriString); + Address uri(uriString); // Update conference address to ensure that a conference info can be successfully searched by its // address - const long long &uriSipAddressId = d->insertSipAddress(Address::create(uri->getUriWithoutGruu())); + const long long &uriSipAddressId = d->insertSipAddress(uri.getUriWithoutGruu()); *session << "UPDATE conference_info SET uri_sip_address_id = :uriSipAddressId WHERE id = :conferenceInfoId", soci::use(uriSipAddressId), soci::use(dbConferenceInfoId); } @@ -4580,8 +4625,8 @@ shared_ptr<EventLog> MainDb::getEvent(const unique_ptr<MainDb> &mainDb, const lo *d->dbSession.getBackendSession() << Statements::get(Statements::SelectConferenceEvent), soci::into(row), soci::use(storageId); - ConferenceId conferenceId(Address::create(row.get<string>(16))->getSharedFromThis(), - Address::create(row.get<string>(17))); + ConferenceId conferenceId(Address(row.get<string>(16)), Address(row.get<string>(17)), + mainDb->getCore()->createConferenceIdParams()); shared_ptr<AbstractChatRoom> chatRoom = d->findChatRoom(conferenceId); if (!chatRoom) return shared_ptr<EventLog>(); @@ -4983,7 +5028,8 @@ list<shared_ptr<Content>> MainDb::getMediaContents(const ConferenceId &conferenc list<shared_ptr<Content>> result = list<shared_ptr<Content>>(); #ifdef HAVE_DB_STORAGE static const string query = - "SELECT name, path, size, content_type.value, conference_chat_message_event.time " + "SELECT name, path, size, content_type.value, conference_chat_message_event.time, " + "conference_chat_message_event.imdn_message_id " " FROM chat_message_file_content " " JOIN chat_message_content ON chat_message_content.id = chat_message_file_content.chat_message_content_id " " JOIN content_type ON content_type.id = chat_message_content.content_type_id " @@ -5004,6 +5050,8 @@ list<shared_ptr<Content>> MainDb::getMediaContents(const ConferenceId &conferenc int size = row.get<int>(2); ContentType contentType(row.get<string>(3)); time_t creation = d->dbSession.getTime(row, 4); + string messageId = row.get<string>(5); + lDebug() << "Fetched media content [" << name << "] message id is [" << messageId << "]"; auto fileContent = FileContent::create<FileContent>(); fileContent->setFileName(name); @@ -5011,6 +5059,7 @@ list<shared_ptr<Content>> MainDb::getMediaContents(const ConferenceId &conferenc fileContent->setFilePath(path); fileContent->setContentType(contentType); fileContent->setCreationTimestamp(creation); + fileContent->setRelatedChatMessageId(messageId); result.push_back(fileContent); } @@ -5025,7 +5074,8 @@ list<shared_ptr<Content>> MainDb::getDocumentContents(const ConferenceId &confer list<shared_ptr<Content>> result = list<shared_ptr<Content>>(); #ifdef HAVE_DB_STORAGE static const string query = - "SELECT name, path, size, content_type.value, conference_chat_message_event.time " + "SELECT name, path, size, content_type.value, conference_chat_message_event.time, " + "conference_chat_message_event.imdn_message_id " " FROM chat_message_file_content " " JOIN chat_message_content ON chat_message_content.id = chat_message_file_content.chat_message_content_id " " JOIN content_type ON content_type.id = chat_message_content.content_type_id " @@ -5045,6 +5095,8 @@ list<shared_ptr<Content>> MainDb::getDocumentContents(const ConferenceId &confer int size = row.get<int>(2); ContentType contentType(row.get<string>(3)); time_t creation = d->dbSession.getTime(row, 4); + string messageId = row.get<string>(5); + lDebug() << "Fetched document content [" << name << "] message id is [" << messageId << "]"; auto fileContent = FileContent::create<FileContent>(); fileContent->setFileName(name); @@ -5052,6 +5104,7 @@ list<shared_ptr<Content>> MainDb::getDocumentContents(const ConferenceId &confer fileContent->setFilePath(path); fileContent->setContentType(contentType); fileContent->setCreationTimestamp(creation); + fileContent->setRelatedChatMessageId(messageId); result.push_back(fileContent); } @@ -5828,6 +5881,8 @@ void MainDb::loadChatMessageContents(const shared_ptr<ChatMessage> &chatMessage) if (bodyEncodingType == 1) content->setBodyFromUtf8(row.get<string>(3)); else content->setBodyFromLocale(row.get<string>(3)); + content->setRelatedChatMessageId(chatMessage->getImdnMessageId()); + // 1.2 - Fetch contents' app data. // TODO: Do not test backend, encapsulate!!! if (getBackend() == MainDb::Backend::Sqlite3) { @@ -5945,32 +6000,46 @@ void MainDb::disableDisplayNotificationRequired(const std::shared_ptr<const Even // - set the creation time to the earliest one // - assign all events preceeding the latest creation time to the kept chatroom // - destroy the deleted chatroom from DB -void MainDb::addChatroomToList(ChatRoomWeakCompareMap &chatRoomsMap, - const shared_ptr<AbstractChatRoom> &chatRoom) const { +// This function returns TRUE if a chatroom has replaced an existing one or added otherwise FALSE +bool MainDb::addChatroomToList(ChatRoomWeakCompareMap &chatRoomsMap, + const shared_ptr<AbstractChatRoom> &chatRoom, + long long id, + int unreadMessageCount) const { #ifdef HAVE_DB_STORAGE + L_D(); const auto &chatRoomConferenceId = chatRoom->getConferenceId(); shared_ptr<AbstractChatRoom> chatRoomToAdd = nullptr; shared_ptr<AbstractChatRoom> chatRoomToRemove = nullptr; + bool ret = false; try { auto storedChatRoom = chatRoomsMap.at(chatRoomConferenceId); - chatRoomToAdd = mergeChatRooms(chatRoom, storedChatRoom); + chatRoomToAdd = mergeChatRooms(chatRoom, storedChatRoom, id, unreadMessageCount); if (storedChatRoom == chatRoomToAdd) { chatRoomToRemove = chatRoom; } else { chatRoomToRemove = storedChatRoom; + d->cache(chatRoomConferenceId, id); + ret = true; } } catch (std::out_of_range &) { chatRoomToAdd = chatRoom; + d->unreadChatMessageCountCache.insert(chatRoomConferenceId, unreadMessageCount); + d->cache(chatRoomConferenceId, id); + ret = true; } chatRoomsMap.insert_or_assign(chatRoomConferenceId, chatRoomToAdd); if (chatRoomToRemove && chatRoomToRemove->getConference()) { chatRoomToRemove->getConference()->terminate(); } + return ret; #endif + return false; } -shared_ptr<AbstractChatRoom> MainDb::mergeChatRooms(const shared_ptr<AbstractChatRoom> chatRoom1, - const shared_ptr<AbstractChatRoom> chatRoom2) const { +shared_ptr<AbstractChatRoom> MainDb::mergeChatRooms(const shared_ptr<AbstractChatRoom> &chatRoom1, + const shared_ptr<AbstractChatRoom> &chatRoom2, + long long id, + int unreadMessageCount) const { #ifdef HAVE_DB_STORAGE L_D(); shared_ptr<AbstractChatRoom> chatRoomToAdd = nullptr; @@ -5993,10 +6062,13 @@ shared_ptr<AbstractChatRoom> MainDb::mergeChatRooms(const shared_ptr<AbstractCha const auto chatRoom2CreationTime = chatRoom2->getCreationTime(); lInfo() << "Chat rooms with conference id " << chatRoom1ConferenceId << " and " << chatRoom2ConferenceId << " will be merged as they have the same peer address"; - ConferenceId conferenceIdToAdd; - ConferenceId conferenceIdToRemove; time_t creationTimeToAdd = 0; time_t creationTimeToDelete = 0; + + const long long &dbOtherChatRoomId = d->selectChatRoomId(chatRoom2ConferenceId); + + long long dbChatRoomToAddId = 0; + long long dbChatRoomToRemoveId = 0; // The boolean updateChatRoomTable is used to ensure that only the most up-to-date informations stay in the // chat_room table. In fact during a merge it may happen that the same chat room lays in multiple lines of table // chat_room. Here below a snapshot of all entries for the same chat room in a database: @@ -6022,16 +6094,16 @@ shared_ptr<AbstractChatRoom> MainDb::mergeChatRooms(const shared_ptr<AbstractCha if ((chatRoom2LastNotify < chatRoom1LastNotify) || ((chatRoom2LastNotify == chatRoom1LastNotify) && (chatRoom2CreationTime < chatRoom1CreationTime))) { chatRoomToAdd = chatRoom1; - conferenceIdToAdd = chatRoom1ConferenceId; - conferenceIdToRemove = chatRoom2ConferenceId; creationTimeToAdd = chatRoom1CreationTime; creationTimeToDelete = chatRoom2CreationTime; + dbChatRoomToAddId = id; + dbChatRoomToRemoveId = dbOtherChatRoomId; } else { chatRoomToAdd = chatRoom2; - conferenceIdToAdd = chatRoom2ConferenceId; - conferenceIdToRemove = chatRoom1ConferenceId; creationTimeToAdd = chatRoom1CreationTime; creationTimeToDelete = chatRoom2CreationTime; + dbChatRoomToAddId = dbOtherChatRoomId; + dbChatRoomToRemoveId = id; } time_t creationTime = 0; @@ -6042,26 +6114,17 @@ shared_ptr<AbstractChatRoom> MainDb::mergeChatRooms(const shared_ptr<AbstractCha } chatRoomToAdd->setCreationTime(creationTime); - const auto unreadChatMessageCountChatRoomToAdd = d->unreadChatMessageCountCache[conferenceIdToAdd]; - const auto unreadChatMessageCountChatRoomToRemove = d->unreadChatMessageCountCache[conferenceIdToRemove]; - int unreadChatMessageCount = 0; - if (unreadChatMessageCountChatRoomToAdd) { - unreadChatMessageCount += *unreadChatMessageCountChatRoomToAdd; - } - if (unreadChatMessageCountChatRoomToRemove) { - unreadChatMessageCount += *unreadChatMessageCountChatRoomToRemove; + const auto unreadChatMessageCountChatRoom2 = d->unreadChatMessageCountCache[chatRoom2ConferenceId]; + int unreadChatMessageCount = unreadMessageCount; + if (unreadChatMessageCountChatRoom2) { + unreadChatMessageCount += *unreadChatMessageCountChatRoom2; } - d->unreadChatMessageCountCache.insert(conferenceIdToAdd, unreadChatMessageCount); - d->unreadChatMessageCountCache.insert(conferenceIdToRemove, 0); - - const long long &dbChatRoomToAddId = d->selectChatRoomId(conferenceIdToAdd); - const long long &dbChatRoomToRemoveId = d->selectChatRoomId(conferenceIdToRemove); + d->unreadChatMessageCountCache.insert(chatRoom2ConferenceId, unreadChatMessageCount); auto creationTimeToAddSoci = d->dbSession.getTimeWithSociIndicator(creationTimeToAdd); soci::session *session = d->dbSession.getBackendSession(); - lInfo() << "Moving all event of chatroom with ID " << dbChatRoomToRemoveId - << " (conference id: " << conferenceIdToRemove << ") to chatroom with ID " << dbChatRoomToAddId - << " (conference id:" << conferenceIdToAdd << ")"; + lInfo() << "Moving all event of chatroom with ID " << dbChatRoomToRemoveId << " to chatroom with ID " + << dbChatRoomToAddId; // Move conference event that occurred before the latest chatroom was created. // Events such as chat messages are already stored in both chat rooms auto creationTimeToDeleteSoci = d->dbSession.getTimeWithSociIndicator(creationTimeToDelete); @@ -6092,8 +6155,7 @@ shared_ptr<AbstractChatRoom> MainDb::mergeChatRooms(const shared_ptr<AbstractCha } if (dbChatRoomToRemoveId != -1) { - lInfo() << "Deleting chatroom with ID " << dbChatRoomToRemoveId << " (conference id: " << conferenceIdToRemove - << ")"; + lInfo() << "Deleting chatroom with ID " << dbChatRoomToRemoveId; *session << "DELETE FROM chat_room WHERE id = :chatRoomId", soci::use(dbChatRoomToRemoveId); } return chatRoomToAdd; @@ -6134,6 +6196,8 @@ list<shared_ptr<AbstractChatRoom>> MainDb::getChatRooms() { ChatRoomWeakCompareMap chatRoomsMap; shared_ptr<Core> core = getCore(); + LinphoneCore *cCore = getCore()->getCCore(); + bool serverMode = linphone_core_conference_server_enabled(core->getCCore()); soci::session *session = d->dbSession.getBackendSession(); @@ -6148,12 +6212,49 @@ list<shared_ptr<AbstractChatRoom>> MainDb::getChatRooms() { bool typeHasBeenSet = false; d->unreadChatMessageCountCache.clear(); + auto conferenceIdParams = core->createConferenceIdParams(); + conferenceIdParams.enableExtractUri(false); + bool keepGruu = conferenceIdParams.getKeepGruu(); + + bool unifyChatroomAddress = + !!linphone_config_get_bool(linphone_core_get_config(cCore), "misc", "unify_chatroom_address", FALSE); + std::string chatroomDomain; + std::string chatroomGr; + if (unifyChatroomAddress) { + // No need to read the configuration if the core is not configure to unify chatroom addresses + chatroomDomain = L_C_TO_STRING( + linphone_config_get_string(linphone_core_get_config(cCore), "misc", "force_chatroom_domain", "")); + if (keepGruu) { + chatroomGr = L_C_TO_STRING( + linphone_config_get_string(linphone_core_get_config(cCore), "misc", "force_chatroom_gr", "")); + } + } + for (const auto &row : rows) { if (!typeHasBeenSet) { unreadMessageCountType = row.get_properties(12).get_data_type(); typeHasBeenSet = true; } - ConferenceId conferenceId(Address(row.get<string>(1), true), Address(row.get<string>(2), true)); + + Address pAddress(row.get<string>(1), true); + Address oldPAddress(pAddress); + Address lAddress(row.get<string>(2), true); + Address oldLAddress(lAddress); + bool conferenceIdChanged = (!keepGruu && (pAddress.hasUriParam("gr") || lAddress.hasUriParam("gr"))); + if (!chatroomDomain.empty()) { + if (chatroomDomain.compare(pAddress.getDomain()) != 0) { + pAddress.setDomain(chatroomDomain); + conferenceIdChanged = true; + } + if (keepGruu) { + auto pAddressGr = pAddress.getUriParamValue("gr"); + if (!chatroomGr.empty() && (pAddressGr.empty() || chatroomGr.compare(pAddressGr) != 0)) { + pAddress.setUriParam("gr", chatroomGr); + conferenceIdChanged = true; + } + } + } + ConferenceId conferenceId(std::move(pAddress), std::move(lAddress), conferenceIdParams); shared_ptr<AbstractChatRoom> chatRoom = core->findChatRoom(conferenceId, false); if (chatRoom) { @@ -6161,13 +6262,8 @@ list<shared_ptr<AbstractChatRoom>> MainDb::getChatRooms() { continue; } + bool updateFlags = false; const long long &dbChatRoomId = d->dbSession.resolveId(row, 0); - d->cache(conferenceId, dbChatRoomId); - int unreadMessagesCount = 0; - if (unreadMessageCountType == soci::dt_string) unreadMessagesCount = std::stoi(row.get<string>(12, "0")); - else unreadMessagesCount = row.get<int>(12, 0); - d->unreadChatMessageCountCache.insert(conferenceId, unreadMessagesCount); - time_t creationTime = d->dbSession.getTime(row, 3); time_t lastUpdateTime = d->dbSession.getTime(row, 4); int capabilities = row.get<int>(5); @@ -6182,13 +6278,13 @@ list<shared_ptr<AbstractChatRoom>> MainDb::getChatRooms() { chatRoom->setUtf8Subject(subject); } else if (backend == ChatParams::Backend::FlexisipChat) { #ifdef HAVE_ADVANCED_IM + const auto &localAddress = conferenceId.getLocalAddress(); unsigned int lastNotifyId = d->dbSession.getUnsignedInt(row, 7, 0); list<shared_ptr<Participant>> participants = selectChatRoomParticipants(dbChatRoomId); const auto meIt = - std::find_if(participants.begin(), participants.end(), - [&localAddress = conferenceId.getLocalAddress()](const auto &participant) { - return (participant->getAddress()->weakEqual(*localAddress)); - }); + std::find_if(participants.begin(), participants.end(), [&localAddress](const auto &participant) { + return (participant->getAddress()->weakEqual(*localAddress)); + }); shared_ptr<Participant> me; if (meIt != participants.end()) { me = *meIt; @@ -6204,11 +6300,10 @@ list<shared_ptr<AbstractChatRoom>> MainDb::getChatRooms() { const long long &conferenceInfoId = d->dbSession.resolveId(row, 14); shared_ptr<ConferenceInfo> confInfo; if (conferenceInfoId > 0) { - soci::row row; - soci::session *session = d->dbSession.getBackendSession(); - *session << Statements::get(Statements::SelectConferenceInfoFromId), soci::into(row), + soci::row conferenceInfoRow; + *session << Statements::get(Statements::SelectConferenceInfoFromId), soci::into(conferenceInfoRow), soci::use(conferenceInfoId); - confInfo = d->selectConferenceInfo(row); + confInfo = d->selectConferenceInfo(conferenceInfoRow); } if (confInfo) { @@ -6224,23 +6319,31 @@ list<shared_ptr<AbstractChatRoom>> MainDb::getChatRooms() { } } - bool serverMode = linphone_core_conference_server_enabled(core->getCCore()); std::shared_ptr<Conference> conference = nullptr; - if (!serverMode) { + if (serverMode) { + params->enableLocalParticipant(false); + conference = (new ServerConference(core, nullptr, params))->toSharedPtr(); + conference->initFromDb(me, conferenceId, lastNotifyId, false); + chatRoom = conference->getChatRoom(); + conference->setState(ConferenceInterface::State::Created); + } else { if (!me) { lError() << "Unable to find me in: " << conferenceId; continue; } bool hasBeenLeft = !!row.get<int>(8, 0); - conference = (new ClientConference(core, me->getAddress(), nullptr, params))->toSharedPtr(); + conference = (new ClientConference(core, nullptr, params))->toSharedPtr(); conference->initFromDb(me, conferenceId, lastNotifyId, hasBeenLeft); chatRoom = conference->getChatRoom(); + updateFlags = confInfo && !hasBeenLeft; if (hasBeenLeft) { conference->setState(ConferenceInterface::State::Terminated); } else { conference->setState(ConferenceInterface::State::Created); } if (!params->isGroup()) { + auto conferenceIdParams = core->createConferenceIdParams(); + conferenceIdParams.enableExtractUri(false); // TODO: load previous IDs if any static const string query = "SELECT sip_address.value FROM one_to_one_chat_room_previous_conference_id, sip_address" @@ -6248,23 +6351,17 @@ list<shared_ptr<AbstractChatRoom>> MainDb::getChatRooms() { " AND sip_address_id = sip_address.id"; soci::rowset<soci::row> rows = (session->prepare << query, soci::use(dbChatRoomId)); for (const auto &row : rows) { - ConferenceId previousId = - ConferenceId(Address::create(row.get<string>(0), true), conferenceId.getLocalAddress()); + ConferenceId previousId = ConferenceId(Address::create(row.get<string>(0), true), + localAddress, conferenceIdParams); if (previousId != conferenceId) { lInfo() << "Keeping around previous chat room ID [" << previousId - << "] in case BYE is received for exhumed chat room [" << conferenceId << "]"; + << "] in case BYE is received for exhumed chat room " << *conference << " [" + << conferenceId << "]"; auto clientChatRoom = dynamic_pointer_cast<ClientChatRoom>(chatRoom); clientChatRoom->addConferenceIdToPreviousList(previousId); } } } - } else { - params->enableLocalParticipant(false); - conference = - (new ServerConference(core, conferenceId.getLocalAddress(), nullptr, params))->toSharedPtr(); - conference->initFromDb(me, conferenceId, lastNotifyId, false); - chatRoom = conference->getChatRoom(); - conference->setState(ConferenceInterface::State::Created); } for (auto participant : participants) { participant->setConference(conference); @@ -6311,9 +6408,45 @@ list<shared_ptr<AbstractChatRoom>> MainDb::getChatRooms() { chatRoom->setIsEmpty(lastMessageId == 0); chatRoom->setIsMuted(muted, false); + int unreadMessagesCount = 0; + if (unreadMessageCountType == soci::dt_string) unreadMessagesCount = std::stoi(row.get<string>(12, "0")); + else unreadMessagesCount = row.get<int>(12, 0); + lDebug() << "Found chat room in DB: " << conferenceId; - addChatroomToList(chatRoomsMap, chatRoom); + if (addChatroomToList(chatRoomsMap, chatRoom, dbChatRoomId, unreadMessagesCount) && + (conferenceIdChanged || updateFlags)) { + std::string query("UPDATE chat_room SET "); + if (conferenceIdChanged) { + // If the conference ID changed, then update the information in the database so that the next time + // around everything will be alright + auto peerAddress = conferenceId.getPeerAddress(); + auto localAddress = conferenceId.getLocalAddress(); + lInfo() << "Change peer and local address of chatroom [" << chatRoom << "] with ID " << dbChatRoomId + << ":"; + lInfo() << "- peer: " << oldPAddress << " -> " << *peerAddress; + lInfo() << "- local: " << oldLAddress << " -> " << *localAddress; + const long long &peerSipAddressId = d->insertSipAddress(peerAddress); + const long long &localSipAddressId = d->insertSipAddress(localAddress); + query += "peer_sip_address_id = " + Utils::toString(peerSipAddressId) + + ", local_sip_address_id = " + Utils::toString(localSipAddressId) + " "; + } + if (updateFlags) { + // If we end up here, it means that there was a problem with a media conference supporting chat + // capabilities. The core might have lost the connection while the conference was ongoing therefore + // the database could not be updated to reflect the termination of the conference. + lInfo() << "Updating flags of chatroom [" << chatRoom << "] with ID " << dbChatRoomId + << ": setting it as terminated and reset the last notify ID to 0"; + query += "flags = 1, last_notify_id = 0 "; + auto chatConference = chatRoom->getConference(); + if (chatConference) { + chatConference->resetLastNotify(); + chatConference->setState(ConferenceInterface::State::Terminated); + } + } + query += "WHERE id = :chatRoomId"; + *session << query, soci::use(dbChatRoomId); + } } tr.commit(); @@ -7014,31 +7147,6 @@ long long MainDb::insertConferenceInfo(const std::shared_ptr<ConferenceInfo> &co return -1; } -void MainDb::deleteConferenceInfo(long long dbConferenceId) { -#ifdef HAVE_DB_STORAGE - L_D(); - long long peerId; - soci::session *session = d->dbSession.getBackendSession(); - std::string peerIdQuery = - "SELECT uri_sip_address_id FROM conference_info WHERE (conference_info.id = :conferenceInfoId)"; - *session << peerIdQuery, soci::use(dbConferenceId), soci::into(peerId); - if (session->got_data()) { - long long chatRoomId = d->selectChatRoomId(peerId); - if (chatRoomId >= 0) { - long long localId; - std::string localIdQuery = "SELECT local_sip_address_id FROM chat_room WHERE (id = :chatRoomId)"; - *session << localIdQuery, soci::use(dbConferenceId), soci::into(localId); - std::shared_ptr<Address> peer = Address::create(d->selectSipAddressFromId(peerId)); - std::shared_ptr<Address> local = Address::create(d->selectSipAddressFromId(localId)); - d->deleteChatRoom(ConferenceId(peer, local)); - } - } - - *session << "DELETE FROM conference_info WHERE id = :conferenceId", soci::use(dbConferenceId); - d->storageIdToConferenceInfo.erase(dbConferenceId); -#endif -} - void MainDb::cleanupConferenceInfo(time_t expiredBeforeThisTime) { #ifdef HAVE_DB_STORAGE L_D(); @@ -7059,7 +7167,7 @@ void MainDb::cleanupConferenceInfo(time_t expiredBeforeThisTime) { ? "(julianday(:maxExpireTime) - julianday(conference_info.start_time)) * 24 * 60" : "TIMESTAMPDIFF(MINUTES, :maxExpireTime, conference_info.start_time)"; // The logic to find conference that may be deleted because their end time is in the past is the following: - // - compute how much time has elapsed since the start of the conference ans verify yhay this value is larger or + // - compute how much time has elapsed since the start of the conference and verify that this value is larger or // equal to the duration. // - verify that the duration is larger than 0 std::string findQuery = "SELECT conference_info.id, uri_sip_address.value FROM conference_info, sip_address AS " @@ -7091,14 +7199,40 @@ void MainDb::cleanupConferenceInfo(time_t expiredBeforeThisTime) { #endif } +void MainDb::deleteConferenceInfo(long long dbConferenceId) { +#ifdef HAVE_DB_STORAGE + L_D(); + long long peerId; + soci::session *session = d->dbSession.getBackendSession(); + std::string peerIdQuery = + "SELECT uri_sip_address_id FROM conference_info WHERE (conference_info.id = :conferenceInfoId)"; + *session << peerIdQuery, soci::use(dbConferenceId), soci::into(peerId); + if (session->got_data()) { + long long chatRoomId = d->selectChatRoomId(peerId); + if (chatRoomId >= 0) { + long long localId; + std::string localIdQuery = "SELECT local_sip_address_id FROM chat_room WHERE (id = :chatRoomId)"; + *session << localIdQuery, soci::use(dbConferenceId), soci::into(localId); + Address peer(d->selectSipAddressFromId(peerId)); + Address local(d->selectSipAddressFromId(localId)); + d->deleteChatRoom(ConferenceId(std::move(peer), std::move(local), getCore()->createConferenceIdParams())); + } + } + + *session << "DELETE FROM conference_info WHERE id = :conferenceId", soci::use(dbConferenceId); + d->storageIdToConferenceInfo.erase(dbConferenceId); +#endif +} + void MainDb::deleteConferenceInfo(const std::shared_ptr<Address> &address) { #ifdef HAVE_DB_STORAGE if (isInitialized()) { L_DB_TRANSACTION { L_D(); if (address) { - lInfo() << "Deleting conference information linked to conference " << *address; - const long long &uriSipAddressId = d->selectSipAddressId(address, false); + auto prunedAddress = address->getUriWithoutGruu(); + lInfo() << "Deleting conference information linked to conference " << prunedAddress; + const long long &uriSipAddressId = d->selectSipAddressId(prunedAddress, false); const long long &dbConferenceId = d->selectConferenceInfoId(uriSipAddressId); deleteConferenceInfo(dbConferenceId); } @@ -7609,7 +7743,7 @@ std::list<std::shared_ptr<FriendDevice>> MainDb::getDevices(BCTBX_UNUSED(const s const string query = "SELECT device_address_id, display_name FROM friend_devices WHERE sip_address_id = :1"; std::list<std::shared_ptr<FriendDevice>> devicesList; - const auto sipAddress = Address::create(address->getUriWithoutGruu()); + const auto sipAddress = address->getUriWithoutGruu(); long long sipAddressId = d->selectSipAddressId(sipAddress, false); if (sipAddressId > 0) { @@ -7642,6 +7776,7 @@ std::list<std::shared_ptr<FriendDevice>> MainDb::getDevices(BCTBX_UNUSED(const s bool MainDb::import(Backend, const string ¶meters) { #ifdef HAVE_DB_STORAGE L_D(); + bool ret = false; // Backend is useless, it's sqlite3. (Only available legacy backend.) const string uri = "sqlite3://" + LinphonePrivate::Utils::localeToUtf8(parameters); @@ -7652,11 +7787,11 @@ bool MainDb::import(Backend, const string ¶meters) { return false; } - d->importLegacyFriends(inDbSession); - d->importLegacyHistory(inDbSession); - d->importLegacyCallLogs(inDbSession); + ret |= d->importLegacyFriends(inDbSession); + ret |= d->importLegacyHistory(inDbSession); + ret |= d->importLegacyCallLogs(inDbSession); - return true; + return ret; #else return false; #endif diff --git a/src/db/main-db.h b/src/db/main-db.h index 613875b0633b95b4ed1bb22b5d4cd31ed2cc87ee..0d2a283caef53fc766b65ac45d72b7f03bddbad4 100644 --- a/src/db/main-db.h +++ b/src/db/main-db.h @@ -292,7 +292,7 @@ public: // Other. // --------------------------------------------------------------------------- - // Import legacy calls/messages from old db. + // Import legacy calls/messages from old db. Returns true if something was done. bool import(Backend backend, const std::string ¶meters) override; static FilterMask getFilterMaskFromHistoryFilterMask(AbstractChatRoom::HistoryFilterMask historyFilterMask); @@ -306,10 +306,14 @@ private: using ChatRoomWeakCompareMap = std:: unordered_map<ConferenceId, std::shared_ptr<AbstractChatRoom>, ConferenceId::WeakHash, ConferenceId::WeakEqual>; void initCleanup(); - void addChatroomToList(ChatRoomWeakCompareMap &chatRoomsMap, - const std::shared_ptr<AbstractChatRoom> &chatRoom) const; - std::shared_ptr<AbstractChatRoom> mergeChatRooms(const std::shared_ptr<AbstractChatRoom> chatRoom1, - const std::shared_ptr<AbstractChatRoom> chatRoom2) const; + bool addChatroomToList(ChatRoomWeakCompareMap &chatRoomsMap, + const std::shared_ptr<AbstractChatRoom> &chatRoom, + long long id, + int unreadMessageCount) const; + std::shared_ptr<AbstractChatRoom> mergeChatRooms(const std::shared_ptr<AbstractChatRoom> &chatRoom1, + const std::shared_ptr<AbstractChatRoom> &chatRoom2, + long long id, + int unreadMessageCount) const; std::string getConferenceInfoTypeQuery(const std::list<LinphoneStreamType> &capabilities) const; }; diff --git a/src/event/event-publish.cpp b/src/event/event-publish.cpp index 8ee33f1c63efd09f68c81c40c248dc45c2dfcb2a..89f54493d27c2b65e978511b742d08229bbb167b 100644 --- a/src/event/event-publish.cpp +++ b/src/event/event-publish.cpp @@ -43,6 +43,7 @@ LinphoneStatus EventPublish::sendPublish(const std::shared_ptr<const Content> &b mSendCustomHeaders = nullptr; } else mOp->setSentCustomHeaders(nullptr); + fillOpFields(); body_handler = sal_body_handler_from_content((body && !body->isEmpty()) ? body->toC() : nullptr); auto publishOp = dynamic_cast<SalPublishOp *>(mOp); err = publishOp->publish(mName, mExpires, body_handler); @@ -73,7 +74,7 @@ EventPublish::EventPublish(const shared_ptr<Core> &core, LinphonePrivate::SalPub EventPublish::EventPublish(const shared_ptr<Core> &core, const shared_ptr<Account> &account, - const std::shared_ptr<Address> resourceAddr, + const std::shared_ptr<const Address> &resourceAddr, const string &event, int expires) : EventPublish(core, new SalPublishOp(core->getCCore()->sal.get()), event) { @@ -98,13 +99,15 @@ EventPublish::EventPublish(const shared_ptr<Core> &core, } EventPublish::EventPublish(const shared_ptr<Core> &core, - const std::shared_ptr<Address> resource, + const std::shared_ptr<const Address> &resource, const string &event, int expires) : EventPublish(core, nullptr, resource, event, expires) { } -EventPublish::EventPublish(const shared_ptr<Core> &core, const std::shared_ptr<Address> resource, const string &event) +EventPublish::EventPublish(const shared_ptr<Core> &core, + const std::shared_ptr<const Address> &resource, + const string &event) : EventPublish(core, resource, event, -1) { setOneshot(true); setUnrefWhenTerminated(true); @@ -138,6 +141,7 @@ LinphoneStatus EventPublish::accept() { ms_error("EventPublish::accept(): cannot accept publish if subscription wasn't just received."); return -1; } + fillOpFields(); auto publishOp = dynamic_cast<SalPublishOp *>(mOp); err = publishOp->accept(); if (err == 0) { diff --git a/src/event/event-publish.h b/src/event/event-publish.h index 034948f001f914542ed304ad000c1e8c11c859f6..4dc3da38bef2b4072977a50df7cdadbae09584d2 100644 --- a/src/event/event-publish.h +++ b/src/event/event-publish.h @@ -35,14 +35,16 @@ public: EventPublish(const std::shared_ptr<Core> &core, LinphonePrivate::SalPublishOp *op, const std::string &name); EventPublish(const std::shared_ptr<Core> &core, const std::shared_ptr<Account> &account, - const std::shared_ptr<Address> resource, + const std::shared_ptr<const Address> &resource, const std::string &event, int expires); EventPublish(const std::shared_ptr<Core> &core, - const std::shared_ptr<Address> resource, + const std::shared_ptr<const Address> &resource, const std::string &event, int expires); - EventPublish(const std::shared_ptr<Core> &core, const std::shared_ptr<Address> resource, const std::string &event); + EventPublish(const std::shared_ptr<Core> &core, + const std::shared_ptr<const Address> &resource, + const std::string &event); std::string toString() const override; diff --git a/src/event/event-subscribe.cpp b/src/event/event-subscribe.cpp index 8047e515390ed9039f0f70eb37e215f49828cdca..71513d0ea142b291908493ff2f00f902bbd6c700 100644 --- a/src/event/event-subscribe.cpp +++ b/src/event/event-subscribe.cpp @@ -65,7 +65,7 @@ EventSubscribe::EventSubscribe(const shared_ptr<Core> &core, } EventSubscribe::EventSubscribe(const shared_ptr<Core> &core, - const std::shared_ptr<Address> &resource, + const std::shared_ptr<const Address> &resource, const string &event) : EventSubscribe(core, LinphoneSubscriptionIncoming, event, -1) { linphone_configure_op(core->getCCore(), mOp, resource->toC(), nullptr, TRUE); @@ -75,7 +75,7 @@ EventSubscribe::EventSubscribe(const shared_ptr<Core> &core, } EventSubscribe::EventSubscribe(const shared_ptr<Core> &core, - const std::shared_ptr<Address> &resource, + const std::shared_ptr<const Address> &resource, const string &event, int expires) : EventSubscribe(core, LinphoneSubscriptionOutgoing, event, expires) { @@ -85,7 +85,7 @@ EventSubscribe::EventSubscribe(const shared_ptr<Core> &core, } EventSubscribe::EventSubscribe(const shared_ptr<Core> &core, - const std::shared_ptr<Address> &resource, + const std::shared_ptr<const Address> &resource, const std::shared_ptr<Account> &account, const string &event, int expires) @@ -106,15 +106,15 @@ LinphoneStatus EventSubscribe::send(const std::shared_ptr<const Content> &body) int err; if (mDir != LinphoneSubscriptionOutgoing) { - ms_error("EventSubscribe::send(): cannot send or update something that is not an outgoing subscription."); + lError() << "EventSubscribe::send(): cannot send or update something that is not an outgoing subscription."; return -1; } switch (mSubscriptionState) { case LinphoneSubscriptionIncomingReceived: case LinphoneSubscriptionTerminated: case LinphoneSubscriptionOutgoingProgress: - ms_error("EventSubscribe::send(): cannot update subscription while in state [%s]", - linphone_subscription_state_to_string(mSubscriptionState)); + lError() << "EventSubscribe::send(): cannot update subscription while in state [" + << linphone_subscription_state_to_string(mSubscriptionState) << "]"; return -1; case LinphoneSubscriptionNone: case LinphoneSubscriptionActive: @@ -133,7 +133,7 @@ LinphoneStatus EventSubscribe::send(const std::shared_ptr<const Content> &body) if (mRequestAddress) { mOp->setRequestUri(mRequestAddress->asStringUriOnly()); } - + fillOpFields(); const LinphoneContent *cBody = (body && !body->isEmpty()) ? body->toC() : nullptr; body_handler = sal_body_handler_from_content(cBody); auto subscribeOp = dynamic_cast<SalSubscribeOp *>(mOp); @@ -155,11 +155,13 @@ LinphoneStatus EventSubscribe::refresh() { LinphoneStatus EventSubscribe::accept() { int err; if (mSubscriptionState != LinphoneSubscriptionIncomingReceived) { - ms_error("EventSubscribe::accept(): cannot accept subscription if subscription wasn't just received."); + lError() << "EventSubscribe::accept(): cannot accept subscription if subscription wasn't just received."; return -1; } + fillOpFields(); auto subscribeOp = dynamic_cast<SalSubscribeOp *>(mOp); err = subscribeOp->accept(); + if (err == 0) { setState(LinphoneSubscriptionActive); } @@ -169,7 +171,7 @@ LinphoneStatus EventSubscribe::accept() { LinphoneStatus EventSubscribe::deny(LinphoneReason reason) { int err; if (mSubscriptionState != LinphoneSubscriptionIncomingReceived) { - ms_error("EventSubscribe::deny(): cannot deny subscription if subscription wasn't just received."); + lError() << "EventSubscribe::deny(): cannot deny subscription if subscription wasn't just received."; return -1; } auto subscribeOp = dynamic_cast<SalSubscribeOp *>(mOp); @@ -182,11 +184,11 @@ LinphoneStatus EventSubscribe::notify(const std::shared_ptr<const Content> &body SalBodyHandler *body_handler; if (mSubscriptionState != LinphoneSubscriptionActive && mSubscriptionState != LinphoneSubscriptionIncomingReceived) { - ms_error("EventSubscribe::notify(): cannot notify if subscription is not active."); + lError() << "EventSubscribe::notify(): cannot notify if subscription is not active."; return -1; } if (mDir != LinphoneSubscriptionIncoming) { - ms_error("EventSubscribe::notify(): cannot notify if not an incoming subscription."); + lError() << "EventSubscribe::notify(): cannot notify if not an incoming subscription."; return -1; } const LinphoneContent *cBody = (body && !body->isEmpty()) ? body->toC() : nullptr; @@ -205,10 +207,14 @@ LinphoneSubscriptionState EventSubscribe::getState() const { void EventSubscribe::setState(LinphoneSubscriptionState state) { if (mSubscriptionState != state) { - ms_message("Event [%p] moving to subscription state %s", this, linphone_subscription_state_to_string(state)); + lInfo() << "Event [" << this << "] moving to subscription state " + << linphone_subscription_state_to_string(state); mSubscriptionState = state; ref(); - linphone_core_notify_subscription_state_changed(getCore()->getCCore(), this->toC(), state); + try { + linphone_core_notify_subscription_state_changed(getCore()->getCCore(), this->toC(), state); + } catch (const bad_weak_ptr &) { + } LINPHONE_HYBRID_OBJECT_INVOKE_CBS(Event, this, linphone_event_cbs_get_subscribe_state_changed, state); if (state == LinphoneSubscriptionTerminated || state == LinphoneSubscriptionError) { release(); diff --git a/src/event/event-subscribe.h b/src/event/event-subscribe.h index 38c83f1ee16824ff8e3fd331d491b9f4bf1364d7..1f96b7bd8baa576510b2b798c390c4d26b5fe280 100644 --- a/src/event/event-subscribe.h +++ b/src/event/event-subscribe.h @@ -45,18 +45,20 @@ public: const std::string &name, bool isOutOfDialog); EventSubscribe(const std::shared_ptr<Core> &core, - const std::shared_ptr<Address> &resource, + const std::shared_ptr<const Address> &resource, const std::string &event); EventSubscribe(const std::shared_ptr<Core> &core, - const std::shared_ptr<Address> &resource, + const std::shared_ptr<const Address> &resource, const std::string &event, int expires); EventSubscribe(const std::shared_ptr<Core> &core, - const std::shared_ptr<Address> &resource, + const std::shared_ptr<const Address> &resource, const std::shared_ptr<Account> &account, const std::string &event, int expires); + virtual ~EventSubscribe() = default; + std::string toString() const override; LinphoneStatus send(const std::shared_ptr<const Content> &body) override; diff --git a/src/event/event.cpp b/src/event/event.cpp index d6efc5819f69705842f67c2ecb85a7e474282c37..08a85e38fa56bfa0c0253e07078f30daf221cafb 100644 --- a/src/event/event.cpp +++ b/src/event/event.cpp @@ -42,8 +42,9 @@ Event::~Event() { if (mEi) linphone_error_info_unref(mEi); try { - if (getCore()) { - LinphoneCore *lc = this->getCore()->getCCore(); + auto core = getCore(); + if (core) { + LinphoneCore *lc = core->getCCore(); if (lc != NULL && linphone_core_get_global_state(lc) != LinphoneGlobalOff) { if (mOp) mOp->release(); } @@ -54,6 +55,29 @@ Event::~Event() { if (mSendCustomHeaders) sal_custom_header_free(mSendCustomHeaders); } +void Event::fillOpFields() { + if (!mOp) return; + auto dir = mOp->getDir(); + auto resourceSalAddress = (dir == SalOp::Dir::Incoming) ? mOp->getToAddress() : mOp->getFromAddress(); + auto accountIdentity = Address::create(resourceSalAddress); + auto account = getCore()->findAccountByIdentityAddress(accountIdentity); + if (account) { + // Set the contact address to avoid putting down a local address + auto state = account->getState(); + auto contactAddress = account->getContactAddress(); + if (contactAddress && contactAddress->isValid() && (state == LinphoneRegistrationOk)) { + // setContactAddress clones the SalAddress + Address contactUri(contactAddress->getUri()); + contactUri.merge(*accountIdentity); + mOp->setContactAddress(contactUri.getImpl()); + } + auto realm = account->getAccountParams()->getRealm(); + if (!realm.empty()) { + mOp->setRealm(L_STRING_TO_C(realm)); + } + } +} + LinphoneReason Event::getReason() const { return linphone_error_info_get_reason(getErrorInfo()); } @@ -111,19 +135,19 @@ const std::string &Event::getCallId() const { return mOp->getCallId(); } -const std::shared_ptr<Address> Event::getResource() const { +std::shared_ptr<Address> Event::getResource() const { return cacheTo(); } -const std::shared_ptr<Address> Event::getRequestAddress() const { +std::shared_ptr<Address> Event::getRequestAddress() const { return cacheRequestAddress(); } -void Event::setRequestAddress(const std::shared_ptr<Address> &requestAddress) { +void Event::setRequestAddress(const std::shared_ptr<const Address> &requestAddress) { mRequestAddress = requestAddress->clone()->toSharedPtr(); } -const std::shared_ptr<Address> Event::getRemoteContact() const { +std::shared_ptr<Address> Event::getRemoteContact() const { if (!mRemoteContactAddress) { mRemoteContactAddress = Address::create(); } @@ -131,7 +155,7 @@ const std::shared_ptr<Address> Event::getRemoteContact() const { return mRemoteContactAddress; } -const std::shared_ptr<Address> Event::cacheFrom() const { +std::shared_ptr<Address> Event::cacheFrom() const { if (!mFromAddress) { mFromAddress = Address::create(); } @@ -139,7 +163,7 @@ const std::shared_ptr<Address> Event::cacheFrom() const { return mFromAddress; } -const std::shared_ptr<Address> Event::cacheTo() const { +std::shared_ptr<Address> Event::cacheTo() const { if (!mToAddress) { mToAddress = Address::create(); } @@ -147,7 +171,7 @@ const std::shared_ptr<Address> Event::cacheTo() const { return mToAddress; } -const std::shared_ptr<Address> Event::cacheRequestAddress() const { +std::shared_ptr<Address> Event::cacheRequestAddress() const { if (!mRequestAddress) { mRequestAddress = Address::create(); } diff --git a/src/event/event.h b/src/event/event.h index 6ff9bd8b221e09f80df36706548f555704d08aed..b2087ae36063f2b091efd76888c969aa0748827d 100644 --- a/src/event/event.h +++ b/src/event/event.h @@ -83,12 +83,12 @@ public: const std::string &getCallId() const; - const std::shared_ptr<Address> getRemoteContact() const; + std::shared_ptr<Address> getRemoteContact() const; - const std::shared_ptr<Address> getResource() const; + std::shared_ptr<Address> getResource() const; - const std::shared_ptr<Address> getRequestAddress() const; - void setRequestAddress(const std::shared_ptr<Address> &requestAddress); + std::shared_ptr<Address> getRequestAddress() const; + void setRequestAddress(const std::shared_ptr<const Address> &requestAddress); LinphonePrivate::SalEventOp *getOp() const; void setManualRefresherMode(bool manual); @@ -105,9 +105,10 @@ public: virtual void terminate() = 0; protected: - const std::shared_ptr<Address> cacheFrom() const; - const std::shared_ptr<Address> cacheTo() const; - const std::shared_ptr<Address> cacheRequestAddress() const; + std::shared_ptr<Address> cacheFrom() const; + std::shared_ptr<Address> cacheTo() const; + std::shared_ptr<Address> cacheRequestAddress() const; + void fillOpFields(); mutable std::shared_ptr<Address> mFromAddress = nullptr; mutable std::shared_ptr<Address> mToAddress = nullptr; diff --git a/src/friend/friend-list.cpp b/src/friend/friend-list.cpp index e94e608874cf50ec00b05561b4d5b3f372422ad9..bd3a84888b7b83ddb36e4eb8931e3635aa6f5df9 100644 --- a/src/friend/friend-list.cpp +++ b/src/friend/friend-list.cpp @@ -221,7 +221,7 @@ void FriendList::exportFriendsAsVcard4File(const std::string &vcardFile) const { for (const auto &f : friends) { std::shared_ptr<Vcard> vcard = f->getVcard(); if (vcard) { - ostrm << vcard->asVcard4String(); + ostrm << vcard->asVcard4StringWithBase64Picture(); } } ostrm.close(); diff --git a/src/friend/friend.h b/src/friend/friend.h index 58457399227089b16e3deea897393da58819bb7c..5907303c8cb9bee98034d28ab352803156e7cea9 100644 --- a/src/friend/friend.h +++ b/src/friend/friend.h @@ -190,16 +190,16 @@ private: static LinphoneSecurityLevel getSecurityLevelFromChatRoomSecurityLevel(AbstractChatRoom::SecurityLevel level); static LinphoneSecurityLevel getSecurityLevelForDevices(const std::list<std::shared_ptr<FriendDevice>> &devices); - LinphoneSubscribePolicy mSubscribePolicy = LinphoneSPAccept; + LinphoneSubscribePolicy mSubscribePolicy = LinphoneSPDeny; LinphoneSubscriptionState mOutSubState; - bool mSubscribe = true; + bool mSubscribe = false; bool mSubscribeActive = false; bool mIsStarred = false; bool mCommit = false; bool mIncSubscribePending = false; bool mPresenceReceived = false; bool mInitialSubscribesSent = false; /* Used to know if initial subscribe message was sent or not. */ - std::shared_ptr<Address> mUri = nullptr; + std::shared_ptr<Address> mUri; std::string mNativeUri; std::string mRefKey; long long mStorageId = -1; @@ -213,7 +213,7 @@ private: mutable std::map<std::string, std::string> mSipUriToPhoneNumberMap; BuddyInfo *mInfo = nullptr; - std::shared_ptr<Vcard> mVcard = nullptr; + std::shared_ptr<Vcard> mVcard; FriendList *mFriendList = nullptr; mutable ListHolder<Address> mAddresses; diff --git a/src/http/http-client.cpp b/src/http/http-client.cpp index 1be14de13bd9415c900b2274a262c8002c3bde49..5abf55a593eac8aa3ff77aefe994ef27f9e4e730 100644 --- a/src/http/http-client.cpp +++ b/src/http/http-client.cpp @@ -117,7 +117,7 @@ HttpRequest::HttpRequest(HttpClient &client, const std::string &method, const st HttpRequest &HttpRequest::addHeader(const std::string &headerName, const std::string &headerValue) { belle_sip_message_add_header(BELLE_SIP_MESSAGE(mRequest), - belle_sip_header_create(headerName.c_str(), L_STRING_TO_C(headerValue))); + belle_http_header_create(headerName.c_str(), L_STRING_TO_C(headerValue))); return *this; } @@ -169,6 +169,11 @@ void HttpRequest::cancel() { delete this; } +void HttpRequest::setAuthInfo(const std::string &username, const std::string &domain) { + mAuthUsername = username; + mAuthDomain = domain; +} + void HttpRequest::abortAuthentication() { /* notify the previously received response */ belle_http_response_t *resp = belle_http_request_get_response(mRequest); @@ -203,7 +208,24 @@ void HttpRequest::processIOError(BCTBX_UNUSED(const belle_sip_io_error_event_t * void HttpRequest::processAuthRequested(belle_sip_auth_event_t *event) { try { auto core = mClient.getCore(); - auto status = linphone_core_fill_belle_sip_auth_event(core->getCCore(), event, NULL, NULL); + // If some auth info where positionned in the request, use them + // When the request header did not hold a From header, + // the belle_sip_auth_event_t does not set username and domain + const char *authUsername = NULL; + const char *authDomain = NULL; + if (!mAuthUsername.empty()) { + authUsername = mAuthUsername.c_str(); + if (belle_sip_auth_event_get_username(event) == NULL) { + belle_sip_auth_event_set_username(event, authUsername); + } + } + if (!mAuthDomain.empty()) { + authDomain = mAuthDomain.c_str(); + if (belle_sip_auth_event_get_domain(event) == NULL) { + belle_sip_auth_event_set_domain(event, authDomain); + } + } + auto status = linphone_core_fill_belle_sip_auth_event(core->getCCore(), event, authUsername, authDomain); switch (status) { case AuthStatus::NoAuth: case AuthStatus::Done: @@ -263,7 +285,7 @@ std::string HttpResponse::getHeaderValue(const std::string &headerName) const { } const Content &HttpResponse::getBody() const { - if (mBody.isEmpty()) { + if (mBody.isEmpty() && mResponse) { auto bh = belle_sip_message_get_body_handler(BELLE_SIP_MESSAGE(mResponse)); if (bh) { mBody = Content((SalBodyHandler *)bh, false); diff --git a/src/http/http-client.h b/src/http/http-client.h index 69ca3d7951aa980cb08338c198406a3879356a75..6ed93e0a60febd41812d50099543a7a6f65e64f8 100644 --- a/src/http/http-client.h +++ b/src/http/http-client.h @@ -21,8 +21,8 @@ #ifndef http_client_h #define http_client_h +#include "content/content.h" #include "core/core.h" - #include "json/json.h" #include <functional> @@ -85,6 +85,7 @@ public: /* Execute the request, ie send it and upon response execute the responseHandler lambda.*/ void execute(const ResponseHandler &responseHandler); void cancel(); + void setAuthInfo(const std::string &username, const std::string &domain); private: void abortAuthentication(); @@ -107,6 +108,8 @@ private: belle_http_request_t *mRequest; belle_http_request_listener_t *mListener = nullptr; bool mAuthPending = false; + std::string mAuthUsername; + std::string mAuthDomain; }; class LINPHONE_PUBLIC HttpClient : public CoreAccessor { diff --git a/src/ldap/ldap-contact-provider.cpp b/src/ldap/ldap-contact-provider.cpp index 146a661269100f25f836effc7edc11987b99497b..50342617a0dc127ca553b1b5bdc5d1d19679148c 100644 --- a/src/ldap/ldap-contact-provider.cpp +++ b/src/ldap/ldap-contact-provider.cpp @@ -472,9 +472,10 @@ int LdapContactProvider::buildContact(LdapContactFields *contact, if (mConfig.count("sip_domain") > 0 && mConfig.at("sip_domain")[0] != "") linphone_address_set_domain(la, mConfig.at("sip_domain")[0].c_str()); char *newSip = linphone_address_as_string(la); - char *phoneNumber = - linphone_account_normalize_phone_number(linphone_core_get_default_account(mCore->getCCore()), - attributes[attributeIndex].second.c_str()); + const char *username = linphone_address_get_username(la); + char *phoneNumber = username ? linphone_account_normalize_phone_number( + linphone_core_get_default_account(mCore->getCCore()), username) + : nullptr; if (contact->mSip.count(newSip) == 0 || contact->mSip[newSip] == "") contact->mSip[newSip] = (phoneNumber ? phoneNumber : ""); if (phoneNumber) ms_free(phoneNumber); diff --git a/src/nat/ice-service.h b/src/nat/ice-service.h index 95b034692a3235c95ffe75389a68681527ff1da8..a97d4fd508265fee0d69f5db452f5bf5a3de9c06 100644 --- a/src/nat/ice-service.h +++ b/src/nat/ice-service.h @@ -40,7 +40,7 @@ public: bool isActive() const; - /* Returns true if ICE has completed succesfully. */ + /* Returns true if ICE has completed successfully. */ bool hasCompleted() const; /* Returns true if ICE is running. */ diff --git a/src/nat/nat-policy.cpp b/src/nat/nat-policy.cpp index 88f02f41aa985f3293c020504f9e85347ecf5de5..fcdfba2a9df0967684526f7a5020af8684451d98 100644 --- a/src/nat/nat-policy.cpp +++ b/src/nat/nat-policy.cpp @@ -21,6 +21,7 @@ #include "nat-policy.h" #include <cstring> +#include <regex> #include "core/core.h" #include "private.h" @@ -368,17 +369,16 @@ bool NatPolicy::processJsonConfigurationResponse(const std::shared_ptr<NatPolicy if (root.isMember(urisKey) && root[urisKey].isArray() && !root[urisKey].empty()) { auto firstUri = root[urisKey].begin(); if (firstUri->isString()) { - if (belle_generic_uri_t *uri = belle_generic_uri_parse(firstUri->asString().c_str())) { - std::string hostname = belle_generic_uri_get_host(uri); - int port = belle_generic_uri_get_port(uri); - belle_sip_object_unref(uri); - if (!hostname.empty()) { - if (port == 0) { - sharedNatPolicy->mStunServer = hostname; - } else { - sharedNatPolicy->mStunServer = hostname + ":" + std::to_string(port); - } - } + auto uri = firstUri->asString(); + /* This regex pattern extracts the part after "turn:" and before "?" in a TURN URI. + * ^turn: -> Matches the literal "turn:" at the beginning of the string. + * ([^?]+) -> Captures everything after "turn:" up to (but not including) the first "?". + * The captured group (match[1]) contains the desired hostname and port. + * */ + std::regex pattern(R"(^turn:([^?]+))"); + std::smatch match; + if (std::regex_search(uri, match, pattern)) { + sharedNatPolicy->mStunServer = match[1].str(); } } } @@ -395,18 +395,23 @@ bool NatPolicy::processJsonConfigurationResponse(const std::shared_ptr<NatPolicy } void NatPolicy::updateTurnConfiguration(const std::function<void(bool)> &completionRoutine) { - mCompletionRoutine = completionRoutine; - auto &httpClient = getCore()->getHttpClient(); - auto &httpRequest = httpClient.createRequest("GET", mTurnConfigurationEndpoint); - - std::weak_ptr<NatPolicy> natPolicyRef = shared_from_this(); - httpRequest.execute([natPolicyRef, completionRoutine](const HttpResponse &response) -> void { - auto sharedNatPolicy = natPolicyRef.lock(); - if (sharedNatPolicy) { - bool ret = sharedNatPolicy->processJsonConfigurationResponse(sharedNatPolicy, response); - if (sharedNatPolicy->mCompletionRoutine) sharedNatPolicy->mCompletionRoutine(ret); - } - }); + try { + mCompletionRoutine = completionRoutine; + auto &httpClient = getCore()->getHttpClient(); + auto &httpRequest = httpClient.createRequest("GET", mTurnConfigurationEndpoint); + + std::weak_ptr<NatPolicy> natPolicyRef = shared_from_this(); + httpRequest.execute([natPolicyRef](const HttpResponse &response) -> void { + auto sharedNatPolicy = natPolicyRef.lock(); + if (sharedNatPolicy) { + bool ret = sharedNatPolicy->processJsonConfigurationResponse(sharedNatPolicy, response); + if (sharedNatPolicy->mCompletionRoutine) sharedNatPolicy->mCompletionRoutine(ret); + } + }); + } catch (const std::exception &e) { + lError() << __func__ + << ": Error while creating or sending HTTP request to update TURN configuration : " << e.what(); + } } bool NatPolicy::needToUpdateTurnConfiguration() { diff --git a/src/sal/call-op.cpp b/src/sal/call-op.cpp index 3258180b11aacbd9d5b9c7c1fe9346a08ec72cb9..35d9b594cfb7615673f8f66b0668e3f8e7de7978 100644 --- a/src/sal/call-op.cpp +++ b/src/sal/call-op.cpp @@ -182,6 +182,7 @@ void SalCallOp::fillInvite(belle_sip_request_t *invite) { // For backward compatibility, always set SDP as first content in the multipart. if (body.getContentType() == ContentType::Sdp) { contents.push_front(&body); + if (getSal()->mediaDisabled()) mSdpOffering = true; } else { contents.push_back(&body); } diff --git a/src/sal/event-op.cpp b/src/sal/event-op.cpp index 1766b52f272236332773ab51a4d8d7982897c204..76a6a6642eb6e9b5ad91ae668d59be108197553e 100644 --- a/src/sal/event-op.cpp +++ b/src/sal/event-op.cpp @@ -304,6 +304,7 @@ void SalSubscribeOp::handleSubscribeResponse(unsigned int statusCode, const char } int SalSubscribeOp::subscribe(const string &eventName, int expires, const SalBodyHandler *bodyHandler) { + mDir = Dir::Outgoing; if (!mDialog) { fillCallbacks(); auto request = buildRequest("SUBSCRIBE"); @@ -331,11 +332,13 @@ int SalSubscribeOp::subscribe(const string &eventName, int expires, const SalBod } int SalSubscribeOp::accept() { + mDir = Dir::Incoming; if (mPendingServerTransaction) { auto request = belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(mPendingServerTransaction)); auto expiresHeader = belle_sip_message_get_header_by_type(request, belle_sip_header_expires_t); auto response = createResponseFromRequest(request, 200); belle_sip_message_add_header(BELLE_SIP_MESSAGE(response), BELLE_SIP_HEADER(expiresHeader)); + belle_sip_message_add_header(BELLE_SIP_MESSAGE(response), BELLE_SIP_HEADER(createContact())); belle_sip_server_transaction_send_response(mPendingServerTransaction, response); } return 0; @@ -546,6 +549,7 @@ void SalPublishOp::publishRefresherListenerCb(BCTBX_UNUSED(belle_sip_refresher_t } int SalPublishOp::publish(const string &eventName, int expires, const SalBodyHandler *bodyHandler) { + mDir = Dir::Outgoing; if (!mRefresher || !belle_sip_refresher_get_transaction(mRefresher)) { fillCallbacks(); auto request = buildRequest("PUBLISH"); @@ -572,12 +576,14 @@ int SalPublishOp::publish(const string &eventName, int expires, const SalBodyHan } int SalPublishOp::accept() { + mDir = Dir::Incoming; if (mPendingServerTransaction) { auto request = belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(mPendingServerTransaction)); auto expiresHeader = belle_sip_message_get_header_by_type(request, belle_sip_header_expires_t); int expires = expiresHeader ? belle_sip_header_expires_get_expires(expiresHeader) : 600; auto response = createResponseFromRequest(request, 200); + belle_sip_message_add_header(BELLE_SIP_MESSAGE(response), BELLE_SIP_HEADER(createContact())); if (expires > 0) { belle_sip_message_add_header(BELLE_SIP_MESSAGE(response), belle_sip_header_create("SIP-ETag", mETag.c_str())); diff --git a/src/sal/offeranswer.cpp b/src/sal/offeranswer.cpp index 043c8af8869751ced2d19f86649dc99298444ffb..059926e9f5965870aa56497b0b480839965b16e6 100644 --- a/src/sal/offeranswer.cpp +++ b/src/sal/offeranswer.cpp @@ -566,6 +566,16 @@ SalStreamDescription OfferAnswerEngine::initiateOutgoingStream(const SalStreamDe return result; } +int OfferAnswerEngine::getExtensionId(int localExtensionId, int remoteExtensionId) { + // If any of the two is set to 0 then one (or both) of the party does not support the extension. + if (localExtensionId == 0 || remoteExtensionId == 0) return 0; + + // If they are not configured with the same id, then use the remote + if (localExtensionId != remoteExtensionId) return remoteExtensionId; + + return localExtensionId; +} + OfferAnswerEngine::optional_sal_stream_configuration OfferAnswerEngine::initiateOutgoingConfiguration( const SalStreamDescription &local_offer, const SalStreamDescription &remote_answer, @@ -666,15 +676,12 @@ OfferAnswerEngine::optional_sal_stream_configuration OfferAnswerEngine::initiate } } - resultCfg.mixer_to_client_extension_id = (remoteCfg.mixer_to_client_extension_id == 0) - ? localCfg.mixer_to_client_extension_id - : remoteCfg.mixer_to_client_extension_id; - resultCfg.client_to_mixer_extension_id = (remoteCfg.client_to_mixer_extension_id == 0) - ? localCfg.client_to_mixer_extension_id - : remoteCfg.client_to_mixer_extension_id; - resultCfg.frame_marking_extension_id = (remoteCfg.frame_marking_extension_id == 0) - ? localCfg.frame_marking_extension_id - : remoteCfg.frame_marking_extension_id; + resultCfg.mixer_to_client_extension_id = + getExtensionId(localCfg.mixer_to_client_extension_id, remoteCfg.mixer_to_client_extension_id); + resultCfg.client_to_mixer_extension_id = + getExtensionId(localCfg.client_to_mixer_extension_id, remoteCfg.client_to_mixer_extension_id); + resultCfg.frame_marking_extension_id = + getExtensionId(localCfg.frame_marking_extension_id, remoteCfg.frame_marking_extension_id); resultCfg.conference_ssrc = remoteCfg.conference_ssrc; @@ -924,15 +931,12 @@ OfferAnswerEngine::optional_sal_stream_configuration OfferAnswerEngine::initiate resultCfg.rtcp_mux = remoteCfg.rtcp_mux && localCfg.rtcp_mux; - resultCfg.mixer_to_client_extension_id = (localCfg.mixer_to_client_extension_id == 0) - ? remoteCfg.mixer_to_client_extension_id - : localCfg.mixer_to_client_extension_id; - resultCfg.client_to_mixer_extension_id = (localCfg.client_to_mixer_extension_id == 0) - ? remoteCfg.client_to_mixer_extension_id - : localCfg.client_to_mixer_extension_id; - resultCfg.frame_marking_extension_id = (localCfg.frame_marking_extension_id == 0) - ? remoteCfg.frame_marking_extension_id - : localCfg.frame_marking_extension_id; + resultCfg.mixer_to_client_extension_id = + getExtensionId(localCfg.mixer_to_client_extension_id, remoteCfg.mixer_to_client_extension_id); + resultCfg.client_to_mixer_extension_id = + getExtensionId(localCfg.client_to_mixer_extension_id, remoteCfg.client_to_mixer_extension_id); + resultCfg.frame_marking_extension_id = + getExtensionId(localCfg.frame_marking_extension_id, remoteCfg.frame_marking_extension_id); resultCfg.conference_ssrc = localCfg.conference_ssrc; diff --git a/src/sal/offeranswer.h b/src/sal/offeranswer.h index c2edc0dd20b9f76460678d067c1f19ae41212785..bcee5602ebf18c9cffe2a6c7a43a5df18abd66a3 100644 --- a/src/sal/offeranswer.h +++ b/src/sal/offeranswer.h @@ -120,6 +120,9 @@ private: const SalStreamDescription &remoteStream, const unsigned int &remoteCfgIdx, SalStreamConfiguration &resultCfg); + + static int getExtensionId(int localExtensionId, int remoteExtensionId); + MSFactory *mMsFactory = nullptr; bool mUseOneMatchingCodec = false; bool mAnswerWithOwnNumbering = false; diff --git a/src/sal/op.cpp b/src/sal/op.cpp index 795eed04d024f7b1979c8708ec734c26f980113e..26e33ab8e668d8af6cd4de205bb4dbb31710c3cd 100644 --- a/src/sal/op.cpp +++ b/src/sal/op.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2024 Belledonne Communications SARL. + * Copyright (c) 2010-2025 Belledonne Communications SARL. * * This file is part of Liblinphone * (see https://gitlab.linphone.org/BC/public/liblinphone). @@ -18,12 +18,13 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include "sal/op.h" + #include <cstring> #include "bellesip_sal/sal_impl.h" #include "c-wrapper/internal/c-tools.h" #include "content/header/header-param.h" -#include "sal/op.h" using namespace std; @@ -52,6 +53,7 @@ SalOp::~SalOp() { if (mPendingServerTransaction) belle_sip_object_unref(mPendingServerTransaction); if (mPendingUpdateServerTransaction) belle_sip_object_unref(mPendingUpdateServerTransaction); if (mEvent) belle_sip_object_unref(mEvent); + if (mSupportedHeader) belle_sip_object_unref(mSupportedHeader); sal_error_info_reset(&mErrorInfo); sal_error_info_reset(&mReasonErrorInfo); @@ -570,7 +572,9 @@ belle_sip_request_t *SalOp::buildRequest(const string &method) { belle_sip_message_add_header(BELLE_SIP_MESSAGE(request), BELLE_SIP_HEADER(privacyHeader)); } - if (mRoot->mSupportedHeader) { + if (mUseSupportedTags) { + if (mSupportedHeader) belle_sip_message_add_header(BELLE_SIP_MESSAGE(request), mSupportedHeader); + } else if (mRoot->mSupportedHeader) { belle_sip_message_add_header(BELLE_SIP_MESSAGE(request), mRoot->mSupportedHeader); } return request; @@ -1182,4 +1186,15 @@ void SalOp::setChannelBankIdentifier(const std::string &identifier) { mChannelBankIdentifier = identifier; } +void SalOp::makeSupportedHeader(const std::list<std::string> &supportedTags) { + if (mSupportedHeader) { + belle_sip_object_unref(mSupportedHeader); + mSupportedHeader = nullptr; + } + mUseSupportedTags = true; + if (supportedTags.empty()) return; + mSupportedHeader = Sal::createSupportedHeader(supportedTags); + if (mSupportedHeader) belle_sip_object_ref(mSupportedHeader); +} + LINPHONE_END_NAMESPACE diff --git a/src/sal/op.h b/src/sal/op.h index 8c97aa5d589d181ece7cd2f402ee75ec853e7cf8..f3d3022933037e2efb294b125cb041a99ae54532 100644 --- a/src/sal/op.h +++ b/src/sal/op.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2024 Belledonne Communications SARL. + * Copyright (c) 2010-2025 Belledonne Communications SARL. * * This file is part of Liblinphone * (see https://gitlab.linphone.org/BC/public/liblinphone). @@ -254,6 +254,8 @@ public: return mDialog; } + void makeSupportedHeader(const std::list<std::string> &supportedTags); + protected: enum class State { Early = 0, @@ -369,6 +371,7 @@ protected: std::string mEntityTag; // As defined by rfc3903 (I.E publih) std::string mChannelBankIdentifier; ReleaseCb mReleaseCb = nullptr; + belle_sip_header_t *mSupportedHeader = nullptr; const belle_sip_listener_callbacks_t *mCallbacks = nullptr; SalErrorInfo mErrorInfo; @@ -403,6 +406,7 @@ protected: bool mSupportsSessionTimers = false; bool mOpReleased = false; bool mOwnsDialog = true; + bool mUseSupportedTags = false; friend class Sal; friend class Call; diff --git a/src/sal/sal.cpp b/src/sal/sal.cpp index 6ce866861cdfb57ea45d4bb016a51e1e1acea33d..f4c7c10f76dabfa813353b8f1f2c1ac6761e7797 100644 --- a/src/sal/sal.cpp +++ b/src/sal/sal.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2022 Belledonne Communications SARL. + * Copyright (c) 2010-2025 Belledonne Communications SARL. * * This file is part of Liblinphone * (see https://gitlab.linphone.org/BC/public/liblinphone). @@ -18,26 +18,23 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include "sal/sal.h" + #include <algorithm> #include "bctoolbox/crypto.hh" #include "bctoolbox/defs.h" #include "bctoolbox/utils.hh" - #include "bellesip_sal/sal_impl.h" +#include "account/account.h" +#include "c-wrapper/internal/c-tools.h" #include "private.h" #include "sal/call-op.h" #include "sal/event-op.h" #include "sal/message-op.h" #include "sal/presence-op.h" #include "sal/refer-op.h" -#include "sal/sal.h" -#include "sal/sal_media_description.h" -#include "tester_utils.h" - -#include "account/account.h" -#include "c-wrapper/internal/c-tools.h" using namespace std; @@ -615,14 +612,26 @@ bool Sal::isTransportAvailable(SalTransport tr) { } } +belle_sip_header_t *Sal::createSupportedHeader(const list<string> &tags) { + std::ostringstream result; + for (auto &tag : tags) { + if (tag != tags.front()) { + result << ", "; + } + result << tag; + } + + return belle_sip_header_create("Supported", result.str().c_str()); +} + void Sal::makeSupportedHeader() { if (mSupportedHeader) { belle_sip_object_unref(mSupportedHeader); mSupportedHeader = nullptr; } - string tags = Utils::join(mSupportedTags, ", "); - if (tags.empty()) return; - mSupportedHeader = belle_sip_header_create("Supported", tags.c_str()); + if (mSupportedTags.empty()) return; + std::list<string> tagList(mSupportedTags.begin(), mSupportedTags.end()); + mSupportedHeader = createSupportedHeader(tagList); if (mSupportedHeader) belle_sip_object_ref(mSupportedHeader); } diff --git a/src/sal/sal.h b/src/sal/sal.h index d2a6b44a69f7e08637001413814ed8894dcc8e88..4084a99a44691f5bc32941997271596ad3015b81 100644 --- a/src/sal/sal.h +++ b/src/sal/sal.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2022 Belledonne Communications SARL. + * Copyright (c) 2010-2025 Belledonne Communications SARL. * * This file is part of Liblinphone * (see https://gitlab.linphone.org/BC/public/liblinphone). @@ -460,6 +460,7 @@ private: void setTlsProperties(); int addListenPort(SalAddress *addr, bool isTunneled); + static belle_sip_header_t *createSupportedHeader(const std::list<std::string> &tags); void makeSupportedHeader(); void addPendingAuth(SalOp *op); void removePendingAuth(SalOp *op); diff --git a/src/sal/sal_stream_description.cpp b/src/sal/sal_stream_description.cpp index a593608ca61d3111c412605655006d415962327d..9f4ee3b500a5155cc8ac0a690d7a9584436b1a9c 100644 --- a/src/sal/sal_stream_description.cpp +++ b/src/sal/sal_stream_description.cpp @@ -757,6 +757,11 @@ void SalStreamDescription::createActualCfg(const SalMediaDescription *salMediaDe char *extmap_urn = (char *)bctbx_malloc0(strlen(attr_value) + 1); int value = 0; if (sscanf(attr_value, "%i %s", &value, extmap_urn) > 0) { + if (value < 1 || value > 15) { + lError() << "Extmap value out of range for \"" << extmap_urn << "\""; + continue; + } + if (strcasecmp(extmap_urn, "urn:ietf:params:rtp-hdrext:sdes:mid") == 0) { actualCfg.mid_rtp_ext_header_id = value; } else if (strcasecmp(extmap_urn, "urn:ietf:params:rtp-hdrext:csrc-audio-level") == 0) { @@ -1213,7 +1218,7 @@ SalStreamDescription::toSdpMediaDescription(const SalMediaDescription *salMediaD } if (actualCfg.conference_ssrc) { - char *ssrc_attribute = ms_strdup_printf("%u", actualCfg.conference_ssrc); + char *ssrc_attribute = ms_strdup_printf("%u cname:%s", actualCfg.conference_ssrc, actualCfg.rtcp_cname.c_str()); belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create("ssrc", ssrc_attribute)); ms_free(ssrc_attribute); } diff --git a/src/utils/utils.cpp b/src/utils/utils.cpp index 438af6f7f742bb6871522f565241e3e1b87bdbc1..b9c30d268584cf2256ed3166ea2b48ea94dc7b1d 100644 --- a/src/utils/utils.cpp +++ b/src/utils/utils.cpp @@ -24,8 +24,10 @@ #include <list> #include <sstream> -#include <bctoolbox/charconv.h> -#include <bctoolbox/port.h> +#include "bctoolbox/charconv.h" +#include "bctoolbox/defs.h" +#include "bctoolbox/port.h" +#include "bctoolbox/vfs_encrypted.hh" #include "c-wrapper/internal/c-tools.h" #include "conference/conference-info.h" @@ -164,6 +166,11 @@ bool Utils::containsInsensitive(const string &haystack, const string &needle) { return lowercaseHaystack.find(lowercaseNeedle) != std::string::npos; } +bool Utils::endsWith(const string &haystack, const string &needle) { + if (needle.size() > haystack.size()) return false; + return std::equal(needle.rbegin(), needle.rend(), haystack.rbegin()); +} + std::vector<string> Utils::stringToLower(const std::vector<string> &strs) { std::vector<std::string> results; for (const auto &str : strs) { @@ -467,7 +474,7 @@ std::shared_ptr<Address> Utils::getSipAddress(const std::string &str, const std: return address; } -std::string Utils::getXconId(const std::shared_ptr<Address> &address) { +std::string Utils::getXconId(const std::shared_ptr<const Address> &address) { // CCMP user ID can only be made up by only 40 different characters // (https://www.rfc-editor.org/rfc/rfc6501#section-4.6.5) We are therefore taking the identity and transform invalid // characters into valid ones in a way that the resulting string is unique for any given mIdentity string @@ -635,4 +642,38 @@ Utils::configGetStringList(LpConfig *lpconfig, const std::string §ion, const return cppList; } +string Utils::getFileExtension(const string &filePath) { + size_t pos = filePath.rfind('.', filePath.length()); + if (pos != string::npos) { + return (filePath.substr(pos + 1, filePath.length() - pos)); + } + + return ""; +} + +string Utils::convertFileToBase64(const string &filePath) { + auto file = bctbx_file_open(bctbx_vfs_get_standard(), filePath.c_str(), "r"); + size_t file_size = (size_t)bctbx_file_size(file); + unsigned char *buffer = (unsigned char *)bctbx_malloc(file_size); + bctbx_file_read(file, buffer, file_size, 0); + bctbx_file_close(file); + + size_t b64Size = 0; + bctbx_base64_encode(nullptr, &b64Size, buffer, file_size); + unsigned char *base64Buffer = (unsigned char *)bctbx_malloc(b64Size + 1); + bctbx_base64_encode(base64Buffer, &b64Size, buffer, file_size); + base64Buffer[b64Size] = '\0'; + + string base64AsString = "data:image/"; + string fileExtension = Utils::getFileExtension(filePath); + base64AsString.append(fileExtension); + base64AsString.append(";base64,"); + base64AsString.append(std::string((char *)base64Buffer)); + + bctbx_free(buffer); + bctbx_free(base64Buffer); + + return base64AsString; +} + LINPHONE_END_NAMESPACE diff --git a/src/utils/xml-utils.cpp b/src/utils/xml-utils.cpp index 4df445bd849bfa2b657ee3287feaba54b4c144c5..1029a125f1a83fcf9cfca8aeb6fa5f86d724971b 100644 --- a/src/utils/xml-utils.cpp +++ b/src/utils/xml-utils.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2022 Belledonne Communications SARL. + * Copyright (c) 2010-2025 Belledonne Communications SARL. * * This file is part of Liblinphone * (see https://gitlab.linphone.org/BC/public/liblinphone). @@ -19,6 +19,7 @@ */ #include "utils/xml-utils.h" + #include "address/address.h" #include "core/core.h" #include "http/http-client.h" @@ -49,37 +50,27 @@ MediaStatusType XmlUtils::mediaDirectionToMediaStatus(LinphoneMediaDirection dir return MediaStatusType::sendrecv; } -bool XmlUtils::sendCcmpRequest(const std::shared_ptr<Core> core, - const std::string ccmpServerUrl, - const std::shared_ptr<Address> from, - const std::string body, - belle_http_request_listener_t *listener) { - belle_http_request_t *req = - belle_http_request_create("POST", belle_generic_uri_parse(ccmpServerUrl.c_str()), - belle_sip_header_content_type_create("application", "ccmp+xml"), nullptr, nullptr); - if (!req) { - lError() << "Cannot create a http request from config url [" << ccmpServerUrl << "]"; - return false; - } +bool XmlUtils::sendCcmpRequest(const std::shared_ptr<Core> &core, + const std::string &ccmpServerUrl, + const std::shared_ptr<const Address> &from, + const std::string &body, + const std::function<void(const HttpResponse &)> &listener) { + try { + auto &httpClient = core->getHttpClient(); + auto &httpRequest = httpClient.createRequest("POST", ccmpServerUrl); + httpRequest.addHeader("From", from->asStringUriOnly()); - const auto fromStr = from->asStringUriOnly(); - belle_sip_message_add_header(BELLE_SIP_MESSAGE(req), belle_http_header_create("From", fromStr.c_str())); + if (!body.empty()) { + auto content = Content(ContentType("application/ccmp+xml"), body); + httpRequest.setBody(content); + } - if (!body.empty()) { - belle_sip_message_set_body(BELLE_SIP_MESSAGE(req), body.c_str(), body.size()); + httpRequest.execute([listener](const HttpResponse &response) -> void { listener(response); }); + } catch (const std::exception &e) { + lError() << __func__ << ": Error while sending CCMP request : " << e.what(); + return false; } - // Set the same User-Agent header as for the SAL - belle_sip_header_user_agent_t *userAgentHeader = belle_sip_header_user_agent_new(); - belle_sip_object_ref(userAgentHeader); - belle_sip_header_user_agent_set_products(userAgentHeader, nullptr); - const auto cCore = core->getCCore(); - belle_sip_header_user_agent_add_product(userAgentHeader, linphone_core_get_user_agent(cCore)); - belle_sip_message_add_header(BELLE_SIP_MESSAGE(req), BELLE_SIP_HEADER(userAgentHeader)); - - belle_http_provider_send_request(core->getHttpClient().getProvider(), req, listener); - belle_sip_object_data_set(BELLE_SIP_OBJECT(req), "listener", listener, belle_sip_object_unref); - belle_sip_object_unref(userAgentHeader); return true; } diff --git a/src/utils/xml-utils.h b/src/utils/xml-utils.h index e8f2ee243f8000f2b18869280a9eb06ea14fbcdd..016bde43b88bfda41f3f14a522aebd217915d369 100644 --- a/src/utils/xml-utils.h +++ b/src/utils/xml-utils.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2024 Belledonne Communications SARL. + * Copyright (c) 2010-2025 Belledonne Communications SARL. * * This file is part of Liblinphone * (see https://gitlab.linphone.org/BC/public/liblinphone). @@ -23,6 +23,7 @@ #include <belle-sip/http-listener.h> +#include "http/http-client.h" #include "linphone/types.h" #include "xml/conference-info.h" @@ -36,11 +37,12 @@ class Address; namespace XmlUtils { LinphoneMediaDirection mediaStatusToMediaDirection(Xsd::ConferenceInfo::MediaStatusType status); Xsd::ConferenceInfo::MediaStatusType mediaDirectionToMediaStatus(LinphoneMediaDirection direction); -bool sendCcmpRequest(const std::shared_ptr<Core> core, - const std::string ccmpServerUrl, - const std::shared_ptr<Address> from, - const std::string body, - belle_http_request_listener_t *listener); +bool sendCcmpRequest(const std::shared_ptr<Core> &core, + const std::string &ccmpServerUrl, + const std::shared_ptr<const Address> &from, + const std::string &body, + const std::function<void(const HttpResponse &)> &listener); + } // namespace XmlUtils LINPHONE_END_NAMESPACE diff --git a/src/vcard/carddav-context.cpp b/src/vcard/carddav-context.cpp index 331da43ba4d7a4db19b941952800696a2af465bc..3e98190663d3af00c4819e84c148df0307ad1b2b 100644 --- a/src/vcard/carddav-context.cpp +++ b/src/vcard/carddav-context.cpp @@ -56,7 +56,8 @@ void CardDAVContext::deleteVcard(const shared_ptr<Friend> &f) { shared_ptr<Vcard> vcard = f->getVcard(); if (vcard && !vcard->getUid().empty() && !vcard->getEtag().empty()) { if (vcard->getUrl().empty()) { - string url = CardDAVContext::generateUrlFromServerAddressAndUid(mSyncUri); + string url = generateUrlFromServerAddressAndUid(mSyncUri); + lInfo() << "[CardDAV] vCard newly generated URL is [" << url << "]"; if (url.empty()) { const string msg = "vCard doesn't have an URL, and friendlist doesn't have a CardDAV server set either, " @@ -67,6 +68,8 @@ void CardDAVContext::deleteVcard(const shared_ptr<Friend> &f) { } else { vcard->setUrl(url); } + } else { + lInfo() << "[CardDAV] vCard existing URL is [" << vcard->getUrl() << "]"; } sendQuery(CardDAVQuery::createDeleteQuery(this, vcard)); } else { @@ -90,9 +93,15 @@ void CardDAVContext::putVcard(const shared_ptr<Friend> &f) { shared_ptr<Vcard> vcard = f->getVcard(); if (vcard) { - if (vcard->getUid().empty()) vcard->generateUniqueId(); + if (vcard->getUid().empty()) { + vcard->generateUniqueId(); + lInfo() << "[CardDAV] vCard newly generated UID is [" << vcard->getUid() << "]"; + } else { + lInfo() << "[CardDAV] vCard existing UID is [" << vcard->getUid() << "]"; + } if (vcard->getUrl().empty()) { - string url = CardDAVContext::generateUrlFromServerAddressAndUid(mSyncUri); + string url = generateUrlFromServerAddressAndUid(mSyncUri); + lInfo() << "[CardDAV] vCard newly generated URL is [" << url << "]"; if (url.empty()) { const string msg = "vCard doesn't have an URL, and friendlist doesn't have a CardDAV server set either, can't push it"; @@ -102,6 +111,8 @@ void CardDAVContext::putVcard(const shared_ptr<Friend> &f) { } else { vcard->setUrl(url); } + } else { + lInfo() << "[CardDAV] vCard existing URL is [" << vcard->getUrl() << "]"; } shared_ptr<CardDAVQuery> query = CardDAVQuery::createPutQuery(this, vcard); query->setUserData(linphone_friend_ref(f->toC())); @@ -170,7 +181,7 @@ void CardDAVContext::clientToServerSyncDone(bool success, const string &msg) { void CardDAVContext::userPrincipalUrlRetrieved(string principalUrl) { if (!principalUrl.empty()) { - string fullUrl = mScheme + "://" + mHost + principalUrl; + string fullUrl = getUrlSchemeHostAndPort() + principalUrl; lDebug() << "[CardDAV] User principal URL is [" << fullUrl << "], updating sync URI and querying address book home"; mSyncUri = fullUrl; @@ -189,7 +200,7 @@ void CardDAVContext::userPrincipalUrlRetrieved(string principalUrl) { void CardDAVContext::userAddressBookHomeUrlRetrieved(string addressBookHomeUrl) { if (!addressBookHomeUrl.empty()) { - string fullUrl = mScheme + "://" + mHost + addressBookHomeUrl; + string fullUrl = getUrlSchemeHostAndPort() + addressBookHomeUrl; lDebug() << "[CardDAV] User address book home URL is [" << fullUrl << "], updating sync URI and querying address books list"; mSyncUri = fullUrl; @@ -206,7 +217,7 @@ void CardDAVContext::addressBookUrlAndCtagRetrieved(const list<CardDAVResponse> string url = addressbook.mUrl; string displayName = addressbook.mDisplayName; if (ctag.empty() || ctag != mCtag) { - string fullUrl = mScheme + "://" + mHost + url; + string fullUrl = getUrlSchemeHostAndPort() + url; lInfo() << "[CardDAV] User address book [" << displayName << "] URL is [" << fullUrl << "] has CTAG [" << ctag << "] but our local one is [" << mCtag << "], fetching vCards"; mSyncUri = fullUrl; @@ -287,9 +298,9 @@ void CardDAVContext::queryWellKnown(shared_ptr<CardDAVQuery> query) { char &back = mHost.back(); const char *latestQueryChar = &back; if (latestQueryChar != NULL && strcmp(latestQueryChar, "/") == 0) { - query->mUrl = mScheme + "://" + mHost + wellKnown; + query->mUrl = getUrlSchemeHostAndPort() + wellKnown; } else { - query->mUrl = mScheme + "://" + mHost + "/" + wellKnown; + query->mUrl = getUrlSchemeHostAndPort() + "/" + wellKnown; } lInfo() << "[CardDAV] Trying a well-known query on URL " << query->mUrl; sendQuery(query); @@ -302,8 +313,16 @@ void CardDAVContext::sendQuery(const shared_ptr<CardDAVQuery> &query, bool cance mHttpRequest = nullptr; } mQuery = query; + + string url = query->mUrl; + if (url.rfind("http://", 0) != 0 && url.rfind("https://", 0) != 0) { + lWarning() << "[CardDAV] Request URL [" << url << "] doesn't to be full"; + url = getUrlSchemeHostAndPort() + url; + lWarning() << "[CardDAV] Newly computed request URL is [" << url << "]"; + } + try { - auto &httpRequest = getCore()->getHttpClient().createRequest(query->mMethod, query->mUrl); + auto &httpRequest = getCore()->getHttpClient().createRequest(query->mMethod, url); mHttpRequest = &httpRequest; } catch (const std::exception &e) { lError() << "Could not build http request: " << e.what(); @@ -353,7 +372,9 @@ void CardDAVContext::setSchemeAndHostIfNotDoneYet(shared_ptr<CardDAVQuery> query belle_generic_uri_t *uri = belle_generic_uri_parse(query->mUrl.c_str()); mScheme = belle_generic_uri_get_scheme(uri); mHost = belle_generic_uri_get_host(uri); - lInfo() << "[CardDAV] Extracted scheme [" << mScheme << "] and host [" << mHost << "] from latest query"; + mPort = belle_generic_uri_get_port(uri); + lInfo() << "[CardDAV] Extracted scheme [" << mScheme << "], host [" << mHost << "] and port [" << mPort + << "] from latest query"; } } @@ -362,7 +383,7 @@ void CardDAVContext::processRedirect(shared_ptr<CardDAVQuery> query, const strin string newLocation = location; if (newLocation.rfind(mScheme, 0) != 0 && newLocation.rfind("/", 0) == 0) { // If newLocation doesn't start with http scheme but only starts with '/', recompute full URL - newLocation = mScheme + "://" + mHost + newLocation; + newLocation = getUrlSchemeHostAndPort() + newLocation; } lInfo() << "[CardDAV] Redirecting query from [" << query->mUrl << "] to [" << newLocation << "]"; query->mUrl = newLocation; @@ -493,23 +514,28 @@ void CardDAVContext::vcardsFetched(const list<CardDAVResponse> &vCards) { const list<shared_ptr<Friend>> &friends = mFriendList->getFriends(); list<shared_ptr<Friend>> friendsToRemove; for (const auto &f : friends) { + lDebug() << "[CardDAV] Found friend [" << f->getName() << "] with eTag [" << f->getVcard()->getEtag() << "]"; + shared_ptr<Vcard> vcard = f->getVcard(); + const auto responseIt = find_if(vCardsToPull.cbegin(), vCardsToPull.cend(), [&](const auto &response) { - shared_ptr<Vcard> vcard = f->getVcard(); - if (response.mUrl.empty() || !vcard || vcard->getUrl().empty()) return false; - return response.mUrl == vcard->getUrl(); + if (!vcard || vcard->getUrl().empty()) return false; + + // It's possible response.mUrl only contains the end of the URI (no scheme nor domain), + // so it would be different from vcard->getUrl() + return Utils::endsWith(vcard->getUrl(), response.mUrl); }); if (responseIt == vCardsToPull.cend()) { lInfo() << "[CardDAV] Local friend [" << f->getName() << "] with eTag [" << f->getVcard()->getEtag() << "] isn't in the remote vCard list, will be removed"; friendsToRemove.push_back(f); } else { - shared_ptr<Vcard> vcard = f->getVcard(); const string etag = vcard->getEtag(); - lInfo() << "[CardDAV] Local friend [" << f->getName() << "] eTag is [" << etag - << "], remote vCard eTag is [" << responseIt->mEtag << "]"; if (!etag.empty() && (etag == responseIt->mEtag)) { - lInfo() << "[CardDAV] Contact is already up-to-date, do not ask server for vCard: " << f->getName(); + lInfo() << "[CardDAV] Contact [" << f->getName() << "] is already up-to-date, do not ask server for it"; vCardsToPull.erase(responseIt); + } else { + lInfo() << "[CardDAV] Contact [" << f->getName() << "] local eTag is [" << etag + << "] and the remote one is [" << responseIt->mEtag << "], fetching changes"; } } } @@ -520,6 +546,10 @@ void CardDAVContext::vcardsFetched(const list<CardDAVResponse> &vCards) { mContactRemovedCb(this, f); } } + + for (auto &vcard : vCardsToPull) { + lInfo() << "[CardDAV] Pulling vCard [" << vcard.mUrl << "] with eTag [" << vcard.mEtag << "]"; + } pullVcards(vCardsToPull); } @@ -640,6 +670,13 @@ string CardDAVContext::generateUrlFromServerAddressAndUid(const string &serverUr return url; } +string CardDAVContext::getUrlSchemeHostAndPort() const { + if (mPort > 0 && ((mScheme == "http" && mPort != 80) || (mScheme == "https" && mPort != 443))) { + return mScheme + "://" + mHost + ":" + std::to_string(mPort); + } + return mScheme + "://" + mHost; +} + #ifdef HAVE_XML2 list<CardDAVResponse> CardDAVContext::parseAddressBookUrlAndCtagValueFromXmlResponse(const string &body) { @@ -772,6 +809,10 @@ list<CardDAVResponse> CardDAVContext::parseVcardsEtagsFromXmlResponse(const stri string url = xmlCtx.getTextContent("d:href"); CardDAVResponse response; response.mEtag = etag; + if (url.empty()) { + lError() << "[CardDAV] Found vCard object with eTag [" << etag << "] but no URL, skipping it!"; + continue; + } response.mUrl = url; result.push_back(std::move(response)); lInfo() << "[CardDAV] Found vCard object with eTag [" << etag << "] and URL [" << url << "]"; diff --git a/src/vcard/carddav-context.h b/src/vcard/carddav-context.h index 7976b75ac5c4552a5fd72ca8f8f88e33f6179edf..fb1fd86ced98a8da432f47db05a37762c48028da 100644 --- a/src/vcard/carddav-context.h +++ b/src/vcard/carddav-context.h @@ -107,10 +107,13 @@ private: std::list<CardDAVResponse> parseVcardsEtagsFromXmlResponse(const std::string &body); std::list<CardDAVResponse> parseVcardsFromXmlResponse(const std::string &body); + std::string getUrlSchemeHostAndPort() const; + std::string mCtag = ""; std::string mSyncUri = ""; std::string mScheme = "http"; std::string mHost = ""; + int mPort = 0; bool mWellKnownQueried = true; HttpRequest *mHttpRequest = nullptr; std::shared_ptr<CardDAVQuery> mQuery = nullptr; diff --git a/src/vcard/carddav-query.cpp b/src/vcard/carddav-query.cpp index 1d664d190957084d5f601dfbf93cd4113d059457..887ffdc3dcfa033974e5952e99af4fcb75665bcf 100644 --- a/src/vcard/carddav-query.cpp +++ b/src/vcard/carddav-query.cpp @@ -195,7 +195,7 @@ shared_ptr<CardDAVQuery> CardDAVQuery::createAddressBookCtagPropfindQuery(CardDA shared_ptr<CardDAVQuery> CardDAVQuery::createPutQuery(CardDAVContext *context, const shared_ptr<Vcard> &vcard) { shared_ptr<CardDAVQuery> query = make_shared<CardDAVQuery>(context); query->mIfmatch = vcard->getEtag(); - query->mBody = vcard->asVcard4String(); + query->mBody = vcard->asVcard4StringWithBase64Picture(); query->mMethod = "PUT"; query->mUrl = vcard->getUrl(); query->mType = Type::Put; diff --git a/src/vcard/vcard.cpp b/src/vcard/vcard.cpp index 9fc0adb94fd3778447901d6d7beb98daa14d8d50..47f4a16362387dcfce5e971259df4a9fdeffc24a 100644 --- a/src/vcard/vcard.cpp +++ b/src/vcard/vcard.cpp @@ -18,7 +18,9 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include <bctoolbox/defs.h> +#include "bctoolbox/defs.h" + +#include "linphone/utils/utils.h" #include "friend/friend-phone-number.h" #include "sal/sal.h" @@ -380,7 +382,31 @@ void Vcard::addSipAddress(const string &sipAddress) { } const string &Vcard::asVcard4String() const { - return mBelCard->toFoldedString(); + vcard4String = mBelCard->toFoldedString(); + return vcard4String; +} + +const string &Vcard::asVcard4StringWithBase64Picture() const { + const string &photo = getPhoto(); + if (photo.empty()) { + return asVcard4String(); + } + + if (photo.rfind("file:", 0) == 0) { + string photoPath = photo.substr(5); + lInfo() << "[vCard] PHOTO with local file [" << photo << "] found, converting it to base64"; + + string base64PhotoAsString = Utils::convertFileToBase64(photoPath); + shared_ptr<belcard::BelCard> clone = shared_ptr<belcard::BelCard>( + belcard::BelCardParser::getInstance(mUseVCard3Grammar)->parseOne(mBelCard->toFoldedString())); + const shared_ptr<belcard::BelCardPhoto> clonePhoto = clone->getPhotos().front(); + clonePhoto->setValue(base64PhotoAsString); + + vcard4String = clone->toFoldedString(); + return vcard4String; + } + + return asVcard4String(); } void Vcard::editMainSipAddress(const string &sipAddress) { @@ -627,6 +653,10 @@ const string &Vcard::asVcard4String() const { return emptyString; } +const string &Vcard::asVcard4StringWithBase64Picture() const { + return emptyString; +} + void Vcard::editMainSipAddress(BCTBX_UNUSED(const string &sipAddress)) { } diff --git a/src/vcard/vcard.h b/src/vcard/vcard.h index eaa9a00785a9b093b81956bfc02803a196b391bb..59e7391be224e8ffa35bd876ce2c8983f2938b5a 100644 --- a/src/vcard/vcard.h +++ b/src/vcard/vcard.h @@ -91,6 +91,7 @@ public: void addPhoneNumberWithLabel(const std::shared_ptr<const FriendPhoneNumber> &phoneNumber); void addSipAddress(const std::string &sipAddress); const std::string &asVcard4String() const; + const std::string &asVcard4StringWithBase64Picture() const; void editMainSipAddress(const std::string &sipAddress); bool generateUniqueId(); void removeExtentedPropertiesByName(const std::string &name); @@ -114,6 +115,7 @@ private: std::array<unsigned char, VCARD_MD5_HASH_SIZE> mMd5; #endif /* VCARD_ENABLED */ mutable ListHolder<Address> mSipAddresses; + mutable std::string vcard4String; bool mUseVCard3Grammar = false; }; diff --git a/tester/CMakeLists.txt b/tester/CMakeLists.txt index ea6a2569835efb7d75521705e111cd94e5c4d8df..b58e258193ae5127a96a48e653d955965217d980 100644 --- a/tester/CMakeLists.txt +++ b/tester/CMakeLists.txt @@ -145,8 +145,10 @@ set(RC_FILES rcfiles/claire_rc rcfiles/claire_sips_rc rcfiles/chloe_rc + rcfiles/chloe_dual_proxy_rc rcfiles/conference_focus_rc rcfiles/empty_rc + rcfiles/empty_with_some_db_rc rcfiles/friends_rc rcfiles/groupchat_rc rcfiles/invalid_friends_rc @@ -220,6 +222,7 @@ set(RC_FILES ) set(IMAGE_FILES + images/bc.jpeg images/linphone.svg images/linphonesiteqr.jpg images/linphonesiteqr_captured.jpg diff --git a/tester/account_tester.c b/tester/account_tester.c index 5b165abf59c7d13c7da72faca3ac2e9b0bc92f66..b3ee26ccf746efa305457409579d43d33bfacd9d 100644 --- a/tester/account_tester.c +++ b/tester/account_tester.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2022 Belledonne Communications SARL. + * Copyright (c) 2010-2025 Belledonne Communications SARL. * * This file is part of Liblinphone * (see https://gitlab.linphone.org/BC/public/liblinphone). @@ -345,6 +345,27 @@ static void registration_state_changed_callback_on_account(void) { linphone_core_manager_destroy(marie); } +static void reconnection_after_network_change(void) { + LinphoneCoreManager *marie = linphone_core_manager_new("marie_rc"); + + BC_ASSERT_TRUE(wait_for_until(marie->lc, NULL, &marie->stat.number_of_LinphoneRegistrationOk, 1, 10000)); + + linphone_core_set_network_reachable(marie->lc, FALSE); + linphone_core_set_network_reachable(marie->lc, TRUE); + BC_ASSERT_TRUE(wait_for_until(marie->lc, NULL, &marie->stat.number_of_LinphoneRegistrationOk, 2, 10000)); + /* simulate a transport error*/ + sal_set_send_error(linphone_core_get_sal(marie->lc), -1); + linphone_core_refresh_registers(marie->lc); + BC_ASSERT_TRUE(wait_for_until(marie->lc, NULL, &marie->stat.number_of_LinphoneRegistrationFailed, 1, 10000)); + sal_set_send_error(linphone_core_get_sal(marie->lc), 0); + linphone_core_set_network_reachable(marie->lc, FALSE); + linphone_core_set_network_reachable(marie->lc, TRUE); + + BC_ASSERT_TRUE(wait_for_until(marie->lc, NULL, &marie->stat.number_of_LinphoneRegistrationOk, 3, 5000)); + + linphone_core_manager_destroy(marie); +} + static void no_unregister_when_changing_transport(void) { LinphoneCoreManager *marie = linphone_core_manager_create("marie_rc"); LinphoneCoreManager *pauline = linphone_core_manager_create("pauline_rc"); @@ -373,7 +394,7 @@ static void no_unregister_when_changing_transport(void) { initialPaulineStats = pauline->stat; - // Change pauline tranport + // Change pauline transport LinphoneAccount *pauline_account = linphone_core_get_default_account(pauline->lc); LinphoneAccountParams *params = linphone_account_params_clone(linphone_account_get_params(pauline_account)); linphone_account_params_set_transport(params, LinphoneTransportUdp); @@ -394,7 +415,7 @@ static void no_unregister_when_changing_transport(void) { linphone_core_manager_destroy(pauline); } -static void Unregister_at_stop(void) { +static void unregister_at_stop(void) { LinphoneCoreManager *marie = linphone_core_manager_create("marie_rc"); LinphoneCoreManager *pauline = linphone_core_manager_create("pauline_rc"); @@ -468,7 +489,7 @@ static void account_dependency_to_self(void) { linphone_core_clear_proxy_config(marie->lc); BC_ASSERT_TRUE(wait_for(marie->lc, NULL, &marie->stat.number_of_LinphoneRegistrationCleared, 2)); - LinphoneAccountParams *marie_dependent_params = linphone_account_params_new(marie->lc); + LinphoneAccountParams *marie_dependent_params = linphone_account_params_new(marie->lc, TRUE); linphone_account_params_set_identity_address(marie_dependent_params, marie_secondary_address); linphone_account_params_set_server_addr(marie_dependent_params, "sip:external.example.org:5068;transport=tcp"); @@ -491,6 +512,55 @@ static void account_dependency_to_self(void) { linphone_core_manager_destroy(marie); } +static void supported_tags_handled_by_account_test(bool_t empty_list) { + LinphoneCoreManager *marie = linphone_core_manager_new("marie_rc"); + LinphoneAccount *marie_account = linphone_core_get_default_account(marie->lc); + + BC_ASSERT_TRUE(wait_for_until(marie->lc, NULL, &marie->stat.number_of_LinphoneRegistrationOk, 1, 5000)); + + LinphoneAccountCbs *cbs = linphone_factory_create_account_cbs(linphone_factory_get()); + linphone_account_cbs_set_registration_state_changed(cbs, registration_state_changed_on_account); + linphone_account_add_callbacks(marie_account, cbs); + linphone_account_cbs_unref(cbs); + + // Clone Marie's account parameters to modify them + LinphoneAccountParams *marie_account_params = + linphone_account_params_clone(linphone_account_get_params(marie_account)); + // Define a new list of supported tags + bctbx_list_t *list = NULL; + if (!empty_list) { + list = bctbx_list_append(list, "replaces"); + list = bctbx_list_append(list, "outbound"); + list = bctbx_list_append(list, "path"); + } + // Apply the new list of supported tags to Marie's account + linphone_account_params_set_supported_tags_list(marie_account_params, list); + linphone_account_set_params(marie_account, marie_account_params); + linphone_account_params_unref(marie_account_params); + + // Simulate a network reconnection to trigger re-registration + linphone_core_set_network_reachable(marie->lc, FALSE); + linphone_core_set_network_reachable(marie->lc, TRUE); + + BC_ASSERT_TRUE(wait_for_until(marie->lc, NULL, &marie->stat.number_of_LinphoneRegistrationOk, 3, 5000)); + + // Retrieve Marie's contact address and verify the "gr" parameter is absent + LinphoneAddress *marie_contact_address = linphone_account_get_contact_address(marie_account); + const char *gruu = linphone_address_get_uri_param(marie_contact_address, "gr"); + BC_ASSERT_PTR_NULL(gruu); + + bctbx_list_free(list); + linphone_core_manager_destroy(marie); +} + +static void supported_tags_handled_by_account_without_gruu_test(void) { + supported_tags_handled_by_account_test(false); +} + +static void supported_tags_handled_by_account_empty_list_test(void) { + supported_tags_handled_by_account_test(true); +} + static test_t account_tests[] = { TEST_NO_TAG("Simple account creation", simple_account_creation), TEST_NO_TAG("Simple account creation with removal", simple_account_creation_with_removal), @@ -504,7 +574,12 @@ static test_t account_tests[] = { TEST_NO_TAG("Account dependency to self", account_dependency_to_self), TEST_NO_TAG("Registration state changed callback on account", registration_state_changed_callback_on_account), TEST_NO_TAG("No unregister when changing transport", no_unregister_when_changing_transport), - TEST_NO_TAG("Unregister at stop", Unregister_at_stop)}; + TEST_NO_TAG("Unregister at stop", unregister_at_stop), + TEST_NO_TAG("Account reconnection after multiple network changes", reconnection_after_network_change), + TEST_NO_TAG("Supported tags handled by account (without gruu)", + supported_tags_handled_by_account_without_gruu_test), + TEST_NO_TAG("Supported tags handled by account (empty list)", supported_tags_handled_by_account_empty_list_test), +}; test_suite_t account_test_suite = {"Account", NULL, diff --git a/tester/audio_bypass_tester.c b/tester/audio_bypass_tester.c index 2f7c00b7a4a3b02fae64219ec752693db5507ca4..3e882dbe7272ea2548122067a1abe2b947a15fdb 100644 --- a/tester/audio_bypass_tester.c +++ b/tester/audio_bypass_tester.c @@ -428,7 +428,7 @@ static void only_enable_payload(LinphoneCore *lc, const char *mime, int rate, in /* * set some conservative jitter buffer params to be more robust to late ticks. - * This is important so that the audio comparison is succesful*/ + * This is important so that the audio comparison is successful*/ static void set_jitter_buffer_params(LinphoneCore *lc) { int jitter_buffer_ms = 300; linphone_config_set_int(linphone_core_get_config(lc), "rtp", "jitter_buffer_min_size", jitter_buffer_ms); @@ -514,4 +514,4 @@ test_suite_t audio_bypass_suite = {"Audio Bypass", liblinphone_tester_after_each, sizeof(audio_bypass_tests) / sizeof(audio_bypass_tests[0]), audio_bypass_tests, - 0}; + 2}; diff --git a/tester/audio_video_conference_tester.c b/tester/audio_video_conference_tester.c index 3ddb55850724c13504ce5421664f4e5f835e4c57..55abb9f18955fc6e1c96fd07dd6fa0a4abc67f97 100644 --- a/tester/audio_video_conference_tester.c +++ b/tester/audio_video_conference_tester.c @@ -660,6 +660,16 @@ static void simple_conference_base(LinphoneCoreManager *marie, BC_ASSERT_PTR_NOT_NULL(laure_conference); if (laure_conference) { BC_ASSERT_PTR_NOT_NULL(linphone_conference_get_call(laure_conference)); + LinphoneParticipant *marie_participant = + linphone_conference_find_participant(laure_conference, marie->identity); + if (marie_event_log_enabled) { + BC_ASSERT_PTR_NOT_NULL(marie_participant); + } else { + BC_ASSERT_PTR_NULL(marie_participant); + } + if (marie_participant) { + BC_ASSERT_TRUE(linphone_participant_is_admin(marie_participant)); + } } if (laure_call) { liblinphone_tester_check_rtcp_2(((is_remote_conf) ? focus : marie), laure); @@ -672,6 +682,16 @@ static void simple_conference_base(LinphoneCoreManager *marie, BC_ASSERT_PTR_NOT_NULL(pauline_conference); if (pauline_conference) { BC_ASSERT_PTR_NOT_NULL(linphone_conference_get_call(pauline_conference)); + LinphoneParticipant *marie_participant = + linphone_conference_find_participant(pauline_conference, marie->identity); + if (pauline_event_log_enabled && marie_event_log_enabled) { + BC_ASSERT_PTR_NOT_NULL(marie_participant); + } else { + BC_ASSERT_PTR_NULL(marie_participant); + } + if (marie_participant) { + BC_ASSERT_TRUE(linphone_participant_is_admin(marie_participant)); + } } if (pauline_call) { liblinphone_tester_check_rtcp_2(((is_remote_conf) ? focus : marie), pauline); @@ -685,6 +705,11 @@ static void simple_conference_base(LinphoneCoreManager *marie, if (l_conference) { BC_ASSERT_TRUE(linphone_conference_is_in(l_conference)); BC_ASSERT_EQUAL(linphone_conference_get_participant_count(l_conference), 2, int, "%d"); + LinphoneParticipant *marie_participant = linphone_conference_get_me(l_conference); + BC_ASSERT_PTR_NOT_NULL(marie_participant); + if (marie_participant) { + BC_ASSERT_TRUE(linphone_participant_is_admin(marie_participant)); + } } BC_ASSERT_PTR_NOT_NULL(linphone_core_get_current_call(pauline->lc)); @@ -1711,11 +1736,12 @@ static void simple_conference_with_participant_addition_from_non_admin(void) { for (bctbx_list_t *it = lcs; it; it = bctbx_list_next(it)) { LinphoneCore *c = (LinphoneCore *)bctbx_list_get_data(it); - LinphoneAddress *m_uri = linphone_address_new(linphone_core_get_identity(c)); + LinphoneAddress *m_uri = (linphone_core_conference_server_enabled(c)) + ? linphone_address_clone(marie_conference_address) + : linphone_address_new(linphone_core_get_identity(c)); LinphoneConference *m_conference = linphone_core_search_conference(c, NULL, m_uri, marie_conference_address, NULL); linphone_address_unref(m_uri); - BC_ASSERT_PTR_NOT_NULL(m_conference); if (m_conference) { BC_ASSERT_EQUAL(linphone_conference_get_participant_count(m_conference), @@ -2112,12 +2138,13 @@ static void simple_conference_with_one_participant_base(bool_t local) { const LinphoneAddress *marie_conference_address = linphone_conference_get_conference_address(conf); for (bctbx_list_t *it = all_manangers_in_conf; it; it = bctbx_list_next(it)) { LinphoneCoreManager *m = (LinphoneCoreManager *)bctbx_list_get_data(it); - - LinphoneAddress *uri = linphone_address_new(linphone_core_get_identity(m->lc)); + LinphoneAddress *m_uri = (linphone_core_conference_server_enabled(m->lc)) + ? linphone_address_clone(marie_conference_address) + : linphone_address_new(linphone_core_get_identity(m->lc)); LinphoneConference *conference = - linphone_core_search_conference(m->lc, NULL, uri, marie_conference_address, NULL); + linphone_core_search_conference(m->lc, NULL, m_uri, marie_conference_address, NULL); BC_ASSERT_PTR_NOT_NULL(conference); - linphone_address_unref(uri); + linphone_address_unref(m_uri); if (conference) { int no_parts = (local || (m == marie)) ? 3 : 2; BC_ASSERT_EQUAL(linphone_conference_get_participant_count(conference), no_parts, int, "%d"); @@ -2145,11 +2172,13 @@ static void simple_conference_with_one_participant_base(bool_t local) { BC_ASSERT_TRUE(wait_for_list(lcs, &m->stat.number_of_participants_removed, 1, 3000)); BC_ASSERT_TRUE(wait_for_list(lcs, &m->stat.number_of_participant_devices_removed, 1, 3000)); - LinphoneAddress *uri = linphone_address_new(linphone_core_get_identity(m->lc)); + LinphoneAddress *m_uri = (linphone_core_conference_server_enabled(m->lc)) + ? linphone_address_clone(marie_conference_address) + : linphone_address_new(linphone_core_get_identity(m->lc)); LinphoneConference *conference = - linphone_core_search_conference(m->lc, NULL, uri, marie_conference_address, NULL); + linphone_core_search_conference(m->lc, NULL, m_uri, marie_conference_address, NULL); BC_ASSERT_PTR_NOT_NULL(conference); - linphone_address_unref(uri); + linphone_address_unref(m_uri); if (conference) { BC_ASSERT_EQUAL(linphone_conference_get_participant_count(conference), no_parts, int, "%d"); } @@ -2171,11 +2200,13 @@ static void simple_conference_with_one_participant_base(bool_t local) { BC_ASSERT_TRUE(wait_for_list(lcs, &m->stat.number_of_participants_removed, 2, 3000)); BC_ASSERT_TRUE(wait_for_list(lcs, &m->stat.number_of_participant_devices_removed, 2, 3000)); int no_parts = (local || (m == marie)) ? 1 : 0; - LinphoneAddress *uri = linphone_address_new(linphone_core_get_identity(m->lc)); + LinphoneAddress *m_uri = (linphone_core_conference_server_enabled(m->lc)) + ? linphone_address_clone(marie_conference_address) + : linphone_address_new(linphone_core_get_identity(m->lc)); LinphoneConference *conference = - linphone_core_search_conference(m->lc, NULL, uri, marie_conference_address, NULL); + linphone_core_search_conference(m->lc, NULL, m_uri, marie_conference_address, NULL); BC_ASSERT_PTR_NOT_NULL(conference); - linphone_address_unref(uri); + linphone_address_unref(m_uri); if (conference) { BC_ASSERT_EQUAL(linphone_conference_get_participant_count(conference), no_parts, int, "%d"); } @@ -2334,11 +2365,9 @@ static void simple_conference_with_user_defined_layout(const LinphoneConferenceL all_manangers_in_conf = bctbx_list_append(all_manangers_in_conf, marie); const LinphoneAddress *marie_conference_address = linphone_conference_get_conference_address(conf); - LinphoneAddress *focus_identity = linphone_address_new(linphone_core_get_identity(focus_mgr->lc)); LinphoneConference *focus_conference = - linphone_core_search_conference(focus_mgr->lc, NULL, focus_identity, marie_conference_address, NULL); + linphone_core_search_conference(focus_mgr->lc, NULL, marie_conference_address, marie_conference_address, NULL); BC_ASSERT_PTR_NOT_NULL(focus_conference); - linphone_address_unref(focus_identity); for (bctbx_list_t *it = all_manangers_in_conf; it; it = bctbx_list_next(it)) { LinphoneCoreManager *m = (LinphoneCoreManager *)bctbx_list_get_data(it); @@ -2476,11 +2505,9 @@ static void simple_conference_with_user_defined_layout(const LinphoneConferenceL BC_ASSERT_EQUAL(new_layout, remote_conf_layout, int, "%d"); } - LinphoneAddress *furi = linphone_address_new(linphone_core_get_identity(focus_mgr->lc)); - LinphoneConference *fconf = - linphone_core_search_conference(focus_mgr->lc, NULL, furi, local_conference_address, NULL); + LinphoneConference *fconf = linphone_core_search_conference(focus_mgr->lc, NULL, local_conference_address, + local_conference_address, NULL); BC_ASSERT_PTR_NOT_NULL(fconf); - linphone_address_unref(furi); if (fconf) { LinphoneParticipant *participant = linphone_conference_find_participant(fconf, michelle->identity); BC_ASSERT_PTR_NOT_NULL(participant); @@ -2891,11 +2918,13 @@ static void simple_conference_with_one_participant(void) { for (bctbx_list_t *it = all_manangers_in_conf; it; it = bctbx_list_next(it)) { LinphoneCoreManager *m = (LinphoneCoreManager *)bctbx_list_get_data(it); - LinphoneAddress *uri = linphone_address_new(linphone_core_get_identity(m->lc)); + LinphoneAddress *m_uri = (linphone_core_conference_server_enabled(m->lc)) + ? linphone_address_clone(marie_conference_address) + : linphone_address_new(linphone_core_get_identity(m->lc)); LinphoneConference *conference = - linphone_core_search_conference(m->lc, NULL, uri, marie_conference_address, NULL); + linphone_core_search_conference(m->lc, NULL, m_uri, marie_conference_address, NULL); BC_ASSERT_PTR_NOT_NULL(conference); - linphone_address_unref(uri); + linphone_address_unref(m_uri); if (conference) { BC_ASSERT_EQUAL(linphone_conference_get_participant_count(conference), 3, int, "%d"); } @@ -2918,11 +2947,13 @@ static void simple_conference_with_one_participant(void) { for (bctbx_list_t *it = all_manangers_in_conf; it; it = bctbx_list_next(it)) { LinphoneCoreManager *m = (LinphoneCoreManager *)bctbx_list_get_data(it); - LinphoneAddress *uri = linphone_address_new(linphone_core_get_identity(m->lc)); + LinphoneAddress *m_uri = (linphone_core_conference_server_enabled(m->lc)) + ? linphone_address_clone(marie_conference_address) + : linphone_address_new(linphone_core_get_identity(m->lc)); LinphoneConference *conference = - linphone_core_search_conference(m->lc, NULL, uri, marie_conference_address, NULL); + linphone_core_search_conference(m->lc, NULL, m_uri, marie_conference_address, NULL); BC_ASSERT_PTR_NOT_NULL(conference); - linphone_address_unref(uri); + linphone_address_unref(m_uri); if (conference) { BC_ASSERT_EQUAL(linphone_conference_get_participant_count(conference), 2, int, "%d"); } @@ -2941,12 +2972,13 @@ static void simple_conference_with_one_participant(void) { all_manangers_in_conf = bctbx_list_remove(all_manangers_in_conf, michelle); for (bctbx_list_t *it = all_manangers_in_conf; it; it = bctbx_list_next(it)) { LinphoneCoreManager *m = (LinphoneCoreManager *)bctbx_list_get_data(it); - - LinphoneAddress *uri = linphone_address_new(linphone_core_get_identity(m->lc)); + LinphoneAddress *m_uri = (linphone_core_conference_server_enabled(m->lc)) + ? linphone_address_clone(marie_conference_address) + : linphone_address_new(linphone_core_get_identity(m->lc)); LinphoneConference *conference = - linphone_core_search_conference(m->lc, NULL, uri, marie_conference_address, NULL); + linphone_core_search_conference(m->lc, NULL, m_uri, marie_conference_address, NULL); BC_ASSERT_PTR_NOT_NULL(conference); - linphone_address_unref(uri); + linphone_address_unref(m_uri); if (conference) { BC_ASSERT_EQUAL(linphone_conference_get_participant_count(conference), 1, int, "%d"); } @@ -4371,28 +4403,29 @@ static void simple_conference_with_file_player(void) { BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallStreamsRunning, 2 * no_participants, liblinphone_tester_sip_timeout)); - LinphoneAccount *marie_account = linphone_core_get_default_account(marie->lc); - LinphoneAddress *marie_addr = linphone_account_get_contact_address(marie_account); - LinphoneConference *l_conference = linphone_core_search_conference(marie->lc, NULL, marie_addr, NULL, NULL); + LinphoneConference *l_conference = linphone_core_get_conference(marie->lc); BC_ASSERT_PTR_NOT_NULL(l_conference); - BC_ASSERT_TRUE(linphone_conference_is_in(l_conference)); - BC_ASSERT_EQUAL(linphone_conference_get_participant_count(l_conference), no_unique_participants, int, "%d"); + if (l_conference) { + BC_ASSERT_TRUE(linphone_conference_is_in(l_conference)); + BC_ASSERT_EQUAL(linphone_conference_get_participant_count(l_conference), no_unique_participants, int, "%d"); - BC_ASSERT_PTR_NULL(linphone_core_get_current_call(marie->lc)); + BC_ASSERT_PTR_NULL(linphone_core_get_current_call(marie->lc)); - LinphonePlayer *player = linphone_conference_get_player(l_conference); - LinphonePlayerCbs *player_cbs = NULL; - BC_ASSERT_PTR_NOT_NULL(player); - if (player) { - player_cbs = linphone_factory_create_player_cbs(linphone_factory_get()); - linphone_player_cbs_set_eof_reached(player_cbs, on_eof); - linphone_player_cbs_set_user_data(player_cbs, marie); - linphone_player_add_callbacks(player, player_cbs); - linphone_core_set_record_file(pauline->lc, pauline_recordpath); - linphone_core_set_record_file(laure->lc, laure_recordpath); - linphone_core_set_record_file(michelle->lc, michelle_recordpath); - BC_ASSERT_EQUAL(linphone_player_open(player, hellopath), 0, int, "%d"); - BC_ASSERT_EQUAL(linphone_player_start(player), 0, int, "%d"); + LinphonePlayer *player = linphone_conference_get_player(l_conference); + LinphonePlayerCbs *player_cbs = NULL; + BC_ASSERT_PTR_NOT_NULL(player); + if (player) { + player_cbs = linphone_factory_create_player_cbs(linphone_factory_get()); + linphone_player_cbs_set_eof_reached(player_cbs, on_eof); + linphone_player_cbs_set_user_data(player_cbs, marie); + linphone_player_add_callbacks(player, player_cbs); + linphone_core_set_record_file(pauline->lc, pauline_recordpath); + linphone_core_set_record_file(laure->lc, laure_recordpath); + linphone_core_set_record_file(michelle->lc, michelle_recordpath); + BC_ASSERT_EQUAL(linphone_player_open(player, hellopath), 0, int, "%d"); + BC_ASSERT_EQUAL(linphone_player_start(player), 0, int, "%d"); + } + if (player_cbs) linphone_player_cbs_unref(player_cbs); } // The waiting duration must be at least as long as the wav file @@ -4402,13 +4435,12 @@ static void simple_conference_with_file_player(void) { terminate_conference(participants, marie, NULL, NULL, FALSE); - BC_ASSERT_PTR_NULL(linphone_core_search_conference(marie->lc, NULL, marie_addr, NULL, NULL)); + 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"); bctbx_list_free(lcs); bctbx_list_free(participants); - if (player_cbs) linphone_player_cbs_unref(player_cbs); BC_ASSERT_TRUE(liblinphone_tester_check_recorded_audio(hellopath, pauline_recordpath)); BC_ASSERT_TRUE(liblinphone_tester_check_recorded_audio(hellopath, laure_recordpath)); BC_ASSERT_TRUE(liblinphone_tester_check_recorded_audio(hellopath, michelle_recordpath)); @@ -12696,10 +12728,14 @@ static void try_to_create_second_conference_with_local_participant(void) { BC_ASSERT_TRUE(linphone_conference_params_local_participant_enabled(new_maries_conference_params)); LinphoneConference *new_maries_conference = linphone_core_create_conference_with_params(marie->lc, new_maries_conference_params); - BC_ASSERT_PTR_NULL(new_maries_conference); + // As a side effect of changes made to method linphone_core_create_conference_with_params, it is now possible to + // have multiple conference created by the core + BC_ASSERT_PTR_NOT_NULL(new_maries_conference); linphone_conference_params_unref(new_maries_conference_params); + linphone_conference_terminate(new_maries_conference); + linphone_conference_unref(new_maries_conference); - linphone_core_terminate_conference(marie->lc); + linphone_conference_terminate(conference); int idx = 0; unsigned int no_participants = (unsigned int)bctbx_list_size(participants); @@ -12758,12 +12794,12 @@ static void try_to_create_second_conference_with_local_participant(void) { idx++; } - // Verify that a second conference is not created - BC_ASSERT_EQUAL(marie->stat.number_of_LinphoneConferenceStateCreationPending, 1, int, "%d"); + // Verify that a second conference is created + BC_ASSERT_EQUAL(marie->stat.number_of_LinphoneConferenceStateCreationPending, 2, int, "%d"); BC_ASSERT_EQUAL(marie->stat.number_of_LinphoneConferenceStateCreated, 1, int, "%d"); - BC_ASSERT_EQUAL(marie->stat.number_of_LinphoneConferenceStateTerminationPending, 1, int, "%d"); - BC_ASSERT_EQUAL(marie->stat.number_of_LinphoneConferenceStateTerminated, 1, int, "%d"); - BC_ASSERT_EQUAL(marie->stat.number_of_LinphoneConferenceStateDeleted, 1, int, "%d"); + BC_ASSERT_EQUAL(marie->stat.number_of_LinphoneConferenceStateTerminationPending, 2, int, "%d"); + BC_ASSERT_EQUAL(marie->stat.number_of_LinphoneConferenceStateTerminated, 2, int, "%d"); + BC_ASSERT_EQUAL(marie->stat.number_of_LinphoneConferenceStateDeleted, 2, int, "%d"); ms_free(lcm_stats); diff --git a/tester/bearer-auth.cpp b/tester/bearer-auth.cpp index 94b46bb862c4eede7d6d09f8b0367cef5fbbae36..fba9ac064cb2672eef1219c75b6c96ec7f821eb4 100644 --- a/tester/bearer-auth.cpp +++ b/tester/bearer-auth.cpp @@ -355,6 +355,94 @@ static void provisioning_with_http_bearer_expired_token() { linphone_core_manager_destroy(lcm); } +static void provisioning_with_http_bearer_expired_token_invalid_refresh_token() { + bellesip::HttpServer authorizationServer; + HttpServerWithBearerAuth httpServer("sip.example.org", "abcdefgh1"); + httpServer.addResource("/provisioning", "application/xml", "rcfiles/marie_xml"); + + bellesip::AuthenticatedRegistrar registrar({"sip:marie@sip.example.com;access_token=abcdefgh1"}, "sip.example.org", + "tcp"); + registrar.addAuthMode(BELLE_SIP_AUTH_MODE_HTTP_BEARER); + + authorizationServer.Post("/oauth/token", [](BCTBX_UNUSED(const httplib::Request &req), httplib::Response &res) { + res.status = 400; + constexpr const char *body = R"( +{ + "error":"Invalid refresh token", +} + )"; + res.set_content(body, "application/json"); + }); + + LinphoneCoreManager *lcm = linphone_core_manager_new("empty_rc"); + + std::string provisioningUri = httpServer.getUrl() + "/provisioning"; + + /* set access token and provisioning uri */ + LinphoneBearerToken *token = + linphone_factory_create_bearer_token(linphone_factory_get(), "expiredtoken", time(NULL) - 30); + LinphoneBearerToken *refresh_token = + linphone_factory_create_bearer_token(linphone_factory_get(), "xyz", time(NULL) + 3600); + LinphoneAuthInfo *ai = + linphone_factory_create_auth_info_3(linphone_factory_get(), "marie", token, "sip.example.org"); + linphone_auth_info_set_access_token(ai, token); + linphone_auth_info_set_refresh_token(ai, refresh_token); + linphone_auth_info_set_authorization_server(ai, "auth.example.org"); + linphone_auth_info_set_token_endpoint_uri(ai, (authorizationServer.mRootUrl + "/oauth/token").c_str()); + linphone_bearer_token_unref(token); + linphone_bearer_token_unref(refresh_token); + linphone_core_add_auth_info(lcm->lc, ai); + linphone_auth_info_unref(ai); + + /* set provisioning uri and attempt to load it */ + linphone_core_set_provisioning_uri(lcm->lc, provisioningUri.c_str()); + + LinphoneCoreCbs *cbs = linphone_factory_create_core_cbs(linphone_factory_get()); + linphone_core_cbs_set_authentication_requested(cbs, bearer_auth_info_requested_2); + linphone_core_add_callbacks(lcm->lc, cbs); + + /* now restart the provisioning */ + linphone_core_stop(lcm->lc); + linphone_core_start(lcm->lc); + + BC_ASSERT_TRUE(wait_for(lcm->lc, NULL, &lcm->stat.number_of_authentication_info_requested, 1)); + /* resubmit new access token */ + token = linphone_factory_create_bearer_token(linphone_factory_get(), "abcdefgh1", time(NULL) + 30); + refresh_token = linphone_factory_create_bearer_token(linphone_factory_get(), "xyz2", time(NULL) + 3600); + ai = linphone_factory_create_auth_info_3(linphone_factory_get(), "marie", token, "sip.example.org"); + linphone_auth_info_set_access_token(ai, token); + linphone_auth_info_set_refresh_token(ai, refresh_token); + linphone_auth_info_set_token_endpoint_uri(ai, (authorizationServer.mRootUrl + "/oauth/token").c_str()); + linphone_bearer_token_unref(token); + linphone_bearer_token_unref(refresh_token); + linphone_core_add_auth_info(lcm->lc, ai); + linphone_auth_info_unref(ai); + + BC_ASSERT_TRUE(wait_for(lcm->lc, NULL, &lcm->stat.number_of_LinphoneConfiguringSuccessful, 1)); + BC_ASSERT_TRUE(wait_for(lcm->lc, NULL, &lcm->stat.number_of_LinphoneGlobalOn, 2)); + + BC_ASSERT_EQUAL(lcm->stat.number_of_authentication_info_requested, 1, int, "%i"); + linphone_core_manager_setup_dns(lcm); + + /* modify the registrar uri to setup correct port number */ + LinphoneAccount *account = linphone_core_get_default_account(lcm->lc); + if (BC_ASSERT_PTR_NOT_NULL(account)) { + LinphoneAccountParams *params = linphone_account_params_clone(linphone_account_get_params(account)); + LinphoneAddress *addr = linphone_address_clone(linphone_account_params_get_server_address(params)); + linphone_address_set_port(addr, belle_sip_uri_get_port(registrar.getAgent().getListeningUri())); + linphone_account_params_set_server_address(params, addr); + linphone_account_params_enable_register(params, TRUE); + linphone_address_unref(addr); + linphone_account_set_params(account, params); + linphone_account_params_unref(params); + } + BC_ASSERT_TRUE(wait_for(lcm->lc, nullptr, &lcm->stat.number_of_LinphoneRegistrationOk, 1)); + BC_ASSERT_EQUAL(lcm->stat.number_of_authentication_info_requested, 1, int, "%i"); + + linphone_core_cbs_unref(cbs); + linphone_core_manager_destroy(lcm); +} + static void test_bearer_auth_refresh_token(void) { bellesip::HttpServer authorizationServer; bellesip::AuthenticatedRegistrar registrar({"sip:bob@sip.example.com;access_token=abcdefgh1"}, "sip.example.com", @@ -420,7 +508,83 @@ static void test_bearer_auth_refresh_token(void) { linphone_core_manager_destroy(lcm); } -static void provisioning_with_http_bearer_then_register_with_digest(void) { +static void test_bearer_auth_refresh_token_non_working() { + bellesip::HttpServer authorizationServer; + bellesip::AuthenticatedRegistrar registrar({"sip:bob@sip.example.com;access_token=abcdefgh1"}, "sip.example.com", + "tcp"); + registrar.addAuthMode(BELLE_SIP_AUTH_MODE_HTTP_BEARER); + + authorizationServer.Post("/oauth/token", [](BCTBX_UNUSED(const httplib::Request &req), httplib::Response &res) { + res.status = 400; + constexpr const char *body = R"( +{ + "error": "invalid bearer token" +} + )"; + res.set_content(body, "application/json"); + }); + + LinphoneCoreManager *lcm = linphone_core_manager_new("empty_rc"); + LinphoneAccountParams *account_params = linphone_core_create_account_params(lcm->lc); + LinphoneAddress *identity = linphone_factory_create_address(linphone_factory_get(), "sip:bob@sip.example.com"); + LinphoneAddress *server_addr = + linphone_factory_create_address(linphone_factory_get(), registrar.getAgent().getListeningUriAsString().c_str()); + linphone_account_params_set_identity_address(account_params, identity); + linphone_account_params_set_server_address(account_params, server_addr); + linphone_account_params_enable_register(account_params, TRUE); + linphone_address_unref(server_addr); + + LinphoneAccount *account = linphone_core_create_account(lcm->lc, account_params); + linphone_core_add_account(lcm->lc, account); + linphone_account_params_unref(account_params); + linphone_account_unref(account); + + /* create expired access token */ + LinphoneBearerToken *access_token = + linphone_factory_create_bearer_token(linphone_factory_get(), "abcdefgh0", time(NULL) - 20); + LinphoneBearerToken *refresh_token = + linphone_factory_create_bearer_token(linphone_factory_get(), "xyz", time(NULL) + 3600); + + LinphoneAuthInfo *ai = linphone_factory_create_auth_info_3( + linphone_factory_get(), linphone_address_get_username(identity), access_token, "sip.example.com"); + linphone_auth_info_set_refresh_token(ai, refresh_token); + linphone_auth_info_set_token_endpoint_uri(ai, (authorizationServer.mRootUrl + "/oauth/token").c_str()); + linphone_bearer_token_unref(access_token); + linphone_bearer_token_unref(refresh_token); + linphone_core_add_auth_info(lcm->lc, ai); + linphone_auth_info_unref(ai); + bctbx_message("Added AuthInfo."); + + BC_ASSERT_TRUE(wait_for(lcm->lc, NULL, &lcm->stat.number_of_authentication_info_requested, 1)); + access_token = linphone_factory_create_bearer_token(linphone_factory_get(), "abcdefgh1", time(NULL) + 30); + refresh_token = linphone_factory_create_bearer_token(linphone_factory_get(), "xyz2", time(NULL) + 3600); + + ai = linphone_factory_create_auth_info_3(linphone_factory_get(), linphone_address_get_username(identity), + access_token, "sip.example.com"); + linphone_auth_info_set_refresh_token(ai, refresh_token); + linphone_auth_info_set_token_endpoint_uri(ai, (authorizationServer.mRootUrl + "/oauth/token").c_str()); + linphone_auth_info_set_authorization_server(ai, "auth.example.org"); + linphone_bearer_token_unref(access_token); + linphone_bearer_token_unref(refresh_token); + linphone_core_add_auth_info(lcm->lc, ai); + linphone_auth_info_unref(ai); + + BC_ASSERT_TRUE(wait_for(lcm->lc, NULL, &lcm->stat.number_of_LinphoneRegistrationOk, 1)); + BC_ASSERT_EQUAL(lcm->stat.number_of_authentication_info_requested, 1, int, "%i"); + const bctbx_list_t *ai_list = linphone_core_get_auth_info_list(lcm->lc); + BC_ASSERT_EQUAL((int)bctbx_list_size(ai_list), 1, int, "%i"); + if (ai_list) { + /* Make sure that the refresh token was updated*/ + const LinphoneAuthInfo *updated_ai = (const LinphoneAuthInfo *)ai_list->data; + const LinphoneBearerToken *new_refresh_token = linphone_auth_info_get_refresh_token(updated_ai); + BC_ASSERT_STRING_EQUAL(linphone_bearer_token_get_token(new_refresh_token), "xyz2"); + } + + linphone_address_unref(identity); + linphone_core_manager_destroy(lcm); +} + +static void provisioning_with_http_bearer_then_register_with_digest_base(bool with_username_in_auth_info) { HttpServerWithBearerAuth httpServer("sip.example.org", "abcdefgh"); httpServer.addResource("/provisioning", "application/xml", "rcfiles/marie_digest_auth_xml"); @@ -448,7 +612,8 @@ static void provisioning_with_http_bearer_then_register_with_digest(void) { /* set access token */ LinphoneBearerToken *token = linphone_factory_create_bearer_token(linphone_factory_get(), "abcdefgh", time(NULL) + 30); - LinphoneAuthInfo *ai = linphone_factory_create_auth_info_3(linphone_factory_get(), NULL, token, "sip.example.org"); + LinphoneAuthInfo *ai = linphone_factory_create_auth_info_3( + linphone_factory_get(), with_username_in_auth_info ? "marie" : NULL, token, "sip.example.org"); linphone_auth_info_set_access_token(ai, token); linphone_bearer_token_unref(token); linphone_core_add_auth_info(lcm->lc, ai); @@ -495,17 +660,29 @@ static void provisioning_with_http_bearer_then_register_with_digest(void) { linphone_core_manager_destroy(lcm); } +static void provisioning_with_http_bearer_then_register_with_digest(void) { + provisioning_with_http_bearer_then_register_with_digest_base(false); +} + +static void provisioning_with_http_bearer_then_register_with_digest_with_username_in_auth_info(void) { + provisioning_with_http_bearer_then_register_with_digest_base(true); +} static test_t bearer_auth_tests[] = { {"Bearer auth requested", test_bearer_auth}, {"Bearer auth set before", test_bearer_auth_set_before}, {"Provisioning with http bearer auth", provisioning_with_http_bearer_auth}, {"Provisioning with http bearer auth requested on demand", provisioning_with_http_bearer_auth_requested_on_demand}, {"Provisioning with http bearer auth but expired access token", provisioning_with_http_bearer_expired_token}, + {"Provisioning with http bearer auth but expired access token and invalid refresh token", + provisioning_with_http_bearer_expired_token_invalid_refresh_token}, {"Provisioning with http bearer auth requested aborted", provisioning_with_http_bearer_auth_requested_on_demand_aborted}, {"Bearer auth with refresh", test_bearer_auth_refresh_token}, + {"Bearer auth with refresh non working", test_bearer_auth_refresh_token_non_working}, {"Provisioning with http bearer providing SIP account using digest auth", - provisioning_with_http_bearer_then_register_with_digest}}; + provisioning_with_http_bearer_then_register_with_digest}, + {"Provisioning with http bearer providing SIP account using digest auth 2", + provisioning_with_http_bearer_then_register_with_digest_with_username_in_auth_info}}; /* * TODO: future tests diff --git a/tester/call-twisted-cases.cpp b/tester/call-twisted-cases.cpp index a2dea52052473d45baa2e83be3fba46acc0e9f64..50c2a3befef10d373874a890f3b4a12284f6fd6b 100644 --- a/tester/call-twisted-cases.cpp +++ b/tester/call-twisted-cases.cpp @@ -166,7 +166,7 @@ static void call_challenged_after_180_base(bool_t with_account) { identity_address = linphone_address_new(identity); const char *realm = linphone_auth_info_get_realm(ai); - LinphoneAccountParams *account_params = linphone_account_params_new(marie->lc); + LinphoneAccountParams *account_params = linphone_account_params_new(marie->lc, TRUE); char *agent_listening_uri = ms_strdup(agent.getListeningUriAsString().c_str()); server_address = linphone_address_new(agent_listening_uri); linphone_account_params_set_server_address(account_params, server_address); diff --git a/tester/call_single_tester.c b/tester/call_single_tester.c index 51acaf68aa812a0a88b05e61610918f7ef50e161..c9746f9588ead89b675e5dd5cd714b2870a62bbd 100644 --- a/tester/call_single_tester.c +++ b/tester/call_single_tester.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2022 Belledonne Communications SARL. + * Copyright (c) 2010-2025 Belledonne Communications SARL. * * This file is part of Liblinphone * (see https://gitlab.linphone.org/BC/public/liblinphone). @@ -3331,9 +3331,11 @@ void call_paused_resumed_base(bool_t multicast, bool_t with_losses, bool_t accep linphone_call_ref(call_pauline); const LinphoneCallParams *marie_call_lparams = linphone_call_get_params(call_marie); + BC_ASSERT_FALSE(linphone_call_params_ringing_disabled(marie_call_lparams)); BC_ASSERT_TRUE(linphone_call_params_tone_indications_enabled(marie_call_lparams)); const LinphoneCallParams *pauline_call_lparams = linphone_call_get_params(call_pauline); + BC_ASSERT_FALSE(linphone_call_params_ringing_disabled(pauline_call_lparams)); BC_ASSERT_TRUE(linphone_call_params_tone_indications_enabled(pauline_call_lparams)); /*check if video stream is not offered*/ @@ -5860,7 +5862,7 @@ static void call_state_changed_3(BCTBX_UNUSED(LinphoneCore *lc), ms_free(from); } -static void call_with_transport_change_base(bool_t succesfull_call) { +static void call_with_transport_change_base(bool_t successfull_call) { LinphoneSipTransports sip_tr; LinphoneCoreManager *marie; LinphoneCoreManager *pauline; @@ -5894,14 +5896,14 @@ static void call_with_transport_change_base(bool_t succesfull_call) { } } - if (succesfull_call) { + if (successfull_call) { BC_ASSERT_TRUE(call(marie, pauline)); end_call(marie, pauline); } else linphone_core_invite(marie->lc, "nexiste_pas"); - if (succesfull_call) BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneCallEnd, 1)); + if (successfull_call) BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneCallEnd, 1)); BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneCallReleased, 1)); - if (succesfull_call) { + if (successfull_call) { BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneCallEnd, 1)); BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneCallReleased, 1)); } @@ -6990,6 +6992,7 @@ static void simple_call_with_gruu(void) { BC_ASSERT_TRUE(linphone_address_has_uri_param(pauline_addr, "gr")); BC_ASSERT_STRING_EQUAL(linphone_address_get_domain(pauline_addr), "sip.example.org"); + linphone_core_disable_call_ringing(marie->lc, TRUE); linphone_core_enable_call_tone_indications(marie->lc, FALSE); marie_call = linphone_core_invite_address(marie->lc, pauline_addr); @@ -7019,9 +7022,11 @@ static void simple_call_with_gruu(void) { BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneCallStreamsRunning, 1)); const LinphoneCallParams *marie_call_lparams = linphone_call_get_params(marie_call); + BC_ASSERT_TRUE(linphone_call_params_ringing_disabled(marie_call_lparams)); BC_ASSERT_FALSE(linphone_call_params_tone_indications_enabled(marie_call_lparams)); const LinphoneCallParams *pauline_call_lparams = linphone_call_get_params(pauline_call); + BC_ASSERT_FALSE(linphone_call_params_ringing_disabled(pauline_call_lparams)); BC_ASSERT_TRUE(linphone_call_params_tone_indications_enabled(pauline_call_lparams)); contact_addr = linphone_address_new(linphone_call_get_remote_contact(marie_call)); @@ -7954,6 +7959,15 @@ void call_with_core_without_media(void) { linphone_core_set_nortp_timeout(pauline->lc, 3); disable_all_audio_codecs_except_one(pauline->lc, "opus", 48000); + // disable_all_video_codecs_except_one(pauline->lc, "vp8"); + + LinphoneVideoActivationPolicy *pol = linphone_factory_create_video_activation_policy(linphone_factory_get()); + linphone_video_activation_policy_set_automatically_accept(pol, TRUE); + linphone_core_set_video_activation_policy(pauline->lc, pol); + linphone_core_set_video_device(pauline->lc, liblinphone_tester_mire_id); + linphone_core_enable_video_capture(pauline->lc, TRUE); + linphone_core_enable_video_display(pauline->lc, TRUE); + linphone_video_activation_policy_unref(pol); BC_ASSERT_NOT_EQUAL(marie->stat.number_of_LinphoneCoreFirstCallStarted, 1, int, "%d"); BC_ASSERT_NOT_EQUAL(pauline->stat.number_of_LinphoneCoreFirstCallStarted, 1, int, "%d"); @@ -8139,8 +8153,9 @@ void call_with_core_without_media(void) { "a=ssrc:1592583860 cname:mPaUVqJCMZayFJgz\r\n" "a=ssrc:1592583860 msid:dee12576-f30b-4893-83d3-eac27ee10d8d e879805a-7d32-4872-9781-3b0a5ae539ff\r\n"); - BC_ASSERT_PTR_NOT_NULL( - linphone_core_invite_address_with_params_2(marie->lc, pauline->identity, params, "Coucou", content)); + linphone_call_params_add_custom_content(params, content); + + BC_ASSERT_PTR_NOT_NULL(linphone_core_invite_address_with_params_2(marie->lc, pauline->identity, params, "", NULL)); linphone_content_unref(content); linphone_call_params_unref(params); diff --git a/tester/call_video_tester.cpp b/tester/call_video_tester.cpp index 4e43e911fedc4c331e5ad4f5a0c1bbedda946ebd..2a96caeeb2e21645b339dcbd78b180cced56647c 100644 --- a/tester/call_video_tester.cpp +++ b/tester/call_video_tester.cpp @@ -356,10 +356,10 @@ _request_video(LinphoneCoreManager *caller, LinphoneCoreManager *callee, bool_t } /* - * This function requests the addon of a video stream, initiated by "callee" and potentiall accepted by "caller", + * This function requests the addon of a video stream, initiated by "callee" and potentially accepted by "caller", * and asserts a number of things after this is done. * However the video addon may fail due to video policy, so that there is no insurance that video is actually added. - * This function returns TRUE if video was succesfully added, FALSE otherwise or if video is already there. + * This function returns TRUE if video was successfully added, FALSE otherwise or if video is already there. **/ bool_t request_video(LinphoneCoreManager *caller, LinphoneCoreManager *callee, bool_t accept_with_params) { stats initial_caller_stat = caller->stat; diff --git a/tester/conference-event-tester.cpp b/tester/conference-event-tester.cpp index 0d806d9c3141f0ebf1962079e21a2897835ee7d9..4c1e44f864fb7557e0c574fd8b45ca4736ced0c5 100644 --- a/tester/conference-event-tester.cpp +++ b/tester/conference-event-tester.cpp @@ -743,11 +743,11 @@ static const char *aliceUri = "sip:alice@example.com"; static const char *frankUri = "sip:frank@example.com"; static const char *confUri = "sips:conf233@example.com"; -L_ENABLE_ATTR_ACCESS(ServerConference, shared_ptr<ServerConferenceEventHandler>, eventHandler); +L_ENABLE_ATTR_ACCESS(ServerConference, shared_ptr<ServerConferenceEventHandler>, mEventHandler); class ConferenceEventTester : public ClientConference { public: - ConferenceEventTester(const shared_ptr<Core> &core, const std::shared_ptr<Address> &confAddr); + ConferenceEventTester(const shared_ptr<Core> &core); virtual ~ConferenceEventTester(); private: @@ -779,8 +779,9 @@ public: virtual void init(SalCallOp *op = nullptr, ConferenceListener *confListener = nullptr) override; }; -ConferenceEventTester::ConferenceEventTester(const shared_ptr<Core> &core, const std::shared_ptr<Address> &confAddr) - : ClientConference(core, confAddr, nullptr, ConferenceParams::create(core)) { +ConferenceEventTester::ConferenceEventTester(const shared_ptr<Core> &core) + : ClientConference(core, nullptr, ConferenceParams::create(core)) { + getCurrentParams()->setAccount(core->getDefaultAccount()); getCurrentParams()->enableAudio(true); } @@ -865,22 +866,20 @@ void ConferenceEventTester::onParticipantDeviceRemoved(const shared_ptr<Conferen class ServerConferenceTester : public ServerConference { public: - ServerConferenceTester(const std::shared_ptr<Core> &core, - const std::shared_ptr<Address> &myAddress, - std::shared_ptr<CallSessionListener> listener) - : ServerConference(core, myAddress, listener, ConferenceParams::create(core)) { + ServerConferenceTester(const std::shared_ptr<Core> &core, std::shared_ptr<CallSessionListener> listener) + : ServerConference(core, listener, ConferenceParams::create(core)) { + getCurrentParams()->setAccount(core->getDefaultAccount()); getCurrentParams()->enableLocalParticipant(false); getCurrentParams()->enableAudio(true); getCurrentParams()->enableChat(false); } - virtual ~ServerConferenceTester() { - } + virtual ~ServerConferenceTester() = default; /* ConferenceInterface */ // Addressing compilation error -Werror=overloaded-virtual using LinphonePrivate::Conference::addParticipant; - bool addParticipant(const std::shared_ptr<const Address> &addr) override { + bool addParticipant(const std::shared_ptr<Address> &addr) override { bool status = Conference::addParticipant(addr); std::shared_ptr<Participant> p = findParticipant(addr); p->addDevice(addr); @@ -993,16 +992,15 @@ static void setParticipantAsAdmin(shared_ptr<Conference> localConf, std::shared_ void first_notify_parsing() { LinphoneCoreManager *marie = linphone_core_manager_new("marie_rc"); LinphoneAddress *confAddress = linphone_core_interpret_url(marie->lc, confUri); - std::shared_ptr<Address> addr = Address::toCpp(confAddress)->getSharedFromThis(); - linphone_address_unref(confAddress); - shared_ptr<ConferenceEventTester> tester = dynamic_pointer_cast<ConferenceEventTester>( - (new ConferenceEventTester(marie->lc->cppPtr, addr))->toSharedPtr()); + shared_ptr<ConferenceEventTester> tester = + dynamic_pointer_cast<ConferenceEventTester>((new ConferenceEventTester(marie->lc->cppPtr))->toSharedPtr()); tester->init(); LinphoneAddress *bobAddr = linphone_core_interpret_url(marie->lc, bobUri); LinphoneAddress *aliceAddr = linphone_core_interpret_url(marie->lc, aliceUri); size_t size = strlen(first_notify) + strlen(confUri); char *notify = new char[size]; + std::shared_ptr<Address> addr = Address::toCpp(confAddress)->getSharedFromThis(); tester->setConferenceAddress(addr); const_cast<ConferenceId &>(tester->handler->getConferenceId()).setPeerAddress(addr); @@ -1032,25 +1030,24 @@ void first_notify_parsing() { bctbx_free(bobAddrStr); bctbx_free(aliceAddrStr); + linphone_address_unref(confAddress); linphone_address_unref(bobAddr); linphone_address_unref(aliceAddr); - tester = nullptr; linphone_core_manager_destroy(marie); } void first_notify_with_extensions_parsing() { LinphoneCoreManager *marie = linphone_core_manager_new("marie_rc"); LinphoneAddress *confAddress = linphone_core_interpret_url(marie->lc, confUri); - std::shared_ptr<Address> addr = Address::toCpp(confAddress)->getSharedFromThis(); - linphone_address_unref(confAddress); - shared_ptr<ConferenceEventTester> tester = dynamic_pointer_cast<ConferenceEventTester>( - (new ConferenceEventTester(marie->lc->cppPtr, addr))->toSharedPtr()); + shared_ptr<ConferenceEventTester> tester = + dynamic_pointer_cast<ConferenceEventTester>((new ConferenceEventTester(marie->lc->cppPtr))->toSharedPtr()); tester->init(); LinphoneAddress *bobAddr = linphone_core_interpret_url(marie->lc, bobUri); LinphoneAddress *aliceAddr = linphone_core_interpret_url(marie->lc, aliceUri); size_t size = strlen(first_notify) + strlen(confUri); char *notify = new char[size]; + std::shared_ptr<Address> addr = Address::toCpp(confAddress)->getSharedFromThis(); tester->setConferenceAddress(addr); const_cast<ConferenceId &>(tester->handler->getConferenceId()).setPeerAddress(addr); @@ -1080,25 +1077,24 @@ void first_notify_with_extensions_parsing() { bctbx_free(bobAddrStr); bctbx_free(aliceAddrStr); + linphone_address_unref(confAddress); linphone_address_unref(bobAddr); linphone_address_unref(aliceAddr); - tester = nullptr; linphone_core_manager_destroy(marie); } void first_notify_parsing_wrong_conf() { LinphoneCoreManager *marie = linphone_core_manager_new("marie_rc"); LinphoneAddress *confAddress = linphone_core_interpret_url(marie->lc, "sips:conf322@example.com"); - std::shared_ptr<Address> addr = Address::toCpp(confAddress)->getSharedFromThis(); - linphone_address_unref(confAddress); - shared_ptr<ConferenceEventTester> tester = dynamic_pointer_cast<ConferenceEventTester>( - (new ConferenceEventTester(marie->lc->cppPtr, addr))->toSharedPtr()); + shared_ptr<ConferenceEventTester> tester = + dynamic_pointer_cast<ConferenceEventTester>((new ConferenceEventTester(marie->lc->cppPtr))->toSharedPtr()); tester->init(); LinphoneAddress *bobAddr = linphone_core_interpret_url(marie->lc, bobUri); LinphoneAddress *aliceAddr = linphone_core_interpret_url(marie->lc, aliceUri); size_t size = strlen(first_notify) + strlen(confUri); char *notify = new char[size]; + std::shared_ptr<Address> addr = Address::toCpp(confAddress)->getSharedFromThis(); tester->setConferenceAddress(addr); const_cast<ConferenceId &>(tester->handler->getConferenceId()).setPeerAddress(addr); snprintf(notify, size, first_notify, confUri); @@ -1121,19 +1117,17 @@ void first_notify_parsing_wrong_conf() { bctbx_free(bobAddrStr); bctbx_free(aliceAddrStr); + linphone_address_unref(confAddress); linphone_address_unref(bobAddr); linphone_address_unref(aliceAddr); - tester = nullptr; linphone_core_manager_destroy(marie); } void participant_added_parsing() { LinphoneCoreManager *marie = linphone_core_manager_new("marie_rc"); LinphoneAddress *confAddress = linphone_core_interpret_url(marie->lc, confUri); - std::shared_ptr<Address> addr = Address::toCpp(confAddress)->getSharedFromThis(); - linphone_address_unref(confAddress); - shared_ptr<ConferenceEventTester> tester = dynamic_pointer_cast<ConferenceEventTester>( - (new ConferenceEventTester(marie->lc->cppPtr, addr))->toSharedPtr()); + shared_ptr<ConferenceEventTester> tester = + dynamic_pointer_cast<ConferenceEventTester>((new ConferenceEventTester(marie->lc->cppPtr))->toSharedPtr()); tester->init(); LinphoneAddress *bobAddr = linphone_core_interpret_url(marie->lc, bobUri); LinphoneAddress *aliceAddr = linphone_core_interpret_url(marie->lc, aliceUri); @@ -1143,6 +1137,7 @@ void participant_added_parsing() { size_t size2 = strlen(participant_added_notify) + strlen(confUri); char *notify_added = new char[size2]; + std::shared_ptr<Address> addr = Address::toCpp(confAddress)->getSharedFromThis(); tester->setConferenceAddress(addr); const_cast<ConferenceId &>(tester->handler->getConferenceId()).setPeerAddress(addr); snprintf(notify, size, first_notify, confUri); @@ -1183,10 +1178,10 @@ void participant_added_parsing() { BC_ASSERT_TRUE(!tester->participants.find(frankAddrStr)->second); bctbx_free(frankAddrStr); + linphone_address_unref(confAddress); linphone_address_unref(bobAddr); linphone_address_unref(aliceAddr); linphone_address_unref(frankAddr); - tester = nullptr; linphone_core_manager_destroy(marie); } @@ -1194,10 +1189,8 @@ void participant_not_added_parsing() { LinphoneCoreManager *marie = linphone_core_manager_new("marie_rc"); setup_mgr_for_conference(marie, NULL); LinphoneAddress *confAddress = linphone_core_interpret_url(marie->lc, confUri); - std::shared_ptr<Address> addr = Address::toCpp(confAddress)->getSharedFromThis(); - linphone_address_unref(confAddress); - shared_ptr<ConferenceEventTester> tester = dynamic_pointer_cast<ConferenceEventTester>( - (new ConferenceEventTester(marie->lc->cppPtr, addr))->toSharedPtr()); + shared_ptr<ConferenceEventTester> tester = + dynamic_pointer_cast<ConferenceEventTester>((new ConferenceEventTester(marie->lc->cppPtr))->toSharedPtr()); tester->init(); LinphoneAddress *bobAddr = linphone_core_interpret_url(marie->lc, bobUri); LinphoneAddress *aliceAddr = linphone_core_interpret_url(marie->lc, aliceUri); @@ -1211,6 +1204,7 @@ void participant_not_added_parsing() { size_t size4 = strlen(participant_device_not_added_notify) + strlen(confUri) + sizeof(int); char *notify_device_not_added = new char[size4]; + std::shared_ptr<Address> addr = Address::toCpp(confAddress)->getSharedFromThis(); tester->setConferenceAddress(addr); const_cast<ConferenceId &>(tester->handler->getConferenceId()).setPeerAddress(addr); const_cast<ConferenceId &>(tester->handler->getConferenceId()) @@ -1289,20 +1283,20 @@ void participant_not_added_parsing() { (initial_marie_stats.number_of_LinphoneSubscriptionOutgoingProgress + 1), liblinphone_tester_sip_timeout)); + tester->handler->unsubscribe(); + + linphone_address_unref(confAddress); linphone_address_unref(bobAddr); linphone_address_unref(aliceAddr); linphone_address_unref(frankAddr); - tester = nullptr; destroy_mgr_in_conference(marie); } void participant_deleted_parsing() { LinphoneCoreManager *marie = linphone_core_manager_new("marie_rc"); LinphoneAddress *confAddress = linphone_core_interpret_url(marie->lc, confUri); - std::shared_ptr<Address> addr = Address::toCpp(confAddress)->getSharedFromThis(); - linphone_address_unref(confAddress); - shared_ptr<ConferenceEventTester> tester = dynamic_pointer_cast<ConferenceEventTester>( - (new ConferenceEventTester(marie->lc->cppPtr, addr))->toSharedPtr()); + shared_ptr<ConferenceEventTester> tester = + dynamic_pointer_cast<ConferenceEventTester>((new ConferenceEventTester(marie->lc->cppPtr))->toSharedPtr()); tester->init(); LinphoneAddress *bobAddr = linphone_core_interpret_url(marie->lc, bobUri); LinphoneAddress *aliceAddr = linphone_core_interpret_url(marie->lc, aliceUri); @@ -1311,6 +1305,7 @@ void participant_deleted_parsing() { size_t size2 = strlen(participant_deleted_notify) + strlen(confUri); char *notify_deleted = new char[size2]; + std::shared_ptr<Address> addr = Address::toCpp(confAddress)->getSharedFromThis(); tester->setConferenceAddress(addr); const_cast<ConferenceId &>(tester->handler->getConferenceId()).setPeerAddress(addr); snprintf(notify, size, first_notify, confUri); @@ -1348,19 +1343,17 @@ void participant_deleted_parsing() { bctbx_free(bobAddrStr); bctbx_free(aliceAddrStr); + linphone_address_unref(confAddress); linphone_address_unref(bobAddr); linphone_address_unref(aliceAddr); - tester = nullptr; linphone_core_manager_destroy(marie); } void participant_admined_parsing() { LinphoneCoreManager *marie = linphone_core_manager_new("marie_rc"); LinphoneAddress *confAddress = linphone_core_interpret_url(marie->lc, confUri); - std::shared_ptr<Address> addr = Address::toCpp(confAddress)->getSharedFromThis(); - linphone_address_unref(confAddress); - shared_ptr<ConferenceEventTester> tester = dynamic_pointer_cast<ConferenceEventTester>( - (new ConferenceEventTester(marie->lc->cppPtr, addr))->toSharedPtr()); + shared_ptr<ConferenceEventTester> tester = + dynamic_pointer_cast<ConferenceEventTester>((new ConferenceEventTester(marie->lc->cppPtr))->toSharedPtr()); tester->init(); LinphoneAddress *bobAddr = linphone_core_interpret_url(marie->lc, bobUri); LinphoneAddress *aliceAddr = linphone_core_interpret_url(marie->lc, aliceUri); @@ -1369,6 +1362,7 @@ void participant_admined_parsing() { size_t size2 = strlen(participant_admined_notify) + strlen(confUri); char *notify_admined = new char[size2]; + std::shared_ptr<Address> addr = Address::toCpp(confAddress)->getSharedFromThis(); tester->setConferenceAddress(addr); const_cast<ConferenceId &>(tester->handler->getConferenceId()).setPeerAddress(addr); snprintf(notify, size, first_notify, confUri); @@ -1405,19 +1399,17 @@ void participant_admined_parsing() { bctbx_free(bobAddrStr); bctbx_free(aliceAddrStr); + linphone_address_unref(confAddress); linphone_address_unref(bobAddr); linphone_address_unref(aliceAddr); - tester = nullptr; linphone_core_manager_destroy(marie); } void participant_unadmined_parsing() { LinphoneCoreManager *marie = linphone_core_manager_new("marie_rc"); LinphoneAddress *confAddress = linphone_core_interpret_url(marie->lc, confUri); - std::shared_ptr<Address> addr = Address::toCpp(confAddress)->getSharedFromThis(); - linphone_address_unref(confAddress); - shared_ptr<ConferenceEventTester> tester = dynamic_pointer_cast<ConferenceEventTester>( - (new ConferenceEventTester(marie->lc->cppPtr, addr))->toSharedPtr()); + shared_ptr<ConferenceEventTester> tester = + dynamic_pointer_cast<ConferenceEventTester>((new ConferenceEventTester(marie->lc->cppPtr))->toSharedPtr()); tester->init(); LinphoneAddress *bobAddr = linphone_core_interpret_url(marie->lc, bobUri); LinphoneAddress *aliceAddr = linphone_core_interpret_url(marie->lc, aliceUri); @@ -1426,6 +1418,7 @@ void participant_unadmined_parsing() { size_t size2 = strlen(participant_unadmined_notify) + strlen(confUri); char *notify_unadmined = new char[size2]; + std::shared_ptr<Address> addr = Address::toCpp(confAddress)->getSharedFromThis(); tester->setConferenceAddress(addr); const_cast<ConferenceId &>(tester->handler->getConferenceId()).setPeerAddress(addr); snprintf(notify, size, first_notify, confUri); @@ -1462,9 +1455,9 @@ void participant_unadmined_parsing() { bctbx_free(bobAddrStr); bctbx_free(aliceAddrStr); + linphone_address_unref(confAddress); linphone_address_unref(bobAddr); linphone_address_unref(aliceAddr); - tester = nullptr; linphone_core_manager_destroy(marie); } @@ -1472,14 +1465,13 @@ void send_first_notify() { LinphoneCoreManager *marie = linphone_core_manager_new("marie_rc"); LinphoneCoreManager *pauline = linphone_core_manager_new(transport_supported(LinphoneTransportTls) ? "pauline_rc" : "pauline_tcp_rc"); - std::shared_ptr<Address> addr = Address::toCpp(pauline->identity)->getSharedFromThis(); - shared_ptr<ConferenceEventTester> tester = dynamic_pointer_cast<ConferenceEventTester>( - (new ConferenceEventTester(marie->lc->cppPtr, addr))->toSharedPtr()); + shared_ptr<ConferenceEventTester> tester = + dynamic_pointer_cast<ConferenceEventTester>((new ConferenceEventTester(marie->lc->cppPtr))->toSharedPtr()); tester->init(); auto params = ConferenceParams::create(pauline->lc->cppPtr); params->enableAudio(true); - shared_ptr<Conference> localConf = - (new ServerConference(pauline->lc->cppPtr, addr, nullptr, params))->toSharedPtr(); + params->enableLocalParticipant(false); + shared_ptr<Conference> localConf = (new ServerConference(pauline->lc->cppPtr, nullptr, params))->toSharedPtr(); localConf->init(); std::shared_ptr<ConferenceListenerInterfaceTester> confListener = std::make_shared<ConferenceListenerInterfaceTester>(); @@ -1498,8 +1490,9 @@ void send_first_notify() { alice->setAdmin(true); ServerConferenceEventHandler *localHandler = - (L_ATTR_GET(dynamic_pointer_cast<ServerConference>(localConf).get(), eventHandler)).get(); + (L_ATTR_GET(dynamic_pointer_cast<ServerConference>(localConf).get(), mEventHandler)).get(); localConf->setState(ConferenceInterface::State::Instantiated); + std::shared_ptr<Address> addr = Address::toCpp(pauline->identity)->getSharedFromThis(); localConf->setConferenceAddress(addr); auto content = localHandler->createNotifyFullState(NULL); @@ -1515,9 +1508,6 @@ void send_first_notify() { BC_ASSERT_TRUE(!tester->participants.find(bobAddr->toString())->second); BC_ASSERT_TRUE(tester->participants.find(aliceAddr->toString())->second); - tester = nullptr; - localConf = nullptr; - alice = nullptr; linphone_core_manager_destroy(marie); linphone_core_manager_destroy(pauline); } @@ -1526,9 +1516,7 @@ void send_added_notify_through_address() { LinphoneCoreManager *marie = linphone_core_manager_new("marie_rc"); LinphoneCoreManager *pauline = linphone_core_manager_new(transport_supported(LinphoneTransportTls) ? "pauline_rc" : "pauline_tcp_rc"); - std::shared_ptr<Address> addr = Address::toCpp(pauline->identity)->getSharedFromThis(); - shared_ptr<ServerConferenceTester> localConf = - make_shared<ServerConferenceTester>(pauline->lc->cppPtr, addr, nullptr); + shared_ptr<ServerConferenceTester> localConf = make_shared<ServerConferenceTester>(pauline->lc->cppPtr, nullptr); localConf->init(); std::shared_ptr<ConferenceListenerInterfaceTester> confListener = std::make_shared<ConferenceListenerInterfaceTester>(); @@ -1547,6 +1535,7 @@ void send_added_notify_through_address() { localConf->addParticipant(aliceAddr); setParticipantAsAdmin(localConf, aliceAddr, true); localConf->setState(ConferenceInterface::State::Instantiated); + std::shared_ptr<Address> addr = Address::toCpp(pauline->identity)->getSharedFromThis(); localConf->setConferenceAddress(addr); BC_ASSERT_EQUAL(confListener->participants.size(), 2, size_t, "%zu"); BC_ASSERT_EQUAL(confListener->participantDevices.size(), 2, size_t, "%zu"); @@ -1572,7 +1561,6 @@ void send_added_notify_through_address() { BC_ASSERT_EQUAL(localConf->getLastNotify(), (lastNotifyCount + 1), size_t, "%zu"); - localConf = nullptr; linphone_core_manager_destroy(marie); linphone_core_manager_destroy(pauline); } @@ -1877,7 +1865,7 @@ add_participant_to_conference_through_call(bctbx_list_t **mgrs, size_t increment = 0; if (participantSize == 0) { // Also me is added as participant - increment = 2; + increment = 1 + ((conf->getCurrentParams()->localParticipantEnabled()) ? 1 : 0); } else { increment = 1; } @@ -1885,11 +1873,12 @@ add_participant_to_conference_through_call(bctbx_list_t **mgrs, BC_ASSERT_EQUAL(confListener->participantDevices.size(), (participantDeviceSize + increment), size_t, "%zu"); if (participantCall) { - auto cppAddress = Call::toCpp(participantCall)->getToAddress()->clone()->toSharedPtr(); + auto cppAddress = Call::toCpp(participantCall)->getLog()->getToAddress()->clone()->toSharedPtr(); auto addressParams = cppAddress->getUriParams(); for (const auto &[name, value] : addressParams) { cppAddress->removeUriParam(name); } + BC_ASSERT_TRUE(linphone_address_weak_equal(cppAddress->toC(), participant_mgr->identity)); const auto participant = confListener->participants.find(cppAddress->asStringUriOnly()); BC_ASSERT_TRUE(participant != confListener->participants.end()); @@ -1936,13 +1925,11 @@ void send_added_notify_through_call() { bctbx_list_t *mgrs = NULL; mgrs = bctbx_list_append(mgrs, pauline); - std::shared_ptr<Address> addr = Address::toCpp(pauline->identity)->getSharedFromThis(); stats initialPaulineStats = pauline->stat; { auto params = ConferenceParams::create(pauline->lc->cppPtr); params->enableAudio(true); - shared_ptr<Conference> localConf = - (new ServerConference(pauline->lc->cppPtr, addr, nullptr, params))->toSharedPtr(); + shared_ptr<Conference> localConf = (new ServerConference(pauline->lc->cppPtr, nullptr, params))->toSharedPtr(); localConf->init(); BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneConferenceStateCreationPending, @@ -2017,14 +2004,12 @@ void send_removed_notify_through_call() { bctbx_list_t *removed_mgrs = NULL; - std::shared_ptr<Address> addr = Address::toCpp(pauline->identity)->getSharedFromThis(); stats initialPaulineStats = pauline->stat; - { auto params = ConferenceParams::create(pauline->lc->cppPtr); params->enableAudio(true); - shared_ptr<Conference> localConf = - (new ServerConference(pauline->lc->cppPtr, addr, nullptr, params))->toSharedPtr(); + params->enableLocalParticipant(false); + shared_ptr<Conference> localConf = (new ServerConference(pauline->lc->cppPtr, nullptr, params))->toSharedPtr(); localConf->init(); BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneConferenceStateCreationPending, @@ -2116,9 +2101,7 @@ void send_removed_notify() { LinphoneCoreManager *marie = linphone_core_manager_new("marie_rc"); LinphoneCoreManager *pauline = linphone_core_manager_new(transport_supported(LinphoneTransportTls) ? "pauline_rc" : "pauline_tcp_rc"); - std::shared_ptr<Address> addr = Address::toCpp(pauline->identity)->getSharedFromThis(); - shared_ptr<ServerConferenceTester> localConf = - make_shared<ServerConferenceTester>(pauline->lc->cppPtr, addr, nullptr); + shared_ptr<ServerConferenceTester> localConf = make_shared<ServerConferenceTester>(pauline->lc->cppPtr, nullptr); localConf->init(); std::shared_ptr<ConferenceListenerInterfaceTester> confListener = std::make_shared<ConferenceListenerInterfaceTester>(); @@ -2134,6 +2117,7 @@ void send_removed_notify() { localConf->addParticipant(aliceAddr); setParticipantAsAdmin(localConf, aliceAddr, true); localConf->setState(ConferenceInterface::State::Instantiated); + std::shared_ptr<Address> addr = Address::toCpp(pauline->identity)->getSharedFromThis(); localConf->setConferenceAddress(addr); BC_ASSERT_EQUAL(confListener->participants.size(), 2, size_t, "%zu"); @@ -2156,7 +2140,6 @@ void send_removed_notify() { BC_ASSERT_TRUE(confListener->participants.find(aliceAddr->toString())->second); BC_ASSERT_EQUAL(localConf->getLastNotify(), (lastNotifyCount + 1), int, "%d"); - localConf = nullptr; linphone_core_manager_destroy(marie); linphone_core_manager_destroy(pauline); } @@ -2165,9 +2148,7 @@ void send_admined_notify() { LinphoneCoreManager *marie = linphone_core_manager_new("marie_rc"); LinphoneCoreManager *pauline = linphone_core_manager_new(transport_supported(LinphoneTransportTls) ? "pauline_rc" : "pauline_tcp_rc"); - std::shared_ptr<Address> addr = Address::toCpp(pauline->identity)->getSharedFromThis(); - shared_ptr<ServerConferenceTester> localConf = - make_shared<ServerConferenceTester>(pauline->lc->cppPtr, addr, nullptr); + shared_ptr<ServerConferenceTester> localConf = make_shared<ServerConferenceTester>(pauline->lc->cppPtr, nullptr); localConf->init(); std::shared_ptr<ConferenceListenerInterfaceTester> confListener = std::make_shared<ConferenceListenerInterfaceTester>(); @@ -2183,6 +2164,7 @@ void send_admined_notify() { localConf->addParticipant(aliceAddr); setParticipantAsAdmin(localConf, aliceAddr, true); localConf->setState(ConferenceInterface::State::Instantiated); + std::shared_ptr<Address> addr = Address::toCpp(pauline->identity)->getSharedFromThis(); localConf->setConferenceAddress(addr); BC_ASSERT_EQUAL(confListener->participants.size(), 2, size_t, "%zu"); @@ -2205,7 +2187,6 @@ void send_admined_notify() { BC_ASSERT_EQUAL(localConf->getLastNotify(), (lastNotifyCount + 1), int, "%d"); - localConf = nullptr; linphone_core_manager_destroy(marie); linphone_core_manager_destroy(pauline); } @@ -2214,9 +2195,7 @@ void send_unadmined_notify() { LinphoneCoreManager *marie = linphone_core_manager_new("marie_rc"); LinphoneCoreManager *pauline = linphone_core_manager_new(transport_supported(LinphoneTransportTls) ? "pauline_rc" : "pauline_tcp_rc"); - std::shared_ptr<Address> addr = Address::toCpp(pauline->identity)->getSharedFromThis(); - shared_ptr<ServerConferenceTester> localConf = - make_shared<ServerConferenceTester>(pauline->lc->cppPtr, addr, nullptr); + shared_ptr<ServerConferenceTester> localConf = make_shared<ServerConferenceTester>(pauline->lc->cppPtr, nullptr); localConf->init(); std::shared_ptr<ConferenceListenerInterfaceTester> confListener = std::make_shared<ConferenceListenerInterfaceTester>(); @@ -2232,6 +2211,7 @@ void send_unadmined_notify() { localConf->addParticipant(aliceAddr); setParticipantAsAdmin(localConf, aliceAddr, true); localConf->setState(ConferenceInterface::State::Instantiated); + std::shared_ptr<Address> addr = Address::toCpp(pauline->identity)->getSharedFromThis(); localConf->setConferenceAddress(addr); BC_ASSERT_EQUAL(confListener->participants.size(), 2, size_t, "%zu"); @@ -2252,7 +2232,6 @@ void send_unadmined_notify() { BC_ASSERT_TRUE(!confListener->participants.find(bobAddr->toString())->second); BC_ASSERT_EQUAL(localConf->getLastNotify(), (lastNotifyCount + 1), int, "%d"); - localConf = nullptr; linphone_core_manager_destroy(marie); linphone_core_manager_destroy(pauline); } @@ -2261,9 +2240,8 @@ void send_subject_changed_notify() { LinphoneCoreManager *marie = linphone_core_manager_new("marie_rc"); LinphoneCoreManager *pauline = linphone_core_manager_new(transport_supported(LinphoneTransportTls) ? "pauline_rc" : "pauline_tcp_rc"); - std::shared_ptr<Address> addr = Address::toCpp(pauline->identity)->getSharedFromThis(); shared_ptr<ServerConferenceTester> localConf = dynamic_pointer_cast<ServerConferenceTester>( - (new ServerConferenceTester(pauline->lc->cppPtr, addr, nullptr))->toSharedPtr()); + (new ServerConferenceTester(pauline->lc->cppPtr, nullptr))->toSharedPtr()); localConf->init(); std::shared_ptr<ConferenceListenerInterfaceTester> confListener = std::make_shared<ConferenceListenerInterfaceTester>(); @@ -2280,6 +2258,7 @@ void send_subject_changed_notify() { localConf->setSubject("A random test subject"); setParticipantAsAdmin(localConf, aliceAddr, true); localConf->setState(ConferenceInterface::State::Instantiated); + std::shared_ptr<Address> addr = Address::toCpp(pauline->identity)->getSharedFromThis(); localConf->setConferenceAddress(addr); BC_ASSERT_STRING_EQUAL(confListener->confSubject.c_str(), "A random test subject"); @@ -2303,7 +2282,6 @@ void send_subject_changed_notify() { BC_ASSERT_TRUE(confListener->participants.find(aliceAddr->toString())->second); BC_ASSERT_EQUAL(localConf->getLastNotify(), (lastNotifyCount + 1), int, "%d"); - localConf = nullptr; linphone_core_manager_destroy(marie); linphone_core_manager_destroy(pauline); } @@ -2316,8 +2294,7 @@ void send_device_added_notify() { _linphone_core_add_callbacks(pauline->lc, cbs, TRUE); linphone_core_cbs_unref(cbs); - std::shared_ptr<Address> addr = Address::toCpp(pauline->identity)->getSharedFromThis(); - shared_ptr<Conference> localConf = (new ServerConferenceTester(pauline->lc->cppPtr, addr, nullptr))->toSharedPtr(); + shared_ptr<Conference> localConf = (new ServerConferenceTester(pauline->lc->cppPtr, nullptr))->toSharedPtr(); ; localConf->init(); std::shared_ptr<ConferenceListenerInterfaceTester> confListener = @@ -2335,6 +2312,7 @@ void send_device_added_notify() { shared_ptr<Participant> alice = localConf->findParticipant(aliceAddr); setParticipantAsAdmin(localConf, aliceAddr, true); localConf->setState(ConferenceInterface::State::Instantiated); + std::shared_ptr<Address> addr = Address::toCpp(pauline->identity)->getSharedFromThis(); localConf->setConferenceAddress(addr); BC_ASSERT_EQUAL(confListener->participantDevices.size(), 2, size_t, "%zu"); BC_ASSERT_TRUE(confListener->participantDevices.find(bobAddr->toString()) != @@ -2407,15 +2385,13 @@ void send_device_added_notify() { linphone_event_unref(lev); localConf = nullptr; - alice = nullptr; linphone_core_manager_destroy(pauline); } void send_device_removed_notify() { LinphoneCoreManager *pauline = linphone_core_manager_new(transport_supported(LinphoneTransportTls) ? "pauline_rc" : "pauline_tcp_rc"); - std::shared_ptr<Address> addr = Address::toCpp(pauline->identity)->getSharedFromThis(); - shared_ptr<Conference> localConf = (new ServerConferenceTester(pauline->lc->cppPtr, addr, nullptr))->toSharedPtr(); + shared_ptr<Conference> localConf = (new ServerConferenceTester(pauline->lc->cppPtr, nullptr))->toSharedPtr(); localConf->init(); std::shared_ptr<ConferenceListenerInterfaceTester> confListener = std::make_shared<ConferenceListenerInterfaceTester>(); @@ -2433,6 +2409,7 @@ void send_device_removed_notify() { shared_ptr<Participant> alice = localConf->findParticipant(aliceAddr); setParticipantAsAdmin(localConf, aliceAddr, true); localConf->setState(ConferenceInterface::State::Instantiated); + std::shared_ptr<Address> addr = Address::toCpp(pauline->identity)->getSharedFromThis(); localConf->setConferenceAddress(addr); BC_ASSERT_EQUAL(confListener->participantDevices.size(), 2, size_t, "%zu"); @@ -2463,8 +2440,6 @@ void send_device_removed_notify() { BC_ASSERT_EQUAL(confListener->participantDevices.find(bobAddr->toString())->second, 0, size_t, "%zu"); BC_ASSERT_EQUAL(confListener->participantDevices.find(aliceAddr->toString())->second, 0, size_t, "%zu"); - localConf = nullptr; - alice = nullptr; linphone_core_manager_destroy(pauline); } @@ -2472,16 +2447,14 @@ void one_to_one_keyword() { LinphoneCoreManager *marie = linphone_core_manager_new("marie_rc"); LinphoneCoreManager *pauline = linphone_core_manager_new(transport_supported(LinphoneTransportTls) ? "pauline_rc" : "pauline_tcp_rc"); - std::shared_ptr<Address> addr = Address::toCpp(pauline->identity)->getSharedFromThis(); - shared_ptr<ConferenceEventTester> tester = dynamic_pointer_cast<ConferenceEventTester>( - (new ConferenceEventTester(marie->lc->cppPtr, addr))->toSharedPtr()); + shared_ptr<ConferenceEventTester> tester = + dynamic_pointer_cast<ConferenceEventTester>((new ConferenceEventTester(marie->lc->cppPtr))->toSharedPtr()); tester->init(); auto params = ConferenceParams::create(pauline->lc->cppPtr); params->enableAudio(false); params->enableChat(true); params->setGroup(false); - shared_ptr<Conference> localConf = - (new ServerConference(pauline->lc->cppPtr, addr, nullptr, params))->toSharedPtr(); + shared_ptr<Conference> localConf = (new ServerConference(pauline->lc->cppPtr, nullptr, params))->toSharedPtr(); localConf->init(); std::shared_ptr<ConferenceListenerInterfaceTester> confListener = std::make_shared<ConferenceListenerInterfaceTester>(); @@ -2490,12 +2463,14 @@ void one_to_one_keyword() { std::shared_ptr<Address> bobAddr = Address::toCpp(cBobAddr)->getSharedFromThis(); linphone_address_unref(cBobAddr); + std::shared_ptr<Address> addr = Address::toCpp(pauline->identity)->getSharedFromThis(); + // Create basic chat room with OneToOne capability to ensure that one to one is added to notify pauline->lc->cppPtr->getOrCreateBasicChatRoom(addr, addr); localConf->Conference::addParticipant(bobAddr); ServerConferenceEventHandler *localHandler = - (L_ATTR_GET(dynamic_pointer_cast<ServerConference>(localConf).get(), eventHandler)).get(); + (L_ATTR_GET(dynamic_pointer_cast<ServerConference>(localConf).get(), mEventHandler)).get(); localConf->setState(ConferenceInterface::State::Instantiated); localConf->setConferenceAddress(addr); auto content = localHandler->createNotifyFullState(NULL); @@ -2509,8 +2484,6 @@ void one_to_one_keyword() { BC_ASSERT_EQUAL(tester->participantDevices.find(bobAddr->toString())->second, 0, size_t, "%zu"); BC_ASSERT_TRUE(tester->oneToOne); - tester = nullptr; - localConf = nullptr; linphone_core_manager_destroy(marie); linphone_core_manager_destroy(pauline); } diff --git a/tester/db/chatroom_conference.db b/tester/db/chatroom_conference.db index 81939c746585a6983f393238cfcce1fffead0938..91e7585788b70c7fe2e5b709273ce1e8962f7a9c 100644 Binary files a/tester/db/chatroom_conference.db and b/tester/db/chatroom_conference.db differ diff --git a/tester/external_domain_tester.c b/tester/external_domain_tester.c index 7d48c9304fd225ea6929f650d812ab6844653105..59ecb855c9e66ae294def5c9e0821aca7126a7fb 100644 --- a/tester/external_domain_tester.c +++ b/tester/external_domain_tester.c @@ -65,7 +65,7 @@ static void send_chat_message_to_group_chat_room(bctbx_list_t *coresList, msgText, FALSE); } else { BC_ASSERT_TRUE(wait_for_list(coresList, &m->stat.number_of_LinphoneMessageReceived, - recipients_initial_stats[idx].number_of_LinphoneMessageReceived + 1, 5000)); + recipients_initial_stats[idx].number_of_LinphoneMessageReceived + 1, 10000)); LinphoneChatMessage *recipientLastMsg = m->stat.last_received_chat_message; BC_ASSERT_PTR_NOT_NULL(recipientLastMsg); diff --git a/tester/flexisip_tester.c b/tester/flexisip_tester.c index 205b45b454fc1e204fadb5403d94daf9cbfd9840..7396948f5e41ad1638ddd3dd493640b3bc1abfdc 100644 --- a/tester/flexisip_tester.c +++ b/tester/flexisip_tester.c @@ -330,6 +330,7 @@ static void call_forking(void) { LinphoneCoreManager *marie2 = linphone_core_manager_new("marie_rc"); LinphoneCoreManager *marie3 = linphone_core_manager_new("marie_rc"); bctbx_list_t *lcs = bctbx_list_append(NULL, pauline->lc); + LinphoneCall *marie2_call, *marie3_call; lcs = bctbx_list_append(lcs, marie->lc); lcs = bctbx_list_append(lcs, marie2->lc); @@ -342,11 +343,16 @@ static void call_forking(void) { linphone_core_invite_address(pauline->lc, marie->identity); /*pauline should hear ringback*/ - BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallOutgoingRinging, 1, 3000)); + BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallOutgoingRinging, 1, 10000)); /*all devices from Marie should be ringing*/ - BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallIncomingReceived, 1, 3000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &marie2->stat.number_of_LinphoneCallIncomingReceived, 1, 3000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &marie3->stat.number_of_LinphoneCallIncomingReceived, 1, 3000)); + BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallIncomingReceived, 1, 10000)); + BC_ASSERT_TRUE(wait_for_list(lcs, &marie2->stat.number_of_LinphoneCallIncomingReceived, 1, 10000)); + BC_ASSERT_TRUE(wait_for_list(lcs, &marie3->stat.number_of_LinphoneCallIncomingReceived, 1, 10000)); + + marie2_call = linphone_core_get_current_call(marie2->lc); + if (BC_ASSERT_PTR_NOT_NULL(marie2_call)) marie2_call = linphone_call_ref(marie2_call); + marie3_call = linphone_core_get_current_call(marie3->lc); + if (BC_ASSERT_PTR_NOT_NULL(marie3_call)) marie3_call = linphone_call_ref(marie3_call); /*marie accepts the call on its first device*/ linphone_call_accept(linphone_core_get_current_call(marie->lc)); @@ -359,6 +365,23 @@ static void call_forking(void) { BC_ASSERT_TRUE(wait_for_list(lcs, &marie2->stat.number_of_LinphoneCallEnd, 1, 5000)); BC_ASSERT_TRUE(wait_for_list(lcs, &marie3->stat.number_of_LinphoneCallEnd, 1, 5000)); + if (marie2_call) { + const LinphoneErrorInfo *ei = linphone_call_get_error_info(marie2_call); + BC_ASSERT_EQUAL(linphone_call_get_reason(marie2_call), LinphoneReasonNone, int, "%i"); + BC_ASSERT_EQUAL(linphone_error_info_get_reason(ei), LinphoneReasonNone, int, "%i"); + BC_ASSERT_EQUAL(linphone_error_info_get_protocol_code(ei), 200, int, "%i"); + BC_ASSERT_STRING_EQUAL(linphone_error_info_get_phrase(ei), "Call completed elsewhere"); + linphone_call_unref(marie2_call); + } + if (marie3_call) { + const LinphoneErrorInfo *ei = linphone_call_get_error_info(marie3_call); + BC_ASSERT_EQUAL(linphone_call_get_reason(marie3_call), LinphoneReasonNone, int, "%i"); + BC_ASSERT_EQUAL(linphone_error_info_get_reason(ei), LinphoneReasonNone, int, "%i"); + BC_ASSERT_EQUAL(linphone_error_info_get_protocol_code(ei), 200, int, "%i"); + BC_ASSERT_STRING_EQUAL(linphone_error_info_get_phrase(ei), "Call completed elsewhere"); + linphone_call_unref(marie3_call); + } + linphone_call_terminate(linphone_core_get_current_call(pauline->lc)); BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallEnd, 1, 5000)); BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallEnd, 1, 5000)); @@ -470,6 +493,7 @@ static void call_forking_declined(bool_t declined_globaly) { LinphoneCoreManager *marie2 = linphone_core_manager_new("marie_rc"); LinphoneCoreManager *marie3 = linphone_core_manager_new("marie_rc"); bctbx_list_t *lcs = bctbx_list_append(NULL, pauline->lc); + LinphoneCall *marie2_call, *marie3_call; lcs = bctbx_list_append(lcs, marie->lc); lcs = bctbx_list_append(lcs, marie2->lc); @@ -484,9 +508,14 @@ static void call_forking_declined(bool_t declined_globaly) { /*pauline should hear ringback*/ BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallOutgoingRinging, 1, 3000)); /*all devices from Marie should be ringing*/ - BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallIncomingReceived, 1, 5000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &marie2->stat.number_of_LinphoneCallIncomingReceived, 1, 5000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &marie3->stat.number_of_LinphoneCallIncomingReceived, 1, 5000)); + BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallIncomingReceived, 1, 1000)); + BC_ASSERT_TRUE(wait_for_list(lcs, &marie2->stat.number_of_LinphoneCallIncomingReceived, 1, 1000)); + BC_ASSERT_TRUE(wait_for_list(lcs, &marie3->stat.number_of_LinphoneCallIncomingReceived, 1, 10000)); + + marie2_call = linphone_core_get_current_call(marie2->lc); + if (BC_ASSERT_PTR_NOT_NULL(marie2_call)) marie2_call = linphone_call_ref(marie2_call); + marie3_call = linphone_core_get_current_call(marie3->lc); + if (BC_ASSERT_PTR_NOT_NULL(marie3_call)) marie3_call = linphone_call_ref(marie3_call); /*marie finally declines the call*/ linphone_call_decline(linphone_core_get_current_call(marie->lc), @@ -498,6 +527,21 @@ static void call_forking_declined(bool_t declined_globaly) { BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallEnd, 1, 5000)); BC_ASSERT_TRUE(wait_for_list(lcs, &marie2->stat.number_of_LinphoneCallEnd, 1, 5000)); BC_ASSERT_TRUE(wait_for_list(lcs, &marie3->stat.number_of_LinphoneCallEnd, 1, 5000)); + + if (marie2_call) { + const LinphoneErrorInfo *ei = linphone_call_get_error_info(marie2_call); + BC_ASSERT_EQUAL(linphone_call_get_reason(marie2_call), LinphoneReasonDoNotDisturb, int, "%i"); + BC_ASSERT_EQUAL(linphone_error_info_get_reason(ei), LinphoneReasonDoNotDisturb, int, "%i"); + BC_ASSERT_EQUAL(linphone_error_info_get_protocol_code(ei), 600, int, "%i"); + BC_ASSERT_STRING_EQUAL(linphone_error_info_get_phrase(ei), "Busy Everywhere"); + } + if (marie3_call) { + const LinphoneErrorInfo *ei = linphone_call_get_error_info(marie3_call); + BC_ASSERT_EQUAL(linphone_call_get_reason(marie3_call), LinphoneReasonDoNotDisturb, int, "%i"); + BC_ASSERT_EQUAL(linphone_error_info_get_reason(ei), LinphoneReasonDoNotDisturb, int, "%i"); + BC_ASSERT_EQUAL(linphone_error_info_get_protocol_code(ei), 600, int, "%i"); + BC_ASSERT_STRING_EQUAL(linphone_error_info_get_phrase(ei), "Busy Everywhere"); + } } else { /*pauline should continue ringing and be able to hear a call taken by marie2 */ linphone_call_accept(linphone_core_get_current_call(marie2->lc)); @@ -510,7 +554,8 @@ static void call_forking_declined(bool_t declined_globaly) { BC_ASSERT_TRUE(wait_for_list(lcs, &marie2->stat.number_of_LinphoneCallEnd, 1, 3000)); BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallEnd, 1, 3000)); } - + if (marie2_call) linphone_call_unref(marie2_call); + if (marie3_call) linphone_call_unref(marie3_call); linphone_core_manager_destroy(pauline); linphone_core_manager_destroy(marie); linphone_core_manager_destroy(marie2); diff --git a/tester/group_chat_secure_multialgo_tester.cpp b/tester/group_chat_secure_multialgo_tester.cpp index 0aa48be404df9af38c08465bd7817b4e37ce4ff1..f9f35e00175c990937caf25f256414ff60168c39 100644 --- a/tester/group_chat_secure_multialgo_tester.cpp +++ b/tester/group_chat_secure_multialgo_tester.cpp @@ -609,6 +609,128 @@ static void group_chat_lime_x3dh_soft_migration(void) { // wait bit more to allow imdn exchanges Linphone::Tester::CoreManagerAssert(allCoreMgrs).waitUntil(std::chrono::seconds(3), [] { return false; }); + + // ReStart Marie, Laure and Chloe core with c25519mlk512,c25519k512 (drop c25519) + laure.set_limeAlgoList({lime::CurveId::c25519mlk512, lime::CurveId::c25519k512}); + coresList = bctbx_list_remove(coresList, laure.getLc()); + laure.reStart(); + coresList = bctbx_list_append(coresList, laure.getLc()); + laureStats = laure.getStats(); + BC_ASSERT_TRUE(laure.assertPtrValue(&(laureStats.number_of_X3dhUserCreationSuccess), 1)); + + chloe.set_limeAlgoList({lime::CurveId::c25519mlk512, lime::CurveId::c25519k512}); + coresList = bctbx_list_remove(coresList, chloe.getLc()); + chloe.reStart(); + coresList = bctbx_list_append(coresList, chloe.getLc()); + chloeStats = chloe.getStats(); + BC_ASSERT_TRUE(chloe.assertPtrValue(&(chloeStats.number_of_X3dhUserCreationSuccess), 1)); + + marie.set_limeAlgoList({lime::CurveId::c25519mlk512, lime::CurveId::c25519k512}); + coresList = bctbx_list_remove(coresList, marie.getLc()); + marie.reStart(); + coresList = bctbx_list_append(coresList, marie.getLc()); + marieStats = marie.getStats(); + BC_ASSERT_TRUE(marie.assertPtrValue(&(marieStats.number_of_X3dhUserCreationSuccess), 1)); + + // get back marieCr + marieDeviceAddr = linphone_address_clone(linphone_proxy_config_get_contact(marie.getDefaultProxyConfig())); + marieCr = marie.searchChatRoom(marieDeviceAddr, confAddr); + BC_ASSERT_PTR_NOT_NULL(marieCr); + linphone_address_unref(marieDeviceAddr); + + // Marie send a message to the group + msgText = std::string("To infinity and beyond"); + msg = Linphone::Tester::ConfCoreManager::sendTextMsg(marieCr, msgText); + + // Message is delivered + BC_ASSERT_TRUE(Linphone::Tester::CoreManagerAssert(allCoreMgrs).wait([msg] { + return (linphone_chat_message_get_state(msg) == LinphoneChatMessageStateDelivered); + })); + + linphone_chat_message_unref(msg); + msg = nullptr; + + // everyone get it + for (Linphone::Tester::CoreManager &client : recipients) { + for (auto chatRoom : client.getCore().getChatRooms()) { + BC_ASSERT_TRUE(Linphone::Tester::CoreManagerAssert(allCoreMgrs).wait([chatRoom] { + return chatRoom->getUnreadChatMessageCount() == 1; + })); + LinphoneChatMessage *lastMsg = client.getStats().last_received_chat_message; + BC_ASSERT_PTR_NOT_NULL(lastMsg); + if (lastMsg) { + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_utf8_text(lastMsg), msgText.c_str()); + } + chatRoom->markAsRead(); + } + } + + // wait bit more to allow imdn exchanges + Linphone::Tester::CoreManagerAssert(allCoreMgrs).waitUntil(std::chrono::seconds(3), [] { return false; }); + + // ReStart Marie, Laure and Chloe core with c25519mlk512 only (drop c25519k512) -> pauline won't be able to get the + // message + laure.set_limeAlgoList({lime::CurveId::c25519mlk512}); + coresList = bctbx_list_remove(coresList, laure.getLc()); + laure.reStart(); + coresList = bctbx_list_append(coresList, laure.getLc()); + laureStats = laure.getStats(); + BC_ASSERT_TRUE(laure.assertPtrValue(&(laureStats.number_of_X3dhUserCreationSuccess), 1)); + + chloe.set_limeAlgoList({lime::CurveId::c25519mlk512}); + coresList = bctbx_list_remove(coresList, chloe.getLc()); + chloe.reStart(); + coresList = bctbx_list_append(coresList, chloe.getLc()); + chloeStats = chloe.getStats(); + BC_ASSERT_TRUE(chloe.assertPtrValue(&(chloeStats.number_of_X3dhUserCreationSuccess), 1)); + + marie.set_limeAlgoList({lime::CurveId::c25519mlk512}); + coresList = bctbx_list_remove(coresList, marie.getLc()); + marie.reStart(); + coresList = bctbx_list_append(coresList, marie.getLc()); + marieStats = marie.getStats(); + BC_ASSERT_TRUE(marie.assertPtrValue(&(marieStats.number_of_X3dhUserCreationSuccess), 1)); + + // get back marieCr + marieDeviceAddr = linphone_address_clone(linphone_proxy_config_get_contact(marie.getDefaultProxyConfig())); + marieCr = marie.searchChatRoom(marieDeviceAddr, confAddr); + BC_ASSERT_PTR_NOT_NULL(marieCr); + linphone_address_unref(marieDeviceAddr); + + // Marie send a message to the group + msgText = std::string("Bye Pauline"); + msg = Linphone::Tester::ConfCoreManager::sendTextMsg(marieCr, msgText); + + // Message is delivered + BC_ASSERT_TRUE(Linphone::Tester::CoreManagerAssert(allCoreMgrs).wait([msg] { + return (linphone_chat_message_get_state(msg) == LinphoneChatMessageStateDelivered); + })); + + linphone_chat_message_unref(msg); + msg = nullptr; + + // Laure and Chloe get it + for (Linphone::Tester::CoreManager &client : + std::list<std::reference_wrapper<Linphone::Tester::CoreManager>>{laure, chloe}) { + for (auto chatRoom : client.getCore().getChatRooms()) { + BC_ASSERT_TRUE(Linphone::Tester::CoreManagerAssert(allCoreMgrs).wait([chatRoom] { + return chatRoom->getUnreadChatMessageCount() == 1; + })); + LinphoneChatMessage *lastMsg = client.getStats().last_received_chat_message; + BC_ASSERT_PTR_NOT_NULL(lastMsg); + if (lastMsg) { + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_utf8_text(lastMsg), msgText.c_str()); + } + chatRoom->markAsRead(); + } + } + + // wait bit more to allow imdn exchanges + Linphone::Tester::CoreManagerAssert(allCoreMgrs).waitUntil(std::chrono::seconds(3), [] { return false; }); + + // Pauline does not + BC_ASSERT_EQUAL(pauline.getCore().getChatRooms().front()->getUnreadChatMessageCount(), 0, int, "%d"); + linphone_address_unref(confAddr); bctbx_list_free(coresList); } diff --git a/tester/group_chat_tester.c b/tester/group_chat_tester.c index 056bded8ca33cb8cdc5a2309a404ce27e4ef9a82..68ca6b1c160e1ad870206ff47785a07538087b59 100644 --- a/tester/group_chat_tester.c +++ b/tester/group_chat_tester.c @@ -322,7 +322,15 @@ void setup_chat_room_callbacks(LinphoneChatRoomCbs *cbs) { } void core_chat_room_state_changed(BCTBX_UNUSED(LinphoneCore *core), LinphoneChatRoom *cr, LinphoneChatRoomState state) { - if (state == LinphoneChatRoomStateInstantiated) { + const LinphoneConferenceParams *chat_params = linphone_chat_room_get_current_params(cr); + bool hasAudio = !!linphone_conference_params_audio_enabled(chat_params) || + !!linphone_conference_params_video_enabled(chat_params); + // When a chatroom is instantied as part of a conference, the first state it will be notifed is the Created state as + // the core waits for the full state to arrive before creating it. In fact, it may happen that a core wishes to + // create a conference with chat capabilities but the server doesn't supports it. The client would end up having a + // dangling tchat and needs to destroy it + if ((!hasAudio && (state == LinphoneChatRoomStateInstantiated)) || + (hasAudio && (state == LinphoneChatRoomStateCreated))) { LinphoneChatRoomCbs *cbs = linphone_factory_create_chat_room_cbs(linphone_factory_get()); setup_chat_room_callbacks(cbs); linphone_chat_room_add_callbacks(cr, cbs); @@ -449,6 +457,7 @@ void _send_file_plus_text( msg = linphone_chat_room_create_empty_message(cr); linphone_chat_message_add_file_content(msg, content); + BC_ASSERT_PTR_NULL(linphone_content_get_related_chat_message_id(content)); linphone_content_unref(content); if (text) linphone_chat_message_add_utf8_text_content(msg, text); @@ -465,6 +474,7 @@ void _send_file_plus_text( linphone_content_set_file_path(content2, sendFilepath2); } linphone_chat_message_add_file_content(msg, content2); + BC_ASSERT_PTR_NULL(linphone_content_get_related_chat_message_id(content2)); linphone_content_unref(content2); } @@ -524,6 +534,10 @@ void _receive_file_plus_text(bctbx_list_t *coresList, if (!use_buffer) { linphone_content_set_file_path(fileTransferContent, downloaded_file); } + + BC_ASSERT_STRING_EQUAL(linphone_content_get_related_chat_message_id(fileTransferContent), + linphone_chat_message_get_message_id(msg)); + linphone_chat_message_download_content(msg, fileTransferContent); BC_ASSERT_EQUAL(linphone_chat_message_get_state(msg), LinphoneChatMessageStateFileTransferInProgress, int, "%d"); @@ -558,6 +572,8 @@ void _receive_file_plus_text(bctbx_list_t *coresList, if (!use_buffer) { linphone_content_set_file_path(fileTransferContent2, downloaded_file); } + BC_ASSERT_STRING_EQUAL(linphone_content_get_related_chat_message_id(fileTransferContent2), + linphone_chat_message_get_message_id(msg)); linphone_chat_message_download_content(msg, fileTransferContent2); BC_ASSERT_EQUAL(linphone_chat_message_get_state(msg), LinphoneChatMessageStateFileTransferInProgress, int, "%d"); @@ -1013,7 +1029,7 @@ static void group_chat_room_creation_core_restart(void) { uuid = bctbx_strdup(linphone_config_get_string(linphone_core_get_config(marie->lc), "misc", "uuid", NULL)); } - ms_message("Restart %s'c core", linphone_core_get_identity(marie->lc)); + ms_message("Restart %s's core", linphone_core_get_identity(marie->lc)); initialMarieStats = marie->stat; coresList = bctbx_list_remove(coresList, marie->lc); linphone_core_manager_reinit(marie); @@ -4301,11 +4317,15 @@ static void group_chat_room_send_multipart_custom_content_types(void) { BC_ASSERT_STRING_EQUAL(linphone_content_get_type(content1), "application"); BC_ASSERT_STRING_EQUAL(linphone_content_get_subtype(content1), "vnd.3gpp.mcptt-info+xml"); BC_ASSERT_STRING_EQUAL(linphone_content_get_utf8_text(content1), data1); + BC_ASSERT_STRING_EQUAL(linphone_content_get_related_chat_message_id(content1), + linphone_chat_message_get_message_id(pauline->stat.last_received_chat_message)); LinphoneContent *content2 = bctbx_list_nth_data(contents, 1); BC_ASSERT_STRING_EQUAL(linphone_content_get_type(content2), "application"); BC_ASSERT_STRING_EQUAL(linphone_content_get_subtype(content2), "vnd.3gpp.mcptt-location-info+xml"); BC_ASSERT_STRING_EQUAL(linphone_content_get_utf8_text(content2), data2); + BC_ASSERT_STRING_EQUAL(linphone_content_get_related_chat_message_id(content2), + linphone_chat_message_get_message_id(pauline->stat.last_received_chat_message)); } // Clean db from chat room @@ -6179,6 +6199,195 @@ end: linphone_core_manager_destroy(chloe); } +static void basic_chat_room_with_cpim_base(bool_t use_gruu, bool_t enable_imdn) { + LinphoneCoreManager *marie = linphone_core_manager_create("marie_rc"); + LinphoneCoreManager *pauline = linphone_core_manager_create("pauline_rc"); + bctbx_list_t *coresManagerList = NULL; + coresManagerList = bctbx_list_append(coresManagerList, marie); + coresManagerList = bctbx_list_append(coresManagerList, pauline); + if (!use_gruu) { + linphone_core_remove_supported_tag(marie->lc, "gruu"); + } + bctbx_list_t *coresList = init_core_for_conference(coresManagerList); + start_core_for_conference(coresManagerList); + + if (enable_imdn) { + // Enable IMDN + linphone_im_notif_policy_enable_all(linphone_core_get_im_notif_policy(marie->lc)); + linphone_im_notif_policy_enable_all(linphone_core_get_im_notif_policy(pauline->lc)); + } + + linphone_core_set_chat_messages_aggregation_enabled(marie->lc, TRUE); + linphone_config_set_int(linphone_core_get_config(marie->lc), "sip", "chat_messages_aggregation_delay", 10); + + // Enable CPIM + LinphoneAccount *marie_account = linphone_core_get_default_account(marie->lc); + const LinphoneAccountParams *marie_account_params = linphone_account_get_params(marie_account); + LinphoneAccountParams *cloned_marie_account_params = linphone_account_params_clone(marie_account_params); + linphone_account_params_enable_cpim_in_basic_chat_room(cloned_marie_account_params, TRUE); + linphone_account_set_params(marie_account, cloned_marie_account_params); + linphone_account_params_unref(cloned_marie_account_params); + + LinphoneAccount *pauline_account = linphone_core_get_default_account(pauline->lc); + const LinphoneAccountParams *pauline_account_params = linphone_account_get_params(pauline_account); + LinphoneAccountParams *cloned_pauline_account_params = linphone_account_params_clone(pauline_account_params); + linphone_account_params_enable_cpim_in_basic_chat_room(cloned_pauline_account_params, TRUE); + linphone_account_set_params(pauline_account, cloned_pauline_account_params); + linphone_account_params_unref(cloned_pauline_account_params); + + LinphoneAddress *pauline_contact_address = linphone_account_get_contact_address(pauline_account); + + bctbx_list_t *participantsAddresses = NULL; + participantsAddresses = bctbx_list_append(participantsAddresses, pauline_contact_address); + LinphoneConferenceParams *marie_params = linphone_core_create_conference_params(marie->lc); + linphone_conference_params_enable_chat(marie_params, TRUE); + linphone_conference_params_enable_group(marie_params, FALSE); + LinphoneChatParams *marie_chat_params = linphone_conference_params_get_chat_params(marie_params); + linphone_chat_params_set_backend(marie_chat_params, LinphoneChatRoomBackendBasic); + LinphoneChatRoom *marieCr = linphone_core_create_chat_room_7(marie->lc, marie_params, participantsAddresses); + bctbx_list_free(participantsAddresses); + participantsAddresses = NULL; + linphone_chat_room_params_unref(marie_params); + BC_ASSERT_PTR_NOT_NULL(marieCr); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie->stat.number_of_LinphoneChatRoomStateCreated, 1, + liblinphone_tester_sip_timeout)); + + // Marie sends a message in a basic chatroom + const char *marie_text = "This is a basic chat room"; + LinphoneChatMessage *marie_msg = NULL; + if (marieCr) { + const LinphoneAddress *peer_address = linphone_chat_room_get_peer_address(marieCr); + BC_ASSERT_TRUE(linphone_address_has_uri_param(peer_address, "gr")); + linphone_chat_room_compose(marieCr); + BC_ASSERT_TRUE(wait_for(pauline->lc, marie->lc, &pauline->stat.number_of_LinphoneIsComposingActiveReceived, 1)); + BC_ASSERT_FALSE(wait_for_list(coresList, &pauline->stat.number_of_LinphoneMessageReceived, 1, 2000)); + marie_msg = _send_message(marieCr, marie_text); + BC_ASSERT_TRUE( + wait_for_list(coresList, &marie->stat.number_of_LinphoneMessageSent, 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline->stat.number_of_LinphoneChatRoomStateCreated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline->stat.number_of_LinphoneMessageReceived, 1, + liblinphone_tester_sip_timeout)); + if (enable_imdn) { + BC_ASSERT_TRUE(wait_for_list(coresList, &marie->stat.number_of_LinphoneMessageDeliveredToUser, 1, + liblinphone_tester_sip_timeout)); + } else { + BC_ASSERT_TRUE(wait_for_list(coresList, &marie->stat.number_of_LinphoneMessageDelivered, 1, + liblinphone_tester_sip_timeout)); + } + } + + LinphoneConferenceParams *pauline_params = linphone_core_create_conference_params(pauline->lc); + linphone_conference_params_enable_chat(pauline_params, TRUE); + linphone_conference_params_enable_group(pauline_params, FALSE); + LinphoneChatParams *pauline_chat_params = linphone_conference_params_get_chat_params(pauline_params); + linphone_chat_params_set_backend(pauline_chat_params, LinphoneChatRoomBackendBasic); + const LinphoneAddress *pauline_identity_address = + linphone_account_params_get_identity_address(linphone_account_get_params(pauline_account)); + LinphoneChatRoom *paulineCr = + linphone_core_search_chat_room_2(pauline->lc, pauline_params, pauline_identity_address, NULL, NULL); + BC_ASSERT_PTR_NOT_NULL(paulineCr); + linphone_chat_room_params_unref(pauline_params); + + const char *pauline_text = "Yes Madam"; + LinphoneChatMessage *pauline_msg = NULL; + if (paulineCr) { + linphone_chat_room_compose(paulineCr); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie->stat.number_of_LinphoneIsComposingActiveReceived, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_FALSE(wait_for_list(coresList, &pauline->stat.number_of_LinphoneIsComposingActiveReceived, 2, 2000)); + BC_ASSERT_FALSE(wait_for_list(coresList, &marie->stat.number_of_LinphoneMessageReceived, 1, 2000)); + if (enable_imdn) { + linphone_chat_room_mark_as_read(paulineCr); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie->stat.number_of_LinphoneMessageDisplayed, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline->stat.number_of_LinphoneMessageDisplayed, 1, + liblinphone_tester_sip_timeout)); + } + if (marie_msg) { + linphone_chat_message_unref(marie_msg); + } + LinphoneChatMessage *msg = linphone_chat_room_get_last_message_in_history(paulineCr); + if (BC_ASSERT_PTR_NOT_NULL(msg)) { + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_text(msg), marie_text); + linphone_chat_message_unref(msg); + } + const LinphoneAddress *local_address = linphone_chat_room_get_local_address(paulineCr); + BC_ASSERT_TRUE(linphone_address_has_uri_param(local_address, "gr")); + pauline_msg = _send_message(paulineCr, pauline_text); + BC_ASSERT_TRUE( + wait_for_list(coresList, &pauline->stat.number_of_LinphoneMessageSent, 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_FALSE(wait_for_list(coresList, &marie->stat.number_of_LinphoneChatRoomStateCreated, 2, 2000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &marie->stat.number_of_LinphoneAggregatedMessagesReceived, 1, + liblinphone_tester_sip_timeout)); + if (enable_imdn) { + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline->stat.number_of_LinphoneMessageDeliveredToUser, 1, + liblinphone_tester_sip_timeout)); + } else { + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline->stat.number_of_LinphoneMessageDelivered, 1, + liblinphone_tester_sip_timeout)); + } + } + + LinphoneChatMessageState expected_msg_state = + (enable_imdn) ? LinphoneChatMessageStateDisplayed : LinphoneChatMessageStateDelivered; + if (marieCr) { + if (enable_imdn) { + linphone_chat_room_mark_as_read(marieCr); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline->stat.number_of_LinphoneMessageDisplayed, 2, + liblinphone_tester_sip_timeout)); + // As Marie enables chat message aggregation, the message state changed is not called + BC_ASSERT_FALSE(wait_for_list(coresList, &marie->stat.number_of_LinphoneMessageDisplayed, 2, 1000)); + } + if (pauline_msg) { + linphone_chat_message_unref(pauline_msg); + } + + LinphoneChatMessage *msg = linphone_chat_room_get_last_message_in_history(marieCr); + if (BC_ASSERT_PTR_NOT_NULL(msg)) { + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_text(msg), pauline_text); + linphone_chat_message_unref(msg); + } + BC_ASSERT_EQUAL(linphone_chat_room_get_history_size(marieCr), 2, int, "%d"); + bctbx_list_t *marie_history = linphone_chat_room_get_history(marieCr, 3); + for (bctbx_list_t *item = marie_history; item; item = bctbx_list_next(item)) { + LinphoneChatMessage *msg = (LinphoneChatMessage *)bctbx_list_get_data(item); + BC_ASSERT_EQUAL(linphone_chat_message_get_state(msg), expected_msg_state, int, "%d"); + } + bctbx_list_free_with_data(marie_history, (bctbx_list_free_func)linphone_chat_message_unref); + linphone_core_manager_delete_chat_room(marie, marieCr, coresList); + linphone_chat_room_unref(marieCr); + } + if (paulineCr) { + BC_ASSERT_EQUAL(linphone_chat_room_get_history_size(paulineCr), 2, int, "%d"); + bctbx_list_t *pauline_history = linphone_chat_room_get_history(paulineCr, 3); + for (bctbx_list_t *item = pauline_history; item; item = bctbx_list_next(item)) { + LinphoneChatMessage *msg = (LinphoneChatMessage *)bctbx_list_get_data(item); + BC_ASSERT_EQUAL(linphone_chat_message_get_state(msg), expected_msg_state, int, "%d"); + } + bctbx_list_free_with_data(pauline_history, (bctbx_list_free_func)linphone_chat_message_unref); + linphone_core_manager_delete_chat_room(pauline, paulineCr, coresList); + } + + bctbx_list_free(coresList); + bctbx_list_free(coresManagerList); + + linphone_core_manager_destroy(marie); + linphone_core_manager_destroy(pauline); +} + +static void basic_chat_room_with_cpim_gruu(void) { + basic_chat_room_with_cpim_base(TRUE, FALSE); +} + +static void basic_chat_room_with_cpim_gruu_imdn(void) { + basic_chat_room_with_cpim_base(TRUE, TRUE); +} + +static void basic_chat_room_with_cpim_without_gruu(void) { + basic_chat_room_with_cpim_base(FALSE, FALSE); +} + static void find_one_to_one_chat_room(void) { LinphoneCoreManager *marie = linphone_core_manager_create("marie_rc"); LinphoneCoreManager *pauline = linphone_core_manager_create("pauline_rc"); @@ -9240,6 +9449,26 @@ static void group_chat_forward_file_transfer_message_url(const char *file_transf LinphoneChatRoomEphemeralModeDeviceManaged); const LinphoneAddress *confAddr = linphone_chat_room_get_conference_address(marieCr); + participantsAddresses = bctbx_list_append(NULL, linphone_address_new(linphone_core_get_identity(pauline->lc))); + LinphoneConferenceParams *conference_params = linphone_core_create_conference_params(marie->lc); + linphone_conference_params_enable_chat(conference_params, TRUE); + linphone_conference_params_enable_group(conference_params, FALSE); + linphone_conference_params_set_subject(conference_params, initialSubject); + linphone_conference_params_set_security_level(conference_params, LinphoneConferenceSecurityLevelNone); + LinphoneChatParams *chat_params = linphone_conference_params_get_chat_params(conference_params); + linphone_chat_params_set_backend(chat_params, LinphoneChatRoomBackendFlexisipChat); + linphone_chat_params_set_ephemeral_mode(chat_params, LinphoneChatRoomEphemeralModeDeviceManaged); + // Try search with the actual chat room subject + BC_ASSERT_PTR_NOT_NULL(linphone_core_search_chat_room_2(marie->lc, conference_params, marie->identity, confAddr, + participantsAddresses)); + + linphone_conference_params_set_subject(conference_params, "Default one to one subject"); + // Try search with a different chat room subject. As it is a one-to-one, the subject comparison should be ignored + BC_ASSERT_PTR_NOT_NULL(linphone_core_search_chat_room_2(marie->lc, conference_params, marie->identity, confAddr, + participantsAddresses)); + linphone_conference_params_unref(conference_params); + bctbx_list_free_with_data(participantsAddresses, (bctbx_list_free_func)linphone_address_unref); + // 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, &initialPaulineStats, confAddr, initialSubject, 1, FALSE); @@ -9467,6 +9696,9 @@ test_t group_chat2_tests[] = { TEST_ONE_TAG("IMDN sent from DB state", imdn_sent_from_db_state, "LeaksMemory"), TEST_NO_TAG("IMDN updated for group chat room with one participant offline", imdn_updated_for_group_chat_room_with_one_participant_offline), + TEST_NO_TAG("Basic chat room with CPIM and GRUU", basic_chat_room_with_cpim_gruu), + TEST_NO_TAG("Basic chat room with CPIM, GRUU and IMDN", basic_chat_room_with_cpim_gruu_imdn), + TEST_NO_TAG("Basic chat room with CPIM and without GRUU", basic_chat_room_with_cpim_without_gruu), TEST_NO_TAG("Find one-to-one chat room", find_one_to_one_chat_room), TEST_NO_TAG("Exhumed one-to-one chat room 1", exhume_one_to_one_chat_room_1), TEST_NO_TAG("Exhumed one-to-one chat room 2", exhume_one_to_one_chat_room_2), diff --git a/tester/images/bc.jpeg b/tester/images/bc.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..5bb3242453c8ce4082aacc9428966e3033f115f7 Binary files /dev/null and b/tester/images/bc.jpeg differ diff --git a/tester/liblinphone_tester++.h b/tester/liblinphone_tester++.h index 4b47af46c3f81b94fc180a710b726e169fd74e11..2b1bdb0bc96943d9c6f15e20d7b1fc24698914b7 100644 --- a/tester/liblinphone_tester++.h +++ b/tester/liblinphone_tester++.h @@ -113,7 +113,7 @@ public: BorrowedMut<LinphoneProxyConfig> getDefaultProxyConfig() { return borrowed_mut(linphone_core_get_default_proxy_config(mMgr->lc)); } - LinphonePrivate::Address getIdentity() { + LinphonePrivate::Address getIdentity() const { return *LinphonePrivate::Address::toCpp(mMgr->identity); } void setUseRfc2833ForDtmf(bool value) { @@ -184,7 +184,7 @@ public: LinphoneAccount *getDefaultAccount() const { return linphone_core_get_default_account(mMgr->lc); } - LinphoneCoreManager *getCMgr() { + LinphoneCoreManager *getCMgr() const { return mMgr.get(); }; LinphoneCore *getLc() const { diff --git a/tester/liblinphone_tester.c b/tester/liblinphone_tester.c index ea2ea8d789e82e68eb1d85b7dbe409f7786c7f6c..912c8c05bb951589e4dbf2a239fdfa006123e3d1 100644 --- a/tester/liblinphone_tester.c +++ b/tester/liblinphone_tester.c @@ -45,7 +45,7 @@ static const char *liblinphone_helper = "\t\t\t--auth-domain <test auth domain> (deprecated)\n" "\t\t\t--dns-hosts </etc/hosts -like file to used to override DNS names or 'none' for no overriding (default: " "tester_hosts)> (deprecated)\n" - "\t\t\t--max-failed max number of failed tests until program exit with return code 1. Current default is 2" + "\t\t\t--max-failed max number of failed tests until program exit with return code 1. Current default is 2\n" "\t\t\t--max-cpucount max number of cpu declared at mediastremaer2 level Current default is 2"; typedef struct _MireData { @@ -513,7 +513,7 @@ void liblinphone_tester_add_suites(void) { &local_conference_test_suite_scheduled_conference_audio_only_participant, 581); liblinphone_tester_add_suite_with_default_time( &local_conference_test_suite_scheduled_conference_with_screen_sharing, 581); - liblinphone_tester_add_suite_with_default_time(&local_conference_test_suite_scheduled_conference_with_chat, 200); + liblinphone_tester_add_suite_with_default_time(&local_conference_test_suite_scheduled_conference_with_chat, 600); liblinphone_tester_add_suite_with_default_time(&local_conference_test_suite_scheduled_ice_conference, 563); liblinphone_tester_add_suite_with_default_time(&local_conference_test_suite_impromptu_conference, 384); liblinphone_tester_add_suite_with_default_time(&local_conference_test_suite_encrypted_impromptu_conference, 150); @@ -607,6 +607,8 @@ void liblinphone_tester_add_suites(void) { &local_conference_test_suite_end_to_end_encryption_scheduled_conference_audio_only_participant, 337); liblinphone_tester_add_suite_with_default_time( &local_conference_test_suite_end_to_end_encryption_impromptu_conference, 155); + liblinphone_tester_add_suite_with_default_time( + &local_conference_test_suite_end_to_end_encryption_scheduled_conference_with_chat, 155); #endif // HAVE_EKT_SERVER_PLUGIN liblinphone_tester_add_suite_with_default_time(&clonable_object_test_suite, 0); #ifdef HAVE_DB_STORAGE diff --git a/tester/liblinphone_tester.h b/tester/liblinphone_tester.h index 2229035799362969fa3761912e41a1233cfdeb4b..6af62496afe8f4cafc2fca8303a5f89d0802bf71 100644 --- a/tester/liblinphone_tester.h +++ b/tester/liblinphone_tester.h @@ -63,9 +63,12 @@ extern test_suite_t call_flexfec_suite; extern test_suite_t call_video_advanced_scenarios_test_suite; #endif // if VIDEO_ENABLED +#ifdef HAVE_EKT_SERVER_PLUGIN extern test_suite_t local_conference_test_suite_end_to_end_encryption_scheduled_conference; extern test_suite_t local_conference_test_suite_end_to_end_encryption_scheduled_conference_audio_only_participant; +extern test_suite_t local_conference_test_suite_end_to_end_encryption_scheduled_conference_with_chat; extern test_suite_t local_conference_test_suite_end_to_end_encryption_impromptu_conference; +#endif // HAVE_EKT_SERVER_PLUGIN extern test_suite_t clonable_object_test_suite; extern test_suite_t conference_event_test_suite; extern test_suite_t conference_test_suite; @@ -517,6 +520,7 @@ typedef struct _stats { int number_of_chat_room_subject_changed; + int number_of_active_speaker_participant_device_changed; int number_of_allowed_participant_list_changed; int number_of_participants_added; int number_of_participant_role_changed; diff --git a/tester/lime-db-tester.cpp b/tester/lime-db-tester.cpp index 121b618e1d50dccc954f767120322c4dda45deb1..7b04041958871fddd41d7163c972757ea528a235 100644 --- a/tester/lime-db-tester.cpp +++ b/tester/lime-db-tester.cpp @@ -21,6 +21,7 @@ #include "liblinphone_tester.h" #include "linphone/core.h" #include "private.h" +#include <bctoolbox/vfs.h> using namespace std; @@ -76,9 +77,57 @@ static void db_access_store_db_in_memory_test(void) { linphone_core_manager_destroy(marie); } +/** + * Scenario: + * - create a lime user + * - stop the manager and corrupt the db so it cannot be opened by sqlite3 + * - restart the manager -> the imee should not be created + */ +static void corrupted_db(void) { + LinphoneCoreManager *pauline = linphone_core_manager_create("pauline_rc"); + bctbx_list_t *coresManagerList = NULL; + coresManagerList = bctbx_list_append(coresManagerList, pauline); + set_lime_server_and_curve_list(C25519, coresManagerList); + stats initialPaulineStats = pauline->stat; + bctbx_list_t *coresList = init_core_for_conference(coresManagerList); + start_core_for_conference(coresManagerList); + + // Wait for lime user creation + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline->stat.number_of_X3dhUserCreationSuccess, + initialPaulineStats.number_of_X3dhUserCreationSuccess + 1, + x3dhServer_creationTimeout)); + // Check imee is not null + BC_ASSERT_PTR_NOT_NULL(L_GET_CPP_PTR_FROM_C_OBJECT(pauline->lc)->getEncryptionEngine()); + + // Restart Pauline core, so the encryption engine is stopped and started and looses his cache + linphone_core_set_network_reachable(pauline->lc, FALSE); + coresList = bctbx_list_remove(coresList, pauline->lc); + // Corrupt Pauline database so it is not a valid sqlite3 file anymore + bctbx_vfs_file_t *dbFile = bctbx_file_open2(bctbx_vfs_get_default(), pauline->lime_database_path, O_RDWR); + const uint8_t buf[16] = {0}; + bctbx_file_write2(dbFile, buf, 16); + bctbx_file_close(dbFile); + + linphone_core_manager_reinit(pauline); + bctbx_list_t *tmpCoresManagerList = bctbx_list_append(NULL, pauline); + set_lime_server_and_curve_list(C25519, tmpCoresManagerList); + bctbx_list_t *tmpCoresList = init_core_for_conference(tmpCoresManagerList); + bctbx_list_free(tmpCoresManagerList); + coresList = bctbx_list_concat(coresList, tmpCoresList); + linphone_core_manager_start(pauline, TRUE); + wait_for_list(coresList, 0, 1, 2000); // Make sure Pauline's core restart is all done + // Check imee is null + BC_ASSERT_PTR_NULL(L_GET_CPP_PTR_FROM_C_OBJECT(pauline->lc)->getEncryptionEngine()); + + bctbx_list_free(coresList); + bctbx_list_free(coresManagerList); + linphone_core_manager_destroy(pauline); +} + test_t lime_db_tests[] = { TEST_NO_TAG("Data base access", db_access_test), TEST_NO_TAG("Data base access : Store BD in memory", db_access_store_db_in_memory_test), + TEST_ONE_TAG("Corrupted db", corrupted_db, "LimeX3DH"), }; test_suite_t lime_db_test_suite = {"Lime data bases", diff --git a/tester/lime-user-authentication-tester.cpp b/tester/lime-user-authentication-tester.cpp index 69e9a9b65533b0a10af41ef8c54308c3d95ceed5..748df067d4dce7313f09ac0220aa9b8e392a6135 100644 --- a/tester/lime-user-authentication-tester.cpp +++ b/tester/lime-user-authentication-tester.cpp @@ -463,9 +463,8 @@ static void Digest_Auth_multiservers(void) { } } -const char *wrong_lime_server = "https://lime.wildcard8.linphone.org:8443/lime-server/lime-server.php"; - -static void invalid_lime_server_in_account_curve(const LinphoneTesterLimeAlgo curveId) { +static void invalid_lime_server_in_account_curve(const LinphoneTesterLimeAlgo curveId, + const std::string &wrongLimeServerUrl) { LinphoneCoreManager *marie = linphone_core_manager_create("marie_rc"); linphone_core_manager_skip_lime_user_creation_asserts(marie, TRUE); bctbx_list_t *coresManagerList = NULL; @@ -476,7 +475,7 @@ static void invalid_lime_server_in_account_curve(const LinphoneTesterLimeAlgo cu if (marie_account) { LinphoneAccountParams *params = linphone_account_params_clone(linphone_account_get_params(marie_account)); linphone_account_params_set_lime_algo(params, limeAlgoEnum2String(curveId)); - linphone_account_params_set_lime_server_url(params, wrong_lime_server); + linphone_account_params_set_lime_server_url(params, wrongLimeServerUrl.c_str()); linphone_account_set_params(marie_account, params); linphone_account_params_unref(params); } @@ -485,7 +484,7 @@ static void invalid_lime_server_in_account_curve(const LinphoneTesterLimeAlgo cu bctbx_list_t *coresList = init_core_for_conference(coresManagerList); start_core_for_conference(coresManagerList); - // Wait for lime users to be created on X3DH server, but only for Marie & Pauline + // Wait for lime user to be created on X3DH server: it shall fail BC_ASSERT_FALSE(wait_for_list(coresList, &marie->stat.number_of_X3dhUserCreationSuccess, initialStats.number_of_X3dhUserCreationSuccess + 1, x3dhServer_creationTimeout)); BC_ASSERT_TRUE(wait_for_list(coresList, &marie->stat.number_of_X3dhUserCreationFailure, @@ -498,12 +497,22 @@ static void invalid_lime_server_in_account_curve(const LinphoneTesterLimeAlgo cu } static void invalid_lime_server_in_account(void) { - invalid_lime_server_in_account_curve(C25519); - invalid_lime_server_in_account_curve(C448); + // Server does not exists - unreachable + const std::string wrongLimeServer = "https://lime.wildcard8.linphone.org:8443/lime-server/lime-server.php"; + // URL is not valid + const std::string unparsableLimeServer = "ThisIsNotAnURL"; + + invalid_lime_server_in_account_curve(C25519, wrongLimeServer); + invalid_lime_server_in_account_curve(C448, wrongLimeServer); + invalid_lime_server_in_account_curve(C25519, unparsableLimeServer); + invalid_lime_server_in_account_curve(C448, unparsableLimeServer); if (liblinphone_tester_is_lime_PQ_available()) { - invalid_lime_server_in_account_curve(C25519K512); - invalid_lime_server_in_account_curve(C25519MLK512); - invalid_lime_server_in_account_curve(C448MLK1024); + invalid_lime_server_in_account_curve(C25519K512, wrongLimeServer); + invalid_lime_server_in_account_curve(C25519MLK512, wrongLimeServer); + invalid_lime_server_in_account_curve(C448MLK1024, wrongLimeServer); + invalid_lime_server_in_account_curve(C25519K512, unparsableLimeServer); + invalid_lime_server_in_account_curve(C25519MLK512, unparsableLimeServer); + invalid_lime_server_in_account_curve(C448MLK1024, unparsableLimeServer); } } diff --git a/tester/local-chat-tester-functions.cpp b/tester/local-chat-tester-functions.cpp index 624cee48d5655e0a53ed2005d549fdffcb3dd489..3984d5246617a76bf97b695f1601a6ab38efcfd7 100644 --- a/tester/local-chat-tester-functions.cpp +++ b/tester/local-chat-tester-functions.cpp @@ -415,8 +415,30 @@ void group_chat_room_with_client_restart_base(bool encrypted) { BC_ASSERT_TRUE( CoreManagerAssert({focus, marie, michelle, michelle2, berthe, laure}) .waitUntil(chrono::seconds(10), [&focus, &core] { return checkChatroom(focus, core, -1); })); + + const bctbx_list_t *chat_rooms = linphone_core_get_chat_rooms(core.getLc()); + for (const bctbx_list_t *chat_room_it = chat_rooms; chat_room_it != NULL; + chat_room_it = chat_room_it->next) { + const LinphoneChatRoom *chat_room = + static_cast<const LinphoneChatRoom *>(bctbx_list_get_data(chat_room_it)); + BC_ASSERT_PTR_NOT_NULL(chat_room); + if (chat_room) { + const char *chat_room_identifier = linphone_chat_room_get_identifier(chat_room); + LinphoneChatRoom *found_chat_room = + linphone_core_search_chat_room_by_identifier(core.getLc(), chat_room_identifier); + BC_ASSERT_PTR_NOT_NULL(found_chat_room); + BC_ASSERT_PTR_EQUAL(chat_room, found_chat_room); + } + } }; + // Test invalid peer address + BC_ASSERT_PTR_NULL(linphone_core_search_chat_room_by_identifier( + marie.getLc(), "==sip:toto@sip.conference.org##sip:me@sip.local.org")); + // Test inexistent chat room identifier + BC_ASSERT_PTR_NULL(linphone_core_search_chat_room_by_identifier( + marie.getLc(), "sip:toto@sip.conference.org##sip:me@sip.local.org")); + initialMarieStats = marie.getStats(); initialMichelleStats = michelle.getStats(); initialBertheStats = berthe.getStats(); diff --git a/tester/local-chat-tester.cpp b/tester/local-chat-tester.cpp index 1f49439f23dd72e2e642a929dffa920f60418325..deec756b55604cd72c0a4bf00a96196cc41efd57 100644 --- a/tester/local-chat-tester.cpp +++ b/tester/local-chat-tester.cpp @@ -760,6 +760,7 @@ static void group_chat_room_with_client_deletes_chatroom_after_restart(void) { 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()); + linphone_core_enable_gruu_in_conference_address(laure.getLc(), FALSE); stats focus_stat = focus.getStats(); marie_stat = marie.getStats(); @@ -1455,7 +1456,6 @@ static void group_chat_room_with_client_removed_while_stopped_base(bool_t use_re } setup_mgr_for_conference(michelle.getCMgr(), NULL); - coresList = bctbx_list_append(coresList, michelle.getLc()); if (use_remote_event_list_handler) { BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_LinphoneSubscriptionActive, initialMichelleStats.number_of_LinphoneSubscriptionActive + 1, @@ -2276,13 +2276,18 @@ static void group_chat_room_with_server_database_corruption(void) { } } -static void group_chat_room_bulk_notify_to_participant(void) { +static void group_chat_room_bulk_notify_to_participant_base(bool_t trigger_full_state) { Focus focus("chloe_rc"); { // to make sure focus is destroyed after clients. ClientConference marie("marie_rc", focus.getConferenceFactoryAddress()); ClientConference pauline("pauline_rc", focus.getConferenceFactoryAddress()); ClientConference michelle("michelle_rc", focus.getConferenceFactoryAddress()); + if (trigger_full_state) { + linphone_config_set_int(linphone_core_get_config(focus.getLc()), "misc", + "full_state_trigger_due_to_missing_updates", 1); + } + focus.registerAsParticipantDevice(marie); focus.registerAsParticipantDevice(pauline); focus.registerAsParticipantDevice(michelle); @@ -2484,28 +2489,37 @@ static void group_chat_room_bulk_notify_to_participant(void) { initialPaulineStats = pauline.getStats(); // Pauline comes up online + ms_message("%s turns network on again", linphone_core_get_identity(pauline.getLc())); 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)); + if (trigger_full_state) { + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_NotifyFullStateReceived, + initialPaulineStats.number_of_NotifyFullStateReceived + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_FALSE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneChatRoomConferenceJoined, + initialPaulineStats.number_of_LinphoneChatRoomConferenceJoined + 1, 2000)); + } else { + // 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); @@ -2539,6 +2553,14 @@ static void group_chat_room_bulk_notify_to_participant(void) { } } +static void group_chat_room_bulk_notify_to_participant(void) { + group_chat_room_bulk_notify_to_participant_base(FALSE); +} + +static void group_chat_room_bulk_notify_full_state_to_participant(void) { + group_chat_room_bulk_notify_to_participant_base(TRUE); +} + static void one_to_one_chatroom_backward_compatibility_base(const char *groupchat_spec) { Focus focus("chloe_rc"); { // to make sure focus is destroyed after clients. @@ -2951,6 +2973,8 @@ static void multidomain_group_chat_room(void) { initialMichelleStats = michelle.getStats(); participantsAddresses = bctbx_list_append(NULL, linphone_address_ref(paulineAddr.toC())); participantsAddresses = bctbx_list_append(participantsAddresses, linphone_address_ref(michelleAddr.toC())); + ms_message("%s creates chat on conference server %s", linphone_core_get_identity(marie.getLc()), + focusAuth1DotExampleDotOrgFactoryAddress.toString().c_str()); LinphoneChatRoom *marieCrfocusAuth1DotExampleDotOrg = create_chat_room_client_side(coresList, marie.getCMgr(), &initialMarieStats, participantsAddresses, initialSubject, FALSE, LinphoneChatRoomEphemeralModeDeviceManaged); @@ -3444,6 +3468,7 @@ static void group_chat_room_with_duplications(void) { 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()); + linphone_core_enable_gruu_in_conference_address(laure.getLc(), FALSE); linphone_config_set_string(linphone_core_get_config(laure.getLc()), "misc", "uuid", NULL); linphone_core_remove_linphone_spec(laure.getLc(), "groupchat"); const char *spec = "groupchat/1.2"; @@ -3560,6 +3585,7 @@ static void group_chat_room_with_duplications(void) { ms_message("%s reinitializes one last time its core", linphone_core_get_identity(laure.getLc())); coresList = bctbx_list_remove(coresList, laure.getLc()); linphone_core_manager_reinit(laure.getCMgr()); + linphone_core_enable_gruu_in_conference_address(laure.getLc(), FALSE); // Keep the same uuid linphone_config_set_string(linphone_core_get_config(laure.getLc()), "misc", "uuid", uuid); if (uuid) { @@ -3644,7 +3670,7 @@ static void group_chat_room_with_duplications(void) { })); for (const auto &conferenceId : oldConferenceIds) { - const auto chatRoom = laure.getCore().findChatRoom(conferenceId); + const auto chatRoom = laure.getCore().findChatRoom(conferenceId, false); BC_ASSERT_PTR_NOT_NULL(chatRoom); if (chatRoom) { BC_ASSERT_EQUAL(laureMainDb->getConferenceNotifiedEvents(conferenceId, 0).size(), @@ -3739,8 +3765,9 @@ static test_t local_conference_chat_basic_tests[] = { 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_NO_TAG("Group chat room bulk notify to participant", LinphoneTest::group_chat_room_bulk_notify_to_participant), + TEST_NO_TAG("Group chat room bulk notify full state to participant", + LinphoneTest::group_chat_room_bulk_notify_full_state_to_participant), 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*/ diff --git a/tester/local-conference-edition-tester.cpp b/tester/local-conference-edition-tester.cpp index d7bb080ae73f04f5d639e29ad612e80e7bec8c4b..5f6768ac08171d9141dc1a85a7051874c8df6675 100644 --- a/tester/local-conference-edition-tester.cpp +++ b/tester/local-conference-edition-tester.cpp @@ -305,6 +305,8 @@ static void edit_simple_conference_base(bool_t from_organizer, focus.registerAsParticipantDevice(michelle); focus.registerAsParticipantDevice(lise); + linphone_core_enable_gruu_in_conference_address(focus.getLc(), TRUE); + setup_conference_info_cbs(marie.getCMgr()); linphone_core_set_file_transfer_server(marie.getLc(), file_transfer_url); @@ -353,7 +355,7 @@ static void edit_simple_conference_base(bool_t from_organizer, } LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, initialSubject, - description, TRUE, security_level, TRUE, enable_chat); + description, TRUE, security_level, TRUE, enable_chat, NULL); BC_ASSERT_PTR_NOT_NULL(confAddr); // Chat room creation to send ICS @@ -781,10 +783,7 @@ static void edit_simple_conference_base(bool_t from_organizer, 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)); + check_conference_me(pconference, ((mgr == marie.getCMgr()) || (mgr == focus.getCMgr()))); 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); @@ -879,9 +878,9 @@ static void edit_simple_conference_base(bool_t from_organizer, if (conf_info) { linphone_conference_info_set_subject(conf_info, subject); linphone_conference_info_set_description(conf_info, description2); - // If the edition is not succesfull, the conference information is not updated - // It bring about that if Marie joined the conference, the old conference information information stays - // in the database and it has Marie as a participant + // If the edition is not successfull, the conference information is not updated + // It bring about that if Marie joined the conference, the old conference information stays in the + // database and it has Marie as a participant size_t ics_participant_number = 0; if (add) { linphone_conference_info_add_participant(conf_info, michelle.getCMgr()->identity); @@ -1380,7 +1379,7 @@ static void conference_edition_with_simultaneous_participant_add_remove_base(boo } LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, initialSubject, - description, TRUE, security_level, FALSE, FALSE); + description, TRUE, security_level, FALSE, FALSE, NULL); BC_ASSERT_PTR_NOT_NULL(confAddr); // Chat room creation to send ICS @@ -1788,7 +1787,7 @@ static void conference_cancelled_through_edit_base(bool_t server_restart, bool_t } LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, initialSubject, - description, TRUE, security_level, FALSE, FALSE); + description, TRUE, security_level, FALSE, FALSE, NULL); BC_ASSERT_PTR_NOT_NULL(confAddr); // Chat room creation to send ICS @@ -2312,7 +2311,7 @@ static void conference_cancelled_through_edit_while_active(void) { } LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, initialSubject, - description, TRUE, security_level, FALSE, FALSE); + description, TRUE, security_level, FALSE, FALSE, NULL); BC_ASSERT_PTR_NOT_NULL(confAddr); char *conference_address_str = (confAddr) ? linphone_address_as_string(confAddr) : ms_strdup("<unknown>"); diff --git a/tester/local-conference-tester-functions.cpp b/tester/local-conference-tester-functions.cpp index a3b291c9d059e23d7bfe24fa9d8149e229a48f5e..39785a865ebdbe069dc0ab2f01f87caad9dd3195 100644 --- a/tester/local-conference-tester-functions.cpp +++ b/tester/local-conference-tester-functions.cpp @@ -168,6 +168,19 @@ static bool check_conference_info_by_participant(LinphoneCoreManager *mgr, return found_in_all_participants; } +void check_conference_me(LinphoneConference *conference, bool_t is_admin) { + LinphoneParticipant *me = linphone_conference_get_me(conference); + BC_ASSERT_TRUE(linphone_participant_is_admin(me) == is_admin); + LinphoneAccount *account = linphone_conference_get_account(conference); + BC_ASSERT_PTR_NOT_NULL(account); + if (account) { + const LinphoneAccountParams *account_params = linphone_account_get_params(account); + const LinphoneAddress *account_identity_address = linphone_account_params_get_identity_address(account_params); + BC_ASSERT_PTR_NOT_NULL(account_identity_address); + BC_ASSERT_TRUE(linphone_address_weak_equal(linphone_participant_get_address(me), account_identity_address)); + } +} + LinphoneAddress * create_conference_on_server(Focus &focus, ClientConference &organizer, @@ -179,7 +192,8 @@ create_conference_on_server(Focus &focus, bool_t send_ics, LinphoneConferenceSecurityLevel security_level, bool_t enable_video, - bool_t enable_chat) { + bool_t enable_chat, + LinphoneConferenceParams *ics_chat_room_params) { bctbx_list_t *coresList = bctbx_list_append(NULL, focus.getLc()); coresList = bctbx_list_append(coresList, organizer.getLc()); std::vector<stats> participant_stats; @@ -192,12 +206,19 @@ create_conference_on_server(Focus &focus, const bctbx_list_t *initial_focus_call_logs = linphone_core_get_call_logs(focus.getLc()); 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 is_dialout = (start_time < 0); + // Dialout conferences do not instantiate un conference scheduler therefore they cannot send an ICS + bool will_send_ics = (is_dialout) ? false : !!send_ics; bool found_me = false; char *conference_address_str = NULL; + int duration = 0; char *uid = NULL; LinphoneConferenceInfo *info = NULL; - for (const auto &[mgr, participant_info] : requested_participants) { + LinphoneConferenceScheduler *conference_scheduler = NULL; + for (auto &[mgr, participant_info] : requested_participants) { + if (is_dialout) { + linphone_participant_info_set_role(participant_info, LinphoneParticipantRoleSpeaker); + } LinphoneParticipantInfo *participant_info_clone = linphone_participant_info_clone(participant_info); participants_info = bctbx_list_append(participants_info, participant_info_clone); if (mgr == organizer.getCMgr()) { @@ -209,56 +230,61 @@ create_conference_on_server(Focus &focus, } } - int duration = 0; - if ((end_time >= 0) && (start_time >= 0) && (end_time > start_time)) { - duration = static_cast<int>((end_time - start_time) / 60); // duration is expected to be set in minutes - } 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); - } - + int idx = 0; + std::list<LinphoneCoreManager *> actual_participants; + int call_errors_cnt = 0; + LinphoneAddress *conference_address = NULL; LinphoneAccount *default_account = linphone_core_get_default_account(organizer.getLc()); - - // The organizer creates a conference scheduler - LinphoneConferenceScheduler *conference_scheduler = - linphone_core_create_sip_conference_scheduler(organizer.getLc(), default_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); - - LinphoneConferenceInfo *conf_info = linphone_conference_info_new(); - LinphoneAddress *organizer_address = default_account ? linphone_address_clone(linphone_account_params_get_identity_address( linphone_account_get_params(default_account))) : linphone_address_clone(organizer.getCMgr()->identity); - linphone_conference_info_set_organizer(conf_info, organizer_address); - linphone_conference_info_set_participant_infos(conf_info, participants_info); - linphone_conference_info_set_duration(conf_info, duration); - 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_info_set_security_level(conf_info, security_level); - linphone_conference_info_set_capability(conf_info, LinphoneStreamTypeVideo, enable_video); - linphone_conference_info_set_capability(conf_info, LinphoneStreamTypeText, enable_chat); - - 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 (is_dialout) { + stats focus_stat = focus.getStats(); + // marie creates the conference + LinphoneConferenceParams *conference_params = linphone_core_create_conference_params_2(organizer.getLc(), NULL); + linphone_conference_params_set_account(conference_params, default_account); + linphone_conference_params_enable_audio(conference_params, TRUE); + linphone_conference_params_enable_video(conference_params, enable_video); + linphone_conference_params_enable_chat(conference_params, enable_chat); + linphone_conference_params_set_security_level(conference_params, security_level); + linphone_conference_params_set_subject(conference_params, subject); + LinphoneConference *conference = + linphone_core_create_conference_with_params(organizer.getLc(), conference_params); + linphone_conference_params_unref(conference_params); + BC_ASSERT_PTR_NOT_NULL(conference); + if (conference) { + bctbx_list_t *participant_addresses = NULL; + for (auto &mgr : participants) { + participant_addresses = bctbx_list_append(participant_addresses, mgr->identity); + } + LinphoneCallParams *call_params = linphone_core_create_call_params(organizer.getLc(), NULL); + linphone_call_params_enable_audio(call_params, TRUE); + const LinphoneVideoActivationPolicy *video_policy = + linphone_core_get_video_activation_policy(organizer.getLc()); + linphone_call_params_enable_video( + call_params, linphone_video_activation_policy_get_automatically_initiate(video_policy)); + linphone_call_params_enable_realtime_text(call_params, FALSE); + linphone_conference_invite_participants(conference, participant_addresses, call_params); + bctbx_list_free(participant_addresses); + linphone_call_params_unref(call_params); + if (focus_organizer_common_payload) { + BC_ASSERT_TRUE(wait_for_list(coresList, + &organizer.getStats().number_of_LinphoneConferenceStateCreationPending, + organizer_stat.number_of_LinphoneConferenceStateCreationPending + 1, + liblinphone_tester_sip_timeout)); + conference_address = linphone_address_clone(linphone_conference_get_conference_address(conference)); + } else { + BC_ASSERT_TRUE(wait_for_list(coresList, + &organizer.getStats().number_of_LinphoneConferenceStateCreationFailed, + organizer_stat.number_of_LinphoneConferenceStateCreationFailed + 1, + liblinphone_tester_sip_timeout)); + } + linphone_conference_unref(conference); + } + if (focus_organizer_common_payload) { BC_ASSERT_TRUE(wait_for_list(coresList, &organizer.getStats().number_of_LinphoneCallOutgoingInit, organizer_stat.number_of_LinphoneCallOutgoingInit + 1, @@ -267,11 +293,7 @@ create_conference_on_server(Focus &focus, 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); + linphone_conference_terminate(conference); goto end; } BC_ASSERT_TRUE( @@ -309,31 +331,80 @@ create_conference_on_server(Focus &focus, 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)); + } else { - actual_participants = participants; - } + if ((end_time >= 0) && (start_time >= 0) && (end_time > start_time)) { + duration = static_cast<int>((end_time - start_time) / 60); // duration is expected to be set in minutes + } - BC_ASSERT_TRUE(wait_for_list(coresList, &organizer.getStats().number_of_ConferenceSchedulerStateReady, - organizer_stat.number_of_ConferenceSchedulerStateReady + 1, - liblinphone_tester_sip_timeout)); + for (auto &mgr : participants) { + previous_calls[mgr] = linphone_core_get_call_by_remote_address2(mgr->lc, focus.getCMgr()->identity); + } + + // The organizer creates a conference scheduler + conference_scheduler = linphone_core_create_sip_conference_scheduler(organizer.getLc(), default_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); + + LinphoneConferenceInfo *conf_info = linphone_conference_info_new(); + linphone_conference_info_set_organizer(conf_info, organizer_address); + linphone_conference_info_set_participant_infos(conf_info, participants_info); + linphone_conference_info_set_duration(conf_info, duration); + 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_info_set_security_level(conf_info, security_level); + linphone_conference_info_set_capability(conf_info, LinphoneStreamTypeVideo, enable_video); + linphone_conference_info_set_capability(conf_info, LinphoneStreamTypeText, enable_chat); + + linphone_conference_scheduler_set_info(conference_scheduler, conf_info); + linphone_conference_info_unref(conf_info); - updated_conf_info = linphone_conference_scheduler_get_info(conference_scheduler); - conference_address = linphone_address_clone(linphone_conference_info_get_uri(updated_conf_info)); + BC_ASSERT_TRUE(wait_for_list( + coresList, &organizer.getStats().number_of_ConferenceSchedulerStateAllocationPending, + organizer_stat.number_of_ConferenceSchedulerStateAllocationPending + 1, liblinphone_tester_sip_timeout)); - 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, + BC_ASSERT_TRUE(wait_for_list(coresList, &organizer.getStats().number_of_ConferenceSchedulerStateReady, + organizer_stat.number_of_ConferenceSchedulerStateReady + 1, liblinphone_tester_sip_timeout)); + + actual_participants = participants; + + updated_conf_info = linphone_conference_scheduler_get_info(conference_scheduler); + conference_address = linphone_address_clone(linphone_conference_info_get_uri(updated_conf_info)); + + if (will_send_ics) { + LinphoneConferenceParams *conference_params = NULL; + if (ics_chat_room_params) { + conference_params = linphone_conference_params_clone(ics_chat_room_params); + } else { + + conference_params = linphone_core_create_conference_params_2(organizer.getLc(), NULL); + linphone_conference_params_enable_chat(conference_params, TRUE); + linphone_conference_params_enable_group(conference_params, FALSE); + char subject[200]; + char *conference_address_string = linphone_address_as_string(conference_address); + snprintf(subject, sizeof(subject) - 1, "ICS for conference %s", conference_address_string); + linphone_conference_params_set_subject(conference_params, subject); + ms_free(conference_address_string); + LinphoneChatParams *chat_params = linphone_conference_params_get_chat_params(conference_params); + linphone_chat_params_set_backend(chat_params, LinphoneChatRoomBackendBasic); + } + linphone_conference_scheduler_send_invitations_2(conference_scheduler, conference_params); + linphone_conference_params_unref(conference_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) { + if (will_send_ics) { BC_ASSERT_PTR_NOT_NULL(uid); for (auto &p : participants) { linphone_conference_info_check_participant(info, p->identity, 0); @@ -344,26 +415,26 @@ create_conference_on_server(Focus &focus, linphone_conference_info_unref(info); } - if (dialout && !send_ics && !found_me) { + if (is_dialout && !will_send_ics && !found_me) { add_participant_info_to_list(&participants_info, organizer.getCMgr()->identity, LinphoneParticipantRoleSpeaker, 0); } for (bctbx_list_t *info_it = participants_info; info_it; info_it = bctbx_list_next(info_it)) { LinphoneParticipantInfo *participant_info_el = (LinphoneParticipantInfo *)bctbx_list_get_data(info_it); - if (dialout && !send_ics) { + if (is_dialout && !will_send_ics) { linphone_participant_info_set_role(participant_info_el, LinphoneParticipantRoleUnknown); linphone_participant_info_set_sequence_number(participant_info_el, -1); } else { linphone_participant_info_set_sequence_number(participant_info_el, 0); - if (dialout && send_ics && + if (is_dialout && will_send_ics && (linphone_participant_info_get_role(participant_info_el) == LinphoneParticipantRoleUnknown)) { linphone_participant_info_set_role(participant_info_el, LinphoneParticipantRoleSpeaker); } } } - if (!dialout) { + if (!is_dialout) { // This check is not reliable when the conference is dialing participants check_conference_info_in_db(organizer.getCMgr(), uid, conference_address, organizer.getCMgr()->identity, participants_info, start_time, duration, subject, description, 0, @@ -373,28 +444,27 @@ create_conference_on_server(Focus &focus, idx = 0; for (auto &mgr : participants) { - if (!dialout || !previous_calls[mgr]) { + if (!is_dialout || !previous_calls[mgr]) { auto old_stats = participant_stats[idx]; - if (!!send_ics) { + if (will_send_ics) { // chat room in created state BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneChatRoomStateCreated, 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 (L_GET_CPP_PTR_FROM_C_OBJECT(mgr->lc)->canAggregateChatMessages()) { + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneAggregatedMessagesReceived, + old_stats.number_of_LinphoneAggregatedMessagesReceived + 1, + liblinphone_tester_sip_timeout)); + } else { + 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); @@ -405,6 +475,9 @@ create_conference_on_server(Focus &focus, BC_ASSERT_PTR_NOT_NULL(msg); if (msg) { + const string ics_media_type = ContentType::Icalendar.getMediaType(); + BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_content_type(msg), ics_media_type.c_str()); + 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); @@ -415,10 +488,10 @@ create_conference_on_server(Focus &focus, if (!BC_ASSERT_PTR_NOT_NULL(conf_info_in_db)) { goto end; } - check_conference_info_members(conf_info_in_db, uid, conference_address, organizer_address, - participants_info, start_time, duration, subject, description, 0, - LinphoneConferenceInfoStateNew, - LinphoneConferenceSecurityLevelNone, dialout, TRUE, TRUE, FALSE); + check_conference_info_members( + conf_info_in_db, uid, conference_address, organizer_address, participants_info, start_time, + duration, subject, description, 0, LinphoneConferenceInfoStateNew, + LinphoneConferenceSecurityLevelNone, is_dialout, TRUE, TRUE, FALSE); LinphoneConferenceInfo *conf_info_from_original_content = linphone_factory_create_conference_info_from_icalendar_content(linphone_factory_get(), @@ -433,7 +506,7 @@ create_conference_on_server(Focus &focus, } } - if (!!send_ics || dialout) { + if (will_send_ics || is_dialout) { auto itConferenceMgrs = std::find(actual_participants.begin(), actual_participants.end(), mgr); if (itConferenceMgrs != actual_participants.end()) { LinphoneConferenceInfo *conf_info_in_db = @@ -445,9 +518,9 @@ create_conference_on_server(Focus &focus, check_conference_info_members( conf_info_in_db, uid, conference_address, organizer.getCMgr()->identity, participants_info, start_time, ((start_time > 0) && (end_time > 0)) ? (int)(end_time - start_time) / 60 : 0, - subject, (!!send_ics) ? description : NULL, 0, LinphoneConferenceInfoStateNew, - LinphoneConferenceSecurityLevelNone, dialout, TRUE, TRUE, FALSE); - if (!!send_ics) { + subject, (will_send_ics) ? description : NULL, 0, LinphoneConferenceInfoStateNew, + LinphoneConferenceSecurityLevelNone, is_dialout, TRUE, TRUE, FALSE); + if (will_send_ics) { for (auto &p : participants) { linphone_conference_info_check_participant(conf_info_in_db, p->identity, 0); } @@ -462,7 +535,7 @@ create_conference_on_server(Focus &focus, idx++; } - if (conference_address && !dialout) { + if (conference_address && !is_dialout) { check_conference_info_in_db(organizer.getCMgr(), uid, conference_address, organizer.getCMgr()->identity, participants_info, start_time, duration, subject, description, 0, LinphoneConferenceInfoStateNew, security_level, FALSE, TRUE, enable_video, @@ -489,7 +562,7 @@ create_conference_on_server(Focus &focus, // The following asserts might fail for dial out conference because the client automatically calls the server after // scheduling the conference. Indeed, by the time the verification below is carried out the client might have // already called the server therefore a genuine and valid call log may have already been created - if (!dialout) { + if (!is_dialout) { organizer_call_logs = linphone_core_get_call_logs(organizer.getLc()); if (organizer_call_logs && initial_organizer_call_logs) { BC_ASSERT_EQUAL(bctbx_list_size(organizer_call_logs), bctbx_list_size(initial_organizer_call_logs), size_t, @@ -513,7 +586,7 @@ end: } if (organizer_address) linphone_address_unref(organizer_address); - linphone_conference_scheduler_unref(conference_scheduler); + if (conference_scheduler) linphone_conference_scheduler_unref(conference_scheduler); bctbx_list_free(coresList); bctbx_list_free_with_data(participants_info, (bctbx_list_free_func)linphone_participant_info_unref); return conference_address; @@ -964,7 +1037,6 @@ void wait_for_conference_streams(std::initializer_list<std::reference_wrapper<Co } } } - return audio_direction_check && video_check && device_check && call_check && participant_check && security_check; })); @@ -1142,8 +1214,7 @@ void set_video_settings_in_conference(LinphoneCoreManager *focus, if (media_not_changed) { 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, - liblinphone_tester_sip_timeout)); + 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, @@ -1185,8 +1256,8 @@ void set_video_settings_in_conference(LinphoneCoreManager *focus, int, "%0d"); for (auto mgr : active_cores) { - LinphoneAddress *uri2 = mgr->identity; - LinphoneConference *pconference = linphone_core_search_conference(mgr->lc, NULL, uri2, confAddr, NULL); + const LinphoneAddress *local_address = (mgr == focus) ? confAddr : mgr->identity; + LinphoneConference *pconference = linphone_core_search_conference(mgr->lc, NULL, local_address, confAddr, NULL); BC_ASSERT_PTR_NOT_NULL(pconference); if (pconference) { LinphoneParticipant *p = (mgr == participant) @@ -1278,7 +1349,8 @@ void create_conference_base(time_t start_time, std::list<LinphoneParticipantRole> allowedRoles, bool_t add_participant_after_end, bool_t version_mismatch, - bool_t use_relay_ice_candidates) { + bool_t use_relay_ice_candidates, + bool_t enable_chat) { Focus focus("chloe_rc"); { // to make sure focus is destroyed after clients. bool_t enable_lime = (security_level == LinphoneConferenceSecurityLevelEndToEnd ? TRUE : FALSE); @@ -1381,6 +1453,8 @@ void create_conference_base(time_t start_time, std::list<LinphoneCoreManager *> participants{laure.getCMgr(), pauline.getCMgr()}; std::list<LinphoneCoreManager *> conferenceMgrs{focus.getCMgr(), marie.getCMgr(), pauline.getCMgr(), laure.getCMgr()}; + std::list<LinphoneCoreManager *> allConferenceMgrs{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); @@ -1414,7 +1488,7 @@ void create_conference_base(time_t start_time, LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, initialSubject, - description, TRUE, security_level, enable_video, FALSE); + description, TRUE, security_level, enable_video, enable_chat, NULL); BC_ASSERT_PTR_NOT_NULL(confAddr); char *conference_address_str = (confAddr) ? linphone_address_as_string(confAddr) : ms_strdup("sip:"); @@ -1617,7 +1691,7 @@ void create_conference_base(time_t start_time, const int duration2 = (duration < 0) ? 0 : duration; check_conference_info_in_db(mgr, uid, confAddr, marie.getCMgr()->identity, participants_info, start_time, duration2, initialSubject, description, 0, LinphoneConferenceInfoStateNew, - security_level, FALSE, TRUE, enable_video, FALSE); + security_level, FALSE, TRUE, enable_video, enable_chat); } for (const auto &mgr : conferenceMgrs) { @@ -1634,10 +1708,23 @@ void create_conference_base(time_t start_time, return marie_call && (linphone_call_get_duration(marie_call) > nortp_timeout); }); + // Test invalid peer address + BC_ASSERT_PTR_NULL(linphone_core_search_conference_by_identifier( + marie.getLc(), "==sip:toto@sip.conference.org##sip:me@sip.local.org")); + // Test inexistent chat room identifier + BC_ASSERT_PTR_NULL(linphone_core_search_conference_by_identifier( + marie.getLc(), "sip:toto@sip.conference.org##sip:me@sip.local.org")); + for (auto mgr : conferenceMgrs) { LinphoneConference *pconference = linphone_core_search_conference_2(mgr->lc, confAddr); BC_ASSERT_PTR_NOT_NULL(pconference); if (pconference) { + const char *conference_identifier = linphone_conference_get_identifier(pconference); + LinphoneConference *found_pconference = + linphone_core_search_conference_by_identifier(mgr->lc, conference_identifier); + BC_ASSERT_PTR_NOT_NULL(found_pconference); + BC_ASSERT_PTR_EQUAL(pconference, found_pconference); + const LinphoneConferenceParams *conference_params = linphone_conference_get_current_params(pconference); int no_participants = 0; if (start_time >= 0) { @@ -1649,25 +1736,28 @@ void create_conference_base(time_t start_time, BC_ASSERT_EQUAL((int)linphone_conference_params_get_security_level(conference_params), (int)security_level, int, "%0d"); - - 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"); - if (!(mgr == focus.getCMgr() && security_level == LinphoneConferenceSecurityLevelEndToEnd)) { - 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) { - check_muted({focus, marie, pauline, laure, michelle, berthe}, d, {pauline.getCMgr()}); - 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); + if (!all_listeners) { + 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"); + if (!(mgr == focus.getCMgr() && security_level == LinphoneConferenceSecurityLevelEndToEnd)) { + 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) { + check_muted({focus, marie, pauline, laure, michelle, berthe}, d, {pauline.getCMgr()}); + 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); } - 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()); @@ -1677,6 +1767,13 @@ void create_conference_base(time_t start_time, 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); + const LinphoneCallParams *current_call_lparams = linphone_call_get_params(current_call); + BC_ASSERT_PTR_NOT_NULL(current_call_lparams); + if (current_call_lparams) { + LinphoneConferenceLayout current_layout = + linphone_call_params_get_conference_video_layout(current_call_lparams); + BC_ASSERT_EQUAL(current_layout, layout, int, "%i"); + } if (enable_video && video_direction == LinphoneMediaDirectionSendRecv && layout == LinphoneConferenceLayoutActiveSpeaker) { BC_ASSERT_PTR_NOT_NULL(linphone_call_params_get_used_video_payload_type( @@ -1793,8 +1890,8 @@ void create_conference_base(time_t start_time, BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_lparams), (all_listeners ? 1 : video_negotiated), int, "%0d"); const LinphoneCallParams *call_rparams = linphone_call_get_remote_params(ccall); - BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_rparams), video_negotiated, int, - "%0d"); + BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_rparams), + (all_listeners ? enable_video : video_negotiated), int, "%0d"); } const LinphoneCallParams *call_cparams = linphone_call_get_current_params(ccall); BC_ASSERT_EQUAL(linphone_call_params_video_enabled(call_cparams), video_negotiated, int, "%0d"); @@ -1802,10 +1899,7 @@ void create_conference_base(time_t start_time, } 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)); + check_conference_me(pconference, ((mgr == marie.getCMgr()) || (mgr == focus.getCMgr()))); 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); @@ -1824,43 +1918,44 @@ void create_conference_base(time_t start_time, } } - 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_set_microphone_muted(marie_conference, TRUE); - } - - for (auto mgr : conferenceMgrs) { - if (mgr != marie.getCMgr() && - !(mgr == focus.getCMgr() && security_level == LinphoneConferenceSecurityLevelEndToEnd)) { - BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneParticipantDeviceMuted, 1, - liblinphone_tester_sip_timeout)); - } - if (mgr != focus.getCMgr()) { - LinphoneCall *c1 = linphone_core_get_call_by_remote_address2(mgr->lc, focus.getCMgr()->identity); - if (c1) { - BC_ASSERT_TRUE(linphone_call_get_microphone_muted(c1) == - ((mgr == pauline.getCMgr()) || (mgr == marie.getCMgr()))); - } + if (!all_listeners) { + 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_set_microphone_muted(marie_conference, TRUE); } - if (!(mgr == focus.getCMgr() && security_level == LinphoneConferenceSecurityLevelEndToEnd)) { - LinphoneConference *pconference = linphone_core_search_conference_2(mgr->lc, confAddr); + for (auto mgr : conferenceMgrs) { + if (mgr != marie.getCMgr() && + !(mgr == focus.getCMgr() && security_level == LinphoneConferenceSecurityLevelEndToEnd)) { + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneParticipantDeviceMuted, 1, + liblinphone_tester_sip_timeout)); + } + if (mgr != focus.getCMgr()) { + LinphoneCall *c1 = linphone_core_get_call_by_remote_address2(mgr->lc, focus.getCMgr()->identity); + if (c1) { + BC_ASSERT_TRUE(linphone_call_get_microphone_muted(c1) == + ((mgr == pauline.getCMgr()) || (mgr == marie.getCMgr()))); + } + } - if (pconference) { - 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) { - check_muted({focus, marie, pauline, laure, michelle, berthe}, d, - {marie.getCMgr(), pauline.getCMgr()}); + if (!(mgr == focus.getCMgr() && security_level == LinphoneConferenceSecurityLevelEndToEnd)) { + LinphoneConference *pconference = linphone_core_search_conference_2(mgr->lc, confAddr); + if (pconference) { + 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) { + check_muted({focus, marie, pauline, laure, michelle, berthe}, d, + {marie.getCMgr(), pauline.getCMgr()}); + } } + bctbx_list_free_with_data(participant_device_list, + (void (*)(void *))linphone_participant_device_unref); } - bctbx_list_free_with_data(participant_device_list, - (void (*)(void *))linphone_participant_device_unref); } } } @@ -1985,8 +2080,9 @@ void create_conference_base(time_t start_time, }); for (auto mgr : conferenceMgrs) { - LinphoneAddress *uri = mgr->identity; - LinphoneConference *pconference = linphone_core_search_conference(mgr->lc, NULL, uri, confAddr, NULL); + const LinphoneAddress *local_address = (mgr == focus.getCMgr()) ? confAddr : mgr->identity; + LinphoneConference *pconference = + linphone_core_search_conference(mgr->lc, NULL, local_address, confAddr, NULL); BC_ASSERT_PTR_NOT_NULL(pconference); if (pconference) { const LinphoneConferenceParams *conference_params = @@ -2079,10 +2175,7 @@ void create_conference_base(time_t start_time, 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)); + check_conference_me(pconference, ((mgr == marie.getCMgr()) || (mgr == focus.getCMgr()))); 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); @@ -2291,9 +2384,9 @@ void create_conference_base(time_t start_time, bool pauline_expected_thumbnails_requested = are_participants_camera_streams_requested(pauline.getCMgr(), paulineConference); for (auto mgr : conferenceMgrs) { - const LinphoneAddress *uri = mgr->identity; + const LinphoneAddress *local_address = (mgr == focus.getCMgr()) ? confAddr : mgr->identity; LinphoneConference *pconference = - linphone_core_search_conference(mgr->lc, NULL, uri, confAddr, NULL); + linphone_core_search_conference(mgr->lc, NULL, local_address, confAddr, NULL); BC_ASSERT_PTR_NOT_NULL(pconference); if (pconference) { LinphoneParticipant *p = (mgr == pauline.getCMgr()) @@ -2447,8 +2540,9 @@ void create_conference_base(time_t start_time, liblinphone_tester_sip_timeout)); for (auto mgr : conferenceMgrs) { + const LinphoneAddress *local_address = (mgr == focus.getCMgr()) ? confAddr : mgr->identity; LinphoneConference *pconference = - linphone_core_search_conference(mgr->lc, NULL, mgr->identity, confAddr, NULL); + linphone_core_search_conference(mgr->lc, NULL, local_address, confAddr, NULL); BC_ASSERT_PTR_NOT_NULL(pconference); if (pconference) { if (mgr == pauline.getCMgr()) { @@ -2576,8 +2670,9 @@ void create_conference_base(time_t start_time, } for (auto mgr : conferenceMgrs) { + const LinphoneAddress *local_address = (mgr == focus.getCMgr()) ? confAddr : mgr->identity; LinphoneConference *pconference = - linphone_core_search_conference(mgr->lc, NULL, mgr->identity, confAddr, NULL); + linphone_core_search_conference(mgr->lc, NULL, local_address, confAddr, NULL); BC_ASSERT_PTR_NOT_NULL(pconference); if (pconference) { if (mgr == pauline.getCMgr()) { @@ -2689,8 +2784,9 @@ void create_conference_base(time_t start_time, } for (auto mgr : conferenceMgrs) { + const LinphoneAddress *local_address = (mgr == focus.getCMgr()) ? confAddr : mgr->identity; LinphoneConference *pconference = - linphone_core_search_conference(mgr->lc, NULL, mgr->identity, confAddr, NULL); + linphone_core_search_conference(mgr->lc, NULL, local_address, confAddr, NULL); BC_ASSERT_PTR_NOT_NULL(pconference); if (pconference) { auto info = linphone_conference_get_info(pconference); @@ -2732,12 +2828,27 @@ void create_conference_base(time_t start_time, int no_local_participants = 3; if (uninvited_participant_dials) { if (end_time > 0) { + for (auto mgr : conferenceMgrs) { + LinphoneConferenceInfo *info = + linphone_core_find_conference_information_from_uri(mgr->lc, confAddr); + if (BC_ASSERT_PTR_NOT_NULL(info)) { + linphone_conference_info_unref(info); + } + } time_t now = ms_time(NULL); time_t time_left = end_time - now + linphone_core_get_conference_cleanup_period(focus.getLc()); + // Verify that a participant can be added after the conference ends if the conference is active if (add_participant_after_end && (time_left > 0)) { // wait for the conference to end CoreManagerAssert({focus, marie, pauline, laure, michelle, berthe}) .waitUntil(chrono::seconds((time_left + 1)), [] { return false; }); + for (auto mgr : conferenceMgrs) { + LinphoneConferenceInfo *info = + linphone_core_find_conference_information_from_uri(mgr->lc, confAddr); + if (BC_ASSERT_PTR_NOT_NULL(info)) { + linphone_conference_info_unref(info); + } + } } } stats marie_stat2 = marie.getStats(); @@ -2753,6 +2864,7 @@ void create_conference_base(time_t start_time, conference_address_str); LinphoneCallParams *params = linphone_core_create_call_params(michelle.getLc(), nullptr); + linphone_call_params_enable_video(params, FALSE); 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); @@ -2773,6 +2885,7 @@ void create_conference_base(time_t start_time, } conferenceMgrs.push_back(michelle.getCMgr()); + allConferenceMgrs.push_back(michelle.getCMgr()); members.push_back(michelle.getCMgr()); participantList.insert(std::make_pair( michelle.getCMgr(), add_participant_info_to_list(&participants_info, michelle.getCMgr()->identity, @@ -2781,12 +2894,12 @@ void create_conference_base(time_t start_time, -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_LinphoneConferenceStateCreated, + 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, nb_subscriptions, 5000)); @@ -2802,31 +2915,6 @@ void create_conference_base(time_t start_time, focus_stat2.number_of_LinphoneSubscriptionActive + nb_subscriptions, liblinphone_tester_sip_timeout)); - if (enable_video) { - if (!all_listeners && (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)); - } - - if (enable_ice) { - if (!!!use_relay_ice_candidates) { - BC_ASSERT_TRUE( - check_ice(michelle.getCMgr(), focus.getCMgr(), LinphoneIceStateHostConnection)); - } - 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, &focus.getStats().number_of_participants_added, focus_stat2.number_of_participants_added + 1, liblinphone_tester_sip_timeout)); @@ -2934,10 +3022,68 @@ void create_conference_base(time_t start_time, check_conference_info_in_db(mgr, uid2, confAddr, marie.getCMgr()->identity, participants_info, start_time, duration2, initialSubject, description2, 0, LinphoneConferenceInfoStateNew, security_level, FALSE, TRUE, - enable_video, FALSE); + enable_video, enable_chat); } update_sequence_number(&participants_info, {michelle.getCMgr()->identity}, 0, -1); + if (enable_ice) { + if (!!!use_relay_ice_candidates) { + BC_ASSERT_TRUE(check_ice(michelle.getCMgr(), focus.getCMgr(), LinphoneIceStateHostConnection)); + } + 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)); + } + const LinphoneCallParams *michelle_call_lparams = linphone_call_get_params(michelle_call); + BC_ASSERT_PTR_NOT_NULL(michelle_call_lparams); + if (michelle_call_lparams) { + LinphoneConferenceLayout michelle_layout = + linphone_call_params_get_conference_video_layout(michelle_call_lparams); + BC_ASSERT_EQUAL(michelle_layout, layout, int, "%i"); + } + + if (enable_video) { + ms_message("%s enables video", linphone_core_get_identity(michelle.getLc())); + stats michelle_stat2 = michelle.getStats(); + LinphoneCallParams *michelle_new_params = + linphone_core_create_call_params(michelle.getLc(), michelle_call); + linphone_call_params_enable_video(michelle_new_params, TRUE); + linphone_call_update(michelle_call, michelle_new_params); + linphone_call_params_unref(michelle_new_params); + 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)); + + const LinphoneCallParams *michelle_call_cparams = linphone_call_get_current_params(michelle_call); + if ((layout == LinphoneConferenceLayoutGrid) && + (all_listeners || ((video_direction == LinphoneMediaDirectionRecvOnly) || + (video_direction == LinphoneMediaDirectionInactive)))) { + BC_ASSERT_FALSE(linphone_call_params_video_enabled(michelle_call_cparams)); + } else { + BC_ASSERT_TRUE(linphone_call_params_video_enabled(michelle_call_cparams)); + LinphoneMediaDirection michelle_video_direction = + linphone_call_params_get_video_direction(michelle_call_cparams); + LinphoneMediaDirection expected_video_direction = + (all_speakers) ? video_direction : LinphoneMediaDirectionRecvOnly; + BC_ASSERT_EQUAL(michelle_video_direction, expected_video_direction, int, "%i"); + } + + if (!all_listeners && (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)); + } + } + } else if (participant_list_type == LinphoneConferenceParticipantListTypeClosed) { BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallIncomingEarlyMedia, focus_stat2.number_of_LinphoneCallIncomingEarlyMedia + 1, @@ -3010,8 +3156,9 @@ void create_conference_base(time_t start_time, : LinphoneParticipantDeviceStatePresent; size_t no_devices = static_cast<size_t>(no_local_participants + extra_participants); for (auto mgr : conferenceMgrs) { + const LinphoneAddress *local_address = (mgr == focus.getCMgr()) ? confAddr : mgr->identity; LinphoneConference *pconference = - linphone_core_search_conference(mgr->lc, NULL, mgr->identity, confAddr, NULL); + linphone_core_search_conference(mgr->lc, NULL, local_address, confAddr, NULL); if ((mgr == michelle.getCMgr()) && (participant_list_type == LinphoneConferenceParticipantListTypeClosed)) { BC_ASSERT_PTR_NULL(pconference); @@ -3166,8 +3313,10 @@ void create_conference_base(time_t start_time, 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)); + if (!enable_chat) { + 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, @@ -3232,7 +3381,11 @@ void create_conference_base(time_t start_time, focus.getCMgr(), memberList, confAddr, enable_video); LinphoneConference *conference = linphone_core_search_conference(laure.getLc(), NULL, puri, confAddr, NULL); - BC_ASSERT_PTR_NULL(conference); + if (enable_chat) { + BC_ASSERT_PTR_NOT_NULL(conference); + } else { + BC_ASSERT_PTR_NULL(conference); + } no_local_participants = 3; if (uninvited_participant_dials) { @@ -3243,6 +3396,7 @@ void create_conference_base(time_t start_time, extraParticipantMgrs.push_back(berthe.getCMgr()); conferenceMgrs.push_back(berthe.getCMgr()); + allConferenceMgrs.push_back(berthe.getCMgr()); members.push_back(berthe.getCMgr()); participantList.insert(std::make_pair( berthe.getCMgr(), add_participant_info_to_list(&participants_info, berthe.getCMgr()->identity, @@ -3403,8 +3557,9 @@ void create_conference_base(time_t start_time, } for (auto mgr : conferenceMgrs) { + const LinphoneAddress *local_address = (mgr == focus.getCMgr()) ? confAddr : mgr->identity; LinphoneConference *pconference = - linphone_core_search_conference(mgr->lc, NULL, mgr->identity, confAddr, NULL); + linphone_core_search_conference(mgr->lc, NULL, local_address, confAddr, NULL); if ((participant_list_type == LinphoneConferenceParticipantListTypeOpen) || ((mgr != berthe.getCMgr()) && (mgr != michelle.getCMgr()))) { BC_ASSERT_PTR_NOT_NULL(pconference); @@ -3690,12 +3845,18 @@ void create_conference_base(time_t start_time, 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)); + if (!enable_chat) { + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateDeleted, 1, + liblinphone_tester_sip_timeout)); + } LinphoneConference *pconference = linphone_core_search_conference(mgr->lc, NULL, mgr->identity, confAddr, NULL); - BC_ASSERT_PTR_NULL(pconference); + if (enable_chat) { + BC_ASSERT_PTR_NOT_NULL(pconference); + } else { + BC_ASSERT_PTR_NULL(pconference); + } if (network_restart) { ms_message("%s is back online after %s leaves conference %s", @@ -3769,8 +3930,9 @@ void create_conference_base(time_t start_time, } for (auto mgr : {focus.getCMgr(), marie.getCMgr(), michelle.getCMgr(), berthe.getCMgr()}) { + const LinphoneAddress *local_address = (mgr == focus.getCMgr()) ? confAddr : mgr->identity; LinphoneConference *pconference = - linphone_core_search_conference(mgr->lc, NULL, mgr->identity, confAddr, NULL); + linphone_core_search_conference(mgr->lc, NULL, local_address, confAddr, NULL); if (!remove_participant && (mgr == berthe.getCMgr())) { BC_ASSERT_PTR_NULL(pconference); } else { @@ -3826,8 +3988,10 @@ void create_conference_base(time_t start_time, 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)); + if (!enable_chat) { + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateDeleted, + 1, liblinphone_tester_sip_timeout)); + } } } @@ -3909,8 +4073,10 @@ void create_conference_base(time_t start_time, 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)); + if (!enable_chat) { + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateDeleted, 1, + liblinphone_tester_sip_timeout)); + } BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateTerminationPending, 1, @@ -3921,6 +4087,26 @@ void create_conference_base(time_t start_time, liblinphone_tester_sip_timeout)); } + if (end_time > 0) { + time_t now = ms_time(NULL); + time_t time_left = end_time - now + linphone_core_get_conference_cleanup_period(focus.getLc()); + if (time_left < 0) { + time_left = 0; + } + for (auto mgr : allConferenceMgrs) { + LinphoneConferenceInfo *info = linphone_core_find_conference_information_from_uri(mgr->lc, confAddr); + if (BC_ASSERT_PTR_NOT_NULL(info)) { + linphone_conference_info_unref(info); + } + } + // wait for the conference to end + CoreManagerAssert({focus, marie, pauline, laure, michelle, berthe}) + .waitUntil(chrono::seconds((time_left + 1)), [] { return false; }); + LinphoneConferenceInfo *focus_info = + linphone_core_find_conference_information_from_uri(focus.getLc(), confAddr); + BC_ASSERT_PTR_NULL(focus_info); + } + std::list<LinphoneCoreManager *> allMembers{marie.getCMgr(), pauline.getCMgr()}; if (!version_mismatch) allMembers.push_back(laure.getCMgr()); if ((participant_list_type == LinphoneConferenceParticipantListTypeOpen) && uninvited_participant_dials) { @@ -4112,7 +4298,7 @@ void create_conference_with_screen_sharing_base(time_t start_time, LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, initialSubject, - description, TRUE, security_level, TRUE, FALSE); + description, TRUE, security_level, TRUE, FALSE, NULL); BC_ASSERT_PTR_NOT_NULL(confAddr); char *conference_address_str = (confAddr) ? linphone_address_as_string(confAddr) : ms_strdup("sip:"); @@ -5306,7 +5492,7 @@ void create_conference_with_screen_sharing_chat_base(time_t start_time, LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, initialSubject, - description, TRUE, security_level, TRUE, TRUE); + description, TRUE, security_level, TRUE, TRUE, NULL); BC_ASSERT_PTR_NOT_NULL(confAddr); char *conference_address_str = (confAddr) ? linphone_address_as_string(confAddr) : ms_strdup("sip:"); @@ -5845,8 +6031,7 @@ void create_conference_with_screen_sharing_chat_base(time_t start_time, int nb_members = 0; for (auto remaining_mgr : remaining_members) { remaining_members_stats.push_back(remaining_mgr->stat); - LinphoneCall *pcall = - linphone_core_get_call_by_remote_address2(remaining_mgr->lc, focus.getCMgr()->identity); + LinphoneCall *pcall = linphone_core_get_call_by_remote_address2(remaining_mgr->lc, confAddr); BC_ASSERT_PTR_NOT_NULL(pcall); if (pcall) { const LinphoneCallParams *call_cparams = linphone_call_get_current_params(pcall); @@ -5859,7 +6044,7 @@ void create_conference_with_screen_sharing_chat_base(time_t start_time, int participant_calls_nb = static_cast<int>(bctbx_list_size(participant_calls)); BC_ASSERT_EQUAL(participant_calls_nb, 1, int, "%d"); - LinphoneCall *call = linphone_core_get_call_by_remote_address2(mgr->lc, focus.getCMgr()->identity); + LinphoneCall *call = linphone_core_get_call_by_remote_address2(mgr->lc, confAddr); BC_ASSERT_PTR_NOT_NULL(call); if (call) { ms_message("%s is terminating call with %s", linphone_core_get_identity(mgr->lc), @@ -5960,7 +6145,6 @@ void create_conference_with_screen_sharing_chat_base(time_t start_time, BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateDeleted, 1, liblinphone_tester_sip_timeout)); - for (auto mgr : members) { size_t expected_call_logs = (mgr == pauline.getCMgr()) ? 2 : 1; const bctbx_list_t *call_logs = linphone_core_get_call_logs(mgr->lc); @@ -6010,11 +6194,35 @@ void create_conference_with_late_participant_addition_base(time_t start_time, bool_t accept, bool_t one_addition, LinphoneConferenceSecurityLevel security_level) { - Focus focus("chloe_rc"); + Focus focus("chloe_dual_proxy_rc"); { // to make sure focus is destroyed after clients. bool_t enable_lime = (security_level == LinphoneConferenceSecurityLevelEndToEnd ? TRUE : FALSE); - - ClientConference marie("marie_rc", focus.getConferenceFactoryAddress(), enable_lime); + // Create conference on the server using an account that is not the default one + LinphoneAccount *focus_default_account = linphone_core_get_default_account(focus.getLc()); + LinphoneAccount *focus_conference_account = NULL; + const bctbx_list_t *focus_accounts = linphone_core_get_account_list(focus.getLc()); + for (const bctbx_list_t *focus_account_it = focus_accounts; focus_account_it != NULL; + focus_account_it = focus_account_it->next) { + LinphoneAccount *account = (LinphoneAccount *)(bctbx_list_get_data(focus_account_it)); + if (account != focus_default_account) { + focus_conference_account = account; + break; + } + } + + BC_ASSERT_PTR_NOT_NULL(focus_conference_account); + if (!focus_conference_account) { + // Fallback to default account just to execute the test. Nonetheless the goal of this test is of using a non + // default account + focus_conference_account = focus_default_account; + } + + const LinphoneAccountParams *focus_account_params = linphone_account_get_params(focus_conference_account); + const LinphoneAddress *factory_uri = + linphone_account_params_get_conference_factory_address(focus_account_params); + Address focus_conference_factory = *Address::toCpp(factory_uri); + // Change the conference factory of Marie only + ClientConference marie("marie_rc", focus_conference_factory, enable_lime); ClientConference pauline("pauline_rc", focus.getConferenceFactoryAddress(), enable_lime); ClientConference laure("laure_tcp_rc", focus.getConferenceFactoryAddress(), enable_lime); ClientConference michelle("michelle_rc", focus.getConferenceFactoryAddress(), enable_lime); @@ -6077,6 +6285,7 @@ void create_conference_with_late_participant_addition_base(time_t start_time, participants.push_back(michelle.getCMgr()); } + bool is_dialout = (start_time < 0); time_t end_time = (duration <= 0) ? -1 : (start_time + duration * 60); int actual_duration = (duration < 0) ? 0 : duration; const char *initialSubject = "Weekly recap"; @@ -6093,20 +6302,21 @@ void create_conference_with_late_participant_addition_base(time_t start_time, } LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, initialSubject, - description, TRUE, security_level, TRUE, FALSE); + description, TRUE, security_level, TRUE, FALSE, NULL); BC_ASSERT_PTR_NOT_NULL(confAddr); - // Chat room creation to send ICS - BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneChatRoomStateCreated, 2, - liblinphone_tester_sip_timeout)); + if (!is_dialout) { + // Chat room creation to send ICS + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneChatRoomStateCreated, 2, + liblinphone_tester_sip_timeout)); + update_sequence_number(&participants_info, {}, 0, -1); + } auto members = participants; members.push_back(marie.getCMgr()); auto conferenceMgrs = members; conferenceMgrs.push_back(focus.getCMgr()); - update_sequence_number(&participants_info, {}, 0, -1); - if (start_time < 0) { BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallOutgoingInit, marie_stat.number_of_LinphoneCallOutgoingInit + 1, @@ -6198,7 +6408,8 @@ void create_conference_with_late_participant_addition_base(time_t start_time, nb_subscriptions = 2; // One more subscription for the EKT } - add_participant_info_to_list(&participants_info, marie.getCMgr()->identity, LinphoneParticipantRoleSpeaker, 0); + add_participant_info_to_list(&participants_info, marie.getCMgr()->identity, LinphoneParticipantRoleSpeaker, + (is_dialout ? -1 : 0)); for (auto mgr : participants) { BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallStreamsRunning, 1, liblinphone_tester_sip_timeout)); @@ -6372,10 +6583,7 @@ void create_conference_with_late_participant_addition_base(time_t start_time, 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)); + check_conference_me(pconference, ((mgr == marie.getCMgr()) || (mgr == focus.getCMgr()))); 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); @@ -6452,12 +6660,10 @@ void create_conference_with_late_participant_addition_base(time_t start_time, liblinphone_tester_sip_timeout)); } - LinphoneCall *berthe_call = - linphone_core_get_call_by_remote_address2(berthe.getLc(), focus.getCMgr()->identity); + LinphoneCall *berthe_call = linphone_core_get_call_by_remote_address2(berthe.getLc(), confAddr); BC_ASSERT_PTR_NOT_NULL(berthe_call); - LinphoneCall *michelle_call = - linphone_core_get_call_by_remote_address2(michelle.getLc(), focus.getCMgr()->identity); + LinphoneCall *michelle_call = linphone_core_get_call_by_remote_address2(michelle.getLc(), confAddr); BC_ASSERT_PTR_NOT_NULL(michelle_call); int participant_added = ((one_addition) ? 1 : 2); @@ -6808,7 +7014,7 @@ void create_conference_with_late_participant_addition_base(time_t start_time, focus_stat = focus.getStats(); for (auto mgr : members) { - LinphoneCall *call = linphone_core_get_call_by_remote_address2(mgr->lc, focus.getCMgr()->identity); + LinphoneCall *call = linphone_core_get_call_by_remote_address2(mgr->lc, confAddr); BC_ASSERT_PTR_NOT_NULL(call); if (call) { ms_message("%s is terminating call with %s", linphone_core_get_identity(mgr->lc), @@ -6866,13 +7072,13 @@ void create_conference_with_late_participant_addition_base(time_t start_time, BC_ASSERT_PTR_NULL(pconference); } + const LinphoneAddress *focus_identity = linphone_account_params_get_identity_address(focus_account_params); for (auto mgr : members) { size_t expected_call_logs = (mgr == pauline.getCMgr()) ? 2 : 1; const bctbx_list_t *call_logs = linphone_core_get_call_logs(mgr->lc); BC_ASSERT_EQUAL(bctbx_list_size(call_logs), expected_call_logs, size_t, "%zu"); - bctbx_list_t *mgr_focus_call_log = - linphone_core_get_call_history_2(mgr->lc, focus.getCMgr()->identity, mgr->identity); + bctbx_list_t *mgr_focus_call_log = linphone_core_get_call_history_2(mgr->lc, focus_identity, mgr->identity); BC_ASSERT_PTR_NOT_NULL(mgr_focus_call_log); if (mgr_focus_call_log) { BC_ASSERT_EQUAL(bctbx_list_size(mgr_focus_call_log), expected_call_logs, size_t, "%zu"); @@ -6919,7 +7125,11 @@ void create_conference_with_chat_base(LinphoneConferenceSecurityLevel security_l bool_t join_after_termination, long cleanup_window, bool_t use_relay_ice_candidates, - bool_t client_reenter_conference) { + bool_t client_reenter_conference, + bool_t network_drops, + time_t start_time, + bool_t enable_gruu_in_conference_address) { + Focus focus("chloe_rc"); { // to make sure focus is destroyed after clients. bool_t enable_lime = (security_level == LinphoneConferenceSecurityLevelEndToEnd ? TRUE : FALSE); @@ -6948,13 +7158,31 @@ void create_conference_with_chat_base(LinphoneConferenceSecurityLevel security_l linphone_core_set_video_activation_policy(mgr->lc, pol); linphone_video_activation_policy_unref(pol); + if (mgr != pauline.getCMgr()) { + linphone_core_enable_gruu_in_conference_address(mgr->lc, enable_gruu_in_conference_address); + } + 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); - linphone_core_set_media_encryption(mgr->lc, LinphoneMediaEncryptionSRTP); + linphone_core_set_media_encryption(mgr->lc, enable_lime ? LinphoneMediaEncryptionZRTP + : LinphoneMediaEncryptionNone); + linphone_config_set_bool(linphone_core_get_config(mgr->lc), "sip", "chat_messages_aggregation", TRUE); + linphone_config_set_int(linphone_core_get_config(mgr->lc), "sip", "chat_messages_aggregation_delay", + 2000); + + LinphoneAccount *account = linphone_core_get_default_account(mgr->lc); + const LinphoneAccountParams *account_params = linphone_account_get_params(account); + LinphoneAccountParams *new_account_params = linphone_account_params_clone(account_params); + linphone_account_params_set_conference_factory_address(new_account_params, + focus.getConferenceFactoryAddress().toC()); + linphone_account_params_set_audio_video_conference_factory_address( + new_account_params, focus.getConferenceFactoryAddress().toC()); + linphone_account_set_params(account, new_account_params); + linphone_account_params_unref(new_account_params); } if (!!use_relay_ice_candidates) { @@ -6990,7 +7218,6 @@ void create_conference_with_chat_base(LinphoneConferenceSecurityLevel security_l configure_end_to_end_encrypted_conference_server(focus); } - time_t start_time = ms_time(NULL) - 45; int duration = 1; time_t end_time = (start_time + duration * 60); const char *initialSubject = "Test characters: ^ :) ¤ çà @"; @@ -7016,7 +7243,7 @@ void create_conference_with_chat_base(LinphoneConferenceSecurityLevel security_l LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, initialSubject, - description, TRUE, security_level, TRUE, TRUE); + description, TRUE, security_level, TRUE, TRUE, NULL); BC_ASSERT_PTR_NOT_NULL(confAddr); char *conference_address_str = (confAddr) ? linphone_address_as_string(confAddr) : ms_strdup("sip:"); @@ -7030,8 +7257,11 @@ void create_conference_with_chat_base(LinphoneConferenceSecurityLevel security_l // Restart flexisip focus.reStart(); coresList = bctbx_list_append(coresList, focus.getLc()); + if (security_level == LinphoneConferenceSecurityLevelEndToEnd) { + configure_end_to_end_encrypted_conference_server(focus); + } LinphoneConference *fconference = - linphone_core_search_conference(focus.getLc(), NULL, focus.getIdentity().toC(), confAddr, NULL); + linphone_core_search_conference(focus.getLc(), NULL, confAddr, confAddr, NULL); BC_ASSERT_PTR_NOT_NULL(fconference); if (fconference) { LinphoneChatRoom *cr = linphone_conference_get_chat_room(fconference); @@ -7065,6 +7295,7 @@ void create_conference_with_chat_base(LinphoneConferenceSecurityLevel security_l } int idx = 1; + int nb_client_subscribes = (security_level == LinphoneConferenceSecurityLevelEndToEnd) ? 2 : 1; for (auto mgr : members) { BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallOutgoingProgress, 1, liblinphone_tester_sip_timeout)); @@ -7079,11 +7310,16 @@ void create_conference_with_chat_base(LinphoneConferenceSecurityLevel security_l liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneChatRoomStateCreated, ((mgr == marie.getCMgr()) ? 4 : 1), liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionOutgoingProgress, + nb_client_subscribes, 5000)); 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)); + wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionActive, nb_client_subscribes, 5000)); BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_NotifyFullStateReceived, 1, liblinphone_tester_sip_timeout)); + if (security_level == LinphoneConferenceSecurityLevelEndToEnd) { + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_NotifyEktReceived, 1, + liblinphone_tester_sip_timeout)); + } if ((encryption == LinphoneMediaEncryptionDTLS) || (encryption == LinphoneMediaEncryptionZRTP)) { BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallEncryptedOn, 1, @@ -7092,12 +7328,21 @@ void create_conference_with_chat_base(LinphoneConferenceSecurityLevel security_l liblinphone_tester_sip_timeout)); } + const LinphoneAddress *from = ((mgr == marie.getCMgr()) || (start_time > 0)) ? mgr->identity : confAddr; + const LinphoneAddress *to = ((mgr == marie.getCMgr()) || (start_time > 0)) ? confAddr : mgr->identity; + 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"); + LinphoneCallLog *call_log = linphone_call_get_call_log(pcall); + BC_ASSERT_PTR_NOT_NULL(call_log); + if (call_log) { + BC_ASSERT_TRUE(linphone_address_weak_equal(linphone_call_log_get_from_address(call_log), from)); + BC_ASSERT_TRUE(linphone_address_weak_equal(linphone_call_log_get_to_address(call_log), to)); + } } LinphoneCall *ccall = linphone_core_get_call_by_remote_address2(focus.getLc(), mgr->identity); BC_ASSERT_PTR_NOT_NULL(ccall); @@ -7105,6 +7350,12 @@ void create_conference_with_chat_base(LinphoneConferenceSecurityLevel security_l 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"); + LinphoneCallLog *call_log = linphone_call_get_call_log(ccall); + BC_ASSERT_PTR_NOT_NULL(call_log); + if (call_log) { + BC_ASSERT_TRUE(linphone_address_weak_equal(linphone_call_log_get_from_address(call_log), from)); + BC_ASSERT_TRUE(linphone_address_weak_equal(linphone_call_log_get_to_address(call_log), to)); + } } LinphoneAddress *deviceAddr = @@ -7142,10 +7393,11 @@ void create_conference_with_chat_base(LinphoneConferenceSecurityLevel security_l 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_LinphoneSubscriptionIncomingReceived, + focus_stat.number_of_LinphoneSubscriptionIncomingReceived + 5 * nb_client_subscribes, 5000)); BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneSubscriptionActive, - focus_stat.number_of_LinphoneSubscriptionActive + 5, 5000)); + focus_stat.number_of_LinphoneSubscriptionActive + 5 * nb_client_subscribes, 5000)); BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_chat_room_participants_added, focus_stat.number_of_chat_room_participants_added + 5, @@ -7203,7 +7455,37 @@ void create_conference_with_chat_base(LinphoneConferenceSecurityLevel security_l return nb_devices == 5; })); } - } + + const LinphoneAddress *from = ((mgr == marie.getCMgr()) || (start_time > 0)) ? mgr->identity : confAddr; + const LinphoneAddress *to = ((mgr == marie.getCMgr()) || (start_time > 0)) ? confAddr : mgr->identity; + + 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"); + LinphoneCallLog *call_log = linphone_call_get_call_log(pcall); + BC_ASSERT_PTR_NOT_NULL(call_log); + if (call_log) { + BC_ASSERT_TRUE(linphone_address_weak_equal(linphone_call_log_get_from_address(call_log), from)); + BC_ASSERT_TRUE(linphone_address_weak_equal(linphone_call_log_get_to_address(call_log), to)); + } + } + 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"); + LinphoneCallLog *call_log = linphone_call_get_call_log(ccall); + BC_ASSERT_PTR_NOT_NULL(call_log); + if (call_log) { + BC_ASSERT_TRUE(linphone_address_weak_equal(linphone_call_log_get_from_address(call_log), from)); + BC_ASSERT_TRUE(linphone_address_weak_equal(linphone_call_log_get_to_address(call_log), to)); + } + } + } const std::initializer_list<std::reference_wrapper<ClientConference>> cores2{marie, laure, pauline, michelle, berthe}; @@ -7432,65 +7714,111 @@ void create_conference_with_chat_base(LinphoneConferenceSecurityLevel security_l if (!!client_reenter_conference) { stats marie_stat = marie.getStats(); focus_stat = focus.getStats(); - ms_message("%s hangs up its call to conference %s", linphone_core_get_identity(marie.getLc()), - conference_address_str); - LinphoneCall *pcall = linphone_core_get_call_by_remote_address2(marie.getLc(), focus.getCMgr()->identity); - BC_ASSERT_PTR_NOT_NULL(pcall); - if (pcall) { - linphone_call_terminate(pcall); - 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)); + if (network_drops) { + LinphoneChatRoom *chat_room = linphone_core_search_chat_room(focus.getLc(), NULL, NULL, confAddr, NULL); + BC_ASSERT_PTR_NOT_NULL(chat_room); + ms_message("%s shuts down its network", linphone_core_get_identity(marie.getLc())); + linphone_core_set_network_reachable(marie.getLc(), FALSE); - 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_LinphoneSubscriptionTerminated, - focus_stat.number_of_LinphoneSubscriptionTerminated + 1, - liblinphone_tester_sip_timeout)); + CoreManagerAssert({focus, marie, pauline, michelle, laure, berthe}).waitUntil(chrono::seconds(5), [] { + return false; + }); - BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_chat_room_participants_removed, - focus_stat.number_of_chat_room_participants_removed + 1, - liblinphone_tester_sip_timeout)); - BC_ASSERT_TRUE(wait_for_list( - coresList, &focus.getStats().number_of_chat_room_participant_devices_removed, - focus_stat.number_of_chat_room_participant_devices_removed + 1, liblinphone_tester_sip_timeout)); + ms_message("%s turns on its network", linphone_core_get_identity(marie.getLc())); + linphone_core_set_network_reachable(marie.getLc(), TRUE); - 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)); + marie_stat = marie.getStats(); + LinphoneAccount *account = linphone_core_get_default_account(marie.getLc()); + LinphoneAddress *deviceAddress = linphone_account_get_contact_address(account); + chat_room = linphone_core_search_chat_room(marie.getLc(), NULL, deviceAddress, confAddr, NULL); + BC_ASSERT_PTR_NOT_NULL(chat_room); + if (chat_room) { + BC_ASSERT_FALSE(linphone_chat_room_is_read_only(chat_room)); + BC_ASSERT_EQUAL(linphone_chat_room_get_history_size(chat_room), 2, int, "%d"); + BC_ASSERT_EQUAL(linphone_chat_room_get_state(chat_room), LinphoneChatRoomStateCreated, int, "%i"); + } + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneSubscriptionActive, + marie_stat.number_of_LinphoneSubscriptionActive + nb_client_subscribes, + liblinphone_tester_sip_timeout)); + LinphoneParticipantDeviceIdentity *identity = + linphone_factory_create_participant_device_identity(linphone_factory_get(), deviceAddress, ""); + bctbx_list_t *specs = linphone_core_get_linphone_specs_list(marie.getLc()); + linphone_participant_device_identity_set_capability_descriptor_2(identity, specs); + bctbx_list_t *devices = NULL; + devices = bctbx_list_append(devices, identity); + bctbx_list_free_with_data(specs, ms_free); + linphone_chat_room_set_participant_devices(chat_room, marie.getIdentity().toC(), devices); + bctbx_list_free_with_data(devices, (bctbx_list_free_func)belle_sip_object_unref); + BC_ASSERT_FALSE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallOutgoingInit, + focus_stat.number_of_LinphoneCallOutgoingInit + 1, 500)); + BC_ASSERT_FALSE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallIncomingReceived, + marie_stat.number_of_LinphoneCallIncomingReceived + 1, 500)); + } else { + ms_message("%s hangs up its call to conference %s", linphone_core_get_identity(marie.getLc()), + conference_address_str); + LinphoneCall *pcall = + linphone_core_get_call_by_remote_address2(marie.getLc(), focus.getCMgr()->identity); + BC_ASSERT_PTR_NOT_NULL(pcall); + if (pcall) { + linphone_call_terminate(pcall); + 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 + nb_client_subscribes, + 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)); - for (auto mgr : members) { - if (mgr != marie.getCMgr()) { - BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_participants_removed, 1, - liblinphone_tester_sip_timeout)); - BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_participant_devices_removed, 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_LinphoneSubscriptionTerminated, + focus_stat.number_of_LinphoneSubscriptionTerminated + nb_client_subscribes, + liblinphone_tester_sip_timeout)); + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_chat_room_participants_removed, + focus_stat.number_of_chat_room_participants_removed + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, + &focus.getStats().number_of_chat_room_participant_devices_removed, + focus_stat.number_of_chat_room_participant_devices_removed + 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)); + + for (auto mgr : members) { + if (mgr != marie.getCMgr()) { + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_participants_removed, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_participant_devices_removed, 1, + liblinphone_tester_sip_timeout)); + } } } - } - if (client_restart) { - LinphoneChatRoom *chat_room = linphone_core_search_chat_room(focus.getLc(), NULL, NULL, confAddr, NULL); - BC_ASSERT_PTR_NOT_NULL(chat_room); - if (chat_room) { + if (client_restart) { + LinphoneChatRoom *chat_room = + linphone_core_search_chat_room(focus.getLc(), NULL, NULL, confAddr, NULL); + BC_ASSERT_PTR_NOT_NULL(chat_room); ms_message("%s is restarting its core", linphone_core_get_identity(marie.getLc())); coresList = bctbx_list_remove(coresList, marie.getLc()); marie.reStart(); @@ -7498,29 +7826,29 @@ void create_conference_with_chat_base(LinphoneConferenceSecurityLevel security_l marie_stat = marie.getStats(); LinphoneAccount *account = linphone_core_get_default_account(marie.getLc()); LinphoneAddress *deviceAddress = linphone_account_get_contact_address(account); - LinphoneChatRoom *chat_room = - linphone_core_search_chat_room(marie.getLc(), NULL, deviceAddress, confAddr, NULL); + chat_room = linphone_core_search_chat_room(marie.getLc(), NULL, deviceAddress, confAddr, NULL); BC_ASSERT_PTR_NOT_NULL(chat_room); if (chat_room) { BC_ASSERT_TRUE(linphone_chat_room_is_read_only(chat_room)); BC_ASSERT_EQUAL(linphone_chat_room_get_history_size(chat_room), 2, int, "%d"); BC_ASSERT_EQUAL(linphone_chat_room_get_state(chat_room), LinphoneChatRoomStateTerminated, int, "%i"); + LinphoneParticipantDeviceIdentity *identity = + linphone_factory_create_participant_device_identity(linphone_factory_get(), deviceAddress, + ""); + bctbx_list_t *specs = linphone_core_get_linphone_specs_list(marie.getLc()); + linphone_participant_device_identity_set_capability_descriptor_2(identity, specs); + bctbx_list_t *devices = NULL; + devices = bctbx_list_append(devices, identity); + bctbx_list_free_with_data(specs, ms_free); + linphone_chat_room_set_participant_devices(chat_room, marie.getIdentity().toC(), devices); + bctbx_list_free_with_data(devices, (bctbx_list_free_func)belle_sip_object_unref); } - LinphoneParticipantDeviceIdentity *identity = - linphone_factory_create_participant_device_identity(linphone_factory_get(), deviceAddress, ""); - bctbx_list_t *specs = linphone_core_get_linphone_specs_list(marie.getLc()); - linphone_participant_device_identity_set_capability_descriptor_2(identity, specs); - bctbx_list_t *devices = NULL; - devices = bctbx_list_append(devices, identity); - bctbx_list_free_with_data(specs, ms_free); - linphone_chat_room_set_participant_devices(chat_room, marie.getIdentity().toC(), devices); - bctbx_list_free_with_data(devices, (bctbx_list_free_func)belle_sip_object_unref); + BC_ASSERT_FALSE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallOutgoingInit, + focus_stat.number_of_LinphoneCallOutgoingInit + 1, 500)); + BC_ASSERT_FALSE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallIncomingReceived, + marie_stat.number_of_LinphoneCallIncomingReceived + 1, 500)); } - BC_ASSERT_FALSE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallOutgoingInit, - focus_stat.number_of_LinphoneCallOutgoingInit + 1, 500)); - BC_ASSERT_FALSE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallIncomingReceived, - marie_stat.number_of_LinphoneCallIncomingReceived + 1, 500)); } LinphoneCallParams *new_params = linphone_core_create_call_params(marie.getLc(), nullptr); @@ -7530,7 +7858,7 @@ void create_conference_with_chat_base(LinphoneConferenceSecurityLevel security_l conference_address_str); linphone_core_invite_address_with_params_2(marie.getLc(), confAddr, new_params, NULL, nullptr); linphone_call_params_unref(new_params); - pcall = linphone_core_get_call_by_remote_address2(marie.getLc(), confAddr); + LinphoneCall *pcall = linphone_core_get_call_by_remote_address2(marie.getLc(), confAddr); BC_ASSERT_PTR_NOT_NULL(pcall); if (pcall) { LinphoneCallLog *call_log = linphone_call_get_call_log(pcall); @@ -7544,14 +7872,20 @@ void create_conference_with_chat_base(LinphoneConferenceSecurityLevel security_l BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneChatRoomStateCreated, marie_stat.number_of_LinphoneChatRoomStateCreated + 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_LinphoneSubscriptionOutgoingProgress, + marie_stat.number_of_LinphoneSubscriptionOutgoingProgress + nb_client_subscribes, 5000)); BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneSubscriptionActive, - marie_stat.number_of_LinphoneSubscriptionActive + 1, 5000)); + marie_stat.number_of_LinphoneSubscriptionActive + nb_client_subscribes, + 5000)); BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_NotifyFullStateReceived, marie_stat.number_of_NotifyFullStateReceived + 1, liblinphone_tester_sip_timeout)); + if (security_level == LinphoneConferenceSecurityLevelEndToEnd) { + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_NotifyEktReceived, + marie_stat.number_of_NotifyEktReceived + 1, + liblinphone_tester_sip_timeout)); + } if ((encryption == LinphoneMediaEncryptionDTLS) || (encryption == LinphoneMediaEncryptionZRTP)) { BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneCallEncryptedOn, @@ -7659,7 +7993,7 @@ void create_conference_with_chat_base(LinphoneConferenceSecurityLevel security_l for (auto mgr : members) { int participant_call_ended = - (!!client_reenter_conference && client_restart && (mgr == marie.getCMgr())) ? 2 : 1; + (!!client_reenter_conference && !client_restart && (mgr == marie.getCMgr())) ? 2 : 1; stats focus_stat2 = focus.getStats(); remaining_members.pop_front(); std::list<stats> remaining_members_stats; @@ -7693,7 +8027,8 @@ void create_conference_with_chat_base(LinphoneConferenceSecurityLevel security_l BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallReleased, participant_call_ended, liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionTerminated, - participant_call_ended, liblinphone_tester_sip_timeout)); + participant_call_ended * nb_client_subscribes, + liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateTerminationPending, participant_call_ended, liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateTerminated, @@ -7772,9 +8107,25 @@ void create_conference_with_chat_base(LinphoneConferenceSecurityLevel security_l time_left += focus_cleanup_window; } if (time_left > 0) { + for (auto mgr : conferenceMgrs) { + LinphoneConferenceInfo *info = + linphone_core_find_conference_information_from_uri(mgr->lc, confAddr); + if (BC_ASSERT_PTR_NOT_NULL(info)) { + linphone_conference_info_unref(info); + } + } // wait for the conference to end CoreManagerAssert({focus, marie, pauline, michelle, laure, berthe}) .waitUntil(chrono::seconds((time_left + 1)), [] { return false; }); + LinphoneConferenceInfo *focus_info = + linphone_core_find_conference_information_from_uri(focus.getLc(), confAddr); + if (focus_cleanup_window > 0) { + BC_ASSERT_PTR_NULL(focus_info); + } else { + if (BC_ASSERT_PTR_NOT_NULL(focus_info)) { + linphone_conference_info_unref(focus_info); + } + } } } @@ -8004,6 +8355,503 @@ void create_conference_with_chat_base(LinphoneConferenceSecurityLevel security_l } } +void conference_joined_multiple_times(LinphoneConferenceSecurityLevel security_level, + bool_t enable_chat, + long cleanup_window) { + Focus focus("chloe_rc"); + { // to make sure focus is destroyed after clients. + bool_t enable_lime = (security_level == LinphoneConferenceSecurityLevelEndToEnd ? TRUE : FALSE); + ClientConference marie("marie_rc", focus.getConferenceFactoryAddress(), enable_lime); + ClientConference pauline("pauline_rc", focus.getConferenceFactoryAddress(), enable_lime); + ClientConference laure("laure_tcp_rc", focus.getConferenceFactoryAddress(), enable_lime); + ClientConference michelle("michelle_rc", focus.getConferenceFactoryAddress(), enable_lime); + ClientConference berthe("berthe_rc", focus.getConferenceFactoryAddress(), enable_lime); + + 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); + linphone_core_set_media_encryption(mgr->lc, enable_lime ? LinphoneMediaEncryptionZRTP + : LinphoneMediaEncryptionNone); + linphone_config_set_bool(linphone_core_get_config(mgr->lc), "sip", "chat_messages_aggregation", TRUE); + linphone_config_set_int(linphone_core_get_config(mgr->lc), "sip", "chat_messages_aggregation_delay", + 2000); + + LinphoneAccount *account = linphone_core_get_default_account(mgr->lc); + const LinphoneAccountParams *account_params = linphone_account_get_params(account); + LinphoneAccountParams *new_account_params = linphone_account_params_clone(account_params); + linphone_account_params_set_conference_factory_address(new_account_params, + focus.getConferenceFactoryAddress().toC()); + linphone_account_params_set_audio_video_conference_factory_address( + new_account_params, focus.getConferenceFactoryAddress().toC()); + linphone_account_set_params(account, new_account_params); + linphone_account_params_unref(new_account_params); + } + + // Enable ICE at the account level but not at the core level + enable_stun_in_mgr(mgr, TRUE, TRUE, FALSE, FALSE); + + 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); + } + + int nortp_timeout = 10; + linphone_core_set_nortp_timeout(marie.getLc(), nortp_timeout); + linphone_core_set_file_transfer_server(marie.getLc(), file_transfer_url); + linphone_core_set_conference_participant_list_type(focus.getLc(), LinphoneConferenceParticipantListTypeClosed); + linphone_core_set_conference_cleanup_period(focus.getLc(), cleanup_window); + + std::list<LinphoneCoreManager *> participants{laure.getCMgr(), pauline.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()}; + + if (security_level == LinphoneConferenceSecurityLevelEndToEnd) { + configure_end_to_end_encrypted_conference_server(focus); + } + + time_t start_time = ms_time(NULL); + int duration = 2; + time_t end_time = (start_time + duration * 60); + const char *initialSubject = "Test characters: ^ :) ¤ çà @"; + const char *description = "Paris Baker"; + + bctbx_list_t *participants_info = NULL; + std::map<LinphoneCoreManager *, LinphoneParticipantInfo *> participantList; + participantList.insert( + std::make_pair(laure.getCMgr(), add_participant_info_to_list(&participants_info, laure.getCMgr()->identity, + LinphoneParticipantRoleSpeaker, -1))); + participantList.insert(std::make_pair( + pauline.getCMgr(), add_participant_info_to_list(&participants_info, pauline.getCMgr()->identity, + LinphoneParticipantRoleListener, -1))); + participantList.insert(std::make_pair( + michelle.getCMgr(), add_participant_info_to_list(&participants_info, michelle.getCMgr()->identity, + LinphoneParticipantRoleSpeaker, -1))); + participantList.insert(std::make_pair( + berthe.getCMgr(), add_participant_info_to_list(&participants_info, berthe.getCMgr()->identity, + LinphoneParticipantRoleListener, -1))); + participantList.insert( + std::make_pair(laure.getCMgr(), add_participant_info_to_list(&participants_info, laure.getCMgr()->identity, + LinphoneParticipantRoleSpeaker, -1))); + + LinphoneAddress *confAddr = + create_conference_on_server(focus, marie, participantList, start_time, end_time, initialSubject, + description, TRUE, security_level, TRUE, enable_chat, NULL); + BC_ASSERT_PTR_NOT_NULL(confAddr); + char *conference_address_str = (confAddr) ? linphone_address_as_string(confAddr) : ms_strdup("sip:"); + + // Chat room creation to send ICS + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneChatRoomStateCreated, 4, + liblinphone_tester_sip_timeout)); + + int nb_client_subscribes = (security_level == LinphoneConferenceSecurityLevelEndToEnd) ? 2 : 1; + const LinphoneMediaEncryption encryption = (security_level == LinphoneConferenceSecurityLevelNone) + ? LinphoneMediaEncryptionNone + : LinphoneMediaEncryptionZRTP; + std::map<LinphoneCoreManager *, LinphoneParticipantInfo *> memberList = + fill_member_list(members, participantList, marie.getCMgr(), participants_info); + + long focus_cleanup_window = linphone_core_get_conference_cleanup_period(focus.getLc()); + + // Client will join and leave the conference a number of times + for (int attempt = 1; attempt <= 5; attempt++) { + if ((focus_cleanup_window > 0) && (end_time > 0)) { + time_t now = ms_time(NULL); + time_t time_left = end_time - now; + if (time_left < 0) { + ms_message("Attempt #%0d - conference %s already ended %ld seconds ago", attempt, + conference_address_str, -time_left); + // In order to verify the rejoining of conferences, the loop must have at least 2 iterations + BC_ASSERT_GREATER_STRICT(attempt, 2, int, "%0d"); + break; + } + } + stats focus_stat = focus.getStats(); + 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, LinphoneMediaDirectionSendRecv); + ms_message("Attempt #%0d - %s is entering conference %s", attempt, 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); + 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)); + } + } + + for (auto mgr : members) { + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallOutgoingProgress, attempt, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallUpdating, attempt, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallStreamsRunning, 2 * attempt, + 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, attempt, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneChatRoomStateCreated, + ((mgr == marie.getCMgr()) ? 4 : 1), liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionOutgoingProgress, + attempt * nb_client_subscribes, 5000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionActive, + attempt * nb_client_subscribes, 5000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_NotifyFullStateReceived, attempt, + liblinphone_tester_sip_timeout)); + if (security_level == LinphoneConferenceSecurityLevelEndToEnd) { + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_NotifyEktReceived, attempt, + liblinphone_tester_sip_timeout)); + } + + if ((encryption == LinphoneMediaEncryptionDTLS) || (encryption == LinphoneMediaEncryptionZRTP)) { + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallEncryptedOn, attempt, + 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"); + } + + LinphoneAddress *deviceAddr = + linphone_account_get_contact_address(linphone_core_get_default_account(mgr->lc)); + LinphoneChatRoom *core_chat_room = + linphone_core_search_chat_room(mgr->lc, NULL, deviceAddr, confAddr, NULL); + if (enable_chat) { + BC_ASSERT_PTR_NOT_NULL(core_chat_room); + } else { + BC_ASSERT_PTR_NULL(core_chat_room); + } + + LinphoneChatRoom *conference_chat_room = NULL; + LinphoneConference *pconference = + linphone_core_search_conference(mgr->lc, NULL, mgr->identity, confAddr, NULL); + BC_ASSERT_PTR_NOT_NULL(pconference); + if (pconference) { + conference_chat_room = linphone_conference_get_chat_room(pconference); + if (enable_chat) { + BC_ASSERT_PTR_NOT_NULL(conference_chat_room); + } else { + BC_ASSERT_PTR_NULL(conference_chat_room); + } + if (conference_chat_room) { + BC_ASSERT_FALSE(linphone_chat_room_is_read_only(conference_chat_room)); + } + } + BC_ASSERT_PTR_EQUAL(conference_chat_room, core_chat_room); + } + + if ((encryption == LinphoneMediaEncryptionDTLS) || (encryption == LinphoneMediaEncryptionZRTP)) { + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallEncryptedOn, + attempt * static_cast<int>(members.size()), + liblinphone_tester_sip_timeout)); + } + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallIncomingReceived, + focus_stat.number_of_LinphoneCallIncomingReceived + 5, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallUpdatedByRemote, + focus_stat.number_of_LinphoneCallUpdatedByRemote + 5, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallStreamsRunning, + focus_stat.number_of_LinphoneCallStreamsRunning + 10, + 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, &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 * nb_client_subscribes, 5000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneSubscriptionActive, + focus_stat.number_of_LinphoneSubscriptionActive + 5 * nb_client_subscribes, + 5000)); + + if (enable_chat) { + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_chat_room_participants_added, + focus_stat.number_of_chat_room_participants_added + 5, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_chat_room_participant_devices_added, + focus_stat.number_of_chat_room_participant_devices_added + 5, + 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_conference_participant_devices_present, + focus_stat.number_of_conference_participant_devices_present + 5, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_present, + focus_stat.number_of_participant_devices_present + 5, + liblinphone_tester_sip_timeout)); + + wait_for_conference_streams({focus, marie, pauline, laure, michelle, berthe}, conferenceMgrs, + focus.getCMgr(), memberList, confAddr, TRUE); + + for (auto mgr : members) { + LinphoneConference *pconference = + linphone_core_search_conference(mgr->lc, NULL, mgr->identity, confAddr, NULL); + BC_ASSERT_PTR_NOT_NULL(pconference); + if (pconference) { + LinphoneChatRoom *chat_room = linphone_conference_get_chat_room(pconference); + if (enable_chat) { + BC_ASSERT_PTR_NOT_NULL(chat_room); + } else { + BC_ASSERT_PTR_NULL(chat_room); + } + if (chat_room) { + bctbx_list_t *chat_room_participants = linphone_chat_room_get_participants(chat_room); + BC_ASSERT_EQUAL(bctbx_list_size(chat_room_participants), 4, size_t, "%zu"); + bctbx_list_free_with_data(chat_room_participants, + (bctbx_list_free_func)linphone_participant_unref); + BC_ASSERT_FALSE(linphone_chat_room_is_read_only(chat_room)); + const LinphoneChatRoomParams *params = linphone_chat_room_get_current_params(chat_room); + LinphoneChatParams *chat_params = linphone_conference_params_get_chat_params(params); + BC_ASSERT_PTR_NOT_NULL(chat_params); + if (chat_params) { + LinphoneChatRoomEncryptionBackend encryption_backend = + enable_lime ? LinphoneChatRoomEncryptionBackendLime + : LinphoneChatRoomEncryptionBackendNone; + BC_ASSERT_EQUAL(linphone_chat_params_get_encryption_backend(chat_params), + encryption_backend, int, "%d"); + BC_ASSERT_EQUAL(linphone_chat_params_encryption_enabled(chat_params), !!enable_lime, int, + "%d"); + } + } + BC_ASSERT_TRUE( + CoreManagerAssert({focus, marie, pauline, michelle, laure, berthe}).wait([&pconference] { + bctbx_list_t *devices = linphone_conference_get_participant_device_list(pconference); + size_t nb_devices = bctbx_list_size(devices); + if (devices) { + bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); + } + return nb_devices == 5; + })); + } + } + + // wait a bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, laure, michelle, berthe}).waitUntil(chrono::seconds(2), [] { + return false; + }); + + auto remaining_members = members; + for (auto mgr : members) { + stats focus_stat2 = focus.getStats(); + remaining_members.pop_front(); + std::list<stats> remaining_members_stats; + int nb_members = 0; + for (auto remaining_mgr : remaining_members) { + remaining_members_stats.push_back(remaining_mgr->stat); + LinphoneCall *pcall = + linphone_core_get_call_by_remote_address2(remaining_mgr->lc, focus.getCMgr()->identity); + BC_ASSERT_PTR_NOT_NULL(pcall); + if (pcall) { + const LinphoneCallParams *call_cparams = linphone_call_get_current_params(pcall); + if (linphone_call_params_video_enabled(call_cparams)) { + nb_members++; + } + } + } + const bctbx_list_t *participant_calls = linphone_core_get_calls(mgr->lc); + int participant_calls_nb = static_cast<int>(bctbx_list_size(participant_calls)); + BC_ASSERT_EQUAL(participant_calls_nb, 1, int, "%d"); + + LinphoneCall *call = linphone_core_get_call_by_remote_address2(mgr->lc, focus.getCMgr()->identity); + BC_ASSERT_PTR_NOT_NULL(call); + if (call) { + ms_message("Attempt #%0d - %s is terminating call with %s", attempt, + 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, attempt, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallReleased, attempt, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionTerminated, + attempt * nb_client_subscribes, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateTerminationPending, + attempt, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateTerminated, attempt, + liblinphone_tester_sip_timeout)); + if (!enable_chat) { + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateDeleted, + attempt, liblinphone_tester_sip_timeout)); + } + + LinphoneConference *pconference = + linphone_core_search_conference(mgr->lc, NULL, mgr->identity, confAddr, NULL); + if (enable_chat) { + BC_ASSERT_PTR_NOT_NULL(pconference); + if (pconference) { + BC_ASSERT_PTR_NOT_NULL(linphone_conference_get_chat_room(pconference)); + } + } else { + BC_ASSERT_PTR_NULL(pconference); + } + + participant_calls = linphone_core_get_calls(mgr->lc); + participant_calls_nb = static_cast<int>(bctbx_list_size(participant_calls)); + BC_ASSERT_EQUAL(participant_calls_nb, 0, int, "%d"); + + for (auto remaining_mgr : remaining_members) { + stats stat = remaining_members_stats.front(); + remaining_members_stats.pop_front(); + if (enable_chat) { + BC_ASSERT_TRUE(wait_for_list( + coresList, &remaining_mgr->stat.number_of_chat_room_participants_removed, + stat.number_of_chat_room_participants_removed + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list( + coresList, &remaining_mgr->stat.number_of_chat_room_participant_devices_removed, + stat.number_of_chat_room_participant_devices_removed + 1, liblinphone_tester_sip_timeout)); + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &remaining_mgr->stat.number_of_participants_removed, + stat.number_of_participants_removed + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &remaining_mgr->stat.number_of_participant_devices_removed, + stat.number_of_participant_devices_removed + 1, + liblinphone_tester_sip_timeout)); + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallUpdatedByRemote, + focus_stat2.number_of_LinphoneCallUpdatedByRemote + nb_members, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallStreamsRunning, + focus_stat2.number_of_LinphoneCallStreamsRunning + nb_members, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participants_removed, + focus_stat2.number_of_participants_removed + 1, + 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 + 1, + 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, &focus.getStats().number_of_LinphoneSubscriptionTerminated, + focus_stat.number_of_LinphoneSubscriptionTerminated + 5, + liblinphone_tester_sip_timeout)); + 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_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 : conferenceMgrs) { + LinphoneConferenceInfo *info = linphone_core_find_conference_information_from_uri(mgr->lc, confAddr); + bool info_deleted = false; + if (!!linphone_core_conference_server_enabled(mgr->lc) && (focus_cleanup_window > 0) && + (end_time > 0)) { + time_t now = ms_time(NULL); + time_t time_left = end_time - now; + if (time_left < 0) { + ms_message("Attempt #%0d - conference information of conference %s was deleted on the server " + "core %s because the conference ended %ld seconds ago", + attempt, conference_address_str, linphone_core_get_identity(mgr->lc), -time_left); + info_deleted = true; + } + } + if (info_deleted) { + BC_ASSERT_PTR_NULL(info); + } else { + BC_ASSERT_PTR_NOT_NULL(info); + } + if (info) { + linphone_conference_info_unref(info); + } + } + + // wait a bit more to detect side effect if any + CoreManagerAssert({focus, marie, pauline, laure, michelle, berthe}).waitUntil(chrono::seconds(5), [] { + return false; + }); + } + + if ((focus_cleanup_window > 0) && (end_time > 0)) { + time_t now = ms_time(NULL); + time_t time_left = end_time - now + focus_cleanup_window; + if (time_left < 0) { + time_left = 0; + } + // wait for the conference to end + CoreManagerAssert({focus, marie, pauline, michelle, laure, berthe}) + .waitUntil(chrono::seconds((time_left + 1)), [] { return false; }); + } + + LinphoneConferenceInfo *focus_info = + linphone_core_find_conference_information_from_uri(focus.getLc(), confAddr); + if (focus_cleanup_window > 0) { + BC_ASSERT_PTR_NULL(focus_info); + } else { + if (BC_ASSERT_PTR_NOT_NULL(focus_info)) { + linphone_conference_info_unref(focus_info); + } + } + + ms_free(conference_address_str); + bctbx_list_free_with_data(participants_info, (bctbx_list_free_func)linphone_participant_info_unref); + linphone_address_unref(confAddr); + bctbx_list_free(coresList); + } +} + void two_overlapping_conferences_base(bool_t same_organizer, bool_t dialout) { Focus focus("chloe_rc"); { // to make sure focus is destroyed after clients. @@ -8074,7 +8922,7 @@ void two_overlapping_conferences_base(bool_t same_organizer, bool_t dialout) { } LinphoneAddress *confAddr1 = create_conference_on_server(focus, marie, participantList1, start_time1, end_time1, subject1, description1, - TRUE, security_level, TRUE, FALSE); + TRUE, security_level, TRUE, FALSE, NULL); BC_ASSERT_PTR_NOT_NULL(confAddr1); char *conference1_address_str = (confAddr1) ? linphone_address_as_string(confAddr1) : ms_strdup("sip:"); BC_ASSERT_PTR_NOT_NULL(confAddr1); @@ -8150,17 +8998,17 @@ void two_overlapping_conferences_base(bool_t same_organizer, bool_t dialout) { focus_stat.number_of_LinphoneCallStreamsRunning + 3, liblinphone_tester_sip_timeout)); - LinphoneAddress *focus_uri1 = focus.getCMgr()->identity; LinphoneConference *fconference1 = - linphone_core_search_conference(focus.getLc(), NULL, focus_uri1, confAddr1, NULL); + linphone_core_search_conference(focus.getLc(), NULL, confAddr1, confAddr1, NULL); BC_ASSERT_PTR_NOT_NULL(fconference1); // wait a 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()}) { + const LinphoneAddress *local_address = (mgr == focus.getCMgr()) ? confAddr1 : mgr->identity; LinphoneConference *pconference = - linphone_core_search_conference(mgr->lc, NULL, mgr->identity, confAddr1, NULL); + linphone_core_search_conference(mgr->lc, NULL, local_address, confAddr1, NULL); BC_ASSERT_PTR_NOT_NULL(pconference); if (pconference) { const LinphoneConferenceParams *conference_params = linphone_conference_get_current_params(pconference); @@ -8187,10 +9035,7 @@ void two_overlapping_conferences_base(bool_t same_organizer, bool_t dialout) { 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)); + check_conference_me(pconference, ((mgr == marie.getCMgr()) || (mgr == focus.getCMgr()))); 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); @@ -8238,26 +9083,43 @@ void two_overlapping_conferences_base(bool_t same_organizer, bool_t dialout) { LinphoneAddress *confAddr2 = create_conference_on_server(focus, confCreator2, participantList2, start_time2, end_time2, subject2, - description2, TRUE, security_level, TRUE, FALSE); + description2, TRUE, security_level, TRUE, FALSE, NULL); BC_ASSERT_PTR_NOT_NULL(confAddr2); char *conference2_address_str = (confAddr2) ? linphone_address_as_string(confAddr2) : ms_strdup("sip:"); // Chat room creation to send ICS - if (same_organizer) { + if (dialout) { + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneChatRoomStateCreated, 2, + liblinphone_tester_sip_timeout)); + } else if (same_organizer) { BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneChatRoomStateCreated, 3, liblinphone_tester_sip_timeout)); } else { - BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_LinphoneChatRoomStateCreated, 2, + BC_ASSERT_TRUE(wait_for_list(coresList, &michelle.getStats().number_of_LinphoneChatRoomStateCreated, 1, liblinphone_tester_sip_timeout)); } if (confAddr2) { + mgr_having_two_confs.push_back(pauline.getCMgr()); + mgr_in_conf2.push_back(pauline.getCMgr()); if (dialout) { + // Need to search the call by looking at the conference id parameter (conf-id) as both calls to a + // conference have the same remote address the conference server. + const char *confId2 = linphone_address_get_uri_param(confAddr2, "conf-id"); for (auto mgr : participants2) { - LinphoneCall *pcall = linphone_core_get_call_by_remote_address2(mgr->lc, confAddr2); + LinphoneCall *pcall = NULL; + const bctbx_list_t *call_list = linphone_core_get_calls(mgr->lc); + for (const bctbx_list_t *elem = call_list; elem != NULL; elem = elem->next) { + LinphoneCall *call = (LinphoneCall *)elem->data; + const LinphoneAddress *remote_contact_address = linphone_call_get_remote_contact_address(call); + if (confId2 && + (strcmp(confId2, linphone_address_get_uri_param(remote_contact_address, "conf-id")) == 0)) { + pcall = call; + break; + } + } BC_ASSERT_PTR_NOT_NULL(pcall); - auto it = std::find(participants1.cbegin(), participants1.cend(), mgr); - if (pcall && (it == participants1.cend())) { + if (pcall) { 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), @@ -8268,8 +9130,6 @@ void two_overlapping_conferences_base(bool_t same_organizer, bool_t dialout) { } } } 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); @@ -8322,8 +9182,6 @@ void two_overlapping_conferences_base(bool_t same_organizer, bool_t dialout) { 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_LinphoneChatRoomStateCreated, - ((same_organizer) ? 1 : 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, @@ -8406,14 +9264,14 @@ void two_overlapping_conferences_base(bool_t same_organizer, bool_t dialout) { BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_participant_devices_on_hold, onhold, liblinphone_tester_sip_timeout)); - LinphoneAddress *focus_uri2 = focus.getCMgr()->identity; LinphoneConference *fconference2 = - linphone_core_search_conference(focus.getLc(), NULL, focus_uri2, confAddr2, NULL); + linphone_core_search_conference(focus.getLc(), NULL, confAddr2, confAddr2, NULL); BC_ASSERT_PTR_NOT_NULL(fconference2); for (auto mgr : {focus.getCMgr(), marie.getCMgr(), pauline.getCMgr(), laure.getCMgr()}) { + const LinphoneAddress *local_address = (mgr == focus.getCMgr()) ? confAddr1 : mgr->identity; LinphoneConference *pconference = - linphone_core_search_conference(mgr->lc, NULL, mgr->identity, confAddr1, NULL); + linphone_core_search_conference(mgr->lc, NULL, local_address, confAddr1, NULL); BC_ASSERT_PTR_NOT_NULL(pconference); if (pconference) { bctbx_list_t *devices = linphone_conference_get_participant_device_list(pconference); @@ -8422,8 +9280,7 @@ void two_overlapping_conferences_base(bool_t same_organizer, bool_t dialout) { 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()))))) { + if (mgr == laure.getCMgr()) { BC_ASSERT_EQUAL(bctbx_list_size(devices), 3, size_t, "%zu"); BC_ASSERT_TRUE(linphone_conference_is_in(pconference)); } else { @@ -8464,17 +9321,12 @@ void two_overlapping_conferences_base(bool_t same_organizer, bool_t dialout) { } 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"); + 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), 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)); + check_conference_me(pconference, ((mgr == organizer2.getCMgr()) || (mgr == focus.getCMgr()))); 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); @@ -8499,10 +9351,7 @@ void two_overlapping_conferences_base(bool_t same_organizer, bool_t dialout) { 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()); - } + std::list<LinphoneCoreManager *> mgr_conf2_to_remove{michelle.getCMgr(), marie.getCMgr()}; // Marie and Michelle leave conference2 for (auto mgr : mgr_conf2_to_remove) { LinphoneAddress *uri = mgr->identity; @@ -8548,16 +9397,14 @@ void two_overlapping_conferences_base(bool_t same_organizer, bool_t dialout) { 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)); - } + 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 a bit more to detect side effect if any CoreManagerAssert({focus, marie, pauline, laure}).waitUntil(chrono::seconds(2), [] { return false; }); @@ -8569,26 +9416,21 @@ void two_overlapping_conferences_base(bool_t same_organizer, bool_t dialout) { BC_ASSERT_EQUAL(pauline.getStats().number_of_LinphoneConferenceStateDeleted, pauline_stat.number_of_LinphoneConferenceStateDeleted, int, "%d"); - if (!dialout) { - 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_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) { + const LinphoneAddress *local_address = (mgr == focus.getCMgr()) ? confAddr2 : mgr->identity; LinphoneConference *pconference = - linphone_core_search_conference(mgr->lc, NULL, mgr->identity, confAddr2, NULL); - if (dialout && (mgr == focus.getCMgr())) { - BC_ASSERT_PTR_NULL(pconference); - } else { - BC_ASSERT_PTR_NOT_NULL(pconference); - } + linphone_core_search_conference(mgr->lc, NULL, local_address, confAddr2, NULL); + BC_ASSERT_PTR_NOT_NULL(pconference); if (pconference) { BC_ASSERT_EQUAL(linphone_conference_get_participant_count(pconference), - ((mgr == focus.getCMgr()) ? ((!dialout || same_organizer) ? 1 : 2) : 0), int, "%0d"); + ((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), mgr_in_conf2.size() - 1, size_t, "%zu"); if (devices) { @@ -8603,9 +9445,7 @@ void two_overlapping_conferences_base(bool_t same_organizer, bool_t dialout) { pauline_stat = pauline.getStats(); stats marie_stat = marie.getStats(); std::list<LinphoneCoreManager *> mgr_rejoining{marie.getCMgr()}; - if (!dialout) { - mgr_rejoining.push_back(pauline.getCMgr()); - } + mgr_rejoining.push_back(pauline.getCMgr()); for (auto mgr : mgr_rejoining) { LinphoneConference *pconference = @@ -8617,18 +9457,16 @@ void two_overlapping_conferences_base(bool_t same_organizer, bool_t dialout) { } } - LinphoneAddress *focusUri = focus.getCMgr()->identity; LinphoneConference *conference1 = - linphone_core_search_conference(focus.getLc(), NULL, focusUri, confAddr1, NULL); + linphone_core_search_conference(focus.getLc(), NULL, confAddr1, confAddr1, NULL); + BC_ASSERT_PTR_NOT_NULL(conference1); + + 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) { - 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, @@ -8649,37 +9487,31 @@ void two_overlapping_conferences_base(bool_t same_organizer, bool_t dialout) { 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_conference_participant_devices_on_hold, - focus_stat.number_of_conference_participant_devices_on_hold + 1, - liblinphone_tester_sip_timeout)); - 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_conference_participant_devices_present, - focus_stat.number_of_conference_participant_devices_present + 2, - liblinphone_tester_sip_timeout)); - BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_present, - focus_stat.number_of_participant_devices_present + 2, - liblinphone_tester_sip_timeout)); - } - - for (auto mgr : mgr_in_conf2) { - LinphoneConference *pconference = - linphone_core_search_conference(mgr->lc, NULL, mgr->identity, confAddr2, NULL); - if (dialout && (mgr == focus.getCMgr())) { - BC_ASSERT_PTR_NULL(pconference); - } else { - BC_ASSERT_PTR_NOT_NULL(pconference); - } + // Pauline leaves conference2 + // Pauline and Marie enter conference1 + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_conference_participant_devices_on_hold, + focus_stat.number_of_conference_participant_devices_on_hold + 1, + liblinphone_tester_sip_timeout)); + 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_conference_participant_devices_present, + focus_stat.number_of_conference_participant_devices_present + 2, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_present, + focus_stat.number_of_participant_devices_present + 2, + liblinphone_tester_sip_timeout)); + + for (auto mgr : mgr_in_conf2) { + const LinphoneAddress *local_address = (mgr == focus.getCMgr()) ? confAddr2 : mgr->identity; + LinphoneConference *pconference = + linphone_core_search_conference(mgr->lc, NULL, local_address, confAddr2, NULL); + BC_ASSERT_PTR_NOT_NULL(pconference); if (pconference) { BC_ASSERT_EQUAL(linphone_conference_get_participant_count(pconference), - ((mgr == focus.getCMgr()) ? ((!dialout || same_organizer) ? 1 : 2) : 0), int, "%0d"); + ((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), ((mgr == focus.getCMgr() && !dialout) ? 1 : 0), size_t, - "%zu"); + BC_ASSERT_EQUAL(bctbx_list_size(devices), ((mgr == focus.getCMgr()) ? 1 : 0), size_t, "%zu"); if (devices) { bctbx_list_free_with_data(devices, (void (*)(void *))linphone_participant_device_unref); } @@ -8697,35 +9529,38 @@ void two_overlapping_conferences_base(bool_t same_organizer, bool_t dialout) { 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); + if (conference1) { + 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, &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, @@ -8774,94 +9609,90 @@ void two_overlapping_conferences_base(bool_t same_organizer, bool_t dialout) { // wait a 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()}) { - LinphoneConference *pconference = - linphone_core_search_conference(mgr->lc, NULL, mgr->identity, confAddr2, NULL); - BC_ASSERT_PTR_NOT_NULL(pconference); - if (pconference) { - ms_message("%s is entering conference %s", linphone_core_get_identity(mgr->lc), - conference2_address_str); - linphone_conference_enter(pconference); - } + // Pauline rejoins and leaves conference2 (conference2 is destroyed on the server) + focus_stat = focus.getStats(); + pauline_stat = pauline.getStats(); + for (auto mgr : {pauline.getCMgr()}) { + LinphoneConference *pconference = + linphone_core_search_conference(mgr->lc, NULL, mgr->identity, confAddr2, NULL); + BC_ASSERT_PTR_NOT_NULL(pconference); + 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_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"); + 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_conference_participant_devices_present, - focus_stat.number_of_conference_participant_devices_present + 1, - liblinphone_tester_sip_timeout)); - BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_present, - focus_stat.number_of_participant_devices_present + 1, - liblinphone_tester_sip_timeout)); + // Pauline enters conference2 + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_conference_participant_devices_present, + focus_stat.number_of_conference_participant_devices_present + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participant_devices_present, + focus_stat.number_of_participant_devices_present + 1, + liblinphone_tester_sip_timeout)); - for (auto mgr : mgr_in_conf2) { - LinphoneConference *pconference = - linphone_core_search_conference(mgr->lc, NULL, mgr->identity, confAddr2, NULL); - 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_TRUE(linphone_conference_is_in(pconference) == (mgr != focus.getCMgr())); - BC_ASSERT_STRING_EQUAL(linphone_conference_get_subject(pconference), subject2); + for (auto mgr : mgr_in_conf2) { + const LinphoneAddress *local_address = (mgr == focus.getCMgr()) ? confAddr2 : mgr->identity; + LinphoneConference *pconference = + linphone_core_search_conference(mgr->lc, NULL, local_address, confAddr2, NULL); + 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_TRUE(linphone_conference_is_in(pconference) == (mgr != focus.getCMgr())); + BC_ASSERT_STRING_EQUAL(linphone_conference_get_subject(pconference), subject2); } + } - // wait a bit more to detect side effect if any - CoreManagerAssert({focus, marie, pauline, laure, michelle}).waitUntil(chrono::seconds(2), [] { - return false; - }); + // wait a 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 = mgr->identity; - 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); - } + // Pauline leaves conference2 + focus_stat = focus.getStats(); + pauline_stat = pauline.getStats(); + for (auto mgr : {pauline.getCMgr()}) { + LinphoneAddress *uri = mgr->identity; + 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, &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_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)); - } + 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_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateTerminationPending, 1, liblinphone_tester_sip_timeout)); @@ -8871,7 +9702,7 @@ void two_overlapping_conferences_base(bool_t same_organizer, bool_t dialout) { liblinphone_tester_sip_timeout)); LinphoneConference *conference2 = - linphone_core_search_conference(focus.getLc(), NULL, focusUri, confAddr2, NULL); + linphone_core_search_conference(focus.getLc(), NULL, confAddr2, confAddr2, NULL); BC_ASSERT_PTR_NULL(conference2); // Marie terminates conference1 (conference1 is destroyed on the server) @@ -8994,7 +9825,7 @@ void create_one_participant_conference_toggle_video_base(LinphoneConferenceLayou } LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, initialSubject, - description, TRUE, security_level, TRUE, FALSE); + description, TRUE, security_level, TRUE, FALSE, NULL); BC_ASSERT_PTR_NOT_NULL(confAddr); // Chat room creation to send ICS @@ -9136,10 +9967,7 @@ void create_one_participant_conference_toggle_video_base(LinphoneConferenceLayou 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)); + check_conference_me(pconference, ((mgr == marie.getCMgr()) || (mgr == focus.getCMgr()))); 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); @@ -9660,7 +10488,7 @@ void create_conference_with_active_call_base(bool_t dialout) { time_t start_time = (dialout) ? -1 : (ms_time(NULL) + 5); const int duration = 0; time_t end_time = -1; - bool_t send_ics = TRUE; + bool_t send_ics = !dialout; bctbx_list_t *participants_info = NULL; std::map<LinphoneCoreManager *, LinphoneParticipantInfo *> participantList; @@ -9682,7 +10510,7 @@ void create_conference_with_active_call_base(bool_t dialout) { LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, initialSubject, - description, send_ics, security_level, FALSE, FALSE); + description, send_ics, security_level, FALSE, FALSE, NULL); BC_ASSERT_PTR_NOT_NULL(confAddr); // wait a bit more to detect side effect if any @@ -9734,7 +10562,7 @@ void create_conference_with_active_call_base(bool_t dialout) { if (dialout) { // Chat room creation to send ICS - BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateCreated, + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneChatRoomStateCreated, (send_ics ? 1 : 0), liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateCreated, 1, liblinphone_tester_sip_timeout)); @@ -9802,7 +10630,9 @@ void create_conference_with_active_call_base(bool_t dialout) { } linphone_call_params_unref(new_params); - update_sequence_number(&participants_info, {}, 0, -1); + if (!!send_ics) { + update_sequence_number(&participants_info, {}, 0, -1); + } for (bctbx_list_t *info_it = participants_info; info_it; info_it = bctbx_list_next(info_it)) { LinphoneParticipantInfo *participant_info_el = (LinphoneParticipantInfo *)bctbx_list_get_data(info_it); if (linphone_participant_info_get_role(participant_info_el) == LinphoneParticipantRoleUnknown) { @@ -10072,10 +10902,7 @@ void create_conference_with_active_call_base(bool_t dialout) { 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)); + check_conference_me(pconference, ((mgr == marie.getCMgr()) || (mgr == focus.getCMgr()))); 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); @@ -10227,9 +11054,10 @@ void create_simple_conference_merging_calls_base(bool_t enable_ice, bool_t enable_screen_sharing) { Focus focus("chloe_rc"); { // to make sure focus is destroyed after clients. - ClientConference marie("marie_rc", focus.getConferenceFactoryAddress()); - ClientConference pauline("pauline_rc", focus.getConferenceFactoryAddress()); - ClientConference laure("laure_tcp_rc", focus.getConferenceFactoryAddress()); + bool is_encrypted = (security_level == LinphoneConferenceSecurityLevelEndToEnd); + ClientConference marie("marie_rc", focus.getConferenceFactoryAddress(), is_encrypted); + ClientConference pauline("pauline_rc", focus.getConferenceFactoryAddress(), is_encrypted); + ClientConference laure("laure_tcp_rc", focus.getConferenceFactoryAddress(), is_encrypted); focus.registerAsParticipantDevice(marie); focus.registerAsParticipantDevice(pauline); @@ -10238,6 +11066,7 @@ void create_simple_conference_merging_calls_base(bool_t enable_ice, setup_conference_info_cbs(marie.getCMgr()); linphone_core_set_file_transfer_server(marie.getLc(), file_transfer_url); + LinphoneMediaEncryption encryption = is_encrypted ? LinphoneMediaEncryptionZRTP : LinphoneMediaEncryptionNone; auto focus_proxy_config = focus.getDefaultProxyConfig(); const char *focus_uri = linphone_proxy_config_get_identity(focus_proxy_config); @@ -10268,6 +11097,7 @@ void create_simple_conference_merging_calls_base(bool_t enable_ice, if (mgr != focus.getCMgr()) { linphone_core_set_default_conference_layout(mgr->lc, layout); + linphone_core_set_media_encryption(mgr->lc, encryption); } } @@ -10281,6 +11111,9 @@ void create_simple_conference_merging_calls_base(bool_t enable_ice, for (auto mgr : conferenceMgrs) { enable_stun_in_mgr(mgr, enable_ice, enable_ice, enable_ice, enable_ice); } + if (is_encrypted) { + configure_end_to_end_encrypted_conference_server(focus); + } LinphoneCall *marie_call_pauline = linphone_core_get_current_call(marie.getLc()); BC_ASSERT_PTR_NOT_NULL(marie_call_pauline); @@ -10298,6 +11131,7 @@ void create_simple_conference_merging_calls_base(bool_t enable_ice, const char *initialSubject = "Test characters: ^ :) ¤ çà @"; linphone_conference_params_enable_audio(conf_params, TRUE); linphone_conference_params_enable_video(conf_params, toggle_video); + linphone_conference_params_set_security_level(conf_params, security_level); 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); @@ -10388,8 +11222,9 @@ void create_simple_conference_merging_calls_base(bool_t enable_ice, CoreManagerAssert({focus, marie, pauline, laure}).waitUntil(chrono::seconds(2), [] { return false; }); for (auto mgr : conferenceMgrs) { - LinphoneAddress *uri = mgr->identity; - LinphoneConference *pconference = linphone_core_search_conference(mgr->lc, NULL, uri, confAddr, NULL); + const LinphoneAddress *local_address = (mgr == focus.getCMgr()) ? confAddr : mgr->identity; + LinphoneConference *pconference = + linphone_core_search_conference(mgr->lc, NULL, local_address, confAddr, NULL); BC_ASSERT_PTR_NOT_NULL(pconference); if (pconference) { @@ -10434,6 +11269,27 @@ void create_simple_conference_merging_calls_base(bool_t enable_ice, // wait a bit more to detect side effect if any CoreManagerAssert({focus, marie, pauline, laure}).waitUntil(chrono::seconds(2), [] { return false; }); + // marie creates the conference + LinphoneConferenceParams *conf_params2 = linphone_core_create_conference_params_2(marie.getLc(), NULL); + linphone_conference_params_enable_audio(conf_params2, TRUE); + linphone_conference_params_enable_video(conf_params2, toggle_video); + const char *initialSubject2 = "This is an error"; + linphone_conference_params_set_subject(conf_params2, initialSubject2); + LinphoneConference *conf2 = linphone_core_create_conference_with_params(marie.getLc(), conf_params2); + linphone_conference_params_unref(conf_params2); + BC_ASSERT_PTR_NOT_NULL(conf2); + if (conf2) { + linphone_conference_terminate(conf2); + 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)); + linphone_conference_unref(conf2); + } + participant_stats.clear(); for (auto mgr : members) { participant_stats.push_back(mgr->stat); @@ -10460,9 +11316,8 @@ void create_simple_conference_merging_calls_base(bool_t enable_ice, // wait a bit more to detect side effect if any CoreManagerAssert({focus, marie, pauline, laure}).waitUntil(chrono::seconds(2), [] { return false; }); - LinphoneAddress *focus_addr = focus.getCMgr()->identity; LinphoneConference *fconference = - linphone_core_search_conference(focus.getLc(), NULL, focus_addr, confAddr, NULL); + linphone_core_search_conference(focus.getLc(), NULL, confAddr, confAddr, NULL); BC_ASSERT_PTR_NOT_NULL(fconference); for (auto mgr : members) { @@ -10600,8 +11455,9 @@ void create_simple_conference_merging_calls_base(bool_t enable_ice, } for (auto mgr : conferenceMgrs) { - LinphoneAddress *uri = mgr->identity; - LinphoneConference *pconference = linphone_core_search_conference(mgr->lc, NULL, uri, confAddr, NULL); + const LinphoneAddress *local_address = (mgr == focus.getCMgr()) ? confAddr : mgr->identity; + LinphoneConference *pconference = + linphone_core_search_conference(mgr->lc, NULL, local_address, confAddr, NULL); BC_ASSERT_PTR_NOT_NULL(pconference); if (pconference) { @@ -10883,12 +11739,13 @@ void create_simple_conference_merging_calls_base(bool_t enable_ice, wait_for_list(coresList, &focus.getStats().number_of_LinphoneCallReleased, total_focus_calls, 40000)); for (auto mgr : conferenceMgrs) { + int nb_conferences = (mgr == marie.getCMgr()) ? 2 : 1; // 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, + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateTerminationPending, + nb_conferences, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateTerminated, + nb_conferences, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateDeleted, nb_conferences, liblinphone_tester_sip_timeout)); BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionTerminated, @@ -10927,8 +11784,7 @@ void create_simple_conference_merging_calls_base(bool_t enable_ice, } } -void create_conference_dial_out_base(bool_t send_ics, - LinphoneConferenceLayout layout, +void create_conference_dial_out_base(LinphoneConferenceLayout layout, LinphoneVideoActivationPolicy *pol, bool_t enable_stun, bool_t enable_ice, @@ -10936,10 +11792,13 @@ void create_conference_dial_out_base(bool_t send_ics, bool_t accept, bool_t participant_codec_mismatch, LinphoneConferenceSecurityLevel security_level, - bool_t version_mismatch) { + bool_t version_mismatch, + bool_t enable_chat) { Focus focus("chloe_rc"); { // to make sure focus is destroyed after clients. bool_t enable_lime = (security_level == LinphoneConferenceSecurityLevelEndToEnd ? TRUE : FALSE); + // Cannot send ICS in dial out conferences + bool_t send_ics = FALSE; ClientConference marie("marie_rc", focus.getConferenceFactoryAddress(), enable_lime); ClientConference pauline("pauline_rc", focus.getConferenceFactoryAddress(), enable_lime); @@ -10965,9 +11824,6 @@ void create_conference_dial_out_base(bool_t send_ics, int nb_subscriptions = 1; if (security_level == LinphoneConferenceSecurityLevelEndToEnd) { nb_subscriptions = 2; // One more subscription for the EKT - } - - if (enable_lime) { configure_end_to_end_encrypted_conference_server(focus); } @@ -11037,8 +11893,9 @@ void create_conference_dial_out_base(bool_t send_ics, linphone_core_add_linphone_spec(laure.getLc(), "conference/1.0"); } - LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, -1, -1, initialSubject, - description, send_ics, security_level, TRUE, FALSE); + LinphoneAddress *confAddr = + create_conference_on_server(focus, marie, participantList, -1, -1, initialSubject, description, send_ics, + security_level, TRUE, enable_chat, NULL); BC_ASSERT_PTR_NOT_NULL(confAddr); // Chat room creation to send ICS @@ -11215,17 +12072,19 @@ void create_conference_dial_out_base(bool_t send_ics, (LinphoneParticipantInfo *)bctbx_list_get_data(info_it); const LinphoneAddress *address = linphone_participant_info_get_address(participant_info_el); const int sequence = linphone_participant_info_get_sequence_number(participant_info_el); + bool is_laure_info = linphone_address_weak_equal(address, laure.getCMgr()->identity); bool found = false; for (const auto &mgr : codec_mismatch_members) { found |= !!linphone_address_weak_equal(address, mgr->identity); } LinphoneParticipantRole role = LinphoneParticipantRoleUnknown; if ((!version_mismatch) || (mgr == pauline.getCMgr()) || - (mgr == laure.getCMgr() && linphone_address_weak_equal(address, laure.getCMgr()->identity)) || - ((mgr == berthe.getCMgr() || mgr == michelle.getCMgr()) && - !linphone_address_weak_equal(address, laure.getCMgr()->identity))) { + (mgr == laure.getCMgr() && is_laure_info) || + ((mgr == berthe.getCMgr() || mgr == michelle.getCMgr()) && !is_laure_info)) { LinphoneParticipantRole current_role = linphone_participant_info_get_role(participant_info_el); - if (!found && (current_role == LinphoneParticipantRoleUnknown)) { + if (found) { + role = LinphoneParticipantRoleUnknown; + } else if (current_role == LinphoneParticipantRoleUnknown) { role = LinphoneParticipantRoleSpeaker; } else { role = current_role; @@ -11255,13 +12114,14 @@ void create_conference_dial_out_base(bool_t send_ics, BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_NotifyEktReceived, 1, 2 * liblinphone_tester_sip_timeout)); } + check_conference_info_in_db( + mgr, NULL, confAddr, marie.getCMgr()->identity, participants_info3, 0, 0, initialSubject, + (send_ics) ? description : NULL, 0, LinphoneConferenceInfoStateNew, + mgr == laure.getCMgr() && version_mismatch ? LinphoneConferenceSecurityLevelNone + : security_level, + FALSE, TRUE, TRUE, enable_chat); } - check_conference_info_in_db( - mgr, NULL, confAddr, marie.getCMgr()->identity, participants_info3, 0, 0, initialSubject, - (send_ics) ? description : NULL, 0, LinphoneConferenceInfoStateNew, - mgr == laure.getCMgr() && version_mismatch ? LinphoneConferenceSecurityLevelNone : security_level, - FALSE, TRUE, TRUE, FALSE); LinphoneCall *pcall = linphone_core_get_call_by_remote_address2(mgr->lc, confAddr); if (mgr == laure.getCMgr() && version_mismatch) { BC_ASSERT_PTR_NULL(pcall); @@ -11368,6 +12228,34 @@ void create_conference_dial_out_base(bool_t send_ics, !!((mgr == marie.getCMgr()) ? linphone_video_activation_policy_get_automatically_initiate(mgr_pol) : linphone_video_activation_policy_get_automatically_accept(mgr_pol)); + if (mgr != focus.getCMgr()) { + const LinphoneAddress *from = (mgr == marie.getCMgr()) ? mgr->identity : confAddr; + const LinphoneAddress *to = (mgr == marie.getCMgr()) ? confAddr : mgr->identity; + + 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_PTR_NOT_NULL(call_log); + if (call_log) { + BC_ASSERT_TRUE( + linphone_address_weak_equal(linphone_call_log_get_from_address(call_log), from)); + BC_ASSERT_TRUE(linphone_address_weak_equal(linphone_call_log_get_to_address(call_log), to)); + } + } + LinphoneCall *ccall = linphone_core_get_call_by_remote_address2(focus.getLc(), mgr->identity); + BC_ASSERT_PTR_NOT_NULL(ccall); + if (ccall) { + LinphoneCallLog *call_log = linphone_call_get_call_log(ccall); + BC_ASSERT_PTR_NOT_NULL(call_log); + if (call_log) { + BC_ASSERT_TRUE( + linphone_address_weak_equal(linphone_call_log_get_from_address(call_log), from)); + BC_ASSERT_TRUE(linphone_address_weak_equal(linphone_call_log_get_to_address(call_log), to)); + } + } + } + if (pconference) { int no_participants = 0; if (mgr == focus.getCMgr()) { @@ -11390,8 +12278,10 @@ void create_conference_dial_out_base(bool_t send_ics, !linphone_address_weak_equal(address, laure.getCMgr()->identity))) { LinphoneParticipantRole current_role = linphone_participant_info_get_role(participant_info_el); - if (((mgr == marie.getCMgr()) || !found) && - (current_role == LinphoneParticipantRoleUnknown)) { + if (found && (mgr != marie.getCMgr())) { + role = LinphoneParticipantRoleUnknown; + } else if (((mgr == marie.getCMgr()) || !found) && + (current_role == LinphoneParticipantRoleUnknown)) { role = LinphoneParticipantRoleSpeaker; } else { role = current_role; @@ -11403,7 +12293,7 @@ void create_conference_dial_out_base(bool_t send_ics, check_conference_info_in_db( mgr, NULL, confAddr, marie.getCMgr()->identity, participants_info3, 0, 0, initialSubject, (send_ics || (mgr == marie.getCMgr())) ? description : NULL, 0, - LinphoneConferenceInfoStateNew, security_level, FALSE, TRUE, TRUE, FALSE); + LinphoneConferenceInfoStateNew, security_level, FALSE, TRUE, TRUE, enable_chat); bctbx_list_free_with_data(participants_info3, (bctbx_list_free_func)linphone_participant_info_unref); @@ -11494,10 +12384,7 @@ void create_conference_dial_out_base(bool_t send_ics, 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)); + check_conference_me(pconference, ((mgr == marie.getCMgr()) || (mgr == focus.getCMgr()))); 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); @@ -11550,17 +12437,12 @@ void create_conference_dial_out_base(bool_t send_ics, bool video_available = !!linphone_participant_device_get_stream_availability(d, LinphoneStreamTypeVideo); if (enable_video) { - // if (linphone_conference_is_me(conference, - // linphone_participant_device_get_address(d))) { - // BC_ASSERT_TRUE(video_available == - // video_enabled); } else { 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(video_available); } @@ -11646,14 +12528,21 @@ void create_conference_dial_out_base(bool_t send_ics, 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)); + if (enable_chat) { + BC_ASSERT_FALSE( + wait_for_list(coresList, &mgr->stat.number_of_LinphoneConferenceStateDeleted, 1, 2000)); + } else { + 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); + linphone_core_search_conference(mgr->lc, NULL, mgr->identity, confAddr, NULL); + if (enable_chat) { + BC_ASSERT_PTR_NOT_NULL(pconference); + } else { + BC_ASSERT_PTR_NULL(pconference); + } } } @@ -11776,7 +12665,7 @@ void create_conference_dial_out_base(bool_t send_ics, ((mgr == michelle.getCMgr() || mgr == berthe.getCMgr()) && !linphone_address_weak_equal(address, laure.getCMgr()->identity))) { LinphoneParticipantRole current_role = linphone_participant_info_get_role(participant_info_el); - if ((mgr != marie.getCMgr()) && !accept) { + if ((mgr != marie.getCMgr()) && (found || !accept)) { role = LinphoneParticipantRoleUnknown; } else if (((mgr == marie.getCMgr()) || !found) && (current_role == LinphoneParticipantRoleUnknown)) { @@ -11792,7 +12681,7 @@ void create_conference_dial_out_base(bool_t send_ics, mgr, NULL, confAddr, marie.getCMgr()->identity, participants_info3, 0, 0, initialSubject, ((accept && send_ics) || (mgr == marie.getCMgr())) ? description : NULL, 0, LinphoneConferenceInfoStateNew, (accept) ? security_level : LinphoneConferenceSecurityLevelNone, FALSE, - TRUE, TRUE, FALSE); + TRUE, TRUE, enable_chat); bctbx_list_free_with_data(participants_info3, (bctbx_list_free_func)linphone_participant_info_unref); } @@ -11875,7 +12764,7 @@ void create_conference_with_audio_only_participants_base(LinphoneConferenceSecur LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, initialSubject, - description, TRUE, security_level, TRUE, FALSE); + description, TRUE, security_level, TRUE, FALSE, NULL); BC_ASSERT_PTR_NOT_NULL(confAddr); char *conference_address_str = (confAddr) ? linphone_address_as_string(confAddr) : ms_strdup("sip:"); @@ -12053,10 +12942,7 @@ void create_conference_with_audio_only_participants_base(LinphoneConferenceSecur } 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)); + check_conference_me(pconference, ((mgr == marie.getCMgr()) || (mgr == focus.getCMgr()))); 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); @@ -12211,10 +13097,7 @@ void create_conference_with_audio_only_participants_base(LinphoneConferenceSecur } 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)); + check_conference_me(pconference, ((mgr == marie.getCMgr()) || (mgr == focus.getCMgr()))); 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); @@ -12469,7 +13352,7 @@ void create_simple_conference_dial_out_with_some_calls_declined_base(LinphoneRea : LinphoneParticipantRoleSpeaker; } LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, -1, -1, initialSubject, - description, FALSE, security_level, TRUE, FALSE); + description, FALSE, security_level, TRUE, FALSE, NULL); BC_ASSERT_PTR_NOT_NULL(confAddr); int nb_subscriptions = 1; @@ -12572,9 +13455,6 @@ void create_simple_conference_dial_out_with_some_calls_declined_base(LinphoneRea } 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)); @@ -12767,10 +13647,7 @@ void create_simple_conference_dial_out_with_some_calls_declined_base(LinphoneRea } } 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)); + check_conference_me(pconference, ((mgr == marie.getCMgr()) || (mgr == focus.getCMgr()))); bctbx_list_t *participants_list = linphone_conference_get_participant_list(pconference); if (reason == LinphoneReasonBusy) { no_participants += declining_participants.size(); @@ -13042,10 +13919,10 @@ void change_active_speaker_base(bool transfer_mode) { 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) { + std::list<LinphoneCoreManager *> members{pauline.getCMgr(), laure.getCMgr(), marie.getCMgr()}; + std::list<LinphoneCoreManager *> conferenceMgrs{focus.getCMgr(), marie.getCMgr(), pauline.getCMgr(), + laure.getCMgr()}; + for (LinphoneCoreManager *mgr : conferenceMgrs) { if (mgr != pauline.getCMgr()) { LinphoneVideoActivationPolicy *pol = linphone_factory_create_video_activation_policy(linphone_factory_get()); @@ -13093,14 +13970,16 @@ void change_active_speaker_base(bool transfer_mode) { } LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, initialSubject, - description, TRUE, security_level, TRUE, FALSE); + description, TRUE, security_level, TRUE, FALSE, NULL); BC_ASSERT_PTR_NOT_NULL(confAddr); + char *conference_address_str = (confAddr) ? linphone_address_as_string(confAddr) : ms_strdup("sip:"); // Chat room creation to send ICS BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneChatRoomStateCreated, 2, liblinphone_tester_sip_timeout)); - for (LinphoneCoreManager *mgr : participantsList) { + for (LinphoneCoreManager *mgr : members) { + ms_message("%s is entering conference %s", linphone_core_get_identity(mgr->lc), conference_address_str); LinphoneCallParams *new_params = linphone_core_create_call_params(mgr->lc, nullptr); linphone_call_params_set_video_direction(new_params, LinphoneMediaDirectionSendRecv); if (mgr != pauline.getCMgr()) { @@ -13163,17 +14042,19 @@ void change_active_speaker_base(bool transfer_mode) { liblinphone_tester_sip_timeout)); } + std::map<LinphoneCoreManager *, LinphoneParticipantInfo *> memberList = + fill_member_list(members, participantList, marie.getCMgr(), participants_info); + 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; }); - int nbStreamsAudio = 1; int nbStreamsVideo = 0; int nbStreamsText = 0; - for (LinphoneCoreManager *mgr : cmgrsList) { + for (LinphoneCoreManager *mgr : conferenceMgrs) { nbStreamsVideo = ((mgr != pauline.getCMgr()) ? 3 : 0); LinphoneConference *pconference = linphone_core_search_conference_2(mgr->lc, confAddr); BC_ASSERT_PTR_NOT_NULL(pconference); @@ -13219,10 +14100,7 @@ void change_active_speaker_base(bool transfer_mode) { 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)); + check_conference_me(pconference, ((mgr == marie.getCMgr()) || (mgr == focus.getCMgr()))); 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); @@ -13241,12 +14119,99 @@ void change_active_speaker_base(bool transfer_mode) { } } + // Make pauline and then Marie speak so that we are sure that laure is not the active speaker of none of the + // conference members + const std::initializer_list<std::reference_wrapper<ClientConference>> speakingClients{pauline, marie}; + for (const ClientConference &speakingClient : speakingClients) { + for (const auto &mgr : members) { + linphone_core_enable_mic(mgr->lc, (mgr == speakingClient.getCMgr())); + } + + for (const auto &mgr : members) { + // wait for Marie to become the active speaker + CoreManagerAssert({focus, marie, pauline, laure}) + .waitUntil(chrono::seconds(20), [&mgr, &speakingClient, &confAddr] { + LinphoneConference *pconference = linphone_core_search_conference_2(mgr->lc, confAddr); + LinphoneParticipantDevice *device = + linphone_conference_get_active_speaker_participant_device(pconference); + return device ? linphone_address_weak_equal(linphone_participant_device_get_address(device), + speakingClient.getIdentity().toC()) + : false; + }); + } + } + // 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()); + auto speaking_mgr = + _linphone_conference_video_change(coresList, marie.getCMgr(), pauline.getCMgr(), laure.getCMgr()); + BC_ASSERT_PTR_NOT_NULL(speaking_mgr); + + if (speaking_mgr) { + // TODO: Uncomment lines below when C++20 will be supported + // auto count = + members.remove_if([&speaking_mgr](const auto &client) { return (client == speaking_mgr); }); + // BC_ASSERT_EQUAL(count, 1, size_t, "%zu"); + } // end - for (LinphoneCoreManager *mgr : participantsList) { + ms_message("Speaking participant (%s) exits conference %s", linphone_core_get_identity(speaking_mgr->lc), + conference_address_str); + stats initial_pauline_stat = pauline.getStats(); + stats initial_laure_stat = laure.getStats(); + stats initial_focus_stat = focus.getStats(); + LinphoneCall *speaking_mgr_call = + linphone_core_get_call_by_remote_address2(speaking_mgr->lc, focus.getCMgr()->identity); + BC_ASSERT_PTR_NOT_NULL(speaking_mgr_call); + if (speaking_mgr_call) { + linphone_call_terminate(speaking_mgr_call); + BC_ASSERT_TRUE(wait_for_list(coresList, &speaking_mgr->stat.number_of_LinphoneCallEnd, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &speaking_mgr->stat.number_of_LinphoneCallReleased, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &speaking_mgr->stat.number_of_LinphoneSubscriptionTerminated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, + &speaking_mgr->stat.number_of_LinphoneConferenceStateTerminationPending, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &speaking_mgr->stat.number_of_LinphoneConferenceStateTerminated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &speaking_mgr->stat.number_of_LinphoneConferenceStateDeleted, 1, + liblinphone_tester_sip_timeout)); + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_active_speaker_participant_device_changed, + initial_laure_stat.number_of_active_speaker_participant_device_changed + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_participants_removed, + initial_laure_stat.number_of_participants_removed + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &laure.getStats().number_of_participant_devices_removed, + initial_laure_stat.number_of_participant_devices_removed + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_active_speaker_participant_device_changed, + initial_pauline_stat.number_of_active_speaker_participant_device_changed + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_participants_removed, + initial_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, + initial_pauline_stat.number_of_participant_devices_removed + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_participants_removed, + initial_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, + initial_focus_stat.number_of_participant_devices_removed + 1, + liblinphone_tester_sip_timeout)); + + for (LinphoneCoreManager *mgr : members) { + LinphoneConference *pconference = linphone_core_search_conference_2(mgr->lc, confAddr); + BC_ASSERT_PTR_NOT_NULL(pconference); + if (pconference) { + BC_ASSERT_PTR_NULL(linphone_conference_get_active_speaker_participant_device(pconference)); + } + ms_message("%s exits conference %s", linphone_core_get_identity(mgr->lc), conference_address_str); LinphoneCall *call = linphone_core_get_call_by_remote_address2(mgr->lc, focus.getCMgr()->identity); BC_ASSERT_PTR_NOT_NULL(call); if (call) { @@ -13273,7 +14238,7 @@ void change_active_speaker_base(bool transfer_mode) { BC_ASSERT_TRUE(wait_for_list(coresList, &focus.getStats().number_of_LinphoneConferenceStateDeleted, 1, liblinphone_tester_sip_timeout)); - for (auto mgr : cmgrsList) { + for (auto mgr : conferenceMgrs) { BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneSubscriptionTerminated, 1, liblinphone_tester_sip_timeout)); @@ -13302,6 +14267,7 @@ void change_active_speaker_base(bool transfer_mode) { } bctbx_list_free_with_data(participants_info, (bctbx_list_free_func)linphone_participant_info_unref); + ms_free(conference_address_str); linphone_address_unref(confAddr); bctbx_list_free(coresList); } diff --git a/tester/local-conference-tester-functions.h b/tester/local-conference-tester-functions.h index 96188b66de09ab54c7c209f6bb63093f9365f4b3..aeae4f68bb0a0ca3271361246f198a6c029eb223 100644 --- a/tester/local-conference-tester-functions.h +++ b/tester/local-conference-tester-functions.h @@ -55,6 +55,7 @@ public: [this, factoryUri, encrypted](bool) { configureCoreForConference(factoryUri); _configure_core_for_audio_video_conference(mMgr.get(), factoryUri.toC()); + linphone_core_enable_gruu_in_conference_address(getLc(), FALSE); 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); @@ -130,7 +131,11 @@ private: class Focus : public ConfCoreManager { public: Focus(std::string rc) - : ConfCoreManager(rc, [this](bool) { linphone_core_enable_conference_server(getLc(), TRUE); }) { + : ConfCoreManager(rc, [this](bool) { + linphone_core_enable_gruu_in_conference_address(getLc(), FALSE); + linphone_core_enable_conference_server(getLc(), TRUE); + }) { + configureFocus(); } ~Focus() { @@ -245,15 +250,18 @@ private: linphone_core_enable_rtp_bundle(getLc(), TRUE); linphone_core_set_conference_cleanup_period(getLc(), 1); - 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); - Address factoryAddress = getIdentity(); - linphone_account_params_set_conference_factory_address(new_account_params, factoryAddress.toC()); - 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))); + const bctbx_list_t *accounts = linphone_core_get_account_list(getLc()); + for (const bctbx_list_t *account_it = accounts; account_it != NULL; account_it = account_it->next) { + LinphoneAccount *account = (LinphoneAccount *)(bctbx_list_get_data(account_it)); + 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_address( + new_account_params, linphone_account_params_get_identity_address(account_params)); + 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); @@ -300,6 +308,10 @@ void create_simple_conference_merging_calls_base(bool_t enable_ice, LinphoneConferenceSecurityLevel security_level, bool_t enable_screen_sharing); +void conference_joined_multiple_times(LinphoneConferenceSecurityLevel security_level, + bool_t enable_chat, + long cleanup_window); + void create_conference_base(time_t start_time, int duration, bool_t uninvited_participant_dials, @@ -320,7 +332,8 @@ void create_conference_base(time_t start_time, std::list<LinphoneParticipantRole> allowedRoles, bool_t add_participant_after_end, bool_t version_mismatch, - bool_t slow_ice_negotiation); + bool_t slow_ice_negotiation, + bool_t enable_chat); void create_conference_with_screen_sharing_base(time_t start_time, int duration, @@ -345,7 +358,10 @@ void create_conference_with_chat_base(LinphoneConferenceSecurityLevel security_l bool_t join_after_termination, long cleanup_window, bool_t slow_ice_negotiation, - bool_t client_reenter_conference); + bool_t client_reenter_conference, + bool_t network_drops, + time_t start_time, + bool_t enable_gruu_in_conference_address); void configure_end_to_end_encrypted_conference_server(Focus &focus); @@ -376,7 +392,7 @@ void check_muted(std::initializer_list<std::reference_wrapper<CoreManager>> core const LinphoneParticipantDevice *d, std::list<LinphoneCoreManager *> mutedMgrs); -void two_overlapping_conferences_base(bool_t same_organizer, bool_t dialout); +void two_overlapping_conferences_base(bool_t same_organizer, bool_t is_dialout); void create_conference_with_late_participant_addition_base(time_t start_time, int duration, @@ -390,7 +406,9 @@ void create_one_participant_conference_toggle_video_base(LinphoneConferenceLayou bool_t enable_ice, bool_t enable_stun); -void create_conference_with_active_call_base(bool_t dialout); +void create_conference_with_active_call_base(bool_t is_dialout); + +void check_conference_me(LinphoneConference *conference, bool_t is_me); LinphoneAddress * create_conference_on_server(Focus &focus, @@ -403,7 +421,8 @@ create_conference_on_server(Focus &focus, bool_t send_ics, LinphoneConferenceSecurityLevel security_level, bool_t enable_video, - bool_t enable_chat); + bool_t enable_chat, + LinphoneConferenceParams *ics_chat_room_params); void set_video_settings_in_conference(LinphoneCoreManager *focus, LinphoneCoreManager *participant, @@ -429,8 +448,7 @@ void update_sequence_number(bctbx_list_t **participants_info, int exp_sequence, int exp_new_participant_sequence); -void create_conference_dial_out_base(bool_t send_ics, - LinphoneConferenceLayout layout, +void create_conference_dial_out_base(LinphoneConferenceLayout layout, LinphoneVideoActivationPolicy *pol, bool_t enable_stun, bool_t enable_ice, @@ -438,7 +456,8 @@ void create_conference_dial_out_base(bool_t send_ics, bool_t accept, bool_t participant_codec_mismatch, LinphoneConferenceSecurityLevel security_level, - bool_t version_mismatch); + bool_t version_mismatch, + bool_t enable_chat); void create_conference_with_audio_only_participants_base(LinphoneConferenceSecurityLevel security_level); diff --git a/tester/local-encrypted-conference-tester.cpp b/tester/local-encrypted-conference-tester.cpp index 08e243f999db66966a06c51fd00951ef17f6ca45..2bd0225d072c344f38bafd7d1aeef64bb300a521 100644 --- a/tester/local-encrypted-conference-tester.cpp +++ b/tester/local-encrypted-conference-tester.cpp @@ -30,10 +30,11 @@ using namespace std; using namespace LinphonePrivate; -using namespace LinphoneTest; // ----------------------------------------------------------------------------- +namespace LinphoneTest { + enum class EktXmlContent { FirstNotify, // sSPI SpiInfo, // sSPI + cSPI @@ -73,7 +74,8 @@ static void ekt_xml_composing_parsing_test(EktXmlContent exc) { } } - string xmlBody = L_GET_CPP_PTR_FROM_C_OBJECT(marie->lc)->createXmlFromEktInfo(ei); + string xmlBody = L_GET_CPP_PTR_FROM_C_OBJECT(marie->lc)->createXmlFromEktInfo( + ei, Account::toCpp(marieAccount)->getSharedFromThis()); lInfo() << "Generated XML body : " << endl << xmlBody; auto outputEi = L_GET_CPP_PTR_FROM_C_OBJECT(marie->lc)->createEktInfoFromXml(xmlBody); @@ -116,86 +118,96 @@ static void create_simple_end_to_end_encrypted_conference() { create_conference_base(ms_time(nullptr), -1, FALSE, LinphoneConferenceParticipantListTypeOpen, FALSE, LinphoneMediaEncryptionSRTP, FALSE, LinphoneConferenceLayoutGrid, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, LinphoneMediaDirectionRecvOnly, FALSE, LinphoneConferenceSecurityLevelEndToEnd, - {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE); + {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE, + FALSE); } static void create_simple_end_to_end_encrypted_conference_with_server_restart() { create_conference_base(ms_time(nullptr), -1, FALSE, LinphoneConferenceParticipantListTypeOpen, FALSE, LinphoneMediaEncryptionDTLS, FALSE, LinphoneConferenceLayoutGrid, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, LinphoneMediaDirectionRecvOnly, TRUE, LinphoneConferenceSecurityLevelEndToEnd, - {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE); + {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE, + FALSE); } static void create_simple_end_to_end_encrypted_conference_with_client_restart() { create_conference_base(ms_time(nullptr), -1, FALSE, LinphoneConferenceParticipantListTypeOpen, FALSE, LinphoneMediaEncryptionZRTP, FALSE, LinphoneConferenceLayoutGrid, FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, LinphoneMediaDirectionRecvOnly, TRUE, LinphoneConferenceSecurityLevelEndToEnd, - {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE); + {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE, + FALSE); } static void create_end_to_end_encrypted_conference_with_uninvited_participant() { create_conference_base(ms_time(nullptr), -1, TRUE, LinphoneConferenceParticipantListTypeOpen, TRUE, LinphoneMediaEncryptionSRTP, TRUE, LinphoneConferenceLayoutGrid, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, LinphoneMediaDirectionRecvOnly, TRUE, LinphoneConferenceSecurityLevelEndToEnd, - {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE); + {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE, + FALSE); } static void create_end_to_end_encrypted_conference_with_uninvited_participant_not_allowed() { - create_conference_base(ms_time(nullptr), -1, TRUE, LinphoneConferenceParticipantListTypeClosed, FALSE, - LinphoneMediaEncryptionDTLS, FALSE, LinphoneConferenceLayoutActiveSpeaker, FALSE, FALSE, - FALSE, FALSE, FALSE, FALSE, LinphoneMediaDirectionSendRecv, FALSE, - LinphoneConferenceSecurityLevelEndToEnd, - {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE); + create_conference_base( + ms_time(nullptr), -1, TRUE, LinphoneConferenceParticipantListTypeClosed, FALSE, LinphoneMediaEncryptionDTLS, + FALSE, LinphoneConferenceLayoutActiveSpeaker, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, + LinphoneMediaDirectionSendRecv, FALSE, LinphoneConferenceSecurityLevelEndToEnd, + {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE, FALSE); } static void create_end_to_end_encrypted_conference_starting_immediately() { create_conference_base(ms_time(nullptr), 0, FALSE, LinphoneConferenceParticipantListTypeClosed, FALSE, LinphoneMediaEncryptionZRTP, FALSE, LinphoneConferenceLayoutGrid, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, LinphoneMediaDirectionRecvOnly, FALSE, LinphoneConferenceSecurityLevelEndToEnd, - {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE); + {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE, + FALSE); } static void create_end_to_end_encrypted_conference_starting_in_the_past() { - create_conference_base(ms_time(nullptr) - 640, 11, FALSE, LinphoneConferenceParticipantListTypeClosed, TRUE, - LinphoneMediaEncryptionSRTP, FALSE, LinphoneConferenceLayoutActiveSpeaker, FALSE, FALSE, - FALSE, FALSE, FALSE, FALSE, LinphoneMediaDirectionSendRecv, FALSE, - LinphoneConferenceSecurityLevelEndToEnd, - {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE); + create_conference_base( + ms_time(nullptr) - 640, 11, FALSE, LinphoneConferenceParticipantListTypeClosed, TRUE, + LinphoneMediaEncryptionSRTP, FALSE, LinphoneConferenceLayoutActiveSpeaker, FALSE, FALSE, FALSE, FALSE, FALSE, + FALSE, LinphoneMediaDirectionSendRecv, FALSE, LinphoneConferenceSecurityLevelEndToEnd, + {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE, FALSE); } static void create_simple_end_to_end_encrypted_conference_with_audio_only_participant() { create_conference_base(ms_time(nullptr), -1, FALSE, LinphoneConferenceParticipantListTypeOpen, FALSE, LinphoneMediaEncryptionDTLS, TRUE, LinphoneConferenceLayoutGrid, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, LinphoneMediaDirectionSendRecv, FALSE, LinphoneConferenceSecurityLevelEndToEnd, - {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE); + {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE, + FALSE); } static void create_end_to_end_encrypted_conference_with_audio_only_and_uninvited_participant() { create_conference_base(ms_time(nullptr), -1, TRUE, LinphoneConferenceParticipantListTypeOpen, TRUE, LinphoneMediaEncryptionZRTP, TRUE, LinphoneConferenceLayoutGrid, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, LinphoneMediaDirectionSendRecv, FALSE, LinphoneConferenceSecurityLevelEndToEnd, - {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE); + {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE, + FALSE); } static void create_simple_end_to_end_encrypted_conference_with_audio_only_participant_enabling_video() { create_conference_base(ms_time(nullptr), -1, FALSE, LinphoneConferenceParticipantListTypeOpen, FALSE, LinphoneMediaEncryptionSRTP, TRUE, LinphoneConferenceLayoutGrid, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, LinphoneMediaDirectionSendRecv, FALSE, LinphoneConferenceSecurityLevelEndToEnd, - {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE); + {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE, + FALSE); } static void create_simple_end_to_end_encrypted_ice_conference() { create_conference_base(ms_time(nullptr), -1, TRUE, LinphoneConferenceParticipantListTypeOpen, TRUE, LinphoneMediaEncryptionDTLS, TRUE, LinphoneConferenceLayoutGrid, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, LinphoneMediaDirectionRecvOnly, FALSE, LinphoneConferenceSecurityLevelEndToEnd, - {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE); + {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE, + FALSE); } static void create_end_to_end_encrypted_conference_terminate_call_on_version_mismatch() { create_conference_base(ms_time(nullptr), -1, FALSE, LinphoneConferenceParticipantListTypeOpen, FALSE, LinphoneMediaEncryptionSRTP, FALSE, LinphoneConferenceLayoutGrid, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, LinphoneMediaDirectionRecvOnly, FALSE, LinphoneConferenceSecurityLevelEndToEnd, - {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, TRUE, FALSE); + {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, TRUE, FALSE, + FALSE); } static void create_end_to_end_encrypted_conference_with_late_participant_addition() { @@ -214,9 +226,19 @@ static void create_simple_end_to_end_encrypted_conference_dial_out() { 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, LinphoneConferenceLayoutActiveSpeaker, pol, FALSE, FALSE, + create_conference_dial_out_base(LinphoneConferenceLayoutActiveSpeaker, pol, FALSE, FALSE, + LinphoneConferenceParticipantListTypeClosed, TRUE, FALSE, + LinphoneConferenceSecurityLevelEndToEnd, FALSE, FALSE); + linphone_video_activation_policy_unref(pol); +} + +static void create_simple_end_to_end_encrypted_conference_dial_out_with_chat() { + 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(LinphoneConferenceLayoutActiveSpeaker, pol, FALSE, FALSE, LinphoneConferenceParticipantListTypeClosed, TRUE, FALSE, - LinphoneConferenceSecurityLevelEndToEnd, FALSE); + LinphoneConferenceSecurityLevelEndToEnd, FALSE, TRUE); linphone_video_activation_policy_unref(pol); } @@ -224,9 +246,9 @@ static void create_end_to_end_encrypted_conference_dial_out_terminate_call_on_ve 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, LinphoneConferenceLayoutActiveSpeaker, pol, FALSE, FALSE, + create_conference_dial_out_base(LinphoneConferenceLayoutActiveSpeaker, pol, FALSE, FALSE, LinphoneConferenceParticipantListTypeClosed, TRUE, FALSE, - LinphoneConferenceSecurityLevelEndToEnd, TRUE); + LinphoneConferenceSecurityLevelEndToEnd, TRUE, FALSE); linphone_video_activation_policy_unref(pol); } @@ -252,7 +274,7 @@ static void create_simple_end_to_end_encrypted_conference_with_screen_sharing() static void connection_method_check(LinphoneConference *conference, LinphoneParticipantDevice *participant_device) { LinphoneConferenceCbs *cbs = linphone_conference_get_current_callbacks(conference); - auto berthe = (LinphoneCoreManager *)linphone_conference_cbs_get_user_data(cbs); + auto berthe = static_cast<LinphoneCoreManager *>(linphone_conference_cbs_get_user_data(cbs)); LinphoneParticipantDeviceJoiningMethod expected_joining_method = LinphoneParticipantDeviceJoiningMethodDialedIn; if (linphone_address_weak_equal(berthe->identity, linphone_participant_device_get_address(participant_device))) { expected_joining_method = LinphoneParticipantDeviceJoiningMethodDialedOut; @@ -307,11 +329,11 @@ static void create_simple_end_to_end_encrypted_conference_with_participant_added Focus focus("chloe_rc"); { // to make sure focus is destroyed after clients. bool enable_lime = true; - LinphoneTest::ClientConference marie("marie_rc", focus.getConferenceFactoryAddress(), enable_lime); - LinphoneTest::ClientConference pauline("pauline_rc", focus.getConferenceFactoryAddress(), enable_lime); - LinphoneTest::ClientConference laure("laure_tcp_rc", focus.getConferenceFactoryAddress(), enable_lime); - LinphoneTest::ClientConference michelle("michelle_rc", focus.getConferenceFactoryAddress(), enable_lime); - LinphoneTest::ClientConference berthe("berthe_rc", focus.getConferenceFactoryAddress(), enable_lime); + ClientConference marie("marie_rc", focus.getConferenceFactoryAddress(), enable_lime); + ClientConference pauline("pauline_rc", focus.getConferenceFactoryAddress(), enable_lime); + ClientConference laure("laure_tcp_rc", focus.getConferenceFactoryAddress(), enable_lime); + ClientConference michelle("michelle_rc", focus.getConferenceFactoryAddress(), enable_lime); + ClientConference berthe("berthe_rc", focus.getConferenceFactoryAddress(), enable_lime); focus.registerAsParticipantDevice(marie); focus.registerAsParticipantDevice(pauline); @@ -392,7 +414,7 @@ static void create_simple_end_to_end_encrypted_conference_with_participant_added LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, initialSubject, - description, TRUE, LinphoneConferenceSecurityLevelEndToEnd, TRUE, FALSE); + description, TRUE, LinphoneConferenceSecurityLevelEndToEnd, TRUE, FALSE, NULL); BC_ASSERT_PTR_NOT_NULL(confAddr); char *conference_address_str = (confAddr) ? linphone_address_as_string(confAddr) : ms_strdup("sip:"); @@ -872,16 +894,27 @@ static void create_simple_end_to_end_encrypted_conference_with_participant_added } } -static void create_simple_end_to_end_encrypted_conference_with_participant_added_by_admin_call_accepted(void) { +static void create_simple_end_to_end_encrypted_conference_with_participant_added_by_admin_call_accepted() { create_simple_end_to_end_encrypted_conference_with_participant_added_by_admin(true); } -static void create_simple_end_to_end_encrypted_conference_with_participant_added_by_admin_call_declined(void) { +static void create_simple_end_to_end_encrypted_conference_with_participant_added_by_admin_call_declined() { create_simple_end_to_end_encrypted_conference_with_participant_added_by_admin(false); } -static void create_encrypted_conference_with_chat(void) { - create_conference_with_chat_base(LinphoneConferenceSecurityLevelEndToEnd, FALSE, FALSE, TRUE, 1, FALSE, FALSE); +static void create_encrypted_conference_with_chat() { + create_conference_with_chat_base(LinphoneConferenceSecurityLevelEndToEnd, FALSE, FALSE, TRUE, 1, FALSE, TRUE, FALSE, + ms_time(NULL), FALSE); +} + +static void create_encrypted_conference_with_chat_and_cores_restart(void) { + create_conference_with_chat_base(LinphoneConferenceSecurityLevelEndToEnd, TRUE, TRUE, TRUE, 1, FALSE, TRUE, FALSE, + (ms_time(NULL) - 45), FALSE); +} + +static void create_encrypted_conference_with_chat_network_drops_and_participant_rejoining(void) { + create_conference_with_chat_base(LinphoneConferenceSecurityLevelEndToEnd, FALSE, FALSE, FALSE, -1, TRUE, TRUE, TRUE, + (ms_time(NULL) - 45), TRUE); } static void scheduling_failure_check(LinphoneConferenceScheduler *scheduler, LinphoneConferenceSchedulerState state) { @@ -900,8 +933,8 @@ static void scheduling_failure_check(LinphoneConferenceScheduler *scheduler, Lin static void failure_in_creating_end_to_end_encrypted_conference_bad_server_config() { Focus focus("chloe_rc"); { // to make sure focus is destroyed after clients. - LinphoneTest::ClientConference marie("marie_rc", focus.getConferenceFactoryAddress(), TRUE); - LinphoneTest::ClientConference pauline("pauline_rc", focus.getConferenceFactoryAddress(), TRUE); + ClientConference marie("marie_rc", focus.getConferenceFactoryAddress(), TRUE); + ClientConference pauline("pauline_rc", focus.getConferenceFactoryAddress(), TRUE); focus.registerAsParticipantDevice(marie); focus.registerAsParticipantDevice(pauline); @@ -999,87 +1032,255 @@ static void failure_in_creating_end_to_end_encrypted_conference_bad_server_confi } } +static void create_simple_end_to_end_encrypted_conference_terminated_early() { + Focus focus("chloe_rc"); + { // to make sure focus is destroyed after clients. + bool enable_lime = true; + ClientConference marie("marie_rc", focus.getConferenceFactoryAddress(), enable_lime); + ClientConference pauline("pauline_rc", focus.getConferenceFactoryAddress(), enable_lime); + + focus.registerAsParticipantDevice(marie); + focus.registerAsParticipantDevice(pauline); + + setup_conference_info_cbs(marie.getCMgr()); + LinphoneMediaEncryption encryption = LinphoneMediaEncryptionZRTP; + + bctbx_list_t *coresList = nullptr; + + for (auto mgr : {focus.getCMgr(), marie.getCMgr(), 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 (mgr != focus.getCMgr()) { + linphone_core_set_default_conference_layout(mgr->lc, LinphoneConferenceLayoutActiveSpeaker); + linphone_core_set_media_encryption(mgr->lc, encryption); + } + + // Enable ICE at the account level but not at the core level + enable_stun_in_mgr(mgr, TRUE, TRUE, FALSE, FALSE); + + 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); + } + + configure_end_to_end_encrypted_conference_server(focus); + + linphone_core_set_file_transfer_server(marie.getLc(), file_transfer_url); + linphone_core_set_conference_participant_list_type(focus.getLc(), LinphoneConferenceParticipantListTypeClosed); + + BC_ASSERT_TRUE(linphone_core_lime_x3dh_enabled(marie.getLc())); + BC_ASSERT_TRUE(linphone_core_lime_x3dh_enabled(pauline.getLc())); + + stats focus_stat = focus.getStats(); + + std::list<LinphoneCoreManager *> participants{pauline.getCMgr()}; + std::list<LinphoneCoreManager *> conferenceMgrs{focus.getCMgr(), marie.getCMgr(), pauline.getCMgr()}; + std::list<LinphoneCoreManager *> members{marie.getCMgr(), pauline.getCMgr()}; + + time_t start_time = ms_time(nullptr); + time_t end_time = -1; + const char *initialSubject = "E2E conference - short duration"; + const char *description = "Quick end"; + + bctbx_list_t *participants_info = nullptr; + std::map<LinphoneCoreManager *, LinphoneParticipantInfo *> participantList; + participantList.insert(std::make_pair( + pauline.getCMgr(), add_participant_info_to_list(&participants_info, pauline.getCMgr()->identity, + LinphoneParticipantRoleSpeaker, -1))); + + LinphoneAddress *confAddr = + create_conference_on_server(focus, marie, participantList, start_time, end_time, initialSubject, + description, TRUE, LinphoneConferenceSecurityLevelEndToEnd, TRUE, TRUE, NULL); + BC_ASSERT_PTR_NOT_NULL(confAddr); + char *conference_address_str = (confAddr) ? linphone_address_as_string(confAddr) : ms_strdup("sip:"); + + // Chat room creation to send ICS + BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneChatRoomStateCreated, 1, + liblinphone_tester_sip_timeout)); + + LinphoneCallParams *new_params = linphone_core_create_call_params(pauline.getLc(), nullptr); + linphone_call_params_set_media_encryption(new_params, encryption); + linphone_call_params_set_video_direction(new_params, LinphoneMediaDirectionSendRecv); + ms_message("%s is entering conference %s", linphone_core_get_identity(pauline.getLc()), conference_address_str); + linphone_core_invite_address_with_params_2(pauline.getLc(), confAddr, new_params, nullptr, nullptr); + linphone_call_params_unref(new_params); + LinphoneCall *pauline_call = linphone_core_get_call_by_remote_address2(pauline.getLc(), confAddr); + BC_ASSERT_PTR_NOT_NULL(pauline_call); + if (pauline_call) { + LinphoneCallLog *call_log = linphone_call_get_call_log(pauline_call); + BC_ASSERT_TRUE(linphone_call_log_was_conference(call_log)); + } + + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneCallOutgoingProgress, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneCallStreamsRunning, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE( + wait_for_list(coresList, &pauline.getStats().number_of_LinphoneSubscriptionOutgoingProgress, 2, 5000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneSubscriptionActive, 2, 5000)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_NotifyFullStateReceived, 1, + liblinphone_tester_sip_timeout)); + + ms_message("%s terminates the call to conference %s", linphone_core_get_identity(pauline.getLc()), + conference_address_str); + LinphoneConference *pauline_conference = linphone_core_search_conference_2(pauline.getLc(), confAddr); + BC_ASSERT_PTR_NOT_NULL(pauline_conference); + linphone_conference_terminate(pauline_conference); + BC_ASSERT_TRUE( + wait_for_list(coresList, &pauline.getStats().number_of_LinphoneCallEnd, 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.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 + 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, &pauline.getStats().number_of_LinphoneSubscriptionTerminated, 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneConferenceStateTerminationPending, + 1, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneConferenceStateTerminated, 1, + liblinphone_tester_sip_timeout)); + // The conference doesn't go through the Deleted state as it has chat capabilities + BC_ASSERT_PTR_NOT_NULL(linphone_core_search_conference_2(pauline.getLc(), confAddr)); + + bctbx_list_free_with_data(participants_info, (bctbx_list_free_func)linphone_participant_info_unref); + ms_free(conference_address_str); + linphone_address_unref(confAddr); + bctbx_list_free(coresList); + } +} + +static void create_simple_end_to_end_encrypted_conference_merging_calls() { + create_simple_conference_merging_calls_base(FALSE, LinphoneConferenceLayoutActiveSpeaker, FALSE, FALSE, FALSE, + LinphoneConferenceSecurityLevelEndToEnd, FALSE); +} + +static void encrypted_conference_joined_multiple_times(void) { + conference_joined_multiple_times(LinphoneConferenceSecurityLevelEndToEnd, FALSE, -1); +} + +} // namespace LinphoneTest + static test_t local_conference_end_to_end_encryption_scheduled_conference_tests[] = { - TEST_ONE_TAG("First notify", first_notify_ekt_xml_composing_parsing_test, "End2EndConf"), - TEST_ONE_TAG("SPI info", spi_info_ekt_xml_composing_parsing_test, "End2EndConf"), - TEST_ONE_TAG("Cipher transport", cipher_transport_ekt_xml_composing_parsing_test, "End2EndConf"), - TEST_ONE_TAG( - "Create simple end-to-end encrypted conference", create_simple_end_to_end_encrypted_conference, "End2EndConf"), + TEST_ONE_TAG("First notify", LinphoneTest::first_notify_ekt_xml_composing_parsing_test, "End2EndConf"), + TEST_ONE_TAG("SPI info", LinphoneTest::spi_info_ekt_xml_composing_parsing_test, "End2EndConf"), + TEST_ONE_TAG("Cipher transport", LinphoneTest::cipher_transport_ekt_xml_composing_parsing_test, "End2EndConf"), + TEST_ONE_TAG("End-to-End Conference joined multiple times", + LinphoneTest::encrypted_conference_joined_multiple_times, + "End2EndConf"), + TEST_ONE_TAG("Create simple end-to-end encrypted conference", + LinphoneTest::create_simple_end_to_end_encrypted_conference, + "End2EndConf"), TEST_TWO_TAGS("Create simple end-to-end encrypted conference with server restart", - create_simple_end_to_end_encrypted_conference_with_server_restart, + LinphoneTest::create_simple_end_to_end_encrypted_conference_with_server_restart, "LeaksMemory", "End2EndConf"), TEST_TWO_TAGS("Create simple end-to-end encrypted conference with client restart", - create_simple_end_to_end_encrypted_conference_with_client_restart, + LinphoneTest::create_simple_end_to_end_encrypted_conference_with_client_restart, "LeaksMemory", "End2EndConf"), TEST_TWO_TAGS("Create simple end-to-end encrypted ICE conference", - create_simple_end_to_end_encrypted_ice_conference, + LinphoneTest::create_simple_end_to_end_encrypted_ice_conference, "ICE", "End2EndConf"), TEST_ONE_TAG("Create simple end-to-end encrypted conference with screen sharing override", - create_simple_end_to_end_encrypted_conference_with_screen_sharing, + LinphoneTest::create_simple_end_to_end_encrypted_conference_with_screen_sharing, "End2EndConf"), TEST_ONE_TAG("Create end-to-end encrypted conference with uninvited participant", - create_end_to_end_encrypted_conference_with_uninvited_participant, + LinphoneTest::create_end_to_end_encrypted_conference_with_uninvited_participant, "End2EndConf"), TEST_ONE_TAG("Create end-to-end encrypted conference with uninvited participant not allowed", - create_end_to_end_encrypted_conference_with_uninvited_participant_not_allowed, + LinphoneTest::create_end_to_end_encrypted_conference_with_uninvited_participant_not_allowed, "End2EndConf"), TEST_ONE_TAG("Create end-to-end encrypted conference with late participant addition declined", - create_end_to_end_encrypted_conference_with_late_participant_addition_declined, + LinphoneTest::create_end_to_end_encrypted_conference_with_late_participant_addition_declined, "End2EndConf"), TEST_ONE_TAG("Create end-to-end encrypted conference starting immediately", - create_end_to_end_encrypted_conference_starting_immediately, + LinphoneTest::create_end_to_end_encrypted_conference_starting_immediately, "End2EndConf"), TEST_ONE_TAG("Create end-to-end encrypted conference starting in the past", - create_end_to_end_encrypted_conference_starting_in_the_past, + LinphoneTest::create_end_to_end_encrypted_conference_starting_in_the_past, "End2EndConf"), TEST_ONE_TAG("Create end-to-end encrypted conference with late participant addition", - create_end_to_end_encrypted_conference_with_late_participant_addition, + LinphoneTest::create_end_to_end_encrypted_conference_with_late_participant_addition, "End2EndConf"), TEST_ONE_TAG("Create end-to-end encrypted conference terminate call on version mismatch", - create_end_to_end_encrypted_conference_terminate_call_on_version_mismatch, + LinphoneTest::create_end_to_end_encrypted_conference_terminate_call_on_version_mismatch, "End2EndConf"), - TEST_ONE_TAG("Create simple end-to-end encrypted conference with participant added by admin call accepted", - create_simple_end_to_end_encrypted_conference_with_participant_added_by_admin_call_accepted, + TEST_ONE_TAG( + "Create simple end-to-end encrypted conference with participant added by admin call accepted", + LinphoneTest::create_simple_end_to_end_encrypted_conference_with_participant_added_by_admin_call_accepted, + "End2EndConf"), + TEST_ONE_TAG( + "Create simple end-to-end encrypted conference with participant added by admin call declined", + LinphoneTest::create_simple_end_to_end_encrypted_conference_with_participant_added_by_admin_call_declined, + "End2EndConf"), + TEST_ONE_TAG("Failure in creating end-to-end encrypted conference bad server config", + LinphoneTest::failure_in_creating_end_to_end_encrypted_conference_bad_server_config, "End2EndConf"), - TEST_ONE_TAG("Create simple end-to-end encrypted conference with participant added by admin call declined", - create_simple_end_to_end_encrypted_conference_with_participant_added_by_admin_call_declined, + TEST_ONE_TAG("Create simple encrypted conference terminated early", + LinphoneTest::create_simple_end_to_end_encrypted_conference_terminated_early, "End2EndConf"), - TEST_ONE_TAG("Create encrypted conference with chat", create_encrypted_conference_with_chat, "End2EndConf"), - TEST_ONE_TAG("Failure in creating end-to-end encrypted conference bad server config", - failure_in_creating_end_to_end_encrypted_conference_bad_server_config, +}; + +static test_t local_conference_end_to_end_encryption_scheduled_conference_with_chat_tests[] = { + TEST_ONE_TAG( + "Create encrypted conference with chat", LinphoneTest::create_encrypted_conference_with_chat, "End2EndConf"), + TEST_TWO_TAGS("Create encrypted conference with chat and cores restart", + LinphoneTest::create_encrypted_conference_with_chat_and_cores_restart, + "LeaksMemory", + "End2EndConf"), + TEST_ONE_TAG("Create encrypted conference with chat network drops and participant rejoining", + LinphoneTest::create_encrypted_conference_with_chat_network_drops_and_participant_rejoining, + "End2EndConf"), + TEST_ONE_TAG("Create simple end-to-end encrypted dial out conference with chat", + LinphoneTest::create_simple_end_to_end_encrypted_conference_dial_out_with_chat, "End2EndConf"), }; static test_t local_conference_end_to_end_encryption_scheduled_conference_audio_only_participant_tests[] = { TEST_ONE_TAG("Create simple end-to-end encrypted conference with audio only participant", - create_simple_end_to_end_encrypted_conference_with_audio_only_participant, + LinphoneTest::create_simple_end_to_end_encrypted_conference_with_audio_only_participant, "End2EndConf"), TEST_ONE_TAG("Create end-to-end encrypted conference with audio only and uninvited participant", - create_end_to_end_encrypted_conference_with_audio_only_and_uninvited_participant, + LinphoneTest::create_end_to_end_encrypted_conference_with_audio_only_and_uninvited_participant, "End2EndConf"), TEST_ONE_TAG("Create simple end-to-end encrypted conference with audio only participant enabling video", - create_simple_end_to_end_encrypted_conference_with_audio_only_participant_enabling_video, + LinphoneTest::create_simple_end_to_end_encrypted_conference_with_audio_only_participant_enabling_video, "End2EndConf"), TEST_ONE_TAG("Create end-to-end encrypted conference with audio only participants", - create_end_to_end_encryption_conference_with_audio_only_participants, + LinphoneTest::create_end_to_end_encryption_conference_with_audio_only_participants, "End2EndConf"), }; static test_t local_conference_end_to_end_encryption_impromptu_conference_tests[] = { TEST_ONE_TAG("Create simple end-to-end encrypted dial out conference", - create_simple_end_to_end_encrypted_conference_dial_out, + LinphoneTest::create_simple_end_to_end_encrypted_conference_dial_out, "End2EndConf"), TEST_ONE_TAG("Create simple end-to-end encrypted conference dial out with some calls declined", - create_simple_end_to_end_encrypted_conference_dial_out_with_some_calls_declined, + LinphoneTest::create_simple_end_to_end_encrypted_conference_dial_out_with_some_calls_declined, "End2EndConf"), TEST_ONE_TAG("Create simple end-to-end encrypted conference dial out with some calls busy", - create_simple_end_to_end_encrypted_conference_dial_out_with_some_calls_busy, + LinphoneTest::create_simple_end_to_end_encrypted_conference_dial_out_with_some_calls_busy, "End2EndConf"), TEST_ONE_TAG("Create end-to-end encrypted dial out conference terminate call on version mismatch", - create_end_to_end_encrypted_conference_dial_out_terminate_call_on_version_mismatch, + LinphoneTest::create_end_to_end_encrypted_conference_dial_out_terminate_call_on_version_mismatch, + "End2EndConf"), + TEST_ONE_TAG("Create simple end-to-end encrypted conference by merging calls", + LinphoneTest::create_simple_end_to_end_encrypted_conference_merging_calls, "End2EndConf"), }; @@ -1107,6 +1308,19 @@ test_suite_t local_conference_test_suite_end_to_end_encryption_scheduled_confere 0, 4}; +test_suite_t local_conference_test_suite_end_to_end_encryption_scheduled_conference_with_chat = { + "Local conference tester (End to end encryption Conference with chat)", + NULL, + NULL, + liblinphone_tester_before_each, + liblinphone_tester_after_each, + sizeof(local_conference_end_to_end_encryption_scheduled_conference_with_chat_tests) / + sizeof(local_conference_end_to_end_encryption_scheduled_conference_with_chat_tests[0]), + local_conference_end_to_end_encryption_scheduled_conference_with_chat_tests, + 0, + 4 /*cpu_weight : video conference uses more resources */ +}; + test_suite_t local_conference_test_suite_end_to_end_encryption_impromptu_conference = { "Local conference tester (Impromptu Conference End to end encryption)", nullptr, diff --git a/tester/local-ice-conference-tester.cpp b/tester/local-ice-conference-tester.cpp index 1f44240a8fb12aef5fd70c1ba3b29d6e2a829914..d51c1fd8c40124480cd4c0b597ba539ed334f95f 100644 --- a/tester/local-ice-conference-tester.cpp +++ b/tester/local-ice-conference-tester.cpp @@ -32,65 +32,72 @@ 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, LinphoneConferenceSecurityLevelNone, - {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE); + {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE, + FALSE); } 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, LinphoneConferenceSecurityLevelNone, - {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, TRUE); + {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, TRUE, + 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, LinphoneConferenceSecurityLevelNone, - {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE); + {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE, + 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, LinphoneConferenceSecurityLevelNone, - {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE); + {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE, + 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, - LinphoneConferenceSecurityLevelNone, - {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE); + create_conference_base( + ms_time(NULL), -1, FALSE, LinphoneConferenceParticipantListTypeOpen, FALSE, LinphoneMediaEncryptionSRTP, TRUE, + LinphoneConferenceLayoutActiveSpeaker, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, LinphoneMediaDirectionSendRecv, + FALSE, LinphoneConferenceSecurityLevelNone, {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, + FALSE, FALSE, FALSE, 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, LinphoneConferenceSecurityLevelNone, - {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE); + {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE, + 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, LinphoneConferenceSecurityLevelNone, - {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE); + {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE, + 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, LinphoneConferenceSecurityLevelNone, - {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE); + {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE, + FALSE); } static void create_simple_point_to_point_encrypted_ice_conference(void) { - create_conference_base(ms_time(NULL), -1, TRUE, LinphoneConferenceParticipantListTypeOpen, FALSE, - LinphoneMediaEncryptionNone, TRUE, LinphoneConferenceLayoutGrid, TRUE, FALSE, FALSE, FALSE, - FALSE, FALSE, LinphoneMediaDirectionRecvOnly, FALSE, - LinphoneConferenceSecurityLevelPointToPoint, - {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, TRUE); + create_conference_base( + ms_time(NULL), -1, TRUE, LinphoneConferenceParticipantListTypeOpen, FALSE, LinphoneMediaEncryptionNone, TRUE, + LinphoneConferenceLayoutGrid, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, LinphoneMediaDirectionRecvOnly, FALSE, + LinphoneConferenceSecurityLevelPointToPoint, {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, + FALSE, FALSE, TRUE, FALSE); } static void create_simple_ice_conference_merging_calls(void) { @@ -159,7 +166,7 @@ static void abort_call_to_ice_conference(void) { } LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, initialSubject, - description, TRUE, security_level, FALSE, FALSE); + description, TRUE, security_level, FALSE, FALSE, NULL); BC_ASSERT_PTR_NOT_NULL(confAddr); char *confAddrStr = (confAddr) ? linphone_address_as_string(confAddr) : NULL; @@ -337,10 +344,7 @@ static void abort_call_to_ice_conference(void) { 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)); + check_conference_me(pconference, ((mgr == marie.getCMgr()) || (mgr == focus.getCMgr()))); 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); diff --git a/tester/local-impromptu-conference-tester.cpp b/tester/local-impromptu-conference-tester.cpp index 2fc54f4d8b24188f098e90f74435c332b90bfd06..7a8c12cdca7cd893fbeaea8b5ecbaa1cafe19bc7 100644 --- a/tester/local-impromptu-conference-tester.cpp +++ b/tester/local-impromptu-conference-tester.cpp @@ -29,19 +29,29 @@ 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, + create_conference_dial_out_base(LinphoneConferenceLayoutActiveSpeaker, pol, FALSE, FALSE, LinphoneConferenceParticipantListTypeClosed, TRUE, FALSE, - LinphoneConferenceSecurityLevelNone, FALSE); + LinphoneConferenceSecurityLevelNone, FALSE, FALSE); linphone_video_activation_policy_unref(pol); } -static void create_simple_conference_dial_out_and_ics(void) { +static void create_simple_conference_dial_out_with_chat(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(LinphoneConferenceLayoutActiveSpeaker, pol, FALSE, FALSE, + LinphoneConferenceParticipantListTypeClosed, TRUE, FALSE, + LinphoneConferenceSecurityLevelNone, FALSE, TRUE); + linphone_video_activation_policy_unref(pol); +} + +static void create_simple_ice_conference_dial_out(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, + create_conference_dial_out_base(LinphoneConferenceLayoutGrid, pol, TRUE, TRUE, LinphoneConferenceParticipantListTypeOpen, TRUE, FALSE, - LinphoneConferenceSecurityLevelNone, FALSE); + LinphoneConferenceSecurityLevelNone, FALSE, FALSE); linphone_video_activation_policy_unref(pol); } @@ -49,9 +59,9 @@ 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, + create_conference_dial_out_base(LinphoneConferenceLayoutGrid, pol, TRUE, TRUE, LinphoneConferenceParticipantListTypeOpen, FALSE, FALSE, - LinphoneConferenceSecurityLevelNone, FALSE); + LinphoneConferenceSecurityLevelNone, FALSE, FALSE); linphone_video_activation_policy_unref(pol); } @@ -59,9 +69,9 @@ static void create_simple_point_to_point_encrypted_conference_dial_out(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, LinphoneConferenceLayoutActiveSpeaker, pol, FALSE, FALSE, + create_conference_dial_out_base(LinphoneConferenceLayoutActiveSpeaker, pol, FALSE, FALSE, LinphoneConferenceParticipantListTypeClosed, TRUE, FALSE, - LinphoneConferenceSecurityLevelPointToPoint, FALSE); + LinphoneConferenceSecurityLevelPointToPoint, FALSE, FALSE); linphone_video_activation_policy_unref(pol); } @@ -69,9 +79,9 @@ 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, + create_conference_dial_out_base(LinphoneConferenceLayoutActiveSpeaker, pol, FALSE, FALSE, LinphoneConferenceParticipantListTypeClosed, TRUE, TRUE, - LinphoneConferenceSecurityLevelNone, FALSE); + LinphoneConferenceSecurityLevelNone, FALSE, FALSE); linphone_video_activation_policy_unref(pol); } @@ -79,9 +89,9 @@ 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, + create_conference_dial_out_base(LinphoneConferenceLayoutActiveSpeaker, pol, FALSE, FALSE, LinphoneConferenceParticipantListTypeClosed, TRUE, FALSE, - LinphoneConferenceSecurityLevelNone, FALSE); + LinphoneConferenceSecurityLevelNone, FALSE, FALSE); linphone_video_activation_policy_unref(pol); } @@ -89,9 +99,9 @@ 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, + create_conference_dial_out_base(LinphoneConferenceLayoutGrid, pol, FALSE, FALSE, LinphoneConferenceParticipantListTypeClosed, TRUE, FALSE, - LinphoneConferenceSecurityLevelNone, FALSE); + LinphoneConferenceSecurityLevelNone, FALSE, FALSE); linphone_video_activation_policy_unref(pol); } @@ -162,7 +172,7 @@ static void create_simple_conference_dial_out_organizer_codec_mismatch(void) { : LinphoneParticipantRoleSpeaker; } LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, -1, -1, initialSubject, - description, TRUE, security_level, TRUE, FALSE); + description, TRUE, security_level, TRUE, FALSE, NULL); BC_ASSERT_PTR_NULL(confAddr); // Chat room creation to send ICS @@ -286,7 +296,7 @@ static void simple_dial_out_conference_with_no_payloads(void) { : LinphoneParticipantRoleSpeaker; } LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, -1, -1, initialSubject, - description, FALSE, security_level, TRUE, FALSE); + description, FALSE, security_level, TRUE, FALSE, NULL); BC_ASSERT_PTR_NOT_NULL(confAddr); // Chat room creation to send ICS @@ -463,7 +473,7 @@ static void create_conference_dial_out_with_video_activation_and_layout_change(v } LinphoneConferenceSecurityLevel security_level = LinphoneConferenceSecurityLevelNone; LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, -1, -1, initialSubject, - description, FALSE, security_level, TRUE, FALSE); + description, FALSE, security_level, TRUE, FALSE, NULL); BC_ASSERT_PTR_NOT_NULL(confAddr); BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneConferenceStateCreated, 1, @@ -762,10 +772,7 @@ static void create_conference_dial_out_with_video_activation_and_layout_change(v 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)); + check_conference_me(pconference, ((mgr == marie.getCMgr()) || (mgr == focus.getCMgr()))); 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); @@ -1016,8 +1023,9 @@ static test_t local_conference_encrypted_impromptu_conference_tests[] = { static test_t local_conference_impromptu_conference_tests[] = { TEST_NO_TAG("Create simple dial out conference", LinphoneTest::create_simple_conference_dial_out), - TEST_NO_TAG("Create simple dial out ICE conference and ICS sent", - LinphoneTest::create_simple_conference_dial_out_and_ics), + TEST_NO_TAG("Create simple dial out conference with chat", + LinphoneTest::create_simple_conference_dial_out_with_chat), + TEST_NO_TAG("Create simple dial out ICE conference", LinphoneTest::create_simple_ice_conference_dial_out), 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", @@ -1056,7 +1064,7 @@ static test_t local_conference_impromptu_mismatch_conference_tests[] = { LinphoneTest::simple_dial_out_conference_with_no_payloads)}; test_suite_t local_conference_test_suite_encrypted_impromptu_conference = { - "Local conference tester (Impromptu Encrypted Conference)", + "Local conference tester (Impromptu Encrypted Conference Point to Point Encryption)", NULL, NULL, liblinphone_tester_before_each, diff --git a/tester/local-scheduled-conference-tester.cpp b/tester/local-scheduled-conference-tester.cpp index f269c9b507a4fd9427b3c642e9d20a2cd97e8c69..a18edb287cb8534d13d6565efc45abc0267986fe 100644 --- a/tester/local-scheduled-conference-tester.cpp +++ b/tester/local-scheduled-conference-tester.cpp @@ -97,7 +97,7 @@ static void conference_with_media_lost(void) { LinphoneConferenceSecurityLevel security_level = LinphoneConferenceSecurityLevelNone; LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, initialSubject, - description, TRUE, security_level, TRUE, FALSE); + description, TRUE, security_level, TRUE, FALSE, NULL); BC_ASSERT_PTR_NOT_NULL(confAddr); char *conference_address_str = (confAddr) ? linphone_address_as_string(confAddr) : ms_strdup("sip:"); @@ -338,7 +338,7 @@ static void alone_in_conference_with_chat_exits_enter(void) { LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, initialSubject, - description, TRUE, LinphoneConferenceSecurityLevelNone, TRUE, TRUE); + description, TRUE, LinphoneConferenceSecurityLevelNone, TRUE, TRUE, NULL); BC_ASSERT_PTR_NOT_NULL(confAddr); char *conference_address_str = (confAddr) ? linphone_address_as_string(confAddr) : ms_strdup("sip:"); @@ -804,7 +804,7 @@ static void conference_with_participants_late_except_one(void) { LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, initialSubject, - description, TRUE, LinphoneConferenceSecurityLevelNone, TRUE, FALSE); + description, TRUE, LinphoneConferenceSecurityLevelNone, TRUE, FALSE, NULL); BC_ASSERT_PTR_NOT_NULL(confAddr); char *conference_address_str = (confAddr) ? linphone_address_as_string(confAddr) : ms_strdup("sip:"); @@ -865,6 +865,7 @@ static void conference_with_participants_late_except_one(void) { } } + stats pauline_stat = pauline.getStats(); for (auto mgr : members) { BC_ASSERT_TRUE(wait_for_list(coresList, &mgr->stat.number_of_LinphoneCallOutgoingProgress, 1, liblinphone_tester_sip_timeout)); @@ -914,6 +915,21 @@ static void conference_with_participants_late_except_one(void) { focus_stat.number_of_participant_devices_present + 5, liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_participants_added, + pauline_stat.number_of_participants_added + 4, 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 + 4, + liblinphone_tester_sip_timeout)); + BC_ASSERT_FALSE(wait_for_list(coresList, &pauline.getStats().number_of_participant_devices_added, + pauline_stat.number_of_participant_devices_added + 5, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_conference_participant_devices_present, + pauline_stat.number_of_conference_participant_devices_present + 4, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_participant_devices_present, + pauline_stat.number_of_participant_devices_present + 4, + liblinphone_tester_sip_timeout)); + std::map<LinphoneCoreManager *, LinphoneParticipantInfo *> memberList = fill_member_list(members, participantList, marie.getCMgr(), participants_info); wait_for_conference_streams({focus, marie, pauline, laure, michelle, berthe}, conferenceMgrs, focus.getCMgr(), @@ -1705,129 +1721,137 @@ static void schedule_simple_conference_db_conference_scheduler(void) { } static void create_simple_conference(void) { - create_conference_base(ms_time(NULL), -1, FALSE, LinphoneConferenceParticipantListTypeOpen, FALSE, + create_conference_base(ms_time(NULL), 1, FALSE, LinphoneConferenceParticipantListTypeOpen, FALSE, LinphoneMediaEncryptionNone, FALSE, LinphoneConferenceLayoutGrid, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, LinphoneMediaDirectionRecvOnly, FALSE, LinphoneConferenceSecurityLevelNone, - {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE); + {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE, + FALSE); } static void create_simple_point_to_point_encrypted_conference(void) { - create_conference_base(ms_time(NULL), -1, FALSE, LinphoneConferenceParticipantListTypeOpen, FALSE, - LinphoneMediaEncryptionNone, FALSE, LinphoneConferenceLayoutGrid, FALSE, FALSE, FALSE, FALSE, - FALSE, FALSE, LinphoneMediaDirectionRecvOnly, FALSE, - LinphoneConferenceSecurityLevelPointToPoint, - {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE); + create_conference_base( + ms_time(NULL), -1, FALSE, LinphoneConferenceParticipantListTypeOpen, FALSE, LinphoneMediaEncryptionNone, FALSE, + LinphoneConferenceLayoutGrid, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, LinphoneMediaDirectionRecvOnly, FALSE, + LinphoneConferenceSecurityLevelPointToPoint, {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, + FALSE, FALSE, FALSE, 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, LinphoneConferenceSecurityLevelNone, - {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE); + {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE, + FALSE); } 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, LinphoneConferenceSecurityLevelNone, - {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE); + {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE, + 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, - LinphoneConferenceSecurityLevelNone, - {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE); + create_conference_base( + ms_time(NULL), -1, FALSE, LinphoneConferenceParticipantListTypeOpen, FALSE, LinphoneMediaEncryptionZRTP, TRUE, + LinphoneConferenceLayoutActiveSpeaker, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, LinphoneMediaDirectionRecvOnly, + FALSE, LinphoneConferenceSecurityLevelNone, {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, + FALSE, FALSE, FALSE, 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, - LinphoneConferenceSecurityLevelNone, - {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE); + create_conference_base( + ms_time(NULL), -1, FALSE, LinphoneConferenceParticipantListTypeOpen, FALSE, LinphoneMediaEncryptionDTLS, TRUE, + LinphoneConferenceLayoutActiveSpeaker, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, LinphoneMediaDirectionRecvOnly, + FALSE, LinphoneConferenceSecurityLevelNone, {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, + FALSE, FALSE, FALSE, 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, LinphoneConferenceSecurityLevelNone, - {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE); + {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE, + FALSE); } -static void create_conference_with_only_speakers_and_uninvited_participant(void) { - create_conference_base(ms_time(NULL), -1, TRUE, LinphoneConferenceParticipantListTypeOpen, TRUE, - LinphoneMediaEncryptionNone, TRUE, LinphoneConferenceLayoutActiveSpeaker, FALSE, FALSE, - FALSE, FALSE, FALSE, FALSE, LinphoneMediaDirectionSendRecv, FALSE, - LinphoneConferenceSecurityLevelNone, {LinphoneParticipantRoleSpeaker}, FALSE, FALSE, FALSE); +static void create_conference_with_chat_only_speakers_and_uninvited_participant(void) { + create_conference_base( + ms_time(NULL), -1, TRUE, LinphoneConferenceParticipantListTypeOpen, TRUE, LinphoneMediaEncryptionNone, TRUE, + LinphoneConferenceLayoutActiveSpeaker, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, LinphoneMediaDirectionSendRecv, + FALSE, LinphoneConferenceSecurityLevelNone, {LinphoneParticipantRoleSpeaker}, FALSE, FALSE, FALSE, TRUE); } -/* static void create_conference_with_only_listeners_and_uninvited_participant(void) { - create_conference_base(ms_time(NULL), -1, TRUE, LinphoneConferenceParticipantListTypeOpen, TRUE, - LinphoneMediaEncryptionNone, TRUE, LinphoneConferenceLayoutGrid, FALSE, FALSE, FALSE, FALSE, - FALSE, FALSE, LinphoneMediaDirectionSendRecv, TRUE, LinphoneConferenceSecurityLevelNone, - {LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE); + create_conference_base(ms_time(NULL), -1, TRUE, LinphoneConferenceParticipantListTypeOpen, TRUE, + LinphoneMediaEncryptionNone, TRUE, LinphoneConferenceLayoutGrid, FALSE, FALSE, FALSE, FALSE, + FALSE, FALSE, LinphoneMediaDirectionSendRecv, TRUE, LinphoneConferenceSecurityLevelNone, + {LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE, 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, LinphoneConferenceSecurityLevelNone, - {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE); + {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE, + FALSE); } static void create_conference_with_uninvited_participant_added_after_end(void) { create_conference_base(ms_time(NULL), 1, TRUE, LinphoneConferenceParticipantListTypeOpen, TRUE, LinphoneMediaEncryptionNone, TRUE, LinphoneConferenceLayoutGrid, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, LinphoneMediaDirectionRecvOnly, TRUE, LinphoneConferenceSecurityLevelNone, - {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, TRUE, FALSE, FALSE); + {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, TRUE, FALSE, FALSE, + FALSE); } 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, - LinphoneConferenceSecurityLevelNone, - {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE); + create_conference_base( + ms_time(NULL), -1, TRUE, LinphoneConferenceParticipantListTypeClosed, FALSE, LinphoneMediaEncryptionNone, FALSE, + LinphoneConferenceLayoutActiveSpeaker, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, LinphoneMediaDirectionSendRecv, + FALSE, LinphoneConferenceSecurityLevelNone, {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, + FALSE, FALSE, FALSE, 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, LinphoneConferenceSecurityLevelNone, - {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE); + {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE, + FALSE); } static void create_conference_starting_in_the_past(void) { - create_conference_base(ms_time(NULL) - 640, 11, FALSE, LinphoneConferenceParticipantListTypeClosed, TRUE, - LinphoneMediaEncryptionNone, FALSE, LinphoneConferenceLayoutActiveSpeaker, FALSE, FALSE, - FALSE, FALSE, FALSE, FALSE, LinphoneMediaDirectionSendRecv, FALSE, - LinphoneConferenceSecurityLevelNone, - {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE); + create_conference_base( + ms_time(NULL) - 640, 11, FALSE, LinphoneConferenceParticipantListTypeClosed, TRUE, LinphoneMediaEncryptionNone, + FALSE, LinphoneConferenceLayoutActiveSpeaker, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, + LinphoneMediaDirectionSendRecv, FALSE, LinphoneConferenceSecurityLevelNone, + {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE, 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, LinphoneConferenceSecurityLevelNone, - {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE); + {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE, + 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, LinphoneConferenceSecurityLevelNone, - {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE); + {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE, + 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, LinphoneConferenceSecurityLevelNone, - {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE); + {LinphoneParticipantRoleSpeaker, LinphoneParticipantRoleListener}, FALSE, FALSE, FALSE, + FALSE); } static void create_conference_with_late_participant_addition(void) { @@ -1963,7 +1987,7 @@ static void participant_joins_simple_conference_with_screen_sharing(void) { LinphoneConferenceSecurityLevel security_level = LinphoneConferenceSecurityLevelNone; LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, initialSubject, - description, TRUE, security_level, TRUE, FALSE); + description, TRUE, security_level, TRUE, FALSE, NULL); BC_ASSERT_PTR_NOT_NULL(confAddr); char *conference_address_str = (confAddr) ? linphone_address_as_string(confAddr) : ms_strdup("sip:"); @@ -2441,7 +2465,7 @@ static void conference_with_screen_sharing_enabled_since_the_start(void) { LinphoneConferenceSecurityLevel security_level = LinphoneConferenceSecurityLevelNone; LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, initialSubject, - description, TRUE, security_level, TRUE, FALSE); + description, TRUE, security_level, TRUE, FALSE, NULL); BC_ASSERT_PTR_NOT_NULL(confAddr); char *conference_address_str = (confAddr) ? linphone_address_as_string(confAddr) : ms_strdup("sip:"); @@ -2711,7 +2735,7 @@ static void conference_with_screen_sharing_enabled_since_the_start(void) { if (!remaining_members.empty()) { LinphoneConference *conference = - linphone_core_search_conference(focus.getLc(), NULL, focus.getCMgr()->identity, confAddr, NULL); + linphone_core_search_conference(focus.getLc(), NULL, confAddr, confAddr, NULL); BC_ASSERT_PTR_NOT_NULL(conference); if (conference) { LinphoneParticipantDevice *screen_sharing_device = @@ -2887,7 +2911,7 @@ static void conference_with_two_participant_having_screen_sharing_enabled_since_ LinphoneConferenceSecurityLevel security_level = LinphoneConferenceSecurityLevelNone; LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, initialSubject, - description, TRUE, security_level, TRUE, FALSE); + description, TRUE, security_level, TRUE, FALSE, NULL); BC_ASSERT_PTR_NOT_NULL(confAddr); char *conference_address_str = (confAddr) ? linphone_address_as_string(confAddr) : ms_strdup("sip:"); @@ -3308,7 +3332,7 @@ static void conference_with_screen_sharing_participant_only(void) { LinphoneConferenceSecurityLevel security_level = LinphoneConferenceSecurityLevelNone; LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, initialSubject, - description, TRUE, security_level, TRUE, FALSE); + description, TRUE, security_level, TRUE, FALSE, NULL); BC_ASSERT_PTR_NOT_NULL(confAddr); char *conference_address_str = (confAddr) ? linphone_address_as_string(confAddr) : ms_strdup("sip:"); @@ -3745,7 +3769,7 @@ static void create_conference_with_codec_mismatch_base(bool_t organizer_codec_mi LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, initialSubject, - description, TRUE, security_level, TRUE, FALSE); + description, TRUE, security_level, TRUE, FALSE, NULL); BC_ASSERT_PTR_NOT_NULL(confAddr); char *conference_address_str = (confAddr) ? linphone_address_as_string(confAddr) : ms_strdup("sip:"); @@ -3947,10 +3971,7 @@ static void create_conference_with_codec_mismatch_base(bool_t organizer_codec_mi } 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)); + check_conference_me(pconference, ((mgr == marie.getCMgr()) || (mgr == focus.getCMgr()))); 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); @@ -4118,7 +4139,7 @@ static void create_conference_with_server_restart_base(bool_t organizer_first) { LinphoneParticipantRoleSpeaker, -1))); LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, initialSubject, - description, TRUE, security_level, TRUE, FALSE); + description, TRUE, security_level, TRUE, FALSE, NULL); BC_ASSERT_PTR_NOT_NULL(confAddr); char *conference_address_str = (confAddr) ? linphone_address_as_string(confAddr) : ms_strdup("sip:"); @@ -4306,10 +4327,7 @@ static void create_conference_with_server_restart_base(bool_t organizer_first) { } 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)); + check_conference_me(pconference, ((mgr == marie.getCMgr()) || (mgr == focus.getCMgr()))); 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); @@ -4473,7 +4491,7 @@ static void create_simple_conference_with_update_deferred(void) { } LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, initialSubject, - description, TRUE, security_level, TRUE, FALSE); + description, TRUE, security_level, TRUE, FALSE, NULL); BC_ASSERT_PTR_NOT_NULL(confAddr); char *conference_address_str = (confAddr) ? linphone_address_as_string(confAddr) : ms_strdup("sip:"); @@ -4610,10 +4628,7 @@ static void create_simple_conference_with_update_deferred(void) { 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)); + check_conference_me(pconference, ((mgr == marie.getCMgr()) || (mgr == focus.getCMgr()))); 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); @@ -4980,7 +4995,7 @@ static void conference_with_participant_added_outside_valid_time_slot(bool_t bef LinphoneConferenceSecurityLevel security_level = LinphoneConferenceSecurityLevelNone; LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, initialSubject, - description, TRUE, security_level, FALSE, FALSE); + description, TRUE, security_level, FALSE, FALSE, NULL); BC_ASSERT_PTR_NOT_NULL(confAddr); // Chat room creation to send ICS BC_ASSERT_TRUE(wait_for_list(coresList, &marie.getStats().number_of_LinphoneChatRoomStateCreated, 2, @@ -5090,7 +5105,7 @@ static void uninvited_participant_rejoins(void) { } LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, initialSubject, - description, TRUE, security_level, TRUE, FALSE); + description, TRUE, security_level, TRUE, FALSE, NULL); BC_ASSERT_PTR_NOT_NULL(confAddr); // Chat room creation to send ICS @@ -5459,7 +5474,7 @@ static void rejoining_conference_after_end(int cleanup_window) { } LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, initialSubject, - description, TRUE, security_level, TRUE, FALSE); + description, TRUE, security_level, TRUE, FALSE, NULL); BC_ASSERT_PTR_NOT_NULL(confAddr); char *conference_address_str = (confAddr) ? linphone_address_as_string(confAddr) : ms_strdup("sip:"); @@ -5724,9 +5739,17 @@ static void rejoining_conference_after_end_without_cleanup_window(void) { rejoining_conference_after_end(-1); } +static void on_eof(LinphonePlayer *player) { + LinphonePlayerCbs *cbs = linphone_player_get_current_callbacks(player); + LinphoneCoreManager *mgr = (LinphoneCoreManager *)linphone_player_cbs_get_user_data(cbs); + mgr->stat.number_of_player_eof++; +} + static void create_simple_conference_in_sfu_payload_mode(void) { char *pauline_recordpath = bc_tester_file("record-local_scheduled_conference_sfu_pauline.wav"); char *marie_recordpath = bc_tester_file("record-local_scheduled_conference_sfu_marie.wav"); + char *pauline_dummy_recordpath = bc_tester_file("record-local_scheduled_conference_sfu_pauline_dummy.wav"); + char *marie_dummy_recordpath = bc_tester_file("record-local_scheduled_conference_sfu_marie_dummy.wav"); char *soundpath = bc_tester_res("sounds/ahbahouaismaisbon.wav"); time_t start_time = ms_time(nullptr); @@ -5736,12 +5759,15 @@ static void create_simple_conference_in_sfu_payload_mode(void) { ClientConference marie("marie_rc", focus.getConferenceFactoryAddress()); ClientConference pauline("pauline_rc", focus.getConferenceFactoryAddress()); - linphone_core_set_record_file(marie.getCMgr()->lc, marie_recordpath); - linphone_core_set_record_file(pauline.getCMgr()->lc, pauline_recordpath); + unlink(marie_recordpath); + unlink(pauline_recordpath); + + linphone_core_set_record_file(marie.getCMgr()->lc, marie_dummy_recordpath); + linphone_core_set_record_file(pauline.getCMgr()->lc, pauline_dummy_recordpath); linphone_core_set_use_files(marie.getCMgr()->lc, TRUE); linphone_core_set_use_files(pauline.getCMgr()->lc, TRUE); - linphone_core_set_play_file(marie.getCMgr()->lc, soundpath); - linphone_core_set_play_file(pauline.getCMgr()->lc, soundpath); + linphone_core_set_play_file(marie.getCMgr()->lc, nullptr); + linphone_core_set_play_file(pauline.getCMgr()->lc, nullptr); focus.registerAsParticipantDevice(marie); focus.registerAsParticipantDevice(pauline); @@ -5789,7 +5815,7 @@ static void create_simple_conference_in_sfu_payload_mode(void) { LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, initialSubject, - description, TRUE, LinphoneConferenceSecurityLevelNone, TRUE, FALSE); + description, TRUE, LinphoneConferenceSecurityLevelNone, TRUE, FALSE, NULL); BC_ASSERT_PTR_NOT_NULL(confAddr); char *conference_address_str = (confAddr) ? linphone_address_as_string(confAddr) : ms_strdup("sip:"); @@ -5887,13 +5913,6 @@ static void create_simple_conference_in_sfu_payload_mode(void) { LinphoneConference *fconference = linphone_core_search_conference_2(focus.getLc(), confAddr); BC_ASSERT_PTR_NOT_NULL(fconference); - // wait to know if the no RTP timeout is triggered - CoreManagerAssert({focus, marie, pauline}) - .waitUntil(chrono::seconds(nortp_timeout + 1), [&marie, confAddr, nortp_timeout] { - LinphoneCall *marie_call = linphone_core_get_call_by_remote_address2(marie.getLc(), confAddr); - return marie_call && (linphone_call_get_duration(marie_call) > nortp_timeout); - }); - for (auto mgr : conferenceMgrs) { LinphoneConference *pconference = linphone_core_search_conference_2(mgr->lc, confAddr); BC_ASSERT_PTR_NOT_NULL(pconference); @@ -5964,10 +5983,7 @@ static void create_simple_conference_in_sfu_payload_mode(void) { } 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)); + check_conference_me(pconference, ((mgr == marie.getCMgr()) || (mgr == focus.getCMgr()))); bctbx_list_t *participants = linphone_conference_get_participant_list(pconference); for (bctbx_list_t *itp = participants; itp; itp = bctbx_list_next(itp)) { auto p = (LinphoneParticipant *)bctbx_list_get_data(itp); @@ -6001,6 +6017,33 @@ static void create_simple_conference_in_sfu_payload_mode(void) { } } + // Start playing the audio file + for (auto mgr : members) { + LinphoneCall *current_call = linphone_core_get_current_call(mgr->lc); + if (!BC_ASSERT_PTR_NOT_NULL(current_call)) continue; + + LinphonePlayer *player = linphone_call_get_player(current_call); + LinphonePlayerCbs *player_cbs = NULL; + if (!BC_ASSERT_PTR_NOT_NULL(player)) continue; + + player_cbs = linphone_factory_create_player_cbs(linphone_factory_get()); + linphone_player_cbs_set_eof_reached(player_cbs, on_eof); + linphone_player_cbs_set_user_data(player_cbs, mgr); + linphone_player_add_callbacks(player, player_cbs); + linphone_player_cbs_unref(player_cbs); + + if (mgr == marie.getCMgr()) linphone_core_set_record_file(mgr->lc, marie_recordpath); + else linphone_core_set_record_file(mgr->lc, pauline_recordpath); + + BC_ASSERT_EQUAL(linphone_player_open(player, soundpath), 0, int, "%d"); + BC_ASSERT_EQUAL(linphone_player_start(player), 0, int, "%d"); + } + + // Wait some time so the file is finished + CoreManagerAssert({focus, marie, pauline}).waitUntil(chrono::seconds(10), [&marie, &pauline] { + return marie.getStats().number_of_player_eof == 1 && pauline.getStats().number_of_player_eof == 1; + }); + // terminate all calls for (auto mgr : members) { const bctbx_list_t *calls = linphone_core_get_calls(mgr->lc); @@ -6089,23 +6132,40 @@ static void create_simple_conference_in_sfu_payload_mode(void) { unlink(marie_recordpath); unlink(pauline_recordpath); + unlink(marie_dummy_recordpath); + unlink(pauline_dummy_recordpath); } bc_free(pauline_recordpath); bc_free(marie_recordpath); + bc_free(pauline_dummy_recordpath); + bc_free(marie_dummy_recordpath); bc_free(soundpath); } static void create_conference_with_chat(void) { - create_conference_with_chat_base(LinphoneConferenceSecurityLevelNone, FALSE, FALSE, TRUE, -1, TRUE, FALSE); + create_conference_with_chat_base(LinphoneConferenceSecurityLevelNone, FALSE, FALSE, TRUE, 1, TRUE, FALSE, FALSE, + ms_time(NULL), FALSE); +} + +static void create_point_to_point_encrypted_conference_with_chat(void) { + create_conference_with_chat_base(LinphoneConferenceSecurityLevelPointToPoint, FALSE, FALSE, TRUE, -1, TRUE, FALSE, + FALSE, ms_time(NULL), FALSE); } static void create_conference_with_chat_and_participant_rejoining(void) { - create_conference_with_chat_base(LinphoneConferenceSecurityLevelNone, FALSE, FALSE, TRUE, -1, TRUE, TRUE); + create_conference_with_chat_base(LinphoneConferenceSecurityLevelNone, FALSE, FALSE, TRUE, -1, TRUE, TRUE, FALSE, + (ms_time(NULL) - 45), FALSE); +} + +static void create_conference_with_chat_and_cores_restart(void) { + create_conference_with_chat_base(LinphoneConferenceSecurityLevelNone, TRUE, TRUE, TRUE, 1, FALSE, FALSE, FALSE, + (ms_time(NULL) - 45), TRUE); } -static void create_conference_with_chat_with_cores_restart(void) { - create_conference_with_chat_base(LinphoneConferenceSecurityLevelNone, TRUE, TRUE, TRUE, 1, FALSE, FALSE); +static void create_conference_with_chat_network_drops_and_participant_rejoining(void) { + create_conference_with_chat_base(LinphoneConferenceSecurityLevelNone, FALSE, FALSE, FALSE, -1, TRUE, TRUE, TRUE, + (ms_time(NULL) - 45), FALSE); } #ifndef HAVE_EKT_SERVER_PLUGIN @@ -6326,7 +6386,7 @@ static void create_conference_with_chat_with_server_restarted_before_conference_ LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, initialSubject, - description, TRUE, security_level, TRUE, TRUE); + description, TRUE, security_level, TRUE, TRUE, NULL); BC_ASSERT_PTR_NOT_NULL(confAddr); char *conference_address_str = (confAddr) ? linphone_address_as_string(confAddr) : ms_strdup("sip:"); @@ -6751,9 +6811,18 @@ static void create_conference_with_chat_with_server_restarted_before_conference_ } } +static void conference_joined_multiple_times(void) { + conference_joined_multiple_times(LinphoneConferenceSecurityLevelPointToPoint, FALSE, 1); +} + +static void conference_with_chat_joined_multiple_times(void) { + conference_joined_multiple_times(LinphoneConferenceSecurityLevelNone, TRUE, 1); +} + } // namespace LinphoneTest static test_t local_conference_scheduled_conference_basic_tests[] = { + TEST_NO_TAG("Conference joined multiple times", LinphoneTest::conference_joined_multiple_times), TEST_NO_TAG("Call to inexisting conference address", LinphoneTest::call_to_inexisting_conference_address), TEST_NO_TAG("Conference with media lost", LinphoneTest::conference_with_media_lost), TEST_NO_TAG("Conference with participants are late except for one", @@ -6766,10 +6835,8 @@ static test_t local_conference_scheduled_conference_basic_tests[] = { LinphoneTest::create_simple_conference_db_conference_scheduler_server_restart), TEST_NO_TAG("Schedule simple conference using DB conference scheduler", LinphoneTest::schedule_simple_conference_db_conference_scheduler), - // TEST_NO_TAG("Create conference with only listeners and uninvited participant", - // LinphoneTest::create_conference_with_only_listeners_and_uninvited_participant), - TEST_NO_TAG("Create conference with only speakers and uninvited participant", - LinphoneTest::create_conference_with_only_speakers_and_uninvited_participant), + TEST_NO_TAG("Create conference with only listeners and uninvited participant", + LinphoneTest::create_conference_with_only_listeners_and_uninvited_participant), TEST_NO_TAG("Create conference with uninvited participant", LinphoneTest::create_conference_with_uninvited_participant), TEST_NO_TAG("Create simple conference with server restart", @@ -6869,13 +6936,20 @@ static test_t local_conference_scheduled_conference_with_screen_sharing_tests[] LinphoneTest::create_simple_conference_with_screen_sharing_no_video_send_component)}; static test_t local_conference_scheduled_conference_with_chat_tests[] = { + TEST_NO_TAG("Conference with chat joined multiple times", LinphoneTest::conference_with_chat_joined_multiple_times), TEST_NO_TAG("Create conference with chat", LinphoneTest::create_conference_with_chat), + TEST_NO_TAG("Create point-to-point encrypted conference with chat", + LinphoneTest::create_point_to_point_encrypted_conference_with_chat), TEST_NO_TAG("Create conference with chat and participant rejoining", LinphoneTest::create_conference_with_chat_and_participant_rejoining), - TEST_NO_TAG("Create conference with chat with cores restart", - LinphoneTest::create_conference_with_chat_with_cores_restart), + TEST_NO_TAG("Create conference with chat and cores restart", + LinphoneTest::create_conference_with_chat_and_cores_restart), + TEST_NO_TAG("Create conference with chat network drops and participant rejoining", + LinphoneTest::create_conference_with_chat_network_drops_and_participant_rejoining), TEST_NO_TAG("Create conference with chat with server restarted before conference expiration", LinphoneTest::create_conference_with_chat_with_server_restarted_before_conference_expires), + TEST_NO_TAG("Create conference with chat, only speakers and uninvited participant", + LinphoneTest::create_conference_with_chat_only_speakers_and_uninvited_participant), TEST_NO_TAG("Alone in conference with chat exits and enters again", LinphoneTest::alone_in_conference_with_chat_exits_enter)}; @@ -6906,7 +6980,7 @@ test_suite_t local_conference_test_suite_scheduled_conference_advanced = { }; test_suite_t local_conference_test_suite_scheduled_conference_audio_only_participant = { - "Local conference tester (Audio only participants)", + "Local conference tester (Scheduled Conference with Audio only participants)", NULL, NULL, liblinphone_tester_before_each, @@ -6919,7 +6993,7 @@ test_suite_t local_conference_test_suite_scheduled_conference_audio_only_partici }; test_suite_t local_conference_test_suite_scheduled_conference_with_screen_sharing = { - "Local conference tester (Screen sharing)", + "Local conference tester (Scheduled Conference with Screen sharing)", NULL, NULL, liblinphone_tester_before_each, @@ -6932,7 +7006,7 @@ test_suite_t local_conference_test_suite_scheduled_conference_with_screen_sharin }; test_suite_t local_conference_test_suite_scheduled_conference_with_chat = { - "Local conference tester (Conference with chat)", + "Local conference tester (Scheduled Conference with chat)", NULL, NULL, liblinphone_tester_before_each, diff --git a/tester/local-secure-chat-tester.cpp b/tester/local-secure-chat-tester.cpp index ed9c185dba92318c3d706d1d72dbd54da4437e7b..8b06bc932ddcc054035e9acce686e470523cf1db 100644 --- a/tester/local-secure-chat-tester.cpp +++ b/tester/local-secure-chat-tester.cpp @@ -661,7 +661,7 @@ static void secure_group_chat_room_with_multi_account_client(void) { // Verify that the chatroom is associated to the right account for (auto chatRoom : multi_account.getCore().getChatRooms()) { const auto &conference = chatRoom->getConference(); - LinphoneAddress *conference_params_accont_identity = + const LinphoneAddress *conference_params_accont_identity = conference->getAccount()->getAccountParams()->getIdentityAddress()->toC(); BC_ASSERT_TRUE(linphone_address_equal(chatroom_account_identity, conference_params_accont_identity)); } @@ -688,7 +688,7 @@ static void secure_group_chat_room_with_multi_account_client(void) { // Verify that the chatroom is associated to the right account after restart for (auto chatRoom : multi_account.getCore().getChatRooms()) { const auto &conference = chatRoom->getConference(); - LinphoneAddress *conference_params_accont_identity = + const LinphoneAddress *conference_params_accont_identity = conference->getAccount()->getAccountParams()->getIdentityAddress()->toC(); BC_ASSERT_TRUE(linphone_address_equal(chatroom_account_identity, conference_params_accont_identity)); } @@ -763,6 +763,111 @@ static void secure_group_chat_room_with_multi_account_client(void) { } } +static void secure_one_to_one_chat_room_with_client_removed_from_database(void) { + Focus focus("chloe_rc"); + { // to make sure focus is destroyed after clients. + bool_t encrypted = TRUE; + linphone_core_enable_lime_x3dh(focus.getLc(), encrypted); + ClientConference marie("marie_rc", focus.getConferenceFactoryAddress(), encrypted); + ClientConference pauline("pauline_rc", focus.getConferenceFactoryAddress(), encrypted); + + focus.registerAsParticipantDevice(marie); + focus.registerAsParticipantDevice(pauline); + + stats initialMarieStats = marie.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, pauline.getLc()); + + if (encrypted) { + 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(), &initialMarieStats, 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(), &initialPaulineStats, confAddr, initialSubject, 1, FALSE); + BC_ASSERT_PTR_NOT_NULL(paulineCr); + + initialMarieStats = marie.getStats(); + initialPaulineStats = pauline.getStats(); + + LinphoneAddress *paulineContact = + linphone_account_get_contact_address(linphone_core_get_default_account(pauline.getLc())); + + char *paulineContactStr = linphone_address_as_string(paulineContact); + ms_message("All %s's devices are removed", paulineContactStr); + ms_free(paulineContactStr); + for (auto chatRoom : focus.getCore().getChatRooms()) { + auto participant = + chatRoom->findParticipant(Address::toCpp(pauline.getCMgr()->identity)->getSharedFromThis()); + BC_ASSERT_PTR_NOT_NULL(participant); + if (participant) { + auto conference = chatRoom->getConference(); + BC_ASSERT_PTR_NOT_NULL(conference); + if (conference) { + conference->Conference::removeParticipant(participant); + } + } + } + + LinphoneChatMessage *msg = linphone_chat_room_create_message_from_utf8(paulineCr, "Hello everybody"); + linphone_chat_message_send(msg); + + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline}).wait([msg] { + return (linphone_chat_message_get_state(msg) == LinphoneChatMessageStateNotDelivered); + })); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneChatRoomStateTerminated, + initialPaulineStats.number_of_LinphoneChatRoomStateTerminated + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(coresList, &pauline.getStats().number_of_LinphoneChatRoomStateCreated, + initialPaulineStats.number_of_LinphoneChatRoomStateCreated + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(CoreManagerAssert({focus, marie, pauline}).wait([msg] { + return (linphone_chat_message_get_state(msg) == LinphoneChatMessageStateDelivered); + })); + linphone_chat_message_unref(msg); + msg = NULL; + + CoreManagerAssert({focus, marie, pauline}).waitUntil(std::chrono::seconds(2), [] { return false; }); + + for (auto chatRoom : focus.getCore().getChatRooms()) { + for (auto participant : chatRoom->getParticipants()) { + // force deletion by removing devices + auto participantAddress = participant->getAddress(); + linphone_chat_room_set_participant_devices(chatRoom->toC(), 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 + 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); + } +} + } // namespace LinphoneTest static test_t local_conference_secure_chat_tests[] = { @@ -785,7 +890,10 @@ static test_t local_conference_secure_chat_tests[] = { LinphoneTest::secure_group_chat_room_with_multi_account_client, "LeaksMemory"), TEST_NO_TAG("Group chat Lime Server chat room encrypted message", - LinphoneTest::group_chat_room_lime_server_encrypted_message)}; + LinphoneTest::group_chat_room_lime_server_encrypted_message), + TEST_ONE_TAG("Secure one-to-one chat with client removed from database", + LinphoneTest::secure_one_to_one_chat_room_with_client_removed_from_database, + "LeaksMemory")}; test_suite_t local_conference_test_suite_secure_chat = {"Local conference tester (Secure Chat)", NULL, diff --git a/tester/local-transferred-conference-tester.cpp b/tester/local-transferred-conference-tester.cpp index 52623278c5173477e7eacb69d209a075085ff90f..28779a0abae83651cd0ad2614b9149426647ff05 100644 --- a/tester/local-transferred-conference-tester.cpp +++ b/tester/local-transferred-conference-tester.cpp @@ -153,7 +153,7 @@ void create_transfer_conference_base(time_t start_time, LinphoneAddress *confAddr = create_conference_on_server(focus, marie, participantList, start_time, end_time, initialSubject, - description, TRUE, security_level, video_transfer, FALSE); + description, TRUE, security_level, video_transfer, FALSE, NULL); BC_ASSERT_PTR_NOT_NULL(confAddr); char *conference_address_str = (confAddr) ? linphone_address_as_string(confAddr) : ms_strdup("sip:"); @@ -331,10 +331,7 @@ void create_transfer_conference_base(time_t start_time, } 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)); + check_conference_me(pconference, ((mgr == marie.getCMgr()) || (mgr == focus.getCMgr()))); 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); diff --git a/tester/main-db-tester.cpp b/tester/main-db-tester.cpp index 62c502d60caaad12cf4d20311be2421dca8a3488..f95064b89236ad7e4cb6fdf20069535aedc7f509 100644 --- a/tester/main-db-tester.cpp +++ b/tester/main-db-tester.cpp @@ -43,15 +43,24 @@ using namespace LinphonePrivate; class MainDbProvider { public: + constexpr static const char *chatroom_domain = "sip.example.org"; + constexpr static const char *chatroom_gr = "459797d6-40f9-0072-a3ad-e9237e042437"; MainDbProvider() : MainDbProvider("db/linphone.db") { } - MainDbProvider(const char *db_file) { + MainDbProvider(const char *db_file, bool_t keep_gruu = TRUE, bool_t unify_chatroom_address = FALSE) { mCoreManager = linphone_core_manager_create("empty_rc"); char *roDbPath = bc_tester_res(db_file); char *rwDbPath = bc_tester_file(core_db); BC_ASSERT_FALSE(liblinphone_tester_copy_file(roDbPath, rwDbPath)); linphone_config_set_string(linphone_core_get_config(mCoreManager->lc), "storage", "uri", rwDbPath); + linphone_core_enable_gruu_in_conference_address(mCoreManager->lc, keep_gruu); + linphone_config_set_bool(linphone_core_get_config(mCoreManager->lc), "misc", "unify_chatroom_address", + unify_chatroom_address); + linphone_config_set_string(linphone_core_get_config(mCoreManager->lc), "misc", "force_chatroom_domain", + chatroom_domain); + linphone_config_set_string(linphone_core_get_config(mCoreManager->lc), "misc", "force_chatroom_gr", + chatroom_gr); bc_free(roDbPath); bc_free(rwDbPath); linphone_core_manager_start(mCoreManager, false); @@ -66,6 +75,10 @@ public: linphone_core_manager_start(mCoreManager, check_for_proxies); } + LinphoneCoreManager *getCoreManager() const { + return mCoreManager; + } + ~MainDbProvider() { linphone_core_manager_destroy(mCoreManager); } @@ -100,10 +113,10 @@ static void get_messages_count(void) { const MainDb &mainDb = provider.getMainDb(); if (mainDb.isInitialized()) { BC_ASSERT_EQUAL(mainDb.getChatMessageCount(), 5157, int, "%d"); - BC_ASSERT_EQUAL( - mainDb.getChatMessageCount(ConferenceId(Address::create("sip:test-3@sip.linphone.org")->getSharedFromThis(), - Address::create("sip:test-1@sip.linphone.org"))), - 861, int, "%d"); + BC_ASSERT_EQUAL(mainDb.getChatMessageCount( + ConferenceId(Address::create("sip:test-3@sip.linphone.org")->getSharedFromThis(), + Address::create("sip:test-1@sip.linphone.org"), ConferenceIdParams())), + 861, int, "%d"); } else { BC_FAIL("Database not initialized"); } @@ -116,7 +129,7 @@ static void get_unread_messages_count(void) { BC_ASSERT_EQUAL(mainDb.getUnreadChatMessageCount(), 2, int, "%d"); BC_ASSERT_EQUAL(mainDb.getUnreadChatMessageCount( ConferenceId(Address::create("sip:test-3@sip.linphone.org")->getSharedFromThis(), - Address::create("sip:test-1@sip.linphone.org"))), + Address::create("sip:test-1@sip.linphone.org"), ConferenceIdParams())), 0, int, "%d"); } else { BC_FAIL("Database not initialized"); @@ -130,28 +143,28 @@ static void get_history(void) { BC_ASSERT_EQUAL( mainDb .getHistoryRange(ConferenceId(Address::create("sip:test-4@sip.linphone.org")->getSharedFromThis(), - Address::create("sip:test-1@sip.linphone.org")), + Address::create("sip:test-1@sip.linphone.org"), ConferenceIdParams()), 0, -1, MainDb::Filter::ConferenceChatMessageFilter) .size(), 54, size_t, "%zu"); BC_ASSERT_EQUAL( mainDb .getHistoryRange(ConferenceId(Address::create("sip:test-7@sip.linphone.org")->getSharedFromThis(), - Address::create("sip:test-7@sip.linphone.org")), + Address::create("sip:test-7@sip.linphone.org"), ConferenceIdParams()), 0, -1, MainDb::Filter::ConferenceCallFilter) .size(), 0, size_t, "%zu"); BC_ASSERT_EQUAL( mainDb .getHistoryRange(ConferenceId(Address::create("sip:test-1@sip.linphone.org")->getSharedFromThis(), - Address::create("sip:test-1@sip.linphone.org")), + Address::create("sip:test-1@sip.linphone.org"), ConferenceIdParams()), 0, -1, MainDb::Filter::ConferenceChatMessageFilter) .size(), 804, size_t, "%zu"); BC_ASSERT_EQUAL( mainDb .getHistory(ConferenceId(Address::create("sip:test-1@sip.linphone.org")->getSharedFromThis(), - Address::create("sip:test-1@sip.linphone.org")), + Address::create("sip:test-1@sip.linphone.org"), ConferenceIdParams()), 100, MainDb::Filter::ConferenceChatMessageFilter) .size(), 100, size_t, "%zu"); @@ -166,7 +179,7 @@ static void get_conference_notified_events(void) { if (mainDb.isInitialized()) { list<shared_ptr<EventLog>> events = mainDb.getConferenceNotifiedEvents( ConferenceId(Address::create("sip:test-44@sip.linphone.org")->getSharedFromThis(), - Address::create("sip:test-1@sip.linphone.org")), + Address::create("sip:test-1@sip.linphone.org"), ConferenceIdParams()), 1); BC_ASSERT_EQUAL(events.size(), 3, size_t, "%zu"); if (events.size() != 3) return; @@ -360,56 +373,113 @@ static void get_chat_rooms() { } } -static void load_a_lot_of_chatrooms(void) { - long expectedDurationMs = 600; +static void load_a_lot_of_chatrooms_base(bool_t keep_gruu) { + long expectedDurationMs; + long ms; + /* + * This test makes a performance assertion. + * It is then quite unreliable: for example it may be to slower if the database read requires disk access and disk + * is busy compared to the case where the database file is cached by the system into RAM. To circumvent this, we + * give two attempts. It the first fails, there is a second attempt, where normally disk access shall no longer be a + * problem. + */ + for (int attempts = 0; attempts < 2; attempts++) { + expectedDurationMs = 600; #ifndef _WIN32 - int current_priority = getpriority(PRIO_PROCESS, 0); - int err = setpriority(PRIO_PROCESS, 0, -20); - if (err != 0) { - ms_warning("load_a_lot_of_chatrooms(): setpriority failed [%s]- the time measurement may be unreliable", - strerror(errno)); - } + int current_priority = getpriority(PRIO_PROCESS, 0); + int err = setpriority(PRIO_PROCESS, 0, -20); + if (err != 0) { + ms_warning("load_a_lot_of_chatrooms(): setpriority failed [%s]- the time measurement may be unreliable", + strerror(errno)); + } #endif - chrono::high_resolution_clock::time_point start = chrono::high_resolution_clock::now(); - MainDbProvider provider("db/chatrooms.db"); - chrono::high_resolution_clock::time_point end = chrono::high_resolution_clock::now(); - long ms = (long)chrono::duration_cast<chrono::milliseconds>(end - start).count(); + chrono::high_resolution_clock::time_point start = chrono::high_resolution_clock::now(); + MainDbProvider provider("db/chatrooms.db", keep_gruu); + chrono::high_resolution_clock::time_point end = chrono::high_resolution_clock::now(); + ms = (long)chrono::duration_cast<chrono::milliseconds>(end - start).count(); #ifdef ENABLE_SANITIZER - expectedDurationMs = 3000; + expectedDurationMs = 3000; #else #if __APPLE__ - expectedDurationMs = 1000; + expectedDurationMs = 1000; #endif #endif #ifndef __arm__ - float referenceBogomips = 6384.00; // the bogomips on the shuttle-linux (x86_64) - float bogomips = liblinphone_tester_get_cpu_bogomips(); - if (bogomips != 0) { - expectedDurationMs = (long)(((float)expectedDurationMs) * referenceBogomips / bogomips); - bctbx_message("Adjusted expected duration with current bogomips (%f): %li ms", bogomips, expectedDurationMs); - } + float referenceBogomips = 6384.00; // the bogomips on the shuttle-linux (x86_64) + float bogomips = liblinphone_tester_get_cpu_bogomips(); + if (bogomips != 0) { + expectedDurationMs = (long)(((float)expectedDurationMs) * referenceBogomips / bogomips); + bctbx_message("Adjusted expected duration with current bogomips (%f): %li ms", bogomips, + expectedDurationMs); + } #endif - BC_ASSERT_LOWER(ms, expectedDurationMs, long, "%li"); - #ifndef _WIN32 - err = setpriority(PRIO_PROCESS, 0, current_priority); - if (err != 0) { - ms_warning("load_a_lot_of_chatrooms(): cannot restore priority to [%i]: %s", current_priority, strerror(errno)); - } + err = setpriority(PRIO_PROCESS, 0, current_priority); + if (err != 0) { + ms_warning("load_a_lot_of_chatrooms(): cannot restore priority to [%i]: %s", current_priority, + strerror(errno)); + } #endif + if (ms <= expectedDurationMs) break; + } + BC_ASSERT_LOWER(ms, expectedDurationMs, long, "%li"); } -static void load_chatroom_conference(void) { - MainDbProvider provider("db/chatroom_conference.db"); +static void load_a_lot_of_chatrooms(void) { + load_a_lot_of_chatrooms_base(TRUE); +} + +static void load_a_lot_of_chatrooms_cleaning_gruu(void) { + load_a_lot_of_chatrooms_base(FALSE); +} + +static void load_chatroom_conference_base(bool_t keep_gruu) { + MainDbProvider provider("db/chatroom_conference.db", keep_gruu, TRUE); + BC_ASSERT_TRUE(linphone_core_gruu_in_conference_address_enabled(provider.getCoreManager()->lc) == keep_gruu); MainDb &mainDb = provider.getMainDb(); if (mainDb.isInitialized()) { list<shared_ptr<AbstractChatRoom>> chatRooms = mainDb.getChatRooms(); - BC_ASSERT_EQUAL(chatRooms.size(), 1, size_t, "%zu"); + BC_ASSERT_EQUAL(chatRooms.size(), 4, size_t, "%zu"); + for (const auto &chatRoom : chatRooms) { + const auto &conferenceAddress = chatRoom->getConferenceAddress(); + // Basic chatroom do not have a conference address + if (conferenceAddress) { + auto domain = conferenceAddress->getDomain(); + BC_ASSERT_STRING_EQUAL(L_STRING_TO_C(domain), MainDbProvider::chatroom_domain); + auto hasGruu = conferenceAddress->hasUriParam("gr"); + BC_ASSERT_TRUE(hasGruu == !!keep_gruu); + if (hasGruu) { + auto gr = conferenceAddress->getUriParamValue("gr"); + BC_ASSERT_STRING_EQUAL(L_STRING_TO_C(gr), MainDbProvider::chatroom_gr); + } + } + } + + LinphoneAddress *local_address = linphone_address_new("sip:berthe_fmmz-@sip.example.org"); + LinphoneAddress *new_conference_address = linphone_address_new( + "sip:chloe_khumi@sip.example.org;gr=urn:uuid:459797d6-40f9-0072-a3ad-e9237e042437;conf-id=abcdef"); + LinphoneAddress *short_conference_address = + linphone_address_new("sip:chloe_khumi@sip.example.org;gr=urn:uuid:459797d6-40f9-0072-a3ad-e9237e042437"); + LinphoneAddress *long_conference_address = linphone_address_new( + "sip:chloe_khumi@sip.example.org;gr=urn:uuid:459797d6-40f9-0072-a3ad-e9237e042437;conf-id=CvN8Rr69~"); + BC_ASSERT_PTR_NOT_NULL(linphone_core_search_chat_room_2(provider.getCoreManager()->lc, NULL, local_address, + short_conference_address, NULL)); + BC_ASSERT_PTR_NOT_NULL(linphone_core_search_chat_room_2(provider.getCoreManager()->lc, NULL, local_address, + long_conference_address, NULL)); + // Verify that linphone_core_search_chat_room_2 doesn't do a SIP address comparison as stated in the RFC. In + // fact, doing that leads to being too exposed to server instabilities. For example, if a server for whatever + // reason removes the conf-id parameter from the chatroom peer address, it will prevent to client to join any + // new chatroom as all will match the corrupted chat + BC_ASSERT_PTR_NULL(linphone_core_search_chat_room_2(provider.getCoreManager()->lc, NULL, local_address, + new_conference_address, NULL)); + linphone_address_unref(local_address); + linphone_address_unref(new_conference_address); + linphone_address_unref(short_conference_address); + linphone_address_unref(long_conference_address); list<shared_ptr<ConferenceInfo>> conferenceInfos = mainDb.getConferenceInfos(); BC_ASSERT_EQUAL(conferenceInfos.size(), 1, size_t, "%zu"); - for (const auto &conferenceInfo : conferenceInfos) { const auto uri = conferenceInfo->getUri(); BC_ASSERT_PTR_NOT_NULL(mainDb.getConferenceInfoFromURI(uri)); @@ -420,8 +490,17 @@ static void load_chatroom_conference(void) { } } -static void database_with_chatroom_duplicates(void) { - MainDbProvider provider("db/chatroom_duplicates.db"); +static void load_chatroom_conference(void) { + load_chatroom_conference_base(TRUE); +} + +static void load_chatroom_conference_cleaning_gruu(void) { + load_chatroom_conference_base(FALSE); +} + +static void database_with_chatroom_duplicates_base(bool_t keep_gruu) { + MainDbProvider provider("db/chatroom_duplicates.db", keep_gruu); + BC_ASSERT_TRUE(linphone_core_gruu_in_conference_address_enabled(provider.getCoreManager()->lc) == keep_gruu); MainDb &mainDb = provider.getMainDb(); if (mainDb.isInitialized()) { list<shared_ptr<AbstractChatRoom>> chatRooms = mainDb.getChatRooms(); @@ -454,6 +533,14 @@ static void database_with_chatroom_duplicates(void) { } } +static void database_with_chatroom_duplicates(void) { + database_with_chatroom_duplicates_base(TRUE); +} + +static void database_with_chatroom_duplicates_gruu_pruned(void) { + database_with_chatroom_duplicates_base(FALSE); +} + static void search_messages_in_chat_room(void) { MainDbProvider provider("db/chatrooms.db"); MainDb &mainDb = provider.getMainDb(); @@ -541,17 +628,21 @@ static void search_messages_in_chat_room(void) { } } -test_t main_db_tests[] = {TEST_NO_TAG("Get events count", get_events_count), - TEST_NO_TAG("Get messages count", get_messages_count), - TEST_NO_TAG("Get unread messages count", get_unread_messages_count), - TEST_NO_TAG("Get history", get_history), - TEST_NO_TAG("Get conference events", get_conference_notified_events), - TEST_NO_TAG("Get chat rooms", get_chat_rooms), - TEST_NO_TAG("Set/get conference info", set_get_conference_info), - TEST_NO_TAG("Load chatroom and conference", load_chatroom_conference), - TEST_NO_TAG("Database with chatroom duplicates", database_with_chatroom_duplicates), - TEST_NO_TAG("Load a lot of chatrooms", load_a_lot_of_chatrooms), - TEST_NO_TAG("Search messages in chatroom", search_messages_in_chat_room)}; +test_t main_db_tests[] = { + TEST_NO_TAG("Get events count", get_events_count), + TEST_NO_TAG("Get messages count", get_messages_count), + TEST_NO_TAG("Get unread messages count", get_unread_messages_count), + TEST_NO_TAG("Get history", get_history), + TEST_NO_TAG("Get conference events", get_conference_notified_events), + TEST_NO_TAG("Get chat rooms", get_chat_rooms), + TEST_NO_TAG("Set/get conference info", set_get_conference_info), + TEST_NO_TAG("Load chatroom and conference", load_chatroom_conference), + TEST_NO_TAG("Load chatroom and conference cleaning gruu", load_chatroom_conference_cleaning_gruu), + TEST_NO_TAG("Database with chatroom duplicates", database_with_chatroom_duplicates), + TEST_NO_TAG("Database with chatroom duplicates and GRUU pruned", database_with_chatroom_duplicates_gruu_pruned), + TEST_NO_TAG("Load a lot of chatrooms", load_a_lot_of_chatrooms), + TEST_NO_TAG("Load a lot of chatrooms cleaning GRUU", load_a_lot_of_chatrooms_cleaning_gruu), + TEST_NO_TAG("Search messages in chatroom", search_messages_in_chat_room)}; test_suite_t main_db_test_suite = {"MainDb", NULL, diff --git a/tester/message_tester.c b/tester/message_tester.c index c99011a2783e6547c7044225af62233397178e61..72cf5dbd832309d78fcce5e270a74fcec2866056 100644 --- a/tester/message_tester.c +++ b/tester/message_tester.c @@ -1416,6 +1416,7 @@ void transfer_message_base4(LinphoneCoreManager *marie, BC_ASSERT_PTR_NOT_NULL(content); file_transfer_size = (int)linphone_content_get_file_size(content); BC_ASSERT_NOT_EQUAL(0, file_transfer_size, int, "%d"); + BC_ASSERT_PTR_NULL(linphone_content_get_related_chat_message_id(content)); if (two_files) { FILE *file_to_send = NULL; @@ -1433,6 +1434,7 @@ void transfer_message_base4(LinphoneCoreManager *marie, linphone_content_set_size(content, file_size); /*total size to be transferred*/ linphone_content_set_name(content, "ahbahouaismaisbon.wav"); linphone_content_set_user_data(content, file_to_send); + BC_ASSERT_PTR_NULL(linphone_content_get_related_chat_message_id(content)); linphone_chat_message_add_file_content(msg, content); BC_ASSERT_PTR_NOT_NULL(linphone_content_get_user_data(content)); @@ -1641,6 +1643,8 @@ void transfer_message_base4(LinphoneCoreManager *marie, BC_ASSERT_STRING_EQUAL(linphone_content_get_name(content), expected_filename); compare_files(send_filepath, linphone_content_get_file_path(content)); BC_ASSERT_STRING_NOT_EQUAL(linphone_content_get_subtype(content), "vnd.gsma.rcs-ft-http+xml"); + BC_ASSERT_STRING_EQUAL(linphone_content_get_related_chat_message_id(content), + linphone_chat_message_get_message_id(msg)); if (linphone_factory_is_imdn_available(linphone_factory_get())) { BC_ASSERT_FALSE(wait_for_until(pauline->lc, marie->lc, @@ -4610,7 +4614,7 @@ static int message_tester_before_suite(void) { command = bctbx_strdup_printf("mkdir -p %s/.local/share/linphone", home); err = system(command); if (err != -1 && WIFEXITED(err) && WEXITSTATUS(err) == 0) { - bctbx_message("%s done succesfully.", command); + bctbx_message("%s done successfully.", command); } else { bctbx_error("%s failed. Some tests may fail.", command); } diff --git a/tester/presence_server_tester.c b/tester/presence_server_tester.c index bbbd184f0ad0967fb17e2719bb58dc6647bf12b6..0a30d7b32a43d935d8cef9eb850c7fc15647faa2 100644 --- a/tester/presence_server_tester.c +++ b/tester/presence_server_tester.c @@ -320,6 +320,7 @@ static void subscribe_with_late_publish(void) { pauline_lp = linphone_core_get_config(pauline->lc); lf_identity = linphone_address_as_string_uri_only(marie->identity); lf = linphone_core_create_friend_with_address(pauline->lc, lf_identity); + linphone_friend_enable_subscribes(lf, TRUE); bctbx_free(lf_identity); linphone_config_set_int(pauline_lp, "sip", "subscribe_expires", 10); @@ -393,7 +394,6 @@ static void subscribe_with_late_publish(void) { } static void test_forked_subscribe_notify_publish(void) { - LinphoneCoreManager *marie = linphone_core_manager_new("marie_rc"); LinphoneCoreManager *marie2 = linphone_core_manager_new("marie_rc"); LinphoneCoreManager *pauline = @@ -417,7 +417,7 @@ static void test_forked_subscribe_notify_publish(void) { bctbx_free(lf_identity); linphone_config_set_int(pauline_lp, "sip", "subscribe_expires", 5); - + linphone_friend_enable_subscribes(lf, TRUE); linphone_core_add_friend(pauline->lc, lf); /*wait for subscribe acknowledgment*/ @@ -1798,6 +1798,7 @@ static void publish_with_network_state_changes(void) { LinphoneCoreManager *pauline = linphone_core_manager_new(transport_supported(LinphoneTransportTls) ? "pauline_rc" : "pauline_tcp_rc"); LinphoneFriend *marie_as_friend = linphone_core_create_friend_with_address(pauline->lc, get_identity(marie)); + linphone_friend_enable_subscribes(marie_as_friend, TRUE); LinphoneProxyConfig *proxy; LinphoneCoreCbs *cbs = linphone_factory_create_core_cbs(linphone_factory_get()); diff --git a/tester/presence_tester.c b/tester/presence_tester.c index 587e5fd9247bd1513b924f769b8fa2ac23640fb4..cfe1ea5a89aa2f63c7b7af747048124ee6d08be1 100644 --- a/tester/presence_tester.c +++ b/tester/presence_tester.c @@ -53,6 +53,7 @@ static bool_t subscribe_to_callee_presence(LinphoneCoreManager *caller_mgr, Linp LinphoneFriend *friend = linphone_core_create_friend_with_address(caller_mgr->lc, identity); linphone_friend_edit(friend); linphone_friend_enable_subscribes(friend, TRUE); + linphone_friend_set_inc_subscribe_policy(friend, LinphoneSPAccept); linphone_friend_done(friend); LinphoneFriendCbs *cbs = linphone_factory_create_friend_cbs(linphone_factory_get()); @@ -159,6 +160,7 @@ static void simple_subscribe_with_early_notify(void) { linphone_friend_edit(marie_s_friend); linphone_friend_enable_subscribes(marie_s_friend, TRUE); + linphone_friend_set_inc_subscribe_policy(marie_s_friend, LinphoneSPAccept); linphone_friend_done(marie_s_friend); linphone_core_add_friend(marie->lc, marie_s_friend); ms_free(pauline_identity); @@ -170,6 +172,7 @@ static void simple_subscribe_with_early_notify(void) { marie_identity = linphone_address_as_string_uri_only(marie_identity_addr); pauline_s_friend = linphone_core_create_friend_with_address(pauline->lc, marie_identity); linphone_friend_enable_subscribes(pauline_s_friend, FALSE); + linphone_friend_set_inc_subscribe_policy(pauline_s_friend, LinphoneSPWait); linphone_core_add_friend(pauline->lc, pauline_s_friend); ms_free(marie_identity); @@ -354,6 +357,7 @@ static void subscribe_presence_forked(void) { lf = linphone_core_create_friend(marie->lc); linphone_friend_set_address(lf, pauline1->identity); linphone_friend_enable_subscribes(lf, TRUE); + linphone_friend_set_inc_subscribe_policy(lf, LinphoneSPAccept); linphone_core_add_friend(marie->lc, lf); linphone_friend_unref(lf); @@ -393,6 +397,7 @@ static void subscribe_presence_expired(void) { lf = linphone_core_create_friend(marie->lc); linphone_friend_set_address(lf, pauline1->identity); + linphone_friend_set_inc_subscribe_policy(lf, LinphoneSPAccept); linphone_friend_enable_subscribes(lf, TRUE); linphone_core_add_friend(marie->lc, lf); @@ -450,7 +455,7 @@ static void simple_subscribe_with_friend_from_rc(void) { linphone_core_manager_destroy(pauline); } -test_t presence_tests[] = { +static test_t presence_tests[] = { TEST_ONE_TAG("Simple Subscribe", simple_subscribe, "presence"), TEST_ONE_TAG("Simple Subscribe with early NOTIFY", simple_subscribe_with_early_notify, "presence"), TEST_NO_TAG("Simple Subscribe with friend from rc", simple_subscribe_with_friend_from_rc), diff --git a/tester/rcfiles/chloe_dual_proxy_rc b/tester/rcfiles/chloe_dual_proxy_rc new file mode 100644 index 0000000000000000000000000000000000000000..f6b5bfc335090b04e17b48cd50a94376470654c5 --- /dev/null +++ b/tester/rcfiles/chloe_dual_proxy_rc @@ -0,0 +1,64 @@ +[sip] +sip_port=-1 +sip_tcp_port=-1 +sip_tls_port=-1 +default_proxy=0 +ping_with_options=0 +composing_idle_timeout=1 + +[auth_info_0] +username=chloe +userid=chloe +passwd=secret +realm=sip.example.org + +[proxy_0] +reg_proxy=<sip:sip.example.org;transport=tls> +reg_route=<sip:sip.example.org;transport=tls> +reg_identity=sip:chloe@sip.example.org +reg_expires=3600 +reg_sendregister=1 +publish=0 +dial_escape_plus=0 + +[proxy_1] +reg_proxy=<sip:sip.example.org;transport=tls> +reg_route=<sip:sip.example.org;transport=tls> +reg_identity=sip:chloe2@sip.example.org +reg_expires=3600 +reg_sendregister=1 +publish=0 +dial_escape_plus=0 + +[rtp] +audio_rtp_port=-1--1 +video_rtp_port=-1--1 +text_rtp_port=-1--1 + +[video] +display=0 +capture=0 +show_local=0 +size=qcif +enabled=0 +self_view=0 +automatically_initiate=0 +automatically_accept=0 +device=StaticImage: Static picture + +[sound] +echocancellation=0 #to not overload cpu in case of VG + +[net] + +stun_server=stun.example.org + +[storage] +#this will explicitely request to not use database. We need this for a few tests where multiple cores are instanciated, so that they don't +#try to connect to the same default sqlite3 database. +uri=null +call_logs_db_uri=null +zrtp_secrets_db_uri=null + +[tester] +test_env=1 diff --git a/tester/rcfiles/empty_with_some_db_rc b/tester/rcfiles/empty_with_some_db_rc new file mode 100644 index 0000000000000000000000000000000000000000..01ffeb09e09029c12055e1c298e6320c89183cc9 --- /dev/null +++ b/tester/rcfiles/empty_with_some_db_rc @@ -0,0 +1,15 @@ + +[sip] +# Request no port to be bound. +# By default, liblinphone binds on port 5060. This is a problem as multiple bindings for UDP is allowed and liblinphone_tester suite usually runs on parallel +# which cause port conflicts that are undetected. +sip_port=-2 +sip_tcp_port=-2 +sip_tls_port=-2 + +[storage] +# Disable main database only +uri=null + +[tester] +test_env=1 diff --git a/tester/remote-provisioning-tester.cpp b/tester/remote-provisioning-tester.cpp index dda9cf88b4b9d19cfbedb269c1d2f09a8a88d462..99a460b85c649c32b367f59e573f5c1ae3eefedc 100644 --- a/tester/remote-provisioning-tester.cpp +++ b/tester/remote-provisioning-tester.cpp @@ -256,6 +256,7 @@ static void flexiapi_remote_provisioning_flow(void) { flexiAPIClient = make_shared<LinphonePrivate::FlexiAPIClient>(marie->lc); // Clean up + fetched = 0; flexiAPIClient->adminAccountDelete(id)->then([&code, &fetched](LinphonePrivate::FlexiAPIClient::Response response) { code = response.code; fetched = 1; @@ -305,24 +306,23 @@ static void flexiapi_remote_provisioning_contacts_list_flow(void) { wait_for_until(marie->lc, NULL, &fetched, 1, liblinphone_tester_sip_timeout); - fetched = code = 0; - // Create the contacts accounts + fetched = code = 0; flexiAPIClient->adminAccountCreate(usernameContact1, "1234", "MD5", "", true, "", "", "sipinfo") ->then([&code, &fetched, &contactId1](LinphonePrivate::FlexiAPIClient::Response response) { code = response.code; + BC_ASSERT_EQUAL(code, 200, int, "%d"); fetched = 1; contactId1 = response.json()["id"].asInt(); }); wait_for_until(marie->lc, NULL, &fetched, 1, liblinphone_tester_sip_timeout); - BC_ASSERT_EQUAL(code, 200, int, "%d"); fetched = code = 0; - flexiAPIClient->adminAccountCreate(usernameContact2, "1234", "MD5", "", true, "", "", "rfc2833") ->then([&code, &fetched, &contactId2](LinphonePrivate::FlexiAPIClient::Response response) { code = response.code; + BC_ASSERT_EQUAL(code, 200, int, "%d"); fetched = 1; contactId2 = response.json()["id"].asInt(); }); @@ -330,18 +330,19 @@ static void flexiapi_remote_provisioning_contacts_list_flow(void) { wait_for_until(marie->lc, NULL, &fetched, 1, liblinphone_tester_sip_timeout); BC_ASSERT_EQUAL(code, 200, int, "%d"); - fetched = code = 0; + auto responseCb = [&code, &fetched](const LinphonePrivate::FlexiAPIClient::Response &response) { + code = response.code; + BC_ASSERT_EQUAL(code, 200, int, "%d"); + fetched = 1; + }; // Link the contacts - flexiAPIClient->adminAccountContactAdd(contactId0, contactId1); - flexiAPIClient->adminAccountContactAdd(contactId0, contactId2) - ->then([&code, &fetched](const LinphonePrivate::FlexiAPIClient::Response &response) { - code = response.code; - fetched = 1; - }); - + fetched = code = 0; + flexiAPIClient->adminAccountContactAdd(contactId0, contactId1)->then(responseCb); + wait_for_until(marie->lc, NULL, &fetched, 1, liblinphone_tester_sip_timeout); + fetched = code = 0; + flexiAPIClient->adminAccountContactAdd(contactId0, contactId2)->then(responseCb); wait_for_until(marie->lc, NULL, &fetched, 1, liblinphone_tester_sip_timeout); - BC_ASSERT_EQUAL(code, 200, int, "%d"); // Provision it std::string remoteProvisioningURIWithConfirmationKey = remoteProvisioningURI; @@ -433,15 +434,12 @@ static void flexiapi_remote_provisioning_contacts_list_flow(void) { // Clean up flexiAPIClient = make_shared<LinphonePrivate::FlexiAPIClient>(marie->lc); - flexiAPIClient->adminAccountDelete(contactId1); - flexiAPIClient->adminAccountDelete(contactId2) - ->then([&code, &fetched](const LinphonePrivate::FlexiAPIClient::Response &response) { - code = response.code; - fetched = 1; - }); - + fetched = 0; + flexiAPIClient->adminAccountDelete(contactId1)->then(responseCb); + wait_for_until(marie->lc, NULL, &fetched, 1, liblinphone_tester_sip_timeout); + fetched = 0; + flexiAPIClient->adminAccountDelete(contactId2)->then(responseCb); wait_for_until(marie->lc, NULL, &fetched, 1, liblinphone_tester_sip_timeout); - BC_ASSERT_EQUAL(code, 200, int, "%d"); linphone_core_cbs_unref(cbs); ms_free(stats); diff --git a/tester/setup_tester.c b/tester/setup_tester.c index 08a87cef8ac79b16be0cbff013a906a82b9f1699..09e271ce7a8996877b643dcf4f2e92b4f674bf0c 100644 --- a/tester/setup_tester.c +++ b/tester/setup_tester.c @@ -177,19 +177,25 @@ static void linphone_version_update_test(void) { linphone_core_manager_destroy(lcm); } -static void core_init_test(void) { +static void core_init_test_base(bool_t use_database, bool_t disable_main_db_only) { LinphoneCore *lc; FILE *in; - lc = - linphone_factory_create_core_3(linphone_factory_get(), NULL, liblinphone_tester_get_empty_rc(), system_context); + char *empty_with_some_db_rc_path = bc_tester_res("rcfiles/empty_with_some_db_rc"); + lc = linphone_factory_create_core_3( + linphone_factory_get(), NULL, + (disable_main_db_only) ? empty_with_some_db_rc_path : liblinphone_tester_get_empty_rc(), system_context); if (BC_ASSERT_PTR_NOT_NULL(lc)) { linphone_config_set_int(linphone_core_get_config(lc), "lime", "enabled", 0); + linphone_core_enable_database(lc, use_database); linphone_core_start(lc); - const char *uri = linphone_config_get_string(linphone_core_get_config(lc), "storage", "uri", NULL); - BC_ASSERT_STRING_EQUAL(uri, "null"); - in = fopen(uri, "rb"); - if (!BC_ASSERT_PTR_NULL(in)) // "null" file should not exists - fclose(in); + if (use_database) { + const char *uri = linphone_config_get_string(linphone_core_get_config(lc), "storage", "uri", NULL); + BC_ASSERT_STRING_EQUAL(uri, "null"); + in = fopen(uri, "rb"); + if (!BC_ASSERT_PTR_NULL(in)) // "null" file should not exists + fclose(in); + } + BC_ASSERT_TRUE(linphone_core_database_enabled(lc) == use_database); /* until we have good certificates on our test server... */ linphone_core_verify_server_certificates(lc, FALSE); BC_ASSERT_EQUAL(linphone_core_get_global_state(lc), LinphoneGlobalOn, int, "%i"); @@ -237,6 +243,19 @@ static void core_init_test(void) { linphone_core_stop(lc); linphone_core_unref(lc); } + ms_free(empty_with_some_db_rc_path); +} + +static void core_init_test(void) { + core_init_test_base(TRUE, FALSE); +} + +static void core_init_test_some_database(void) { + core_init_test_base(TRUE, TRUE); +} + +static void core_init_test_no_database(void) { + core_init_test_base(FALSE, FALSE); } static void core_init_test_2(void) { @@ -265,6 +284,7 @@ static void core_init_test_2(void) { BC_ASSERT_PTR_NULL(tmp); BC_ASSERT_PTR_NOT_NULL(linphone_core_get_default_proxy_config(lc)); + linphone_core_stop(lc); linphone_core_unref(lc); } @@ -289,7 +309,7 @@ static void core_init_test_3(void) { BC_ASSERT_PTR_NULL(filename); BC_ASSERT_PTR_NULL(factory); BC_ASSERT_PTR_NULL(tmp); - + linphone_core_stop(lc); linphone_core_unref(lc); } } @@ -318,7 +338,7 @@ static void core_init_test_4(void) { char test_tmp_name[1024] = {0}; snprintf(test_tmp_name, sizeof(test_tmp_name), "%s.tmp", filename); BC_ASSERT_STRING_EQUAL(tmp, test_tmp_name); - + linphone_core_stop(lc); linphone_core_unref(lc); } @@ -358,6 +378,7 @@ static void core_init_unref_test(void) { /* until we have good certificates on our test server... */ linphone_core_verify_server_certificates(lc, FALSE); BC_ASSERT_EQUAL(linphone_core_get_global_state(lc), LinphoneGlobalOn, int, "%i"); + linphone_core_stop(lc); linphone_core_unref(lc); } } @@ -383,7 +404,7 @@ static void core_init_stop_start_test(void) { const char *uuid2 = linphone_config_get_string(linphone_core_get_config(lc), "misc", "uuid", NULL); BC_ASSERT_STRING_NOT_EQUAL(uuid2, ""); BC_ASSERT_STRING_EQUAL(uuid, uuid2); - + linphone_core_stop(lc); linphone_core_unref(lc); } } @@ -401,6 +422,7 @@ static void core_set_user_agent(void) { linphone_core_set_user_agent(lc, "part1b", "part2b"); linphone_core_start(lc); BC_ASSERT_EQUAL(strcmp(linphone_core_get_user_agent(lc), "part1b/part2b"), 0, int, "%d"); + linphone_core_stop(lc); linphone_core_unref(lc); } } @@ -455,7 +477,7 @@ static void core_sip_transport_test(void) { LC_SIP_TRANSPORT_RANDOM, int, "%d"); BC_ASSERT_EQUAL(linphone_config_get_int(linphone_core_get_config(lc), "sip", "sip_tls_port", -2), LC_SIP_TRANSPORT_RANDOM, int, "%d"); - + linphone_core_stop(lc); linphone_core_unref(lc); } @@ -527,6 +549,7 @@ static void linphone_interpret_url_test(void) { linphone_address_unref(address); ms_free(tmp); + linphone_core_stop(lc); linphone_core_unref(lc); } @@ -807,6 +830,7 @@ static void chat_room_test(void) { linphone_config_set_int(linphone_core_get_config(lc), "lime", "enabled", 0); linphone_core_start(lc); BC_ASSERT_PTR_NOT_NULL(linphone_core_get_chat_room_from_uri(lc, "sip:toto@titi.com")); + linphone_core_stop(lc); linphone_core_unref(lc); } @@ -961,6 +985,7 @@ static void custom_tones_setup_before_start(void) { if (tone) { BC_ASSERT_STRING_EQUAL(tone, "callonhold2.wav"); } + linphone_core_stop(lc); linphone_core_unref(lc); } @@ -3854,6 +3879,8 @@ test_t setup_tests[] = { TEST_NO_TAG("Linphone proxy config server address change (internal api)", linphone_proxy_config_is_server_config_changed_test), TEST_NO_TAG("Linphone core init/uninit", core_init_test), + TEST_NO_TAG("Linphone core init/uninit with some database", core_init_test_some_database), + TEST_NO_TAG("Linphone core init/uninit without database", core_init_test_no_database), TEST_NO_TAG("Linphone core init/uninit from existing factory rc", core_init_test_2), TEST_NO_TAG("Linphone core init/uninit withtout any rc", core_init_test_3), TEST_NO_TAG("Linphone core init/uninit from existing default rc", core_init_test_4), diff --git a/tester/shared_tester_functions.cpp b/tester/shared_tester_functions.cpp index a5bbbf318513093045ef74d9dec5dc8c2d27eab1..610fad1f23042ca09d0b79bf25d2e45e5e147984 100644 --- a/tester/shared_tester_functions.cpp +++ b/tester/shared_tester_functions.cpp @@ -609,21 +609,25 @@ void check_video_conference_with_local_participant(bctbx_list_t *participants, } } -void _linphone_conference_video_change(bctbx_list_t *lcs, - LinphoneCoreManager *mgr1, - LinphoneCoreManager *mgr2, - LinphoneCoreManager *mgr3) { +LinphoneCoreManager *_linphone_conference_video_change(bctbx_list_t *lcs, + LinphoneCoreManager *mgr1, + LinphoneCoreManager *mgr2, + LinphoneCoreManager *mgr3) { MSMireControl c1 = {{0, 5, 10, 15, 20, 25}}; MSMireControl c3 = {{100, 120, 140, 160, 180, 200}}; + stats initial_mgr1_stat = mgr1->stat; + stats initial_mgr2_stat = mgr2->stat; + stats initial_mgr3_stat = mgr3->stat; for (LinphoneCoreManager *mgr : {mgr1, mgr2, mgr3}) { LinphoneCall *call = linphone_core_get_current_call(mgr->lc); BC_ASSERT_PTR_NOT_NULL(call); - if (!call) return; + if (!call) return NULL; VideoStream *vstream = (VideoStream *)linphone_call_get_stream(call, LinphoneStreamTypeVideo); if (mgr != mgr2) { // mgr2 is audio only - BC_ASSERT_TRUE(vstream && vstream->source && ms_filter_get_id(vstream->source) == MS_MIRE_ID); - if (vstream && vstream->source && ms_filter_get_id(vstream->source) == MS_MIRE_ID) { + bool filterIsMire = (vstream && vstream->source && ms_filter_get_id(vstream->source) == MS_MIRE_ID); + BC_ASSERT_TRUE(filterIsMire); + if (filterIsMire) { if (mgr == mgr1) ms_filter_call_method(vstream->source, MS_MIRE_SET_COLOR, &c1); else ms_filter_call_method(vstream->source, MS_MIRE_SET_COLOR, &c3); } @@ -639,9 +643,17 @@ void _linphone_conference_video_change(bctbx_list_t *lcs, // mgr3 speaks and mgr1's video change linphone_core_enable_mic(mgr1->lc, FALSE); linphone_core_enable_mic(mgr2->lc, FALSE); - lInfo() << __func__ << ": mgr3 speaks"; - wait_for_list(lcs, NULL, 0, 5000); + linphone_core_enable_mic(mgr3->lc, TRUE); + lInfo() << __func__ << ": " << linphone_core_get_identity(mgr3->lc) << " speaks"; + BC_ASSERT_TRUE(wait_for_list(lcs, &mgr1->stat.number_of_active_speaker_participant_device_changed, + initial_mgr1_stat.number_of_active_speaker_participant_device_changed + 1, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(lcs, &mgr2->stat.number_of_active_speaker_participant_device_changed, + initial_mgr2_stat.number_of_active_speaker_participant_device_changed + 1, + liblinphone_tester_sip_timeout)); + linphone_call_check_rtp_sessions(call1); BC_ASSERT_TRUE(linphone_call_compare_video_color(call1, c3, MediaStreamSendRecv, "")); + BC_ASSERT_FALSE(linphone_call_compare_video_color(call1, c1, MediaStreamSendRecv, "")); // mgr1 should see mgr3 as active speaker LinphoneParticipantDevice *device = linphone_conference_get_active_speaker_participant_device(confMgr1); @@ -673,14 +685,21 @@ void _linphone_conference_video_change(bctbx_list_t *lcs, bctbx_list_free_with_data(devices, (bctbx_list_free_func)linphone_participant_device_unref); } - // mgr2 speaks until mgr1's video change - linphone_core_enable_mic(mgr2->lc, TRUE); + // mgr3 speaks until mgr2's video change linphone_core_enable_mic(mgr3->lc, FALSE); - lInfo() << __func__ << ": mgr2 speaks"; + linphone_core_enable_mic(mgr2->lc, TRUE); + linphone_core_enable_mic(mgr1->lc, FALSE); + lInfo() << __func__ << ": " << linphone_core_get_identity(mgr2->lc) << " speaks"; + BC_ASSERT_TRUE(wait_for_list(lcs, &mgr1->stat.number_of_active_speaker_participant_device_changed, + initial_mgr1_stat.number_of_active_speaker_participant_device_changed + 2, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(lcs, &mgr3->stat.number_of_active_speaker_participant_device_changed, + initial_mgr3_stat.number_of_active_speaker_participant_device_changed + 1, + liblinphone_tester_sip_timeout)); /* mg2 is audio-only, so this shall not affect video on other participants */ - wait_for_list(lcs, NULL, 0, 5000); // mgr1 still receives the video of the last active speaker that has video + linphone_call_check_rtp_sessions(call1); BC_ASSERT_TRUE(linphone_call_compare_video_color(call1, c3, MediaStreamSendRecv, "")); BC_ASSERT_FALSE(linphone_call_compare_video_color(call1, c1, MediaStreamSendRecv, "")); @@ -700,10 +719,17 @@ void _linphone_conference_video_change(bctbx_list_t *lcs, } // mgr1 speaks and mgr1's video not change - lInfo() << __func__ << ": mgr1 speaks"; - linphone_core_enable_mic(mgr2->lc, FALSE); + lInfo() << __func__ << ": " << linphone_core_get_identity(mgr1->lc) << " speaks"; linphone_core_enable_mic(mgr1->lc, TRUE); - wait_for_list(lcs, NULL, 0, 5000); + linphone_core_enable_mic(mgr2->lc, FALSE); + linphone_core_enable_mic(mgr3->lc, FALSE); + BC_ASSERT_TRUE(wait_for_list(lcs, &mgr2->stat.number_of_active_speaker_participant_device_changed, + initial_mgr2_stat.number_of_active_speaker_participant_device_changed + 2, + liblinphone_tester_sip_timeout)); + BC_ASSERT_TRUE(wait_for_list(lcs, &mgr3->stat.number_of_active_speaker_participant_device_changed, + initial_mgr3_stat.number_of_active_speaker_participant_device_changed + 2, + liblinphone_tester_sip_timeout)); + linphone_call_check_rtp_sessions(call1); BC_ASSERT_TRUE(linphone_call_compare_video_color(call1, c3, MediaStreamSendRecv, "")); BC_ASSERT_FALSE(linphone_call_compare_video_color(call1, c1, MediaStreamSendRecv, "")); @@ -721,6 +747,8 @@ void _linphone_conference_video_change(bctbx_list_t *lcs, bctbx_list_free_with_data(devices, (bctbx_list_free_func)linphone_participant_device_unref); } + + return mgr1; } const char *_linphone_call_get_subject(LinphoneCall *call) { diff --git a/tester/shared_tester_functions.h b/tester/shared_tester_functions.h index 9f58697570209637bfdcf68195e68467b0492cb6..58f9eddb6a411cd088543ba643cc7d59f2b4c3a1 100644 --- a/tester/shared_tester_functions.h +++ b/tester/shared_tester_functions.h @@ -80,10 +80,10 @@ void check_video_conference_with_local_participant(bctbx_list_t *participants, LinphoneConferenceLayout layout, bool_t local_partifipant); const char *_linphone_call_get_subject(LinphoneCall *call); -void _linphone_conference_video_change(bctbx_list_t *lcs, - LinphoneCoreManager *mgr1, - LinphoneCoreManager *mgr2, - LinphoneCoreManager *mgr3); +LinphoneCoreManager *_linphone_conference_video_change(bctbx_list_t *lcs, + LinphoneCoreManager *mgr1, + LinphoneCoreManager *mgr2, + LinphoneCoreManager *mgr3); int liblinphone_tester_send_data(const void *buffer, size_t length, const char *dest_ip, int dest_port, int sock_type); diff --git a/tester/stun_tester.c b/tester/stun_tester.c index 26466c2a4313771a8ee798637d2da0e8b0e049b3..26389709ed999aae5eb50f9279dd887189724986 100644 --- a/tester/stun_tester.c +++ b/tester/stun_tester.c @@ -436,7 +436,7 @@ static void relayed_ice_turn_to_ice_with_dtls_srtp(void) { } /* specific test that checks that in the case of a call with DTLS-SRTP, ICE, and TURN - * the handshake is started immediately after ICE has succesfully verified the relay pair. + * the handshake is started immediately after ICE has successfully verified the relay pair. */ static void _ice_turn_dtls_call(const CallConfig *config) { LinphoneCoreManager *marie; diff --git a/tester/tester.c b/tester/tester.c index f7fe91ac1febb23e4418c8f6353c25d20244d005..1c255e5a495851b3605873cd319d759c113345ba 100644 --- a/tester/tester.c +++ b/tester/tester.c @@ -108,7 +108,7 @@ const char *flexisip_tester_dns_server = "fs-test-9.linphone.org"; // const char *flexisip_tester_dns_server = "fs-test-sandbox-2.linphone.org"; bctbx_list_t *flexisip_tester_dns_ip_addresses = NULL; -const char *ccmp_server_url = "http://fs-test-9.linphone.org:3333/xml/"; +const char *ccmp_server_url = "http://sip.example.org:3333/xml/"; const char *test_domain = "sipopen.example.org"; const char *auth_domain = "sip.example.org"; const char *test_username = "liblinphone_tester"; @@ -161,7 +161,7 @@ const int x3dhServer_creationTimeout = 20000; // in ms, use this value for d // int liblinphonetester_transport_timeout = 9000; /*milliseconds. it is set to such low value to workaround a problem with our Freebox v6 when connecting to Ipv6 addresses. It was found that the freebox sometimes block SYN-ACK - packets, which prevents connection to be succesful. Thanks to the timeout, it will fallback to IPv4*/ + packets, which prevents connection to be successful. Thanks to the timeout, it will fallback to IPv4*/ char *message_external_body_url = NULL; static const char *notify_content = "<somexml2>blabla</somexml2>"; @@ -686,7 +686,7 @@ static void conference_state_changed(LinphoneConference *conference, LinphoneCon ms_free(newStateStr); if ((newState != LinphoneConferenceStateNone) && (newState != LinphoneConferenceStateInstantiated) && - (newState != LinphoneConferenceStateCreationPending)) { + (newState != LinphoneConferenceStateCreationPending) && !linphone_core_conference_server_enabled(core)) { LinphoneParticipant *me = linphone_conference_get_me(conference); BC_ASSERT_PTR_NOT_NULL(me); } @@ -797,11 +797,21 @@ static void conference_subject_changed(LinphoneConference *conference, BCTBX_UNU LinphoneCoreManager *manager = (LinphoneCoreManager *)linphone_core_get_user_data(core); manager->stat.number_of_subject_changed++; } + +static void +conference_active_speaker_participant_device_changed(LinphoneConference *conference, + BCTBX_UNUSED(const LinphoneParticipantDevice *device)) { + LinphoneCore *core = linphone_conference_get_core(conference); + LinphoneCoreManager *manager = (LinphoneCoreManager *)linphone_core_get_user_data(core); + manager->stat.number_of_active_speaker_participant_device_changed++; +} + static void conference_allowed_participant_list_changed(LinphoneConference *conference) { LinphoneCore *core = linphone_conference_get_core(conference); LinphoneCoreManager *manager = (LinphoneCoreManager *)linphone_core_get_user_data(core); manager->stat.number_of_allowed_participant_list_changed++; } + static void conference_participant_added(LinphoneConference *conference, BCTBX_UNUSED(LinphoneParticipant *participant)) { LinphoneCore *core = linphone_conference_get_core(conference); @@ -905,6 +915,8 @@ void core_conference_state_changed(BCTBX_UNUSED(LinphoneCore *core), cbs, conference_participant_device_screen_sharing_changed); linphone_conference_cbs_set_full_state_received(cbs, conference_full_state_received); linphone_conference_cbs_set_allowed_participant_list_changed(cbs, conference_allowed_participant_list_changed); + linphone_conference_cbs_set_active_speaker_participant_device( + cbs, conference_active_speaker_participant_device_changed); linphone_conference_add_callbacks(conference, cbs); linphone_conference_cbs_unref(cbs); } @@ -2026,10 +2038,9 @@ static LinphoneStatus check_participant_removal(bctbx_list_t *lcs, (conf_initial_stats.number_of_LinphoneConferenceStateDeleted + 1), liblinphone_tester_sip_timeout)); - LinphoneAddress *conf_uri = linphone_address_new(linphone_core_get_identity(conf_mgr->lc)); - conference = linphone_core_search_conference(conf_mgr->lc, NULL, conf_uri, local_conference_address, NULL); + conference = linphone_core_search_conference(conf_mgr->lc, NULL, local_conference_address, + local_conference_address, NULL); BC_ASSERT_PTR_NULL(conference); - linphone_address_unref(conf_uri); } else { expected_no_participants = (participant_size - 1); @@ -3008,6 +3019,7 @@ void linphone_core_manager_reinit(LinphoneCoreManager *mgr) { if (linphone_config_get_string(linphone_core_get_config(mgr->lc), "misc", "uuid", NULL)) uuid = bctbx_strdup(linphone_config_get_string(linphone_core_get_config(mgr->lc), "misc", "uuid", NULL)); linphone_core_set_network_reachable(mgr->lc, FALSE); // to avoid unregister + linphone_core_stop(mgr->lc); linphone_core_unref(mgr->lc); mgr->lc = NULL; } @@ -3449,6 +3461,7 @@ void messages_received(LinphoneCore *lc, BCTBX_UNUSED(LinphoneChatRoom *room), c counters = get_stats(lc); int count = (int)bctbx_list_size(messages); counters->number_of_LinphoneAggregatedMessagesReceived += count; + ms_message("Received %0d aggregated messages", count); } void message_received(LinphoneCore *lc, BCTBX_UNUSED(LinphoneChatRoom *room), LinphoneChatMessage *msg) { @@ -3565,7 +3578,7 @@ void new_subscription_requested(LinphoneCore *lc, LinphoneFriend *lf, const char ms_message("Disabling subscription because friend has numeric host."); linphone_friend_enable_subscribes(lf, FALSE); bctbx_freeaddrinfo(ai); - } + } else linphone_friend_enable_subscribes(lf, TRUE); linphone_core_add_friend(lc, lf); /*accept subscription*/ } diff --git a/tester/turn-server-tester.cpp b/tester/turn-server-tester.cpp index 5bfe1d7878570010e6b6b815ce94649a6346db09..8e4a11291708f6db4d5df90f6a7eb932e3e280d1 100644 --- a/tester/turn-server-tester.cpp +++ b/tester/turn-server-tester.cpp @@ -81,7 +81,7 @@ void update_turn_configuration_test() { "password" : "retset-enohpnilbil", "ttl" : 10, "uris" : [ - "turn://sip1.linphone.org:3479?transport=udp" + "turn:sip1.linphone.org:3479?transport=udp" ] } )"; @@ -116,7 +116,7 @@ void update_turn_configuration_test() { /*then close the call*/ end_call(pauline, marie); - wait_for_until(marie->lc, pauline->lc, 0, 1, 10000); + wait_for_until(marie->lc, pauline->lc, nullptr, 1, 10000); BC_ASSERT_PTR_NULL(linphone_core_find_auth_info(marie->lc, nullptr, "liblinphone-tester", nullptr)); BC_ASSERT_PTR_NULL(linphone_core_find_auth_info(pauline->lc, nullptr, "liblinphone-tester", nullptr)); diff --git a/tester/utils-tester.cpp b/tester/utils-tester.cpp index 06f91b1fbd40fb1399d0b60f78057dbcef4ab514..7ffa392e1ef4e47648f21c5f65c6f9ed90557620 100644 --- a/tester/utils-tester.cpp +++ b/tester/utils-tester.cpp @@ -201,13 +201,16 @@ static void conferenceId_comparisons() { std::shared_ptr<Address> b3 = Address::create("sip:popo@sip.example.org;d=dodo;c=didi;b=dede"); std::shared_ptr<Address> b4 = Address::create("sip:popo@sip.example.org;d=tutu;c=didi;b=dede"); - ConferenceId c1(a1, b1); - ConferenceId c2(a2, b2); - ConferenceId c3(a1, b3); - ConferenceId c4(a3, b2); - ConferenceId c5(a3, b3); - ConferenceId c6(a4, b3); - ConferenceId c7(a3, b4); + ConferenceIdParams conferenceIdParams; + conferenceIdParams.enableExtractUri(false); + conferenceIdParams.setKeepGruu(false); + ConferenceId c1(a1, b1, conferenceIdParams); + ConferenceId c2(a2, b2, conferenceIdParams); + ConferenceId c3(a1, b3, conferenceIdParams); + ConferenceId c4(a3, b2, conferenceIdParams); + ConferenceId c5(a3, b3, conferenceIdParams); + ConferenceId c6(a4, b3, conferenceIdParams); + ConferenceId c7(a3, b4, conferenceIdParams); BC_ASSERT_TRUE(c1 == c2); BC_ASSERT_TRUE(c3 == c2); BC_ASSERT_TRUE(c1 == c4); diff --git a/tester/vcard_tester.cpp b/tester/vcard_tester.cpp index 20292eaed967f40e075e57a46dd7f90dc68bfb2c..c8e9e3e93f45505b2cc3e9a2eac49c3593c67da6 100644 --- a/tester/vcard_tester.cpp +++ b/tester/vcard_tester.cpp @@ -33,8 +33,10 @@ #include "vcard/carddav-context.h" #define CARDDAV_SERVER "http://dav.example.org/baikal/html/card.php" +#define CARDDAV_SERVER_WITH_PORT "http://dav.example.org:80/baikal/html/card.php" #define ME_VCF "http://dav.example.org/baikal/html/card.php/addressbooks/tester/default/me.vcf" #define ME_VCF_2 "/baikal/html/card.php/addressbooks/tester/default/me.vcf" +#define ME_VCF_3 "/baikal/html/card.php/addressbooks/tester/default/unknown.vcf" #define CARDDAV_SYNC_TIMEOUT 15000 using namespace LinphonePrivate; @@ -228,6 +230,146 @@ static void linphone_vcard_phone_numbers_and_sip_addresses(void) { linphone_core_manager_destroy(manager); } +static void linphone_vcard_local_photo_to_base_64(void) { + LinphoneCoreManager *manager = linphone_core_manager_new_with_proxies_check("empty_rc", FALSE); + LinphoneVcard *lvc = linphone_vcard_context_get_vcard_from_buffer( + linphone_core_get_vcard_context(manager->lc), + "BEGIN:VCARD\r\nVERSION:4.0\r\nFN:Sylvain Berfini\r\nTEL;TYPE=work:" + "0952636505\r\nPHOTO:https://gitlab.linphone.org/uploads/-/system/appearance/header_logo/1/" + "logo-BC.png\r\nEND:VCARD\r\n"); + BC_ASSERT_PTR_NOT_NULL(lvc); + if (lvc == nullptr) return; + + char *asString = ms_strdup(linphone_vcard_as_vcard4_string(lvc)); + char *asStringWithBase64Picture = ms_strdup(linphone_vcard_as_vcard4_string_with_base_64_picture(lvc)); + BC_ASSERT_STRING_EQUAL(asString, asStringWithBase64Picture); + linphone_vcard_unref(lvc); + bctbx_free(asString); + bctbx_free(asStringWithBase64Picture); + + lvc = linphone_vcard_context_get_vcard_from_buffer( + linphone_core_get_vcard_context(manager->lc), + "BEGIN:VCARD\r\nVERSION:4.0\r\nFN:Sylvain Berfini\r\nTEL;TYPE=work:" + "0952636505\r\nEND:VCARD\r\n"); + BC_ASSERT_PTR_NOT_NULL(lvc); + if (lvc == nullptr) return; + + const char *base64Image = + "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQIAdgB2AAD//gATQ3JlYXRlZCB3aXRoIEdJTVD/" + "4gKwSUNDX1BST0ZJTEUAAQEAAAKgbGNtcwRAAABtbnRyUkdCIFhZWiAH6QABABcACwAeABxhY3NwQVBQTAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAA9tYAAQAAAADTLWxjbXMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1kZXNjAAABIAAAAEBjcHJ0" + "AAABYAAAADZ3dHB0AAABmAAAABRjaGFkAAABrAAAACxyWFlaAAAB2AAAABRiWFlaAAAB7AAAABRnWFlaAAACAAAAABRyVFJDAAACFAAAACBnVF" + "JDAAACFAAAACBiVFJDAAACFAAAACBjaHJtAAACNAAAACRkbW5kAAACWAAAACRkbWRkAAACfAAAACRtbHVjAAAAAAAAAAEAAAAMZW5VUwAAACQA" + "AAAcAEcASQBNAFAAIABiAHUAaQBsAHQALQBpAG4AIABzAFIARwBCbWx1YwAAAAAAAAABAAAADGVuVVMAAAAaAAAAHABQAHUAYgBsAGkAYwAgAE" + "QAbwBtAGEAaQBuAABYWVogAAAAAAAA9tYAAQAAAADTLXNmMzIAAAAAAAEMQgAABd7///MlAAAHkwAA/ZD///uh///" + "9ogAAA9wAAMBuWFlaIAAAAAAAAG+gAAA49QAAA5BYWVogAAAAAAAAJJ8AAA+" + "EAAC2xFhZWiAAAAAAAABilwAAt4cAABjZcGFyYQAAAAAAAwAAAAJmZgAA8qcAAA1ZAAAT0AAACltjaHJtAAAAAAADAAAAAKPXAABUfAAATM0AA" + "JmaAAAmZwAAD1xtbHVjAAAAAAAAAAEAAAAMZW5VUwAAAAgAAAAcAEcASQBNAFBtbHVjAAAAAAAAAAEAAAAMZW5VUwAAAAgAAAAcAHMAUgBHAEL" + "/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/" + "2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/" + "wgARCAAoACgDAREAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAABwgJBQb/xAAaAQADAQEBAQAAAAAAAAAAAAAFBgcIBAID/" + "9oADAMBAAIQAxAAAAG4RIGjtNg2tzECsEZGrRa/" + "OmvZoJ4ZiVh9i4oZJYziJdcP6+3ylWjOKJAua7wkzLljF4ZRqM3rT4Qg7Sm9Jz0FWdAqPCNnzusWWQO2ylnES3PTLdGf/" + "8QAHhAAAgICAwEBAAAAAAAAAAAABQYCBAMHAAETFBH/2gAIAQEAAQUCPn6i9TtMB9j7+NfBcCbBV/" + "SEupxaSMGdqaaVFUy5h8rEiYzx5qR0sVSAQb+" + "kWQaDtSmlDLvL6qh2o4uvmPNdG6tWG0VbbB6uMzhhBLWTVaya5WLRdyZV6k0CLAbaGvuX2RqKEQuHb7VjWloarDf/" + "xAAsEQACAgEEAAUDAwUAAAAAAAACAwEEBQYREhMABxQhMRUiQSNSYSQyM0JR/9oACAEDAQE/" + "Ac9nqmApxZsA6w97PT0MfVGGXcjbISMa1VcyMSXADa5rCBFZANs2GLQozi3qnOaillzI5mcbg1PBDFYO5Yp4ZdqSYI4tWaornUWs84ZEkZxumg" + "oUCYBI+oSc8GMs6S0n05HUk4zC2JqgCGZoQu6lsCLZDvDE1gy+eGq/YHiV/" + "UNiwqC42kVmg3xgfNzQN54Ua+q60NP2X9QpZHF1vmI4i++HSJTM7ABW1zP4Etp8DMEMFExMFETEjO4zE/ExP5ifxPjWWXVq/" + "V2Qwy7Y1tN6fWqnqrMJdxsNXbsghelcWzfZTMzkVqr3yWSjuEr07y6cUC7essfj9G26N/Ere/" + "LupsxmmwtRUPE6Ox9cFBdbg6KqqhXeud6hZZtTasN2nssdK/TOt6f9ebrV2Tt2bBSb7Fkic5xzHuTGs5Gc/wAkUzt/HjUumvpvK1VHiA+7Fx/" + "bx/cP/NvzHxt/Pz5BeY9uLidE5ewT6llZ/" + "QnNIiZUsJCWTjuUxP8ASuSLCrwRDFdq4QHIbACrF4Y2ah0vpTIGERWO75pa9NTJgX3OyWYqpxLmfCoE0oNE9a2DfY+" + "I7dpnVGK09cKjd1DZ6EU/UJQBP6Ftba6SnfjHawxGtuALKPbmRQUR7M0Hib3prGKuyNBs7s4kNjdfv71W/" + "u5RwKG8+" + "MzJfIdZZjRPlrk03sW7Jrq3EV3lYeOVCH1wQMlYaxb5KrMJiJmwPVHWMFv17bxp1QUtZ4pOMuxkF1dTUF0MgtJoi4KcoqKtoUM3NXdAgzqKSkO" + "XHkW3KdcUclo5vmlq+BUz6uvRa8W2Y+axXQq5SiU8Y4z1VACeEz+kaGTG/" + "t41vhL2tMZgctpmzXtLWDLK1E+FpuVcgquYOS33VLFdIxAnIRINZ98EPA9I4m1gsHXx+Rer1jWvZIrZyBZsjl0oIoGWSC1yw+I7c+" + "yY3COU6i8mdb5dmQrpt4saoLeyvaZYZLMgcbyiv0wveuT/AGhzGlIJmZ4y+Pu8eTWhbmS10Hr6xKRpK36rKCc/" + "4sjSeYUqckstpb69EtKIklmqo4S3EogtU6ax+" + "rcJcwWT7Yq24CexB8HIcooYh659xklMET4GJLPbiYkMzHi5pnzm8sltqaXyN7I4LsliTxlStkwCWHO++IuIvWKJkUyx/" + "pVsrci7CsGXKYy2d8yc5nKbslf1G7OY79WihKH07FOfuGX1sfRRXFJnBytrgribQnqaZhED401T8+dXK9Nk87f0/" + "hnAS33r2Px+Pv8AV7gUVUop1MtLZ/0aTaoTH3jZ+N9LaWxWkMSrE4lRQuCl1my6YO3ftnEdty43aOxzNo/EAsIFShBYCMf/" + "xAAuEQACAgIBAwIEBgIDAAAAAAABAgMEBRESAAYTITEUIjJBByNCQ1FhFTNSU3H/2gAIAQIBAT8BxeLsZWwYYikUUa+W1amPGCrACAZZW/" + "skLHGu3lkKxopY9QYjGYgLBVoC1kGjMgkvwRT33iHHds0rDDHYKiNMVtZIz2AD61jrrnmcvzr4w2r0YkPMUGavjI/" + "lB8bXpmp4+SWP1UirikjbW45JFK9ZLs7uBEaw+HkKr6t8LYq2pj/ZSDg7a/UVgf8A9XogqSCCCCQQRogj7EfY/" + "wAjrBU3weCq2zD5Mpk2axi6ci/" + "IrxxGRsrcX3KUaxLwKeQhDc4wJrrGLBWbOeisQXWjSnHMlnJmHyrczViUuYVvTtKxNeHxtqKHwxqOKpHvTpFeEASKELFFGOKRxgIiKPsqqAqj+" + "h1j8h59I52T7H+/4PX4idsQy1Xz9KMJZgK/" + "5BEAAnhY8fiCB+9ExXyHW3jJZj+V65G7xoZbL1gSZfD2p2+" + "rD6IhpLUyn0Xcsvn042R8HGhPHY6w93KQCxXxcPkkn8ckhEfkZEh5ge54KCZfVnB9eIGj7r3JdreaK7X3aT0TYMXzbH+5P40eQKa5eg9m5Cnn+" + "667QW1pmavLJGscZqN45DIQI1Vk/" + "OHMkCMljyJH1extHz4a01uHwebGzmzXZ1fw86z+aJpF+V+GyvMaDa3oe3Xb9mrm4u0MOCR8Cc692PftMkBepY1v15NZZwT+4JFHt12/" + "fr4C5kqWWjkhZmWJnEZZoZazSqyOo+fg/" + "Pe1Deqr6EHYzl2HJZGWzUjf4eNI05MmiyqdeSQDfEMz8F5HeuAOmOusf3xgKyVndLXlZo1liWEarA/XIX5cZEj/" + "AEiP53A+lfbrvXPQUe3HMEqtLmYfh6ZX15wWIwZpwCPoFZ9BtbEksf36w2Xs4PIQ5GpwMkXJWSQbSSJxqSNvuOS/" + "qUhlOiD1Dk+xe7ik2TgrVshxCulyV6kh0B6C5DJBHZUfTHzcSaGvEo9OqeI7coUpo6tbHx0LA4zszJNHMP8AjLPO8hkUa2qvIQp9VAPWTl/" + "DrBP5q9Grkr6HlHWrWJ7UPP3HlZ5paSID7rxkZf8ApPWZzN3O3Wu3WG9cIYU2Ia0IJ4wwrs6Ub2T9TsSzEk9f/" + "8QAMRAAAgIBAwIEBAMJAAAAAAAAAgMBBAUREhMAIQYUIjEjMkJRQUNSBxUWJDM0RGFi/9oACAEBAAY/" + "Aos2BY97j4KNCvG61fs7ZKEpH7QMSbnFotColjJ00iWWb2VmhigbCpTirLqmKW6ddtGL9QSy3ibIz6YOniYRVmfUNwBLpdrMDSxrCWMr/" + "eqxtZhvfs4cVXXkMqhTPmGb2eNg+xrUUF0NQfEIDJekIvU7tKqP+hZYN4B/zvtJGPsXt0JjIlBRBRIzuGYmNYkSj5on8J/" + "GOshU8zKsHhRGpmL6S9ZKJ8JjC44vlhuTuxC7BxtmwQ7W76mNHmp2MaDmZB1dlPDRY4CoeHaiYCLB4ysKQiLb+" + "UNbFjncZb2MbM6gxjrEm5zZk2taUsYwp9yMy1Ipn7z1JhHb8Y6V4VyLpZSuboxZsKZmpaiJLyozP+PZiC4x10W+BEI+" + "MWmDwV2dAQLvHHison52TEuo1zj5tqqs1ogO3qybyj1aT1TuZyzwJq8ykjy8QsZY4yn2iWmQwnWBXp23SWsR2r2MZd0ot7s9UP8AR3/" + "t2fq3RsIW7ts6zrqPHNqhOTivbQppNd58ZYrhGZaZgz4BcURMuARiRiC+TTWKcULHmJRl6/" + "k7QAS+biuB5d4rP1hLNAOALuOuk69ftBzxxExlP4aVjX6aRNaxa/namumkcQ48UkMT/" + "SlJzHqjrDZPAtVZWIMcKpcKxem4CCFgGXw+" + "RUq2kByPzF3gg2kmjfcvzb2uZsBmsARjrwpmdN5AtctLbGkFyTGoxum4tZUpSsGmh5WO9yY1lahXpJqY36pdtWE/WUerpIWEEFfw/" + "Z85kt35dio2fL1S07ch3FxqGvdSX+8D1YxF6WApu1i3JnRiHr7qcP0ntn3WeomMzHbtMNRiLF25id8kt2NQGQr95n1HQeqyymc+7ZhXFu/" + "PZ79Iddu5R2TqzurCMMS2sX6kV64KhJT9RLUMl7FM9RWfkr+IxTI2OvXayKL+L2LhgK6Mi8yjXQoMFl9Tx16DHY4J9+" + "WzZZpNi5YmNDe8o95nTQAj0LCIEY+//8QAGxABAQEBAQEBAQAAAAAAAAAAAREAITFBcVH/" + "2gAIAQEAAT8hnS2p1BsIiUyMHK9d6ld6WKDUKx6AZmDRLMZ3S+AW1OabePvMB4nvGuACafIJh0BK21D6AHEllG7AZoe5M0bHulWNWpReur/" + "cBkj859Q00HNJmxAkUK9DP2OK7JUtLZKlSEBUJCPcm0YgqwhjUQUj9AJ1nDl9hJIU6cvnRywrZfQJRqXDIqdp1uSJAqlENI5382AwYNieHY0PF" + "CCVvTRMjD0qcE/nBTFQp7gKpiUl2KhXIx0C+PbDtMfTZFHTmJGDQDH7T+i+OlCjV875x3G4ABQMucX7ciinUXbR5d8HHEJBGBc//" + "9oADAMBAAIAAwAAABBy2/TFZwMtDhsf/8QAGREBAQEBAQEAAAAAAAAAAAAAAREAITFB/9oACAEDAQE/" + "EA1JNDbdMCHOcPksS1XAX9l4XKMhozFxAPHuPkxDAKiWqEeAKrjpQQ+ERDiInMVBjHubY5BV0O+" + "2c5IAolCjaEAww0FjIAgABATpBHxH0XhPQkFCNi0JdAwKK2M6ZyslPqYK7w4LAVsJbEahRBWEHaemmpgIWO4lmuIayVjYCLwOkrbloAdV5yKyJ" + "xgJ6/ZIehGBkahcZyGI7RINMNwgle/EbOYgLezUs1wuJPPH9g2j/O1NcTK/" + "ukD8GUTpAOBdPZXx06qw5iUVTpBKnM+YOmxADDIg2VCs9UP6Z0IirU0l3lzIyv8A/8QAGxEBAQEBAQEBAQAAAAAAAAAAAREhADFBUXH/" + "2gAIAQIBAT8Quj2QZSFxIGlTgkK6YUqRjcrSSXIfGmxgoRcODqiqtFjkgpU9wec8oJwirQSJo0eEW5bIrGd9fwxbb9xFeKKaMj5hWR47g4PwAG" + "vuc6GAb76NJ7P6P2cknQN+GjBCh6Q9NTD4yGMOxEgAWeXbAYCnFhRTh44TB2kp+" + "bZAhxYecmlvUKaiy8LcKkKdEAVjQNIXZmtJSpJKY10T1wpJWoOFmYqT3EKcqlngSgI4B1T0ooGBQUSBXet6bEjKJMUHYCoVnxxeqhsgYMRGttx" + "cIRE4UYRcThCPuI8KLTmCrQIzBO4dRvUCDLiUErYzpyFQZPf/xAAZEAEBAQEBAQAAAAAAAAAAAAABEQAhMUH/" + "2gAIAQEAAT8QeS4bSfF0mZlNhd8DVz6s6Ox5SjHqQXo8WooNfEmBSdDXesmEoouQcVFihAzLJgK7SFbMDYUXVYvp7pg5np1aph33fX7OiR0z1A" + "48LeJ6hxLy+" + "053abRtnYUGI1lzwRKDoYgzFSnHclKAiLOsaeqmZx0H3k3rgrGcanW5e0SthRjpxbZZkZV6ez0yNyIbWjuC1g6clxRIDuEKKJxSZ3l9Pc8S7pK" + "kYC2fLgVyu288Fqt1HVdMk8OyoszyXYgVGt1GIwQbApYC9YmhQgjZY/7IxkFrkNeHybZ1cGwsWAS47//Z"; + linphone_vcard_set_photo(lvc, base64Image); + + asString = ms_strdup(linphone_vcard_as_vcard4_string(lvc)); + asStringWithBase64Picture = ms_strdup(linphone_vcard_as_vcard4_string_with_base_64_picture(lvc)); + BC_ASSERT_STRING_EQUAL(asString, asStringWithBase64Picture); + char *base64vCard = ms_strdup(asStringWithBase64Picture); + linphone_vcard_unref(lvc); + bctbx_free(asString); + bctbx_free(asStringWithBase64Picture); + + lvc = linphone_vcard_context_get_vcard_from_buffer( + linphone_core_get_vcard_context(manager->lc), + "BEGIN:VCARD\r\nVERSION:4.0\r\nFN:Sylvain Berfini\r\nTEL;TYPE=work:" + "0952636505\r\n\r\nEND:VCARD\r\n"); + BC_ASSERT_PTR_NOT_NULL(lvc); + if (lvc == nullptr) return; + + char *picture_path = bc_tester_res("images/bc.jpeg"); + std::string picture = "file:"; + picture.append(picture_path); + linphone_vcard_set_photo(lvc, picture.c_str()); + bctbx_free(picture_path); + + asString = ms_strdup(linphone_vcard_as_vcard4_string(lvc)); + asStringWithBase64Picture = ms_strdup(linphone_vcard_as_vcard4_string_with_base_64_picture(lvc)); + BC_ASSERT_STRING_NOT_EQUAL(asString, asStringWithBase64Picture); + BC_ASSERT_STRING_EQUAL(base64vCard, asStringWithBase64Picture); + linphone_vcard_unref(lvc); + bctbx_free(base64vCard); + bctbx_free(asString); + bctbx_free(asStringWithBase64Picture); + + linphone_core_manager_destroy(manager); +} + static void friends_if_no_db_set(void) { LinphoneCoreManager *manager = linphone_core_manager_new_with_proxies_check("empty_rc", FALSE); LinphoneFriend *lf = linphone_core_create_friend(manager->lc); @@ -263,11 +405,22 @@ static void friend_list_created_cb(BCTBX_UNUSED(LinphoneCore *lc), LinphoneFrien } } -static void friend_list_removed_cb(BCTBX_UNUSED(LinphoneCore *lc), LinphoneFriendList *list) { +static void friend_list_removed_cb(LinphoneCore *lc, LinphoneFriendList *list) { LinphoneFriendListStats *stats = (LinphoneFriendListStats *)linphone_friend_list_get_user_data(list); if (stats) { stats->removed_list_count++; } + + // Verify that the removed list is not in the core anymore + const bctbx_list_t *friends_list = linphone_core_get_friends_lists(lc); + bctbx_list_t *copy = bctbx_list_copy(friends_list); + + for (bctbx_list_t *it = copy; it != nullptr; it = it->next) { + LinphoneFriendList *core_list = (LinphoneFriendList *)bctbx_list_get_data(it); + if (core_list) BC_ASSERT_PTR_NOT_EQUAL(core_list, list); + } + + bctbx_list_free(copy); } static void friends_sqlite_storage(void) { @@ -449,7 +602,7 @@ static void carddav_clean( linphone_friend_list_add_callbacks(lfl, cbs); linphone_friend_list_cbs_unref(cbs); linphone_friend_list_set_display_name(lfl, "CardDAV friend list"); - linphone_friend_list_set_uri(lfl, CARDDAV_SERVER); + linphone_friend_list_set_uri(lfl, CARDDAV_SERVER_WITH_PORT); linphone_friend_list_set_type(lfl, LinphoneFriendListTypeCardDAV); linphone_core_add_friend_list(manager->lc, lfl); linphone_friend_list_unref(lfl); @@ -539,6 +692,8 @@ static void carddav_integration(void) { linphone_friend_list_remove_friend(lfl, lf); BC_ASSERT_EQUAL((unsigned int)bctbx_list_size(linphone_friend_list_get_friends(lfl)), 1, unsigned int, "%u"); // a local Sylvain friend is there + LinphoneFriend *sylvain = (LinphoneFriend *)bctbx_list_get_data(linphone_friend_list_get_friends(lfl)); + BC_ASSERT_PTR_NOT_NULL(sylvain); wait_for_until(manager->lc, NULL, &stats->sync_done_count, 3, CARDDAV_SYNC_TIMEOUT); BC_ASSERT_EQUAL(stats->sync_done_count, 3, int, "%i"); @@ -558,11 +713,15 @@ static void carddav_integration(void) { linphone_core_get_vcard_context(manager->lc), "BEGIN:VCARD\r\nVERSION:4.0\r\nFN:Sylvain " "Berfini\r\nIMPP:sip:sberfini@sip.linphone.org\r\nUID:1f08dd48-29ac-4097-8e48-8596d7776283\r\nEND:VCARD\r\n"); - linphone_vcard_set_url(lvc2, ME_VCF_2); + linphone_vcard_set_url(lvc2, ME_VCF_3); lf2 = linphone_core_create_friend_from_vcard(manager->lc, lvc2); linphone_vcard_unref(lvc2); linphone_friend_set_ref_key(lf2, refkey); BC_ASSERT_EQUAL(linphone_friend_list_add_local_friend(lfl, lf2), LinphoneFriendListOK, int, "%d"); + linphone_friend_unref(lf2); + + // To force update + linphone_vcard_set_etag(linphone_friend_get_vcard(sylvain), "wrong"); stats->new_contact_count = 0; stats->removed_contact_count = 0; @@ -581,14 +740,11 @@ static void carddav_integration(void) { BC_ASSERT_EQUAL(bctbx_list_size(linphone_friend_list_get_friends(lfl)), 1, size_t, "%zu"); lf = (LinphoneFriend *)bctbx_list_get_data((linphone_friend_list_get_friends(lfl))); - BC_ASSERT_STRING_EQUAL(linphone_friend_get_ref_key(lf), refkey); - BC_ASSERT_EQUAL(linphone_friend_get_storage_id(lf), linphone_friend_get_storage_id(lf2), long long, "%lld"); addr = linphone_friend_get_address(lf); BC_ASSERT_PTR_NOT_NULL(addr); address = linphone_address_as_string_uri_only(addr); BC_ASSERT_STRING_EQUAL(address, "sip:sylvain@sip.linphone.org"); ms_free(address); - linphone_friend_unref(lf2); linphone_friend_edit(lf); linphone_friend_done(lf); @@ -1065,6 +1221,7 @@ test_t vcard_tests[] = { TEST_NO_TAG("Import a lot of friends from vCards", linphone_vcard_import_a_lot_of_friends_test), TEST_NO_TAG("vCard creation for existing friends", linphone_vcard_update_existing_friends_test), TEST_NO_TAG("vCard phone numbers and SIP addresses", linphone_vcard_phone_numbers_and_sip_addresses), + TEST_NO_TAG("vCard with local photo file to base64", linphone_vcard_local_photo_to_base_64), TEST_NO_TAG("Friends working if no db set", friends_if_no_db_set), TEST_NO_TAG("Friends storage in sqlite database", friends_sqlite_storage), TEST_ONE_TAG("CardDAV clean", carddav_clean, "CardDAV"), // This is to ensure the content of the test addressbook is diff --git a/tools/lpsendmsg.c b/tools/lpsendmsg.c index 401a4b356dc06d45a3b5a9d640e4d9b0c0ba2595..74ce7846ed5d60d250732eefc1b8fd8ec8b13d86 100644 --- a/tools/lpsendmsg.c +++ b/tools/lpsendmsg.c @@ -63,7 +63,7 @@ static void on_msg_state_changed(BCTBX_UNUSED(LinphoneChatMessage *msg), Linphon running = FALSE; break; case LinphoneChatMessageStateDelivered: - printf("Message transmitted succesfully.\n"); + printf("Message transmitted successfully.\n"); running = FALSE; break; case LinphoneChatMessageStateDeliveredToUser: @@ -183,7 +183,7 @@ int main(int argc, char *argv[]) { linphone_address_set_display_name(route, NULL); } - LinphoneAccountParams *account_params = linphone_account_params_new(lc); + LinphoneAccountParams *account_params = linphone_account_params_new(lc, TRUE); linphone_account_params_set_identity_address(account_params, from); tmp = linphone_address_as_string(route); linphone_account_params_set_server_addr(account_params, tmp); diff --git a/tools/test_numbers.c b/tools/test_numbers.c index c312ab3c8695665ce0bfd4ba68f1f17a8325c814..1995ba4dbe64860cacd46d48d7ae81c70c5cc89f 100644 --- a/tools/test_numbers.c +++ b/tools/test_numbers.c @@ -31,7 +31,7 @@ int main(int argc, char *argv[]) { } linphone_core_enable_logs(stderr); linphone_core_set_log_level(ORTP_DEBUG); - LinphoneAccountParams *params = linphone_account_params_new(NULL); + LinphoneAccountParams *params = linphone_account_params_new(nullptr, FALSE); if (argc > 2) linphone_account_params_set_international_prefix(params, argv[2]); if (argc > 3 && strcmp(argv[3], "--escape-plus") == 0) linphone_account_params_set_dial_escape_plus(params, TRUE); account = linphone_core_create_account(NULL, params); diff --git a/wrappers/java/CMakeLists.txt b/wrappers/java/CMakeLists.txt index fcd883da593d17278703f9395c1d77bf351eb97a..bc40f3cdbe71c7b38b660dbeee54a4579554aab3 100644 --- a/wrappers/java/CMakeLists.txt +++ b/wrappers/java/CMakeLists.txt @@ -26,7 +26,9 @@ set(jni_sources "${CMAKE_CURRENT_BINARY_DIR}/src/linphone_jni.cc") # Remove everything after the last point in LINPHONE_VERSION (4.5.0 --> 4.5) to create STRIPPED_LINPHONE_VERSION string(REGEX REPLACE "\\.[^.]*$" "" STRIPPED_LINPHONE_VERSION ${LINPHONE_VERSION}) -if(ANDROID) +if(ANDROID_BUNDLED) + set(_wrapper_platform "bundled-android") +elseif(ANDROID) set(_wrapper_platform "android") else() set(_wrapper_platform "desktop") diff --git a/wrappers/java/classes/org/linphone/core/tools/AndroidPlatformHelper.java b/wrappers/java/classes/org/linphone/core/tools/AndroidPlatformHelper.java index 459484b427dd7bf69989e8e139f1e736d85414bf..04fcba5b8943ac2f62119574e5a1255b73359e43 100644 --- a/wrappers/java/classes/org/linphone/core/tools/AndroidPlatformHelper.java +++ b/wrappers/java/classes/org/linphone/core/tools/AndroidPlatformHelper.java @@ -1652,7 +1652,7 @@ public class AndroidPlatformHelper { if (mAudioHelper != null) mAudioHelper.setAudioManagerInNormalMode(); } - public synchronized boolean isRingingAllowed() { + public synchronized boolean isPlayingSoundAllowed() { AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); int ringerMode = audioManager.getRingerMode(); if (ringerMode == AudioManager.RINGER_MODE_SILENT || ringerMode == AudioManager.RINGER_MODE_VIBRATE) { diff --git a/wrappers/java/classes/org/linphone/core/tools/service/CoreService.java b/wrappers/java/classes/org/linphone/core/tools/service/CoreService.java index 9f2695393b425227c3ee5b021a632820d962a517..c6c4d5e3ae2337c4a4161c37d929129f1a2d926f 100644 --- a/wrappers/java/classes/org/linphone/core/tools/service/CoreService.java +++ b/wrappers/java/classes/org/linphone/core/tools/service/CoreService.java @@ -209,7 +209,7 @@ public class CoreService extends Service { Log.i("[Core Service] Core Manager found, adding our listener"); core.addListener(mListener); mIsListenerAdded = true; - Log.i("[Core Service] CoreListener succesfully added to the Core"); + Log.i("[Core Service] CoreListener successfully added to the Core"); if (core.getCallsNb() > 0) { Log.w("[Core Service] Core listener added while at least one call active !"); diff --git a/wrappers/java/genwrapper.py b/wrappers/java/genwrapper.py index ce95b973be486c1b766997aa1a48d8dd83bd717b..3abb8795e0fe1c593758ab43e63255e34b37a3ff 100755 --- a/wrappers/java/genwrapper.py +++ b/wrappers/java/genwrapper.py @@ -292,7 +292,7 @@ class JavaTranslator: methodDict['enumName'] = methodDict['return'][:-2] methodDict['classCast'] = type(_method.returnType) is AbsApi.ClassType - if self.platform == "android": + if self.platform == "android" or self.platform == "bundled-android": methodDict['params'] = ', '.join(['{0}{1}'.format('@Nullable ' if arg.maybenil else '@NonNull ' if arg.notnil else '', arg.translate(self.langTranslator, namespace=namespace)) for arg in _method.args]) else: methodDict['params'] = ', '.join(['{0}'.format(arg.translate(self.langTranslator, namespace=namespace)) for arg in _method.args]) @@ -638,7 +638,8 @@ class JavaEnum: self.briefDoc = self._class['briefDoc'] self.detailedDoc = self._class['detailedDoc'] self.jniName = _enum.name.translate(JNINameTranslator.get()) - self.isAndroid = (platform == "android") + self.isAndroid = (platform == "android") or (platform == "bundled-android") + self.isBundledAndroid = (platform == "bundled-android") class JniInterface: def __init__(self, javaClass, apiClass): @@ -667,7 +668,8 @@ class JavaInterface: self.briefDoc = self._class['briefDoc'] self.detailedDoc = self._class['detailedDoc'] self.jniMethods = self._class['jniMethods'] - self.isAndroid = (platform == "android") + self.isAndroid = (platform == "android") or (platform == "bundled-android") + self.isBundledAndroid = (platform == "bundled-android") class JavaInterfaceStub: def __init__(self, _interface): @@ -681,7 +683,8 @@ class JavaInterfaceStub: class JavaClass: def __init__(self, package, _class, translator, platform): self._class = translator.translate_class(_class) - self.isAndroid = (platform == "android") + self.isAndroid = (platform == "android") or (platform == "bundled-android") + self.isBundledAndroid = (platform == "bundled-android") self.isLinphoneFactory = self._class['isLinphoneFactory'] self.isLinphoneCore = self._class['isLinphoneCore'] self.isNotLinphoneFactory = not self.isLinphoneFactory diff --git a/wrappers/java/java_class.mustache b/wrappers/java/java_class.mustache index de0e4b87d589214e371996e209655100d567ec1c..a1a2c8e2ad0a42966b948d1e3a075ac91e99f850 100644 --- a/wrappers/java/java_class.mustache +++ b/wrappers/java/java_class.mustache @@ -247,11 +247,13 @@ class {{classImplName}} {{#isLinphoneFactory}}extends{{/isLinphoneFactory}}{{#is } {{/isAndroid}} {{#isAndroid}} +{{^isBundledAndroid}} System.loadLibrary("c++_shared"); loadOptionalLibrary("ffmpeg-linphone"); System.loadLibrary("bctoolbox"); System.loadLibrary("ortp"); System.loadLibrary("mediastreamer2"); +{{/isBundledAndroid}} System.loadLibrary("linphone"); Version.dumpCapabilities(); {{/isAndroid}}