From a0e06eb066961d829cf0a3ae4e5eeaacd2570e6d Mon Sep 17 00:00:00 2001 From: Simon Morlat <simon.morlat@linphone.org> Date: Wed, 19 Mar 2025 13:14:12 +0100 Subject: [PATCH] In order to workaround issues with certain PBX, add a property [sip]/pause_before_transfer to request calls to be automatically paused before requesting a transfer. --- src/call/call.cpp | 36 ++++++++++++++++++++++-- src/conference/session/call-session.cpp | 5 ---- src/conference/session/call-session.h | 1 - src/conference/session/media-session.cpp | 2 +- tester/call_multi_tester.c | 35 +++++++++++++++++++---- 5 files changed, 64 insertions(+), 15 deletions(-) diff --git a/src/call/call.cpp b/src/call/call.cpp index a0580811cc..b962861f2f 100644 --- a/src/call/call.cpp +++ b/src/call/call.cpp @@ -18,7 +18,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include <bctoolbox/defs.h> +#include "bctoolbox/defs.h" #include "c-wrapper/c-wrapper.h" #include "call.h" @@ -1001,11 +1001,41 @@ LinphoneStatus Call::transfer(const shared_ptr<Call> &dest) { } LinphoneStatus Call::transfer(const string &dest) { - return getActiveSession()->transfer(dest); + auto address = getCore()->interpretUrl(dest, true); + return transfer(*address); } LinphoneStatus Call::transfer(const Address &dest) { - return getActiveSession()->transfer(dest); + LinphoneStatus ret = 0; + int pauseBeforeTransfer = + linphone_config_get_int(linphone_core_get_config(getCore()->getCCore()), "sip", "pause_before_transfer", 0); + if (pauseBeforeTransfer && getState() != CallSession::State::Paused) { + lInfo() << *this << " must be paused before transfer."; + ret = pause(); + if (ret == 0) { + /* request a future action to be executed when reaching the Paused state */ + getActiveSession()->addPendingAction([this, dest]() { + switch (getState()) { + case CallSession::State::Paused: + lInfo() << "Call is now paused, requesting the transfer."; + getActiveSession()->transfer(dest); + break; + case CallSession::State::Pausing: + return -1; // try again + break; + default: + // unexpected + lWarning() << "The call could not be paused, transfer request aborted."; + break; + } + /* Even in failure case we return 0 because we don't want the action to be re-tried. */ + return 0; + }); + } + } else { + ret = getActiveSession()->transfer(dest); + } + return ret; } LinphoneStatus Call::updateFromConference(const MediaSessionParams *msp) { diff --git a/src/conference/session/call-session.cpp b/src/conference/session/call-session.cpp index b6045f1bb3..d08b8cb6cf 100644 --- a/src/conference/session/call-session.cpp +++ b/src/conference/session/call-session.cpp @@ -1842,11 +1842,6 @@ LinphoneStatus CallSession::transfer(const Address &address) { return 0; } -LinphoneStatus CallSession::transfer(const string &dest) { - auto address = getCore()->interpretUrl(dest, true); - return transfer(*address); -} - LinphoneStatus CallSession::update(const CallSessionParams *csp, const UpdateMethod method, const string &subject, diff --git a/src/conference/session/call-session.h b/src/conference/session/call-session.h index d48c6425c2..79651523f8 100644 --- a/src/conference/session/call-session.h +++ b/src/conference/session/call-session.h @@ -144,7 +144,6 @@ public: LinphoneStatus terminate(const LinphoneErrorInfo *ei = nullptr); LinphoneStatus transfer(const std::shared_ptr<CallSession> &dest); LinphoneStatus transfer(const Address &dest); - LinphoneStatus transfer(const std::string &dest); LinphoneStatus update(const CallSessionParams *csp, const UpdateMethod method = UpdateMethod::Default, const std::string &subject = "", diff --git a/src/conference/session/media-session.cpp b/src/conference/session/media-session.cpp index 6bd8a71543..6c20061c6f 100644 --- a/src/conference/session/media-session.cpp +++ b/src/conference/session/media-session.cpp @@ -23,7 +23,7 @@ #include <algorithm> -#include <bctoolbox/defs.h> +#include "bctoolbox/defs.h" #include "mediastreamer2/mediastream.h" #include "mediastreamer2/mseventqueue.h" diff --git a/tester/call_multi_tester.c b/tester/call_multi_tester.c index ccc89eb63c..e31c10ab1c 100644 --- a/tester/call_multi_tester.c +++ b/tester/call_multi_tester.c @@ -253,7 +253,7 @@ static void incoming_call_accepted_when_outgoing_call_in_outgoing_ringing_early_ incoming_call_accepted_when_outgoing_call_in_state(LinphoneCallOutgoingEarlyMedia); } -static void _simple_call_transfer(bool_t transferee_is_default_account) { +static void _simple_call_transfer(bool_t transferee_is_default_account, bool_t pause_before_transfer) { LinphoneCoreManager *marie = linphone_core_manager_new("marie_dual_proxy_rc"); LinphoneCoreManager *pauline = linphone_core_manager_new("pauline_tcp_rc"); LinphoneCoreManager *laure = linphone_core_manager_new(get_laure_rc()); @@ -269,6 +269,10 @@ static void _simple_call_transfer(bool_t transferee_is_default_account) { char *marie_identity = linphone_address_as_string(marie->identity); + if (pause_before_transfer) { + linphone_config_set_int(linphone_core_get_config(pauline->lc), "sip", "pause_before_transfer", 1); + } + // Marie calls Pauline BC_ASSERT_TRUE(call(marie, pauline)); marie_calling_pauline = linphone_core_get_current_call(marie->lc); @@ -293,10 +297,26 @@ static void _simple_call_transfer(bool_t transferee_is_default_account) { // Pauline transfers call from Marie to Laure linphone_call_transfer(pauline_called_by_marie, laure_identity); bctbx_free(laure_identity); + if (pause_before_transfer) { + BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallPausing, 1, 2000)); + BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallPaused, 1, 10000)); + BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallPausedByRemote, 1, 10000)); + } + BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallRefered, 1, 2000)); + if (!pause_before_transfer) { + BC_ASSERT_EQUAL(pauline->stat.number_of_LinphoneCallPausing, 0, int, "%i"); + BC_ASSERT_EQUAL(pauline->stat.number_of_LinphoneCallPaused, 0, int, "%i"); + BC_ASSERT_EQUAL(marie->stat.number_of_LinphoneCallPausedByRemote, 0, int, "%i"); + } + // marie pausing pauline BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallPausing, 1, 2000)); - BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallPausedByRemote, 1, 2000)); + if (!pause_before_transfer) { + BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallPausedByRemote, 1, 2000)); + } else { + // Pauline's call is already in Paused state, it won't transition to PausedByRemote. + } BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallPaused, 1, 2000)); // marie calling laure BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallOutgoingProgress, 1, 2000)); @@ -343,11 +363,15 @@ end: } static void simple_call_transfer(void) { - _simple_call_transfer(TRUE); + _simple_call_transfer(TRUE, FALSE); } static void simple_call_transfer_from_non_default_account(void) { - _simple_call_transfer(FALSE); + _simple_call_transfer(FALSE, FALSE); +} + +static void simple_call_transfer_with_pause_before(void) { + _simple_call_transfer(TRUE, TRUE); } static void unattended_call_transfer(void) { @@ -1535,7 +1559,7 @@ end: linphone_core_manager_destroy(pauline); } -test_t multi_call_tests[] = { +static test_t multi_call_tests[] = { TEST_NO_TAG("Call waiting indication", call_waiting_indication), TEST_NO_TAG("Call waiting indication with privacy", call_waiting_indication_with_privacy), TEST_NO_TAG("Second call rejected if first one in progress", second_call_rejected_if_first_one_in_progress), @@ -1555,6 +1579,7 @@ test_t multi_call_tests[] = { // call_with_ice_negotiations_ending_while_accepting_call_back_to_back, "ICE"), TEST_NO_TAG("Simple call transfer", simple_call_transfer), TEST_NO_TAG("Simple call transfer from non default account", simple_call_transfer_from_non_default_account), + TEST_NO_TAG("Simple call transfer with pause before", simple_call_transfer_with_pause_before), TEST_NO_TAG("Unattended call transfer", unattended_call_transfer), TEST_NO_TAG("Unattended call transfer with error", unattended_call_transfer_with_error), TEST_NO_TAG("Call transfer existing outgoing call", call_transfer_existing_call_outgoing_call), -- GitLab