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 &section, 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> &notifyLev, 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> &notifyLev, 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> &params,
+                                     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> &params,
+	                  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 &params) {
+	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 &params) {
+	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 &params);
+	ConferenceId(const std::shared_ptr<const Address> &peerAddress,
+	             const std::shared_ptr<const Address> &localAddress,
+	             const ConferenceIdParams &params);
 	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 &currentParticipant) {
-			    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 &currentParticipant) { 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 &params = 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 &params = 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 &currentParticipant) {
-				                               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 &currentParticipant) {
+					                               return (*p->getAddress() == *currentParticipant->getAddress());
+				                               });
+				for (const auto &d : p->getDevices()) {
+					bool deviceFound = false;
+					if (pIt == currentParticipants.cend()) {
+						deviceFound = false;
+					} else {
+						const auto &currentDevices = (*pIt)->getDevices();
+						const auto &dIt = std::find_if(currentDevices.cbegin(), currentDevices.cend(),
+						                               [&d](const auto &currentDevice) {
+							                               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 &currentDevices = (*pIt)->getDevices();
-					const auto &dIt =
-					    std::find_if(currentDevices.cbegin(), currentDevices.cend(), [&d](const auto &currentDevice) {
-						    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 &currentDevice) {
+					    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 &currentDevice) {
-				    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 &params = 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 &currentStreamLabel = 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 &currentConfParams = 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 &parameters = 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 &currentConfParams = 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 &params, 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 &params) {
 		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 &params) {
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, &amp);
 			/* 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> &params) {
+shared_ptr<AbstractChatRoom>
+CorePrivate::createServerChatRoom(BCTBX_UNUSED(const std::shared_ptr<const Address> &conferenceFactoryUri),
+                                  SalCallOp *op,
+                                  const std::shared_ptr<ConferenceParams> &params) {
 #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> &params) {
+shared_ptr<AbstractChatRoom>
+CorePrivate::createClientChatRoom(const std::shared_ptr<const Address> &conferenceFactoryUri,
+                                  const ConferenceId &conferenceId,
+                                  SalCallOp *op,
+                                  const std::shared_ptr<ConferenceParams> &params) {
 #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> &params,
+std::shared_ptr<AbstractChatRoom>
+CorePrivate::searchChatRoom(const std::shared_ptr<ConferenceParams> &params,
                             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> &params,
-                            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> &params,
+                                                         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> &params,
 		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> &params,
 		}
 
 		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> &params,
-                            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> &params,
-                                                         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 &params = 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> &params);
-	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> &params);
@@ -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> &params,
-	                                                 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> &params,
-	                                                 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> &params,
-	                                                 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> &params,
 	                                                 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> &params,
-                                              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> &params,
+                                                   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(), &params, 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 &parameters) {
 #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 &parameters) {
 		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 &parameters) 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 &section, 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}}