/*
 * Copyright (c) 2010-2021 Belledonne Communications SARL.
 *
 * This file is part of Liblinphone.
 *
 * 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 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

#include "liblinphone_tester.h"
#include "tester_utils.h"
#include "capability_negotiation_tester.h"
#include "linphone/core.h"
#include "c-wrapper/c-wrapper.h"

static void call_with_srtp_default_encryption(void) {
	call_with_default_encryption(LinphoneMediaEncryptionSRTP);
}

static void simple_srtp_call_with_capability_negotiations_with_reinvite(void) {
	LinphoneCoreManager* marie = linphone_core_manager_new("marie_rc");
	LinphoneCoreManager* pauline = linphone_core_manager_new(transport_supported(LinphoneTransportTls) ? "pauline_rc" : "pauline_tcp_rc");
	simple_call_with_capability_negotiations(marie, pauline, LinphoneMediaEncryptionSRTP, LinphoneMediaEncryptionSRTP);
	linphone_core_manager_destroy(marie);
	linphone_core_manager_destroy(pauline);
}

static void simple_srtp_call_with_capability_negotiations_without_reinvite(void) {
	LinphoneCoreManager* marie = linphone_core_manager_new("marie_rc");
	linphone_core_enable_capability_negotiation_reinvite(marie->lc, FALSE);
	LinphoneCoreManager* pauline = linphone_core_manager_new(transport_supported(LinphoneTransportTls) ? "pauline_rc" : "pauline_tcp_rc");
	linphone_core_enable_capability_negotiation_reinvite(pauline->lc, FALSE);
	simple_call_with_capability_negotiations(marie, pauline, LinphoneMediaEncryptionSRTP, LinphoneMediaEncryptionSRTP);
	linphone_core_manager_destroy(marie);
	linphone_core_manager_destroy(pauline);
}

static void simple_srtp_call_with_capability_negotiations_removed_after_update(void) {
	LinphoneCoreManager* marie = linphone_core_manager_new("marie_rc");
	LinphoneCoreManager* pauline = linphone_core_manager_new(transport_supported(LinphoneTransportTls) ? "pauline_rc" : "pauline_tcp_rc");
	simple_call_with_capability_negotiations_removed_after_update(marie, pauline, LinphoneMediaEncryptionSRTP);
	linphone_core_manager_destroy(marie);
	linphone_core_manager_destroy(pauline);
}

static void simple_srtp_call_with_capability_negotiations_with_no_encryption_after_resume(void) {
	LinphoneCoreManager* marie = linphone_core_manager_new("marie_rc");
	LinphoneCoreManager* pauline = linphone_core_manager_new(transport_supported(LinphoneTransportTls) ? "pauline_rc" : "pauline_tcp_rc");
	simple_call_with_capability_negotiations_with_different_encryption_after_resume(marie, pauline, LinphoneMediaEncryptionSRTP, LinphoneMediaEncryptionNone);
	linphone_core_manager_destroy(marie);
	linphone_core_manager_destroy(pauline);
}

static void simple_srtp_call_with_capability_negotiations_with_zrtp_encryption_after_resume(void) {
	LinphoneCoreManager* marie = linphone_core_manager_new("marie_rc");
	LinphoneCoreManager* pauline = linphone_core_manager_new(transport_supported(LinphoneTransportTls) ? "pauline_rc" : "pauline_tcp_rc");
	simple_call_with_capability_negotiations_with_different_encryption_after_resume(marie, pauline, LinphoneMediaEncryptionSRTP, LinphoneMediaEncryptionZRTP);
	linphone_core_manager_destroy(marie);
	linphone_core_manager_destroy(pauline);
}

static void simple_srtp_call_with_capability_negotiations_with_dtls_srtp_encryption_after_resume(void) {
	LinphoneCoreManager* marie = linphone_core_manager_new("marie_rc");
	LinphoneCoreManager* pauline = linphone_core_manager_new(transport_supported(LinphoneTransportTls) ? "pauline_rc" : "pauline_tcp_rc");
	simple_call_with_capability_negotiations_with_different_encryption_after_resume(marie, pauline, LinphoneMediaEncryptionSRTP, LinphoneMediaEncryptionDTLS);
	linphone_core_manager_destroy(marie);
	linphone_core_manager_destroy(pauline);
}

static void simple_srtp_call_with_capability_negotiations_with_resume_and_media_change(void) {
	LinphoneCoreManager* marie = linphone_core_manager_new("marie_rc");
	LinphoneCoreManager* pauline = linphone_core_manager_new(transport_supported(LinphoneTransportTls) ? "pauline_rc" : "pauline_tcp_rc");
	simple_call_with_capability_negotiations_with_resume_and_media_change_base(marie, pauline, LinphoneMediaEncryptionSRTP, LinphoneMediaEncryptionZRTP);
	linphone_core_manager_destroy(marie);
	linphone_core_manager_destroy(pauline);
}

static void unencrypted_srtp_call_with_capability_negotiations(void) {
	LinphoneCoreManager* marie = linphone_core_manager_new("marie_rc");
	// AES_CM_128_HMAC_SHA1_32 UNENCRYPTED_SRTCP is not supported hence it should not be put in the offer or accepted as answer
	linphone_core_set_srtp_crypto_suites(marie->lc, "AES_CM_128_HMAC_SHA1_80 UNENCRYPTED_SRTCP,AES_CM_128_HMAC_SHA1_32 UNENCRYPTED_SRTCP,AES_CM_128_HMAC_SHA1_32 UNAUTHENTICATED_SRTP");

	LinphoneCoreManager* pauline = linphone_core_manager_new(transport_supported(LinphoneTransportTls) ? "pauline_rc" : "pauline_tcp_rc");
	// AES_CM_256_HMAC_SHA1_80 UNENCRYPTED_SRTP UNENCRYPTED_SRTCP and AES_CM_128_HMAC_SHA1_32 UNENCRYPTED_SRTCP are not supported hence they should not be put in the offer or accepted as answer
	linphone_core_set_srtp_crypto_suites(pauline->lc, "AES_CM_256_HMAC_SHA1_80 UNENCRYPTED_SRTP UNENCRYPTED_SRTCP, AES_CM_128_HMAC_SHA1_80 UNAUTHENTICATED_SRTP,AES_CM_128_HMAC_SHA1_80 UNENCRYPTED_SRTP UNENCRYPTED_SRTCP,AES_CM_128_HMAC_SHA1_32 UNENCRYPTED_SRTCP,AES_CM_128_HMAC_SHA1_32 ,AES_CM_128_HMAC_SHA1_32 UNAUTHENTICATED_SRTP");

	simple_call_with_capability_negotiations(marie, pauline, LinphoneMediaEncryptionSRTP, LinphoneMediaEncryptionSRTP);

	// Spaces between words are added on purpose to ensure that parsing of crypto suites is correctly done
	linphone_core_set_srtp_crypto_suites(pauline->lc, "AES_CM_128_HMAC_SHA1_80 UNENCRYPTED_SRTCP , AES_CM_128_HMAC_SHA1_32   UNAUTHENTICATED_SRTP");
	linphone_core_set_srtp_crypto_suites(marie->lc, "AES_CM_256_HMAC_SHA1_80 UNENCRYPTED_SRTP UNENCRYPTED_SRTCP, AES_CM_128_HMAC_SHA1_80 UNAUTHENTICATED_SRTP,AES_CM_128_HMAC_SHA1_80 UNENCRYPTED_SRTCP,AES_CM_128_HMAC_SHA1_32 UNENCRYPTED_SRTCP,AES_CM_128_HMAC_SHA1_32 UNAUTHENTICATED_SRTP");
	simple_call_with_capability_negotiations(pauline, marie, LinphoneMediaEncryptionSRTP, LinphoneMediaEncryptionSRTP);

	linphone_core_manager_destroy(marie);
	linphone_core_manager_destroy(pauline);
}

static void srtp_call_with_capability_negotiations_caller_unencrypted(void) {
	LinphoneCoreManager* marie = linphone_core_manager_new("marie_rc");
	// AES_CM_128_HMAC_SHA1_32 UNENCRYPTED_SRTCP is not supported hence it should not be put in the offer or accepted as answer
	linphone_core_set_srtp_crypto_suites(marie->lc, "AES_CM_128_HMAC_SHA1_80 UNENCRYPTED_SRTCP,AES_CM_128_HMAC_SHA1_32 UNENCRYPTED_SRTCP,AES_CM_128_HMAC_SHA1_32 UNAUTHENTICATED_SRTP");

	LinphoneCoreManager* pauline = linphone_core_manager_new(transport_supported(LinphoneTransportTls) ? "pauline_rc" : "pauline_tcp_rc");
	simple_call_with_capability_negotiations(marie, pauline, LinphoneMediaEncryptionSRTP, LinphoneMediaEncryptionNone);
	linphone_core_manager_destroy(marie);
	linphone_core_manager_destroy(pauline);
}

static void srtp_call_with_capability_negotiations_callee_unencrypted(void) {
	LinphoneCoreManager* marie = linphone_core_manager_new("marie_rc");
	LinphoneCoreManager* pauline = linphone_core_manager_new(transport_supported(LinphoneTransportTls) ? "pauline_rc" : "pauline_tcp_rc");
	// AES_CM_256_HMAC_SHA1_80 UNENCRYPTED_SRTP UNENCRYPTED_SRTCP and AES_CM_128_HMAC_SHA1_32 UNENCRYPTED_SRTCP are not supported hence they should not be put in the offer or accepted as answer
	linphone_core_set_srtp_crypto_suites(pauline->lc, "AES_CM_256_HMAC_SHA1_80 UNENCRYPTED_SRTP UNENCRYPTED_SRTCP, AES_CM_128_HMAC_SHA1_80 UNAUTHENTICATED_SRTP,AES_CM_128_HMAC_SHA1_80 UNENCRYPTED_SRTP UNENCRYPTED_SRTCP,AES_CM_128_HMAC_SHA1_32 UNENCRYPTED_SRTCP");

	simple_call_with_capability_negotiations(marie, pauline, LinphoneMediaEncryptionSRTP, LinphoneMediaEncryptionNone);
	linphone_core_manager_destroy(marie);
	linphone_core_manager_destroy(pauline);
}

static void srtp_call_with_encryption_supported_in_call_params_only(void) {
	call_with_encryption_supported_in_call_params_only_base(LinphoneMediaEncryptionSRTP);
}

static void srtp_call_with_potential_configuration_same_as_actual_configuration(void) {
	call_with_potential_configuration_same_as_actual_configuration_base(LinphoneMediaEncryptionSRTP);
}

static void encrypted_call_with_suite_mismatch_and_capability_negotiations_base(LinphoneCoreManager* caller, LinphoneCoreManager* callee, const LinphoneMediaEncryption optionalEncryption) {

	BC_ASSERT_TRUE(linphone_core_media_encryption_supported(callee->lc,optionalEncryption));
	BC_ASSERT_TRUE(linphone_core_media_encryption_supported(caller->lc,optionalEncryption));

	bctbx_list_t * encryption_list = NULL;
	encryption_list = bctbx_list_append(encryption_list, LINPHONE_INT_TO_PTR(optionalEncryption));

	LinphoneCallParams *caller_params = linphone_core_create_call_params(caller->lc, NULL);
	linphone_call_params_enable_capability_negotiations (caller_params, TRUE);
	linphone_call_params_set_supported_encryptions(caller_params,encryption_list);
	linphone_call_params_enable_mandatory_media_encryption(caller_params,0);
	linphone_call_params_set_media_encryption(caller_params,LinphoneMediaEncryptionNone);
	BC_ASSERT_TRUE(linphone_call_params_is_media_encryption_supported(caller_params, optionalEncryption));

	LinphoneCallParams *callee_params = linphone_core_create_call_params(callee->lc, NULL);
	linphone_call_params_enable_capability_negotiations (callee_params, TRUE);
	linphone_call_params_set_supported_encryptions(callee_params,encryption_list);
	linphone_call_params_enable_mandatory_media_encryption(callee_params,0);
	linphone_call_params_set_media_encryption(callee_params,LinphoneMediaEncryptionNone);
	BC_ASSERT_TRUE(linphone_call_params_is_media_encryption_supported(callee_params, optionalEncryption));

	if (encryption_list) {
		bctbx_list_free(encryption_list);
	}


	encrypted_call_with_params_base(caller, callee, LinphoneMediaEncryptionNone, caller_params, callee_params, TRUE);

	linphone_call_params_unref(caller_params);
	linphone_call_params_unref(callee_params);

	pause_resume_calls(caller, callee);

	LinphoneCall * callerCall = linphone_core_get_current_call(caller->lc);
	BC_ASSERT_PTR_NOT_NULL(callerCall);
	LinphoneCall * calleeCall = linphone_core_get_current_call(callee->lc);
	BC_ASSERT_PTR_NOT_NULL(calleeCall);

	if (calleeCall && callerCall) {
		end_call(callee, caller);
	}

}

static void srtp_call_with_suite_mismatch_and_capability_negotiations_caller_unencrypted(void) {

	encryption_params marie_enc_mgr_params;
	marie_enc_mgr_params.encryption = LinphoneMediaEncryptionNone;
	marie_enc_mgr_params.level = E_DISABLED;
	LinphoneCoreManager * marie = create_core_mgr_with_capability_negotiation_setup("marie_rc", marie_enc_mgr_params, FALSE, FALSE, TRUE);

	linphone_core_set_srtp_crypto_suites(marie->lc, "AES_CM_128_HMAC_SHA1_80 UNENCRYPTED_SRTCP,AES_CM_128_HMAC_SHA1_80 UNENCRYPTED_SRTP,AES_CM_128_HMAC_SHA1_32 UNAUTHENTICATED_SRTP");

	encryption_params pauline_enc_mgr_params;
	pauline_enc_mgr_params.encryption = LinphoneMediaEncryptionNone;
	pauline_enc_mgr_params.level = E_DISABLED;
	LinphoneCoreManager * pauline = create_core_mgr_with_capability_negotiation_setup((transport_supported(LinphoneTransportTls) ? "pauline_rc" : "pauline_tcp_rc"), pauline_enc_mgr_params, FALSE, FALSE, TRUE);

	linphone_core_set_srtp_crypto_suites(pauline->lc, "AES_CM_256_HMAC_SHA1_80 , AES_CM_256_HMAC_SHA1_32");

	encrypted_call_with_suite_mismatch_and_capability_negotiations_base(marie, pauline, LinphoneMediaEncryptionSRTP);

	linphone_core_manager_destroy(marie);
	linphone_core_manager_destroy(pauline);
}

static void srtp_call_with_suite_mismatch_and_capability_negotiations_callee_unencrypted(void) {

	encryption_params marie_enc_mgr_params;
	marie_enc_mgr_params.encryption = LinphoneMediaEncryptionNone;
	marie_enc_mgr_params.level = E_DISABLED;
	LinphoneCoreManager * marie = create_core_mgr_with_capability_negotiation_setup("marie_rc", marie_enc_mgr_params, FALSE, FALSE, TRUE);
	linphone_core_set_srtp_crypto_suites(marie->lc, "AES_CM_128_HMAC_SHA1_80");

	encryption_params pauline_enc_mgr_params;
	pauline_enc_mgr_params.encryption = LinphoneMediaEncryptionNone;
	pauline_enc_mgr_params.level = E_DISABLED;
	LinphoneCoreManager * pauline = create_core_mgr_with_capability_negotiation_setup((transport_supported(LinphoneTransportTls) ? "pauline_rc" : "pauline_tcp_rc"), pauline_enc_mgr_params, FALSE, FALSE, TRUE);
	linphone_core_set_srtp_crypto_suites(pauline->lc, "AES_CM_128_HMAC_SHA1_80 UNENCRYPTED_SRTP UNENCRYPTED_SRTCP");

	encrypted_call_with_suite_mismatch_and_capability_negotiations_base(marie, pauline, LinphoneMediaEncryptionSRTP);

	linphone_core_manager_destroy(marie);
	linphone_core_manager_destroy(pauline);
}

static void srtp_call_with_mandatory_encryption(void) {
	call_with_mandatory_encryption_base(LinphoneMediaEncryptionSRTP, FALSE, FALSE);
}

static void srtp_call_with_mandatory_encryption_and_capability_negotiation_on_both_sides(void) {
	call_with_mandatory_encryption_base(LinphoneMediaEncryptionSRTP, TRUE, TRUE);
}

static void srtp_call_with_mandatory_encryption_and_capability_negotiation_on_caller_side(void) {
	call_with_mandatory_encryption_base(LinphoneMediaEncryptionSRTP, TRUE, FALSE);
}

static void srtp_call_with_mandatory_encryption_and_capability_negotiation_on_callee_side(void) {
	call_with_mandatory_encryption_base(LinphoneMediaEncryptionSRTP, FALSE, TRUE);
}

static void srtp_call_from_opt_enc_to_enc(void) {
	call_from_opt_enc_to_enc_base(LinphoneMediaEncryptionSRTP, TRUE);
}

static void srtp_call_from_enc_to_opt_enc(void) {
	call_from_opt_enc_to_enc_base(LinphoneMediaEncryptionSRTP, FALSE);
}

static void srtp_call_from_enc_to_no_enc(void) {
	call_from_enc_to_no_enc_base(LinphoneMediaEncryptionSRTP, TRUE, TRUE, TRUE);
}

static void srtp_call_from_no_enc_to_enc(void) {
	call_from_enc_to_no_enc_base(LinphoneMediaEncryptionSRTP, TRUE, TRUE, FALSE);
}

static void srtp_call_with_optional_encryption_on_caller(void) {
	call_from_opt_enc_to_none_base(LinphoneMediaEncryptionSRTP, TRUE, FALSE);
}

static void srtp_call_with_optional_encryption_on_callee(void) {
	call_from_opt_enc_to_none_base(LinphoneMediaEncryptionSRTP, FALSE, FALSE);
}

static void srtp_video_call_with_optional_encryption_on_caller(void) {
	call_from_opt_enc_to_none_base(LinphoneMediaEncryptionSRTP, TRUE, TRUE);
}

static void srtp_video_call_with_optional_encryption_on_callee(void) {
	call_from_opt_enc_to_none_base(LinphoneMediaEncryptionSRTP, FALSE, TRUE);
}

static void srtp_call_with_optional_encryption_on_both_sides(void) {
	call_with_optional_encryption_on_both_sides_base(LinphoneMediaEncryptionSRTP, FALSE);
}

static void srtp_video_call_with_optional_encryption_on_both_sides(void) {
	call_with_optional_encryption_on_both_sides_base(LinphoneMediaEncryptionSRTP, TRUE);
}


static void srtp_call_with_video_and_capability_negotiation(void) {
	const LinphoneMediaEncryption encryption = LinphoneMediaEncryptionSRTP; // Desired encryption
	call_with_video_and_capability_negotiation_base(encryption);
}

static void call_with_capability_negotiations_and_unsupported_crypto_in_sdp_update_base(bool_t pauline_supports_unencrypted) {
	const LinphoneMediaEncryption optionalEncryption = LinphoneMediaEncryptionSRTP;

	std::list<LinphoneMediaEncryption> enc_list {optionalEncryption};

	encryption_params pauline_enc_mgr_params;
	pauline_enc_mgr_params.encryption = LinphoneMediaEncryptionNone;
	pauline_enc_mgr_params.level = E_OPTIONAL;
	pauline_enc_mgr_params.preferences = enc_list;

	LinphoneCoreManager * pauline = create_core_mgr_with_capability_negotiation_setup((transport_supported(LinphoneTransportTls) ? "pauline_rc" : "pauline_tcp_rc"), pauline_enc_mgr_params, TRUE, FALSE, TRUE);
	if (pauline_supports_unencrypted) {
		// AES_CM_128_HMAC_SHA1_32 UNENCRYPTED_SRTCP is not supported hence it should not be put in the offer or accepted as answer
		linphone_core_set_srtp_crypto_suites(pauline->lc, "AES_CM_128_HMAC_SHA1_32 UNAUTHENTICATED_SRTP, AES_CM_256_HMAC_SHA1_32 UNENCRYPTED_SRTCP,AES_CM_128_HMAC_SHA1_80 UNENCRYPTED_SRTCP");
	}

	encryption_params marie_enc_mgr_params;
	marie_enc_mgr_params.encryption = LinphoneMediaEncryptionNone;
	marie_enc_mgr_params.level = E_OPTIONAL;
	marie_enc_mgr_params.preferences = enc_list;

	LinphoneCoreManager * marie = create_core_mgr_with_capability_negotiation_setup("marie_rc", marie_enc_mgr_params, TRUE, FALSE, TRUE);
	// AES_CM_128_HMAC_SHA1_32 UNENCRYPTED_SRTCP is not supported hence it should not be put in the offer or accepted as answer
	linphone_core_set_srtp_crypto_suites(marie->lc, "AES_CM_128_HMAC_SHA1_80 UNENCRYPTED_SRTCP,AES_CM_128_HMAC_SHA1_32 UNENCRYPTED_SRTCP,AES_CM_128_HMAC_SHA1_32 UNAUTHENTICATED_SRTP");

	BC_ASSERT_TRUE(linphone_core_media_encryption_supported(pauline->lc,optionalEncryption));
	BC_ASSERT_TRUE(linphone_core_media_encryption_supported(marie->lc,optionalEncryption));

	LinphoneCallParams *marie_params = linphone_core_create_call_params(marie->lc, NULL);
	linphone_call_params_enable_capability_negotiations (marie_params, TRUE);
	linphone_call_params_add_custom_sdp_attribute(marie_params, "acap", "870 crypto:99 AES_CM_256_HMAC_SHA1_32 inline:fWgTsTLqHc/xC7VQl7air+at/Ko1DpXudbS0KG3s UNENCRYPTED_SRTCP");
	// This extra attribute will be added to the 200Ok to Pauline's initial INVITE. It should be ignored
	linphone_call_params_add_custom_sdp_media_attribute(marie_params, LinphoneStreamTypeAudio, "pcfg", "36 a=870 t=1");
	LinphoneCallParams *pauline_params = linphone_core_create_call_params(pauline->lc, NULL);
	linphone_call_params_enable_capability_negotiations (pauline_params, TRUE);
	linphone_call_params_add_custom_sdp_attribute(pauline_params, "acap", "999 crypto:25 AES_CM_256_HMAC_SHA1_80 inline:fWgTsTLqHc/xC7VQl7air+at/Ko1DpXudbS0KG3s UNENCRYPTED_SRTP UNENCRYPTED_SRTCP");
	linphone_call_params_add_custom_sdp_media_attribute(pauline_params, LinphoneStreamTypeAudio, "pcfg", "49 a=999 t=1");

	// Start second call with same call params as the first one
	BC_ASSERT_TRUE(call_with_params(pauline, marie, pauline_params, marie_params));

	linphone_call_params_unref(marie_params);
	linphone_call_params_unref(pauline_params);

	linphone_config_set_int(linphone_core_get_config(pauline->lc), "sip", "defer_update_default", TRUE);
	linphone_config_set_int(linphone_core_get_config(marie->lc), "sip", "defer_update_default", TRUE);

	LinphoneCall * marie_call = linphone_core_get_current_call(marie->lc);
	BC_ASSERT_PTR_NOT_NULL(marie_call);
	LinphoneCall * pauline_call = linphone_core_get_current_call(pauline->lc);
	BC_ASSERT_PTR_NOT_NULL(pauline_call);

	stats marie_stat = marie->stat;
	stats pauline_stat = pauline->stat;
	LinphoneMediaEncryption encryption = LinphoneMediaEncryptionNone;
	bool potentialConfigurationChosen = false;

	// Update call with unsupported cfg
	LinphoneCallParams * params1 = linphone_core_create_call_params(pauline->lc, pauline_call);
	linphone_call_params_add_custom_sdp_media_attribute(params1, LinphoneStreamTypeAudio, "acap", "52 crypto:654 AES_256_CM_HMAC_SHA1_80 inline:fWgTsTLqHc/xC7VQl7air+at/Ko1DpXudbS0KG3s UNAUTHENTICATED_SRTP");
	if (pauline_supports_unencrypted) {
		linphone_call_params_add_custom_sdp_media_attribute(params1, LinphoneStreamTypeAudio, "pcfg", "238 a=52|1|2|999 t=1");
		linphone_call_params_add_custom_sdp_media_attribute(params1, LinphoneStreamTypeVideo, "pcfg", "82 a=3|4|999 t=1");
	} else {
		linphone_call_params_add_custom_sdp_media_attribute(params1, LinphoneStreamTypeAudio, "pcfg", "179 a=52|1|2|3|4|999 t=1");
		linphone_call_params_add_custom_sdp_media_attribute(params1, LinphoneStreamTypeVideo, "pcfg", "294 a=5|6|7|8|999 t=1");
	}
	linphone_call_params_enable_video(params1, TRUE);
	linphone_call_update(pauline_call, params1);
	linphone_call_params_unref(params1);
	BC_ASSERT_TRUE( wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneCallUpdatedByRemote,(marie_stat.number_of_LinphoneCallUpdatedByRemote+1)));

	LinphoneCallParams * params1a = linphone_core_create_call_params(marie->lc, marie_call);
	linphone_call_params_enable_video(params1a, TRUE);
	linphone_call_params_clear_custom_sdp_attributes(params1a);
	linphone_call_accept_update(marie_call, params1a);
	linphone_call_params_unref(params1a);

	linphone_config_set_int(linphone_core_get_config(pauline->lc), "sip", "defer_update_default", FALSE);
	linphone_config_set_int(linphone_core_get_config(marie->lc), "sip", "defer_update_default", FALSE);

	BC_ASSERT_TRUE( wait_for(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallUpdating,(pauline_stat.number_of_LinphoneCallUpdating+1)));
	BC_ASSERT_TRUE( wait_for(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallStreamsRunning,(pauline_stat.number_of_LinphoneCallStreamsRunning+1)));
	BC_ASSERT_TRUE( wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneCallStreamsRunning,(marie_stat.number_of_LinphoneCallStreamsRunning+1)));

	get_expected_encryption_from_call_params(pauline_call, marie_call, &encryption, &potentialConfigurationChosen);

	BC_ASSERT_TRUE( wait_for(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallStreamsRunning,(pauline_stat.number_of_LinphoneCallStreamsRunning+1+((potentialConfigurationChosen) ? 1 : 0))));
	BC_ASSERT_TRUE( wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneCallStreamsRunning,(marie_stat.number_of_LinphoneCallStreamsRunning+1+((potentialConfigurationChosen) ? 1 : 0))));

	liblinphone_tester_set_next_video_frame_decoded_cb(marie_call);
	liblinphone_tester_set_next_video_frame_decoded_cb(pauline_call);

	BC_ASSERT_TRUE( wait_for(pauline->lc,marie->lc,&pauline->stat.number_of_IframeDecoded,(pauline_stat.number_of_IframeDecoded+1)));
	BC_ASSERT_TRUE( wait_for(pauline->lc,marie->lc,&marie->stat.number_of_IframeDecoded,(marie_stat.number_of_IframeDecoded+1)));

	BC_ASSERT_TRUE(linphone_call_params_video_enabled(linphone_call_get_current_params(pauline_call)));
	BC_ASSERT_TRUE(linphone_call_params_video_enabled(linphone_call_get_current_params(marie_call)));

	BC_ASSERT_TRUE(linphone_call_log_video_enabled(linphone_call_get_call_log(pauline_call)));
	BC_ASSERT_TRUE(linphone_call_log_video_enabled(linphone_call_get_call_log(marie_call)));

	liblinphone_tester_check_rtcp(marie, pauline);

	// Check that encryption has not changed after sending update
	if (marie_call) {
		check_stream_encryption(marie_call);
		BC_ASSERT_EQUAL(linphone_call_params_get_media_encryption(linphone_call_get_current_params(marie_call)), encryption, int, "%i");
	}
	if (pauline_call) {
		check_stream_encryption(pauline_call);
		BC_ASSERT_EQUAL(linphone_call_params_get_media_encryption(linphone_call_get_current_params(pauline_call)), encryption, int, "%i");
	}

	linphone_config_set_int(linphone_core_get_config(pauline->lc), "sip", "defer_update_default", TRUE);
	linphone_config_set_int(linphone_core_get_config(marie->lc), "sip", "defer_update_default", TRUE);

	// Update call with unsupported cfg
	marie_stat = marie->stat;
	pauline_stat = pauline->stat;
	LinphoneCallParams * params2 = linphone_core_create_call_params(marie->lc, marie_call);
	linphone_call_params_add_custom_sdp_media_attribute(params2, LinphoneStreamTypeAudio, "acap", "86 crypto:654 AES_256_CM_HMAC_SHA1_80 inline:fWgTsTLqHc/xC7VQl7air+at/Ko1DpXudbS0KG3s UNAUTHENTICATED_SRTP");
	linphone_call_params_add_custom_sdp_media_attribute(params2, LinphoneStreamTypeAudio, "pcfg", "875 a=870|1|2|3|4|86 t=1");
	linphone_call_params_enable_video(params2, FALSE);
	linphone_call_update(marie_call, params2);
	linphone_call_params_unref(params2);
	BC_ASSERT_TRUE( wait_for(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallUpdatedByRemote,(pauline_stat.number_of_LinphoneCallUpdatedByRemote+1)));

	LinphoneCallParams * params2a = linphone_core_create_call_params(pauline->lc, pauline_call);
	linphone_call_params_enable_video(params2a, FALSE);
	linphone_call_params_clear_custom_sdp_attributes(params2a);
	linphone_call_accept_update(pauline_call, params2a);
	linphone_call_params_unref(params2a);

	linphone_config_set_int(linphone_core_get_config(pauline->lc), "sip", "defer_update_default", FALSE);
	linphone_config_set_int(linphone_core_get_config(marie->lc), "sip", "defer_update_default", FALSE);

	BC_ASSERT_TRUE( wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneCallUpdating,(marie_stat.number_of_LinphoneCallUpdating+1)));
	BC_ASSERT_TRUE( wait_for(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallStreamsRunning,(pauline_stat.number_of_LinphoneCallStreamsRunning+1)));
	BC_ASSERT_TRUE( wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneCallStreamsRunning,(marie_stat.number_of_LinphoneCallStreamsRunning+1)));

	encryption = LinphoneMediaEncryptionNone;
	potentialConfigurationChosen = false;
	get_expected_encryption_from_call_params(pauline_call, marie_call, &encryption, &potentialConfigurationChosen);

	BC_ASSERT_TRUE( wait_for(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallStreamsRunning,(pauline_stat.number_of_LinphoneCallStreamsRunning+1+((potentialConfigurationChosen) ? 1 : 0))));
	BC_ASSERT_TRUE( wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneCallStreamsRunning,(marie_stat.number_of_LinphoneCallStreamsRunning+1+((potentialConfigurationChosen) ? 1 : 0))));

	BC_ASSERT_FALSE(linphone_call_params_video_enabled(linphone_call_get_current_params(pauline_call)));
	BC_ASSERT_FALSE(linphone_call_params_video_enabled(linphone_call_get_current_params(marie_call)));

	BC_ASSERT_TRUE(linphone_call_log_video_enabled(linphone_call_get_call_log(pauline_call)));
	BC_ASSERT_TRUE(linphone_call_log_video_enabled(linphone_call_get_call_log(marie_call)));

	liblinphone_tester_check_rtcp(marie, pauline);

	// Check that encryption has not changed after sending update
	if (marie_call) {
		check_stream_encryption(marie_call);
		BC_ASSERT_EQUAL(linphone_call_params_get_media_encryption(linphone_call_get_current_params(marie_call)), encryption, int, "%i");
	}
	if (pauline_call) {
		check_stream_encryption(pauline_call);
		BC_ASSERT_EQUAL(linphone_call_params_get_media_encryption(linphone_call_get_current_params(pauline_call)), encryption, int, "%i");
	}

	end_call(marie, pauline);
	linphone_core_manager_destroy(marie);
	linphone_core_manager_destroy(pauline);
}

static void unencrypted_call_with_capability_negotiations_and_unsupported_crypto_in_sdp_update(void) {
	call_with_capability_negotiations_and_unsupported_crypto_in_sdp_update_base(FALSE);
}

static void srtp_call_with_capability_negotiations_and_unsupported_crypto_in_sdp_update(void) {
	call_with_capability_negotiations_and_unsupported_crypto_in_sdp_update_base(TRUE);
}

static void call_with_capability_negotiations_and_unsupported_crypto_in_sdp_base(bool_t pauline_supports_unencrypted) {
	const LinphoneMediaEncryption optionalEncryption = LinphoneMediaEncryptionSRTP;

	std::list<LinphoneMediaEncryption> enc_list {optionalEncryption};

	encryption_params pauline_enc_mgr_params;
	pauline_enc_mgr_params.encryption = LinphoneMediaEncryptionNone;
	pauline_enc_mgr_params.level = E_OPTIONAL;
	pauline_enc_mgr_params.preferences = enc_list;

	LinphoneCoreManager * pauline = create_core_mgr_with_capability_negotiation_setup((transport_supported(LinphoneTransportTls) ? "pauline_rc" : "pauline_tcp_rc"), pauline_enc_mgr_params, TRUE, FALSE, TRUE);
	if (pauline_supports_unencrypted) {
		// AES_CM_128_HMAC_SHA1_32 UNENCRYPTED_SRTCP is not supported hence it should not be put in the offer or accepted as answer
		linphone_core_set_srtp_crypto_suites(pauline->lc, "AES_CM_128_HMAC_SHA1_32 UNAUTHENTICATED_SRTP, AES_CM_256_HMAC_SHA1_32 UNENCRYPTED_SRTCP,AES_CM_128_HMAC_SHA1_80 UNENCRYPTED_SRTCP");
	}

	encryption_params marie_enc_mgr_params;
	marie_enc_mgr_params.encryption = LinphoneMediaEncryptionNone;
	marie_enc_mgr_params.level = E_OPTIONAL;
	marie_enc_mgr_params.preferences = enc_list;

	LinphoneCoreManager * marie = create_core_mgr_with_capability_negotiation_setup("marie_rc", marie_enc_mgr_params, TRUE, FALSE, TRUE);
	// AES_CM_128_HMAC_SHA1_32 UNENCRYPTED_SRTCP is not supported hence it should not be put in the offer or accepted as answer
	linphone_core_set_srtp_crypto_suites(marie->lc, "AES_CM_128_HMAC_SHA1_80 UNENCRYPTED_SRTCP,AES_CM_128_HMAC_SHA1_32 UNENCRYPTED_SRTCP,AES_CM_128_HMAC_SHA1_32 UNAUTHENTICATED_SRTP");

	BC_ASSERT_TRUE(linphone_core_media_encryption_supported(pauline->lc,optionalEncryption));
	BC_ASSERT_TRUE(linphone_core_media_encryption_supported(marie->lc,optionalEncryption));

	LinphoneCallParams *marie_params = linphone_core_create_call_params(marie->lc, NULL);
	linphone_call_params_enable_capability_negotiations (marie_params, TRUE);
	linphone_call_params_add_custom_sdp_attribute(marie_params, "acap", "870 crypto:99 AES_CM_256_HMAC_SHA1_32 inline:fWgTsTLqHc/xC7VQl7air+at/Ko1DpXudbS0KG3s UNENCRYPTED_SRTCP");
	linphone_call_params_add_custom_sdp_media_attribute(marie_params, LinphoneStreamTypeAudio, "pcfg", "36 a=870 t=1");
	LinphoneCallParams *pauline_params = linphone_core_create_call_params(pauline->lc, NULL);
	linphone_call_params_enable_capability_negotiations (pauline_params, TRUE);
	linphone_call_params_add_custom_sdp_attribute(pauline_params, "acap", "999 crypto:25 AES_CM_256_HMAC_SHA1_80 inline:fWgTsTLqHc/xC7VQl7air+at/Ko1DpXudbS0KG3s UNENCRYPTED_SRTP UNENCRYPTED_SRTCP");

	const LinphoneMediaEncryption expectedEncryption = (pauline_supports_unencrypted) ? optionalEncryption : LinphoneMediaEncryptionNone;
	encrypted_call_with_params_base(marie, pauline, expectedEncryption, marie_params, pauline_params, TRUE);
	if (linphone_core_get_current_call(marie->lc)) {
		end_call(marie, pauline);
	}

	linphone_call_params_unref(marie_params);
	linphone_call_params_unref(pauline_params);

	linphone_core_manager_destroy(marie);
	linphone_core_manager_destroy(pauline);
}

static void unencrypted_call_with_capability_negotiations_and_unsupported_crypto_in_sdp(void) {
	call_with_capability_negotiations_and_unsupported_crypto_in_sdp_base(FALSE);
}

static void srtp_call_with_capability_negotiations_and_unsupported_crypto_in_sdp(void) {
	call_with_capability_negotiations_and_unsupported_crypto_in_sdp_base(TRUE);
}

static void srtp_call_with_toggling_encryption(void) {
	call_with_toggling_encryption_base(LinphoneMediaEncryptionSRTP);
}

test_t srtp_capability_negotiation_tests[] = {
	TEST_NO_TAG("Simple SRTP call with capability negotiations with no encryption after resume", simple_srtp_call_with_capability_negotiations_with_no_encryption_after_resume),
	TEST_NO_TAG("Simple SRTP call with capability negotiations with ZRTP encryption after resume", simple_srtp_call_with_capability_negotiations_with_zrtp_encryption_after_resume),
	TEST_NO_TAG("Simple SRTP call with capability negotiations with DTLS SRTP encryption after resume", simple_srtp_call_with_capability_negotiations_with_dtls_srtp_encryption_after_resume),
	TEST_NO_TAG("Simple SRTP call with capability negotiations removed after update", simple_srtp_call_with_capability_negotiations_removed_after_update),
	TEST_NO_TAG("Simple SRTP call with capability negotiations with resume and media change", simple_srtp_call_with_capability_negotiations_with_resume_and_media_change),
	TEST_NO_TAG("SRTP call with suite mismatch and capability negotiations (caller unencrypted)", srtp_call_with_suite_mismatch_and_capability_negotiations_caller_unencrypted),
	TEST_NO_TAG("SRTP call with suite mismatch and capability negotiations (callee unencrypted)", srtp_call_with_suite_mismatch_and_capability_negotiations_callee_unencrypted),
	TEST_NO_TAG("SRTP call with different encryptions in call params", srtp_call_with_encryption_supported_in_call_params_only),
	TEST_NO_TAG("SRTP call with mandatory encryption", srtp_call_with_mandatory_encryption),
	TEST_NO_TAG("SRTP call with mandatory encryption and capability negotiation on both sides", srtp_call_with_mandatory_encryption_and_capability_negotiation_on_both_sides),
	TEST_NO_TAG("SRTP call with mandatory encryption and capability negotiation on callee side", srtp_call_with_mandatory_encryption_and_capability_negotiation_on_callee_side),
	TEST_NO_TAG("SRTP call with mandatory encryption and capability negotiation on caller side", srtp_call_with_mandatory_encryption_and_capability_negotiation_on_caller_side),
	TEST_NO_TAG("SRTP call from endpoint with mandatory encryption to endpoint with none", srtp_call_from_enc_to_no_enc),
	TEST_NO_TAG("SRTP call from endpoint with no encryption to endpoint with mandatory", srtp_call_from_no_enc_to_enc),
	TEST_NO_TAG("SRTP call from endpoint with optional encryption to endpoint with mandatory", srtp_call_from_opt_enc_to_enc),
	TEST_NO_TAG("SRTP call from endpoint with mandatory encryption to endpoint with optional", srtp_call_from_enc_to_opt_enc),
	TEST_NO_TAG("SRTP call from endpoint with optional encryption to endpoint with none", srtp_call_with_optional_encryption_on_caller),
	TEST_NO_TAG("SRTP call from endpoint with no encryption to endpoint with optional", srtp_call_with_optional_encryption_on_callee),
	TEST_NO_TAG("SRTP call with optional encryption on both sides", srtp_call_with_optional_encryption_on_both_sides),
	TEST_NO_TAG("SRTP video call with optional encryption on caller", srtp_video_call_with_optional_encryption_on_caller),
	TEST_NO_TAG("SRTP video call with optional encryption on callee", srtp_video_call_with_optional_encryption_on_callee),
	TEST_NO_TAG("SRTP video call with optional encryption on both sides", srtp_video_call_with_optional_encryption_on_both_sides),
	TEST_NO_TAG("SRTP call with toggling encryption", srtp_call_with_toggling_encryption)
};

test_t srtp_capability_negotiation_basic_tests[] = {
	TEST_NO_TAG("Call with default SRTP encryption", call_with_srtp_default_encryption),
	TEST_NO_TAG("Simple SRTP call with capability negotiations with reINVITE", simple_srtp_call_with_capability_negotiations_with_reinvite),
	TEST_NO_TAG("Simple SRTP call with capability negotiations without reINVITE", simple_srtp_call_with_capability_negotiations_without_reinvite),
	TEST_NO_TAG("SRTP unencrypted call and capability negotiations", unencrypted_srtp_call_with_capability_negotiations),
	TEST_NO_TAG("SRTP call and capability negotiations (caller unencrypted)", srtp_call_with_capability_negotiations_caller_unencrypted),
	TEST_NO_TAG("SRTP call and capability negotiations (callee unencrypted)", srtp_call_with_capability_negotiations_callee_unencrypted),
	TEST_NO_TAG("Unencrypted call with capability negotiations and unsupported crypto in SDP", unencrypted_call_with_capability_negotiations_and_unsupported_crypto_in_sdp),
	TEST_NO_TAG("SRTP call with capability negotiations and unsupported crypto in SDP", srtp_call_with_capability_negotiations_and_unsupported_crypto_in_sdp),
	TEST_NO_TAG("Unencrypted call with capability negotiations and unsupported crypto in SDP update", unencrypted_call_with_capability_negotiations_and_unsupported_crypto_in_sdp_update),
	TEST_NO_TAG("SRTP call with capability negotiations and unsupported crypto in SDP update", srtp_call_with_capability_negotiations_and_unsupported_crypto_in_sdp_update),
	TEST_NO_TAG("SRTP call with potential configuration same as actual one", srtp_call_with_potential_configuration_same_as_actual_configuration),
	TEST_NO_TAG("SRTP call started with video and capability negotiation", srtp_call_with_video_and_capability_negotiation)
};

test_suite_t srtp_capability_negotiation_test_suite = {"SRTP Capability Negotiation (Encryption Change)", NULL, NULL, liblinphone_tester_before_each, liblinphone_tester_after_each,
								sizeof(srtp_capability_negotiation_tests) / sizeof(srtp_capability_negotiation_tests[0]), srtp_capability_negotiation_tests};

test_suite_t srtp_capability_negotiation_basic_test_suite = {"SRTP Capability Negotiation (Basic)", NULL, NULL, liblinphone_tester_before_each, liblinphone_tester_after_each,
								sizeof(srtp_capability_negotiation_basic_tests) / sizeof(srtp_capability_negotiation_basic_tests[0]), srtp_capability_negotiation_basic_tests};
