summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/asterisk/res_pjsip_session.h11
-rw-r--r--res/res_pjsip_refer.c8
-rw-r--r--res/res_pjsip_session.c26
-rw-r--r--res/res_pjsip_session.exports.in1
4 files changed, 46 insertions, 0 deletions
diff --git a/include/asterisk/res_pjsip_session.h b/include/asterisk/res_pjsip_session.h
index 10e55f133..e2a90662e 100644
--- a/include/asterisk/res_pjsip_session.h
+++ b/include/asterisk/res_pjsip_session.h
@@ -155,6 +155,10 @@ struct ast_sip_session {
struct ast_sip_aor *aor;
/*! From header saved at invite creation */
pjsip_fromto_hdr *saved_from_hdr;
+ /*! Whether the end of the session should be deferred */
+ unsigned int defer_end:1;
+ /*! Session end (remote hangup) requested while termination deferred */
+ unsigned int ended_while_deferred:1;
};
typedef int (*ast_sip_session_request_creation_cb)(struct ast_sip_session *session, pjsip_tx_data *tdata);
@@ -484,6 +488,13 @@ int ast_sip_session_defer_termination(struct ast_sip_session *session);
void ast_sip_session_defer_termination_cancel(struct ast_sip_session *session);
/*!
+ * \brief End the session if it had been previously deferred
+ *
+ * \param session The session to end if it had been deferred
+ */
+void ast_sip_session_end_if_deferred(struct ast_sip_session *session);
+
+/*!
* \brief Register an SDP handler
*
* An SDP handler is responsible for parsing incoming SDP streams and ensuring that
diff --git a/res/res_pjsip_refer.c b/res/res_pjsip_refer.c
index 46295387d..456e09dd8 100644
--- a/res/res_pjsip_refer.c
+++ b/res/res_pjsip_refer.c
@@ -543,6 +543,7 @@ static int refer_attended_task(void *data)
}
}
+ ast_sip_session_end_if_deferred(attended->transferer);
if (response != 200) {
if (!ast_sip_push_task(attended->transferer->serializer,
defer_termination_cancel, attended->transferer)) {
@@ -772,6 +773,7 @@ static int refer_incoming_attended_request(struct ast_sip_session *session, pjsi
/* Push it to the other session, which will have both channels with minimal locking */
if (ast_sip_push_task(other_session->serializer, refer_attended_task, attended)) {
+ ast_sip_session_end_if_deferred(session);
ast_sip_session_defer_termination_cancel(session);
ao2_cleanup(attended);
return 500;
@@ -810,9 +812,12 @@ static int refer_incoming_attended_request(struct ast_sip_session *session, pjsi
response = xfer_response_code2sip(ast_bridge_transfer_blind(1, session->channel,
"external_replaces", context, refer_blind_callback, &refer));
+
+ ast_sip_session_end_if_deferred(session);
if (response != 200) {
ast_sip_session_defer_termination_cancel(session);
}
+
return response;
}
}
@@ -865,9 +870,12 @@ static int refer_incoming_blind_request(struct ast_sip_session *session, pjsip_r
response = xfer_response_code2sip(ast_bridge_transfer_blind(1, session->channel,
exten, context, refer_blind_callback, &refer));
+
+ ast_sip_session_end_if_deferred(session);
if (response != 200) {
ast_sip_session_defer_termination_cancel(session);
}
+
return response;
}
diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c
index eb0d1f054..2949d5b2a 100644
--- a/res/res_pjsip_session.c
+++ b/res/res_pjsip_session.c
@@ -1903,6 +1903,9 @@ int ast_sip_session_defer_termination(struct ast_sip_session *session)
session->defer_terminate = 1;
+ session->defer_end = 1;
+ session->ended_while_deferred = 0;
+
session->scheduled_termination.id = 0;
ao2_ref(session, +1);
session->scheduled_termination.user_data = session;
@@ -1940,6 +1943,7 @@ void ast_sip_session_defer_termination_cancel(struct ast_sip_session *session)
/* Already canceled or timer fired. */
return;
}
+
session->defer_terminate = 0;
if (session->terminate_while_deferred) {
@@ -1951,6 +1955,22 @@ void ast_sip_session_defer_termination_cancel(struct ast_sip_session *session)
sip_session_defer_termination_stop_timer(session);
}
+void ast_sip_session_end_if_deferred(struct ast_sip_session *session)
+{
+ if (!session->defer_end) {
+ return;
+ }
+
+ session->defer_end = 0;
+
+ if (session->ended_while_deferred) {
+ /* Complete the session end started by the remote hangup. */
+ ast_debug(3, "Ending session (%p) after being deferred\n", session);
+ session->ended_while_deferred = 0;
+ session_end(session);
+ }
+}
+
struct ast_sip_session *ast_sip_dialog_get_session(pjsip_dialog *dlg)
{
pjsip_inv_session *inv_session = pjsip_dlg_get_inv_session(dlg);
@@ -2668,6 +2688,12 @@ static void session_inv_on_state_changed(pjsip_inv_session *inv, pjsip_event *e)
}
if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
+ if (session->defer_end) {
+ ast_debug(3, "Deferring session (%p) end\n", session);
+ session->ended_while_deferred = 1;
+ return;
+ }
+
if (ast_sip_push_task(session->serializer, session_end, session)) {
/* Do it anyway even though this is not the right thread. */
session_end(session);
diff --git a/res/res_pjsip_session.exports.in b/res/res_pjsip_session.exports.in
index a39485e66..fdfc5fb47 100644
--- a/res/res_pjsip_session.exports.in
+++ b/res/res_pjsip_session.exports.in
@@ -3,6 +3,7 @@
LINKER_SYMBOL_PREFIXast_sip_session_terminate;
LINKER_SYMBOL_PREFIXast_sip_session_defer_termination;
LINKER_SYMBOL_PREFIXast_sip_session_defer_termination_cancel;
+ LINKER_SYMBOL_PREFIXast_sip_session_end_if_deferred;
LINKER_SYMBOL_PREFIXast_sip_session_register_sdp_handler;
LINKER_SYMBOL_PREFIXast_sip_session_unregister_sdp_handler;
LINKER_SYMBOL_PREFIXast_sip_session_register_supplement;