chan_pjsip, app_transfer: Add TRANSFERSTATUSPROTOCOL variable

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
This commit is contained in:
Dan Cropp 2021-01-22 09:12:12 -06:00 committed by Friendly Automation
parent 6d980de282
commit 55891227e8
5 changed files with 86 additions and 8 deletions

View File

@ -69,6 +69,14 @@
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;
}

View 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) {

View File

@ -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.

View File

@ -2640,6 +2640,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

View File

@ -6474,9 +6474,31 @@ int ast_call(struct ast_channel *chan, const char *addr, int timeout)
\arg the manager interface
*/
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);