chan_pjsip, app_transfer: Add TRANSFERSTATUSPROTOCOL variable
authorDan Cropp <dan@amtelco.com>
Fri, 22 Jan 2021 15:12:12 +0000 (09:12 -0600)
committerFriendly Automation <jenkins2@gerrit.asterisk.org>
Wed, 27 Jan 2021 17:42:42 +0000 (11:42 -0600)
When a Transfer/REFER is executed, TRANSFERSTATUSPROTOCOL variable is
0 when no protocl specific error
SIP example of failure, 3xx-6xx for the SIP error code received

This allows applications to perform actions based on the failure
reason.

ASTERISK-29252 #close
Reported-by: Dan Cropp

Change-Id: Ia6a94784b4925628af122409cdd733c9f29abfc4

apps/app_transfer.c
channels/chan_pjsip.c
doc/CHANGES-staging/app_transferprotocol.txt [new file with mode: 0644]
include/asterisk/channel.h
main/channel.c

index 4fe3dca..d152340 100644 (file)
                                                Transfer unsupported by channel driver.
                                        </value>
                                </variable>
+                               <variable name="TRANSFERSTATUSPROTOCOL">
+                                       <value name="0">
+                                               No error.
+                                       </value>
+                                       <value name="3xx-6xx">
+                                               SIP example - Error result code.
+                                       </value>
+                               </variable>
                        </variablelist>
                </description>
        </application>
@@ -85,6 +93,8 @@ static int transfer_exec(struct ast_channel *chan, const char *data)
        char *dest = NULL;
        char *status;
        char *parse;
+       int protocol = 0;
+       char status_protocol[20];
        AST_DECLARE_APP_ARGS(args,
                AST_APP_ARG(dest);
        );
@@ -92,6 +102,8 @@ static int transfer_exec(struct ast_channel *chan, const char *data)
        if (ast_strlen_zero((char *)data)) {
                ast_log(LOG_WARNING, "Transfer requires an argument ([Tech/]destination)\n");
                pbx_builtin_setvar_helper(chan, "TRANSFERSTATUS", "FAILURE");
+               snprintf(status_protocol, sizeof(status_protocol), "%d", protocol);
+               pbx_builtin_setvar_helper(chan, "TRANSFERSTATUSPROTOCOL", status_protocol);
                return 0;
        } else
                parse = ast_strdupa(data);
@@ -106,6 +118,8 @@ static int transfer_exec(struct ast_channel *chan, const char *data)
                /* Allow execution only if the Tech/destination agrees with the type of the channel */
                if (strncasecmp(ast_channel_tech(chan)->type, tech, len)) {
                        pbx_builtin_setvar_helper(chan, "TRANSFERSTATUS", "FAILURE");
+                       snprintf(status_protocol, sizeof(status_protocol), "%d", protocol);
+                       pbx_builtin_setvar_helper(chan, "TRANSFERSTATUSPROTOCOL", status_protocol);
                        return 0;
                }
        }
@@ -113,10 +127,14 @@ static int transfer_exec(struct ast_channel *chan, const char *data)
        /* Check if the channel supports transfer before we try it */
        if (!ast_channel_tech(chan)->transfer) {
                pbx_builtin_setvar_helper(chan, "TRANSFERSTATUS", "UNSUPPORTED");
+               snprintf(status_protocol, sizeof(status_protocol), "%d", protocol);
+               pbx_builtin_setvar_helper(chan, "TRANSFERSTATUSPROTOCOL", status_protocol);
                return 0;
        }
 
-       res = ast_transfer(chan, dest);
+       /* New transfer API returns a protocol code
+          SIP example, 0 = success, 3xx-6xx are sip error codes for the REFER */
+       res = ast_transfer_protocol(chan, dest, &protocol);
 
        if (res < 0) {
                status = "FAILURE";
@@ -126,7 +144,11 @@ static int transfer_exec(struct ast_channel *chan, const char *data)
                res = 0;
        }
 
+       snprintf(status_protocol, sizeof(status_protocol), "%d", protocol);
+       ast_debug(1, "ast_transfer channel %s TRANSFERSTATUS=%s, TRANSFERSTATUSPROTOCOL=%s\n",
+                 ast_channel_name(chan), status, status_protocol);
        pbx_builtin_setvar_helper(chan, "TRANSFERSTATUS", status);
+       pbx_builtin_setvar_helper(chan, "TRANSFERSTATUSPROTOCOL", status_protocol);
 
        return res;
 }
index 483cd3e..5c19443 100644 (file)
@@ -1982,12 +1982,17 @@ static void xfer_client_on_evsub_state(pjsip_evsub *sub, pjsip_event *event)
                        rdata = event->body.tsx_state.src.rdata;
                        msg = rdata->msg_info.msg;
 
-                       if (!pjsip_method_cmp(&msg->line.req.method, pjsip_get_notify_method())) {
-                               body = msg->body;
-                               if (body && !pj_stricmp2(&body->content_type.type, "message")
-                                       && !pj_stricmp2(&body->content_type.subtype, "sipfrag")) {
-                                       pjsip_parse_status_line((char *)body->data, body->len, &status_line);
+                       if (msg->type == PJSIP_REQUEST_MSG) {
+                               if (!pjsip_method_cmp(&msg->line.req.method, pjsip_get_notify_method())) {
+                                       body = msg->body;
+                                       if (body && !pj_stricmp2(&body->content_type.type, "message")
+                                               && !pj_stricmp2(&body->content_type.subtype, "sipfrag")) {
+                                               pjsip_parse_status_line((char *)body->data, body->len, &status_line);
+                                       }
                                }
+                       } else {
+                               status_line.code = msg->line.status.code;
+                               status_line.reason = msg->line.status.reason;
                        }
                } else {
                        status_line.code = 500;
@@ -2000,12 +2005,16 @@ static void xfer_client_on_evsub_state(pjsip_evsub *sub, pjsip_event *event)
                        res = -1;
 
                        /* If the subscription has terminated, return AST_TRANSFER_SUCCESS for 2XX.
-                        * Any other status code returns AST_TRANSFER_FAILED.
+                        * Return AST_TRANSFER_FAILED for any code < 200.
+                        * Otherwise, return the status code.
                         * 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) {
+                       if (status_line.code < 200) {
                                message = AST_TRANSFER_FAILED;
+                       } else if (status_line.code >= 300) {
+                               message = status_line.code;
                        }
+
                        /* If subscription not terminated and subscription is finished (status code >= 200)
                         * terminate it */
                        if (!is_last) {
diff --git a/doc/CHANGES-staging/app_transferprotocol.txt b/doc/CHANGES-staging/app_transferprotocol.txt
new file mode 100644 (file)
index 0000000..5d3521b
--- /dev/null
@@ -0,0 +1,6 @@
+Subject: chan_pjsip, app_transfer
+
+Added TRANSFERSTATUSPROTOCOL variable.  When transfer is performed,
+transfers can pass a protocol specific error code.
+Example, in SIP 3xx-6xx represent any SIP specific error received when
+performing a REFER.
index baefedd..40069b0 100644 (file)
@@ -2641,6 +2641,18 @@ int ast_settimeout_full(struct ast_channel *c, unsigned int rate, int (*func)(co
 int ast_transfer(struct ast_channel *chan, char *dest);
 
 /*!
+ * \brief Transfer a channel (if supported) receieve protocol result.
+ * \retval -1 on error
+ * \retval 0 if not supported
+ * \retval 1 if supported and requested
+ * \param chan current channel
+ * \param dest destination extension for transfer
+ * \param protocol specific error code in case of failure
+ * Example, sip 0 success, else sip error code
+ */
+int ast_transfer_protocol(struct ast_channel *chan, char *dest, int *protocol);
+
+/*!
  * \brief Inherits channel variable from parent to child channel
  * \param parent Parent channel
  * \param child Child channel
index 9730ed0..784ea28 100644 (file)
@@ -6475,8 +6475,30 @@ int ast_call(struct ast_channel *chan, const char *addr, int timeout)
 */
 int ast_transfer(struct ast_channel *chan, char *dest)
 {
+       int protocol;
+       return ast_transfer_protocol(chan, dest, &protocol);
+}
+
+/*!
+  \brief Transfer a call to dest, if the channel supports transfer
+
+  \param chan channel to transfer
+  \param dest destination to transfer to
+  \param protocol is the protocol result
+  SIP example, 0=success, 3xx-6xx is SIP error code
+
+  Called by:
+       \arg app_transfer
+       \arg the manager interface
+*/
+int ast_transfer_protocol(struct ast_channel *chan, char *dest, int *protocol)
+{
        int res = -1;
 
+       if (protocol) {
+               *protocol = 0;
+       }
+
        /* Stop if we're a zombie or need a soft hangup */
        ast_channel_lock(chan);
        if (!ast_test_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE) && !ast_check_hangup(chan)) {
@@ -6510,6 +6532,13 @@ int ast_transfer(struct ast_channel *chan, char *dest)
                                res = 1;
                        } else {
                                res = -1;
+                               /* Message can contain a protocol specific code
+                                  AST_TRANSFER_SUCCESS indicates success
+                                  Else, failure.  Protocol will be set to the failure reason.
+                                  SIP example, 0 is success, else error code 3xx-6xx */
+                               if (protocol) {
+                                       *protocol = *message;
+                               }
                        }
 
                        ast_frfree(fr);