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

<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: If6c27c757c66f71e8b75e3fe49da53ebe62395dc </div>
<div style="display:none"> Gerrit-Change-Number: 11216 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: Dan Cropp <dan@amtelco.com> </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>