From fa04627a04e991794e7d74032b4385b126854d1f Mon Sep 17 00:00:00 2001
From: Eloi BAIL <eloi.bail@savoirfairelinux.com>
Date: Tue, 24 Nov 2015 11:01:31 -0500
Subject: [PATCH] sip: fix attended transfers

Attended transfers led to an assertion in pjsip. According to high-level
librairie pjsua, unused in Ring, and specially function pjsua_call_on_incoming,
it looks like we should not call pjsip_inv_answer when replace header is
present. This function actually leads to the pjsip assertion.
pjsip_inv_initial_answer should be rather used.

NOTIFY is still missing.

Change-Id: I2e4a09533bd33dd32b2cc61a7d9737dc1fe79738
Tuleap: #122
---
 src/sip/sipvoiplink.cpp | 70 ++++++++++++++++++++---------------------
 1 file changed, 34 insertions(+), 36 deletions(-)

diff --git a/src/sip/sipvoiplink.cpp b/src/sip/sipvoiplink.cpp
index 7f9c324f40..196c52c080 100644
--- a/src/sip/sipvoiplink.cpp
+++ b/src/sip/sipvoiplink.cpp
@@ -399,53 +399,51 @@ transaction_request_cb(pjsip_rx_data *rdata)
     // Check if call has been transfered
     pjsip_tx_data *tdata = 0;
 
-    // If Replace header present
-    if (replaced_dlg) {
-        // Always answer the new INVITE with 200 if the replaced call is in early or confirmed state.
-        if (pjsip_inv_answer(call->inv.get(), PJSIP_SC_OK, NULL, NULL, &response) == PJ_SUCCESS) {
-            if (pjsip_inv_send_msg(call->inv.get(), response) != PJ_SUCCESS)
-                call->inv.reset(); // FIXME: not sure if we need to continue
-        }
+    if (pjsip_inv_initial_answer(call->inv.get(), rdata, PJSIP_SC_TRYING, NULL, NULL, &tdata) != PJ_SUCCESS) {
+        RING_ERR("Could not create answer TRYING");
+        return PJ_FALSE;
+    }
 
-        // Get the INVITE session associated with the replaced dialog.
-        pjsip_inv_session *replaced_inv = pjsip_dlg_get_inv_session(replaced_dlg);
+    if (pjsip_inv_send_msg(call->inv.get(), tdata) != PJ_SUCCESS) {
+        RING_ERR("Could not send msg TRYING");
+        return PJ_FALSE;
+    }
 
-        // Disconnect the "replaced" INVITE session.
-        if (pjsip_inv_end_session(replaced_inv, PJSIP_SC_GONE, NULL, &tdata) == PJ_SUCCESS && tdata) {
-            pjsip_inv_send_msg(replaced_inv, tdata);
-        }
-    } else { // Proceed with normal call flow
-        if (pjsip_inv_initial_answer(call->inv.get(), rdata, PJSIP_SC_TRYING, NULL, NULL, &tdata) != PJ_SUCCESS) {
-            RING_ERR("Could not create answer TRYING");
-            return PJ_FALSE;
-        }
+    call->setState(Call::ConnectionState::TRYING);
 
-        if (pjsip_inv_send_msg(call->inv.get(), tdata) != PJ_SUCCESS) {
-            RING_ERR("Could not send msg TRYING");
-            return PJ_FALSE;
-        }
+    if (pjsip_inv_answer(call->inv.get(), PJSIP_SC_RINGING, NULL, NULL, &tdata) != PJ_SUCCESS) {
+        RING_ERR("Could not create answer RINGING");
+        return PJ_FALSE;
+    }
 
-        call->setState(Call::ConnectionState::TRYING);
+    // contactStr must stay in scope as long as tdata
+    const pj_str_t contactStr(account->getContactHeader(transport->get()));
+    sip_utils::addContactHeader(&contactStr, tdata);
 
-        if (pjsip_inv_answer(call->inv.get(), PJSIP_SC_RINGING, NULL, NULL, &tdata) != PJ_SUCCESS) {
-            RING_ERR("Could not create answer RINGING");
-            return PJ_FALSE;
-        }
+    if (pjsip_inv_send_msg(call->inv.get(), tdata) != PJ_SUCCESS) {
+        RING_ERR("Could not send msg RINGING");
+        return PJ_FALSE;
+    }
 
-        // contactStr must stay in scope as long as tdata
-        const pj_str_t contactStr(account->getContactHeader(transport->get()));
-        sip_utils::addContactHeader(&contactStr, tdata);
+    call->setState(Call::ConnectionState::RINGING);
 
-        if (pjsip_inv_send_msg(call->inv.get(), tdata) != PJ_SUCCESS) {
-            RING_ERR("Could not send msg RINGING");
-            return PJ_FALSE;
-        }
+    Manager::instance().incomingCall(*call, account_id);
 
-        call->setState(Call::ConnectionState::RINGING);
+    if (replaced_dlg) {
+        // Get the INVITE session associated with the replaced dialog.
+        auto replaced_inv = pjsip_dlg_get_inv_session(replaced_dlg);
 
-        Manager::instance().incomingCall(*call, account_id);
+        // Disconnect the "replaced" INVITE session.
+        if (pjsip_inv_end_session(replaced_inv, PJSIP_SC_GONE, nullptr, &tdata) == PJ_SUCCESS && tdata) {
+            pjsip_inv_send_msg(replaced_inv, tdata);
+        }
+
+        // Close call at application level
+        if (auto oldCallid = static_cast<SIPCall*>(replaced_inv->mod_data[mod_ua_.id]))
+            oldCallid->hangup(PJSIP_SC_OK);
     }
 
+
     return PJ_FALSE;
 }
 
-- 
GitLab