diff --git a/apps/app_transfer.c b/apps/app_transfer.c index 4fe3dcac0667ca02a7b8ba507fa962c128549f4d..d152340baaf15be7f8978af7ac5d9d0e09515fb8 100644 --- a/apps/app_transfer.c +++ b/apps/app_transfer.c @@ -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; } diff --git a/channels/chan_pjsip.c b/channels/chan_pjsip.c index 483cd3ef0094688283773244ea6ec43178d3ea92..5c194434685f8284240d979ae708f78afe44cb5b 100644 --- a/channels/chan_pjsip.c +++ b/channels/chan_pjsip.c @@ -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 index 0000000000000000000000000000000000000000..5d3521bbd45addbc48ab8d9f40cf640b90c5ebca --- /dev/null +++ b/doc/CHANGES-staging/app_transferprotocol.txt @@ -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. diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h index baefeddaed7db70f5f4f33e94ab08b8577754197..40069b0178db246a7afcdbb30781e66922b5f61c 100644 --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -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 diff --git a/main/channel.c b/main/channel.c index 9730ed08de318c250f28b118931b2d864a58403a..784ea28447a0e427122fc0cd179e7b7e581c840d 100644 --- a/main/channel.c +++ b/main/channel.c @@ -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);