<p>George Joseph <strong>merged</strong> this change.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/11243">View Change</a></p><div style="white-space:pre-wrap">Approvals:
  George Joseph: Looks good to me, approved; Approved for Submit

</div><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>Now depends on res_pjsip_pubsub so call transfer progress can be monitored<br>and reported<br><br>ASTERISK-26968 #close<br>Reported-by: Dan Cropp<br><br>Change-Id: If6c27c757c66f71e8b75e3fe49da53ebe62395dc<br>---<br>M channels/chan_pjsip.c<br>A doc/UPGRADE-staging/chan_pjsip_refer_fix.txt<br>2 files changed, 143 insertions(+), 3 deletions(-)<br><br></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 d1f7b6a..8ab1549 100644</span><br><span>--- a/channels/chan_pjsip.c</span><br><span>+++ b/channels/chan_pjsip.c</span><br><span>@@ -28,6 +28,7 @@</span><br><span> /*** MODULEINFO</span><br><span>        <depend>pjproject</depend></span><br><span>       <depend>res_pjsip</depend></span><br><span style="color: hsl(120, 100%, 40%);">+        <depend>res_pjsip_pubsub</depend></span><br><span>        <depend>res_pjsip_session</depend></span><br><span>       <support_level>core</support_level></span><br><span>  ***/</span><br><span>@@ -1894,6 +1895,130 @@</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%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Callback function to report status of implicit REFER-NOTIFY subscription.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This function will be called on any state change in the REFER-NOTIFY subscription.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Its primary purpose is to report SUCCESS/FAILURE of a transfer initiated via</span><br><span style="color: hsl(120, 100%, 40%);">+ * \ref transfer_refer as well as to terminate the subscription, if necessary.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</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%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   session = pjsip_evsub_get_mod_data(sub, refer_callback_module.id);</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!session) {</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%);">+   chan = session->channel;</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!chan) {</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 on channel %s\n", ast_channel_name(chan));</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_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. If it is, the far end will not terminate it,</span><br><span style="color: hsl(120, 100%, 40%);">+                   * and the subscription will remain active until it times out.  Terminating it here</span><br><span style="color: hsl(120, 100%, 40%);">+                    * eliminates the unnecessary timeout.</span><br><span style="color: hsl(120, 100%, 40%);">+                         */</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())) {</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")</span><br><span style="color: hsl(120, 100%, 40%);">+                                        && !pj_stricmp2(&body->content_type.subtype, "sipfrag")) {</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,</span><br><span style="color: hsl(120, 100%, 40%);">+                        * 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 subscription not terminated and subscription is finished (status code >= 200)</span><br><span style="color: hsl(120, 100%, 40%);">+                         * terminate it */</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 channel %s completed: %d %.*s (%s)\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                                       ast_channel_name(chan),</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(session->channel, AST_CONTROL_TRANSFER, &message, sizeof(message));</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> static void transfer_refer(struct ast_sip_session *session, const char *target)</span><br><span> {</span><br><span>       pjsip_evsub *sub;</span><br><span>@@ -1902,14 +2027,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>@@ -1927,7 +2058,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>@@ -3144,6 +3274,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>@@ -3180,6 +3312,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>@@ -3205,6 +3338,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>@@ -3223,5 +3358,5 @@</span><br><span>      .load = load_module,</span><br><span>         .unload = unload_module,</span><br><span>     .load_pri = AST_MODPRI_CHANNEL_DRIVER,</span><br><span style="color: hsl(0, 100%, 40%);">-  .requires = "res_pjsip,res_pjsip_session",</span><br><span style="color: hsl(120, 100%, 40%);">+  .requires = "res_pjsip,res_pjsip_session,res_pjsip_pubsub",</span><br><span> );</span><br><span>diff --git a/doc/UPGRADE-staging/chan_pjsip_refer_fix.txt b/doc/UPGRADE-staging/chan_pjsip_refer_fix.txt</span><br><span>new file mode 100644</span><br><span>index 0000000..301930f</span><br><span>--- /dev/null</span><br><span>+++ b/doc/UPGRADE-staging/chan_pjsip_refer_fix.txt</span><br><span>@@ -0,0 +1,5 @@</span><br><span style="color: hsl(120, 100%, 40%);">+Subject: chan_pjsip</span><br><span style="color: hsl(120, 100%, 40%);">+Subject: Core</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+res_pjsip_pubsub is now required so call transfer progress can be monitored</span><br><span style="color: hsl(120, 100%, 40%);">+and reported in the channel variable TRANSFERSTATUS.</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/c/asterisk/+/11243">change 11243</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/+/11243"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: 16 </div>
<div style="display:none"> Gerrit-Change-Id: If6c27c757c66f71e8b75e3fe49da53ebe62395dc </div>
<div style="display:none"> Gerrit-Change-Number: 11243 </div>
<div style="display:none"> Gerrit-PatchSet: 6 </div>
<div style="display:none"> Gerrit-Owner: Dan Cropp <dan@amtelco.com> </div>
<div style="display:none"> Gerrit-Reviewer: Dan Cropp <dan@amtelco.com> </div>
<div style="display:none"> Gerrit-Reviewer: Friendly Automation </div>
<div style="display:none"> Gerrit-Reviewer: George Joseph <gjoseph@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Joshua Colp <jcolp@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Kevin Harwell <kharwell@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Matthew Fredrickson <creslin@digium.com> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>