[Asterisk-code-review] chan_pjsip: Transmit REFER waits for the REFER result setting TRANSF... (...asterisk[master])
Dan Cropp
asteriskteam at digium.com
Tue Apr 2 14:46:08 CDT 2019
Dan Cropp has uploaded this change for review. ( https://gerrit.asterisk.org/c/asterisk/+/11216
Change subject: chan_pjsip: Transmit REFER waits for the REFER result setting TRANSFERSTATUS
......................................................................
chan_pjsip: Transmit REFER waits for the REFER result setting TRANSFERSTATUS
Previously, when a Transfer (REFER) was performed, chan_pjsip would set
the TRANSFERSTATUS to SUCCESS when the REFER was queued up. This did not
reflect a successful/unsuccessful transfer the way chan_sip did.
Added a callback module to process the refer subscription information.
ASTERISK-26968 #close
Reported-by: Dan Cropp
Change-Id: If6c27c757c66f71e8b75e3fe49da53ebe62395dc
---
M channels/chan_pjsip.c
1 file changed, 123 insertions(+), 2 deletions(-)
git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/16/11216/1
diff --git a/channels/chan_pjsip.c b/channels/chan_pjsip.c
index 55cd9ed..c6dc7c2 100644
--- a/channels/chan_pjsip.c
+++ b/channels/chan_pjsip.c
@@ -1886,6 +1886,117 @@
ast_queue_control_data(session->channel, AST_CONTROL_TRANSFER, &message, sizeof(message));
}
+/*! \brief REFER Callback module, used to attach session data structure to subscription */
+static pjsip_module refer_callback_module = {
+ .name = { "REFER Callback", 14 },
+ .id = -1,
+};
+
+/* Callback for transfer subscription. */
+static void xfer_client_on_evsub_state(pjsip_evsub *sub, pjsip_event *event)
+{
+ struct ast_sip_session *session;
+ struct ast_channel *chan = NULL;
+ enum ast_control_transfer message = AST_TRANSFER_SUCCESS;
+ int res = 0;
+
+ if (!event) {
+ return;
+ }
+ if (!(session = (struct ast_sip_session *)pjsip_evsub_get_mod_data(sub, refer_callback_module.id))) {
+ return;
+ }
+ if (!(session->channel) || !(chan = ast_channel_ref(session->channel))) {
+ return;
+ }
+
+ if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACCEPTED) {
+ /* Check if subscription is suppressed and terminate and send completion code, if so. */
+ pjsip_rx_data *rdata;
+ pjsip_generic_string_hdr *refer_sub;
+ const pj_str_t REFER_SUB = { "Refer-Sub", 9 };
+
+ ast_debug(3, "Transfer accepted\n");
+
+ /* Check if response message */
+ if ((event->type == PJSIP_EVENT_TSX_STATE) && (event->body.tsx_state.type == PJSIP_EVENT_RX_MSG)) {
+ rdata = event->body.tsx_state.src.rdata;
+
+ /* Find Refer-Sub header */
+ refer_sub = (pjsip_generic_string_hdr *)
+ pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &REFER_SUB, NULL);
+
+ /* Check if subscription is suppressed */
+ if ((refer_sub) && !(pj_stricmp2(&refer_sub->hvalue, "false"))) {
+ /* Since no subscription is desired, assume that call has been transferred successfully. */
+ /* Terminate subscription. */
+ pjsip_evsub_terminate(sub, PJ_TRUE);
+ res = -1;
+ }
+ }
+ } else if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACTIVE ||
+ pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
+ /* Check for NOTIFY complete or error. */
+ pjsip_msg *msg;
+ pjsip_msg_body *body;
+ pjsip_status_line status_line = { .code = PJSIP_SC_NULL };
+ pj_bool_t is_last;
+ pj_status_t status;
+
+ if (event->type == PJSIP_EVENT_TSX_STATE && event->body.tsx_state.type == PJSIP_EVENT_RX_MSG) {
+ pjsip_rx_data *rdata;
+
+ rdata = event->body.tsx_state.src.rdata;
+ msg = rdata->msg_info.msg;
+
+ if (pjsip_method_cmp(&msg->line.req.method, pjsip_get_notify_method()) == 0) {
+ body = msg->body;
+ if (body && (pj_stricmp2(&body->content_type.type, "message") == 0)
+ && (pj_stricmp2(&body->content_type.subtype, "sipfrag") == 0)) {
+
+ pjsip_parse_status_line((char *)body->data, body->len, &status_line);
+ }
+ }
+ } else {
+ status_line.code = 500;
+ status_line.reason = *pjsip_get_status_text(500);
+ }
+
+ is_last = (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED);
+ /* If the status code is >= 200, the subscription is finished. */
+ if ((status_line.code >= 200) || (is_last)) {
+ res = -1;
+
+ /* If the subscription has terminated, return AST_TRANSFER_SUCCESS for 2XX. */
+ /* Any other status code returns AST_TRANSFER_FAILED. */
+ /* The subscription should not terminate for any code < 200, but if it does, that constitutes a failure. */
+ if ((status_line.code < 200) || (status_line.code >= 300)) {
+ message = AST_TRANSFER_FAILED;
+ }
+ /* If the subscription is not terminated, terminate it now. */
+ if (!is_last) {
+ pjsip_tx_data *tdata;
+
+ status = pjsip_evsub_initiate(sub, pjsip_get_subscribe_method(), 0, &tdata);
+ if (status == PJ_SUCCESS) {
+ pjsip_evsub_send_request(sub, tdata);
+ }
+ }
+ /* Finished. Remove session from subscription */
+ pjsip_evsub_set_mod_data(sub, refer_callback_module.id, NULL);
+ ast_debug(3, "Transfer completed: %d %.*s (%s)\n",
+ status_line.code,
+ (int)status_line.reason.slen, status_line.reason.ptr,
+ (message == AST_TRANSFER_SUCCESS) ? "Success" : "Failure");
+ }
+ }
+
+ if (res) {
+ ast_queue_control_data(chan, AST_CONTROL_TRANSFER, &message, sizeof(message));
+ }
+ ast_channel_unref(chan);
+}
+
static void transfer_refer(struct ast_sip_session *session, const char *target)
{
pjsip_evsub *sub;
@@ -1894,14 +2005,20 @@
pjsip_tx_data *packet;
const char *ref_by_val;
char local_info[pj_strlen(&session->inv_session->dlg->local.info_str) + 1];
+ struct pjsip_evsub_user xfer_cb;
- if (pjsip_xfer_create_uac(session->inv_session->dlg, NULL, &sub) != PJ_SUCCESS) {
+ pj_bzero(&xfer_cb, sizeof(xfer_cb));
+ xfer_cb.on_evsub_state = &xfer_client_on_evsub_state;
+
+ if (pjsip_xfer_create_uac(session->inv_session->dlg, &xfer_cb, &sub) != PJ_SUCCESS) {
message = AST_TRANSFER_FAILED;
ast_queue_control_data(session->channel, AST_CONTROL_TRANSFER, &message, sizeof(message));
return;
}
+ pjsip_evsub_set_mod_data(sub, refer_callback_module.id, session);
+
if (pjsip_xfer_initiate(sub, pj_cstr(&tmp, target), &packet) != PJ_SUCCESS) {
message = AST_TRANSFER_FAILED;
ast_queue_control_data(session->channel, AST_CONTROL_TRANSFER, &message, sizeof(message));
@@ -1919,7 +2036,6 @@
}
pjsip_xfer_send_request(sub, packet);
- ast_queue_control_data(session->channel, AST_CONTROL_TRANSFER, &message, sizeof(message));
}
static int transfer(void *data)
@@ -3127,6 +3243,8 @@
goto end;
}
+ ast_sip_register_service(&refer_callback_module);
+
ast_sip_session_register_supplement(&chan_pjsip_supplement);
ast_sip_session_register_supplement(&chan_pjsip_supplement_response);
@@ -3163,6 +3281,7 @@
ast_sip_session_unregister_supplement(&chan_pjsip_supplement_response);
ast_sip_session_unregister_supplement(&chan_pjsip_supplement);
ast_sip_session_unregister_supplement(&call_pickup_supplement);
+ ast_sip_unregister_service(&refer_callback_module);
ast_custom_function_unregister(&dtmf_mode_function);
ast_custom_function_unregister(&media_offer_function);
ast_custom_function_unregister(&chan_pjsip_dial_contacts_function);
@@ -3188,6 +3307,8 @@
ast_sip_session_unregister_supplement(&chan_pjsip_ack_supplement);
ast_sip_session_unregister_supplement(&call_pickup_supplement);
+ ast_sip_unregister_service(&refer_callback_module);
+
ast_custom_function_unregister(&dtmf_mode_function);
ast_custom_function_unregister(&media_offer_function);
ast_custom_function_unregister(&chan_pjsip_dial_contacts_function);
--
To view, visit https://gerrit.asterisk.org/c/asterisk/+/11216
To unsubscribe, or for help writing mail filters, visit https://gerrit.asterisk.org/settings
Gerrit-Project: asterisk
Gerrit-Branch: master
Gerrit-Change-Id: If6c27c757c66f71e8b75e3fe49da53ebe62395dc
Gerrit-Change-Number: 11216
Gerrit-PatchSet: 1
Gerrit-Owner: Dan Cropp <dan at amtelco.com>
Gerrit-MessageType: newchange
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.digium.com/pipermail/asterisk-code-review/attachments/20190402/cef7b651/attachment-0001.html>
More information about the asterisk-code-review
mailing list