diff --git a/channels/chan_pjsip.c b/channels/chan_pjsip.c index 35c3c96a24..69c0beb9ea 100644 --- a/channels/chan_pjsip.c +++ b/channels/chan_pjsip.c @@ -3266,6 +3266,8 @@ static struct ast_custom_function session_refresh_function = { .write = pjsip_acf_session_refresh_write, }; +static char *app_pjsip_hangup = "PJSIPHangup"; + /*! * \brief Load the module * @@ -3323,6 +3325,13 @@ static int load_module(void) goto end; } + if (ast_register_application_xml(app_pjsip_hangup, pjsip_app_hangup)) { + ast_log(LOG_WARNING, "Unable to register PJSIPHangup dialplan application\n"); + goto end; + } + ast_manager_register_xml(app_pjsip_hangup, EVENT_FLAG_SYSTEM | EVENT_FLAG_CALL, pjsip_action_hangup); + + ast_sip_register_service(&refer_callback_module); ast_sip_session_register_supplement(&chan_pjsip_supplement); @@ -3370,6 +3379,9 @@ end: ast_custom_function_unregister(&chan_pjsip_dial_contacts_function); ast_custom_function_unregister(&chan_pjsip_parse_uri_function); ast_custom_function_unregister(&session_refresh_function); + ast_unregister_application(app_pjsip_hangup); + ast_manager_unregister(app_pjsip_hangup); + ast_channel_unregister(&chan_pjsip_tech); ast_rtp_glue_unregister(&chan_pjsip_rtp_glue); @@ -3399,6 +3411,8 @@ static int unload_module(void) ast_custom_function_unregister(&chan_pjsip_dial_contacts_function); ast_custom_function_unregister(&chan_pjsip_parse_uri_function); ast_custom_function_unregister(&session_refresh_function); + ast_unregister_application(app_pjsip_hangup); + ast_manager_unregister(app_pjsip_hangup); ast_channel_unregister(&chan_pjsip_tech); ao2_ref(chan_pjsip_tech.capabilities, -1); diff --git a/channels/pjsip/dialplan_functions.c b/channels/pjsip/dialplan_functions.c index 0496f048e3..f1365adba9 100644 --- a/channels/pjsip/dialplan_functions.c +++ b/channels/pjsip/dialplan_functions.c @@ -29,563 +29,6 @@ core ***/ -/*** DOCUMENTATION - - - Return a dial string for dialing all contacts on an AOR. - - - - Name of the endpoint - - - Name of an AOR to use, if not specified the configured AORs on the endpoint are used - - - Optional request user to use in the request URI - - - - Returns a properly formatted dial string for dialing all contacts on an AOR. - - - - - Media and codec offerings to be set on an outbound SIP channel prior to dialing. - - - - types of media offered - - - - When read, returns the codecs offered based upon the media choice. - When written, sets the codecs to offer when an outbound dial attempt is made, - or when a session refresh is sent using PJSIP_SEND_SESSION_REFRESH. - - - - PJSIP_SEND_SESSION_REFRESH - - - - - 13.18.0 - 14.7.0 - 15.1.0 - 16.0.0 - - - Get or change the DTMF mode for a SIP call. - - - - - When read, returns the current DTMF mode - When written, sets the current DTMF mode - This function uses the same DTMF mode naming as the dtmf_mode configuration option - - - - - Get or change the on-hold behavior for a SIP call. - - - - - When read, returns the current moh passthrough mode - When written, sets the current moh passthrough mode - If yes, on-hold re-INVITEs are sent. If no, music on hold is generated. - This function can be used to override the moh_passthrough configuration option - - - - - 13.12.0 - 14.1.0 - 15.0.0 - - - W/O: Initiate a session refresh via an UPDATE or re-INVITE on an established media session - - - - The type of update to send. Default is invite. - - - Send the session refresh as a re-INVITE. - - - Send the session refresh as an UPDATE. - - - - - - This function will cause the PJSIP stack to immediately refresh - the media session for the channel. This will be done using either a - re-INVITE (default) or an UPDATE request. - - This is most useful when combined with the PJSIP_MEDIA_OFFER - dialplan function, as it allows the formats in use on a channel to be - re-negotiated after call setup. - - The formats the endpoint supports are not - checked or enforced by this function. Using this function to offer - formats not supported by the endpoint may result - in a loss of media. - - - ; Within some existing extension on an answered channel - same => n,Set(PJSIP_MEDIA_OFFER(audio)=!all,g722) - same => n,Set(PJSIP_SEND_SESSION_REFRESH()=invite) - - - - PJSIP_MEDIA_OFFER - - - - - 13.24.0 - 16.1.0 - 17.0.0 - - - Parse an uri and return a type part of the URI. - - - - URI to parse - - - The type parameter specifies which URI part to read - - - Display name. - - - URI scheme. - - - User part. - - - Password part. - - - Host part. - - - Port number, or zero. - - - User parameter. - - - Method parameter. - - - Transport parameter. - - - TTL param, or -1. - - - Loose routing param, or zero. - - - Maddr param. - - - - - - Parse an URI and return a specified part of the URI. - - - - - - R/O Retrieve media related information. - - When rtp is specified, the - type parameter must be provided. It specifies - which RTP parameter to read. - - - Retrieve the local address for RTP. - - - Retrieve the remote address for RTP. - - - If direct media is enabled, this address is the remote address - used for RTP. - - - Whether or not the media stream is encrypted. - - - The media stream is not encrypted. - - - The media stream is encrypted. - - - - - Whether or not the media stream is currently restricted - due to a call hold. - - - The media stream is not held. - - - The media stream is held. - - - - - - - When rtp is specified, the - media_type parameter may be provided. It specifies - which media stream the chosen RTP parameter should be retrieved - from. - - - Retrieve information from the audio media stream. - If not specified, audio is used - by default. - - - Retrieve information from the video media stream. - - - - - - R/O Retrieve RTCP statistics. - - When rtcp is specified, the - statistic parameter must be provided. It specifies - which RTCP statistic parameter to read. - - - Retrieve a summary of all RTCP statistics. - The following data items are returned in a semi-colon - delineated list: - - - Our Synchronization Source identifier - - - Their Synchronization Source identifier - - - Our lost packet count - - - Received packet jitter - - - Received packet count - - - Transmitted packet jitter - - - Transmitted packet count - - - Remote lost packet count - - - Round trip time - - - Transmitted Media Experience Score - - - Received Media Experience Score - - - - - Retrieve a summary of all RTCP Jitter statistics. - The following data items are returned in a semi-colon - delineated list: - - - Our minimum jitter - - - Our max jitter - - - Our average jitter - - - Our jitter standard deviation - - - Their minimum jitter - - - Their max jitter - - - Their average jitter - - - Their jitter standard deviation - - - - - Retrieve a summary of all RTCP packet loss statistics. - The following data items are returned in a semi-colon - delineated list: - - - Our minimum lost packets - - - Our max lost packets - - - Our average lost packets - - - Our lost packets standard deviation - - - Their minimum lost packets - - - Their max lost packets - - - Their average lost packets - - - Their lost packets standard deviation - - - - - Retrieve a summary of all RTCP round trip time information. - The following data items are returned in a semi-colon - delineated list: - - - Minimum round trip time - - - Maximum round trip time - - - Average round trip time - - - Standard deviation round trip time - - - - - Retrieve a summary of all RTCP Media Experience Score information. - The following data items are returned in a semi-colon - delineated list: - - - Minimum MES based on us analysing received packets. - - - Maximum MES based on us analysing received packets. - - - Average MES based on us analysing received packets. - - - Standard deviation MES based on us analysing received packets. - - - Minimum MES based on data we get in Sender and Receiver Reports sent by the remote end - - - Maximum MES based on data we get in Sender and Receiver Reports sent by the remote end - - - Average MES based on data we get in Sender and Receiver Reports sent by the remote end - - - Standard deviation MES based on data we get in Sender and Receiver Reports sent by the remote end - - - - Transmitted packet count - Received packet count - Transmitted packet jitter - Received packet jitter - Their max jitter - Their minimum jitter - Their average jitter - Their jitter standard deviation - Our max jitter - Our minimum jitter - Our average jitter - Our jitter standard deviation - Transmitted packet loss - Received packet loss - Their max lost packets - Their minimum lost packets - Their average lost packets - Their lost packets standard deviation - Our max lost packets - Our minimum lost packets - Our average lost packets - Our lost packets standard deviation - Round trip time - Maximum round trip time - Minimum round trip time - Average round trip time - Standard deviation round trip time - Our Synchronization Source identifier - Their Synchronization Source identifier - - Current MES based on us analyzing rtt, jitter and loss - in the actual received RTP stream received from the remote end. - I.E. This is the MES for the incoming audio stream. - - - Current MES based on rtt and the jitter and loss values in - RTCP sender and receiver reports we receive from the - remote end. I.E. This is the MES for the outgoing audio stream. - - Max MES based on data we get in Sender and Receiver Reports sent by the remote end - Min MES based on data we get in Sender and Receiver Reports sent by the remote end - Average MES based on data we get in Sender and Receiver Reports sent by the remote end - Standard deviation MES based on data we get in Sender and Receiver Reports sent by the remote end - Max MES based on us analyzing the received RTP stream - Min MES based on us analyzing the received RTP stream - Average MES based on us analyzing the received RTP stream - Standard deviation MES based on us analyzing the received RTP stream - - - - When rtcp is specified, the - media_type parameter may be provided. It specifies - which media stream the chosen RTCP parameter should be retrieved - from. - - - Retrieve information from the audio media stream. - If not specified, audio is used - by default. - - - Retrieve information from the video media stream. - - - - - - R/O The name of the endpoint associated with this channel. - Use the PJSIP_ENDPOINT function to obtain - further endpoint related information. - - - R/O The name of the contact associated with this channel. - Use the PJSIP_CONTACT function to obtain - further contact related information. Note this may not be present and if so - is only available on outgoing legs. - - - R/O The name of the AOR associated with this channel. - Use the PJSIP_AOR function to obtain - further AOR related information. Note this may not be present and if so - is only available on outgoing legs. - - - R/O Obtain information about the current PJSIP channel and its - session. - - When pjsip is specified, the - type parameter must be provided. It specifies - which signalling parameter to read. - - - The SIP call-id. - - - Whether or not the signalling uses a secure transport. - - The signalling uses a non-secure transport. - The signalling uses a secure transport. - - - - The contact URI where requests are sent. - - - The local URI. - - - Tag in From header - - - The remote URI. - - - Tag in To header - - - The request URI of the incoming INVITE - associated with the creation of this channel. - - - The current state of any T.38 fax on this channel. - - T.38 faxing is disabled on this channel. - Asterisk has sent a re-INVITE to the remote end to initiate a T.38 fax. - The remote end has sent a re-INVITE to Asterisk to initiate a T.38 fax. - A T.38 fax session has been enabled. - A T.38 fax session was attempted but was rejected. - - - - On inbound calls, the full IP address and port number that - the INVITE request was received on. On outbound - calls, the full IP address and port number that the INVITE - request was transmitted from. - - - On inbound calls, the full IP address and port number that - the INVITE request was received from. On outbound - calls, the full IP address and port number that the INVITE - request was transmitted to. - - - - - - - - - ; Log the current Call-ID - same => n,Log(NOTICE, ${CHANNEL(pjsip,call-id)}) - - ; Log the destination address of the audio stream - same => n,Log(NOTICE, ${CHANNEL(rtp,dest)}) - - ; Store the round-trip time associated with a - ; video stream in the CDR field video-rtt - same => n,Set(CDR(video-rtt)=${CHANNEL(rtcp,rtt,video)}) - - -***/ - #include "asterisk.h" #include @@ -596,6 +39,7 @@ #include "asterisk/module.h" #include "asterisk/acl.h" #include "asterisk/app.h" +#include "asterisk/conversions.h" #include "asterisk/channel.h" #include "asterisk/stream.h" #include "asterisk/format.h" @@ -1784,3 +1228,121 @@ int pjsip_acf_session_refresh_write(struct ast_channel *chan, const char *cmd, c return ast_sip_push_task_wait_serializer(channel->session->serializer, refresh_write_cb, &rdata); } + +struct hangup_data { + struct ast_sip_session *session; + int response_code; +}; + +/*! + * \brief Serializer task to hangup channel + */ +static int pjsip_hangup(void *obj) +{ + struct hangup_data *hdata = obj; + pjsip_tx_data *packet = NULL; + + if ((hdata->session->inv_session->state != PJSIP_INV_STATE_DISCONNECTED) && + (pjsip_inv_answer(hdata->session->inv_session, hdata->response_code, NULL, NULL, &packet) == PJ_SUCCESS)) { + ast_sip_session_send_response(hdata->session, packet); + } + + return 0; +} + +/*! + * \brief Callback that validates the response code + */ +static int response_code_validator(const char *channel_name, + const char *response) { + int response_code; + + int rc = ast_str_to_int(response, &response_code); + if (rc != 0) { + response_code = ast_sip_str2rc(response); + if (response_code < 0) { + ast_log(LOG_WARNING, "%s: Unrecognized response code parameter '%s'." + " Defaulting to 603 DECLINE\n", + channel_name, response); + return PJSIP_SC_DECLINE; + } + } + + if (response_code < 400 || response_code > 699) { + ast_log(LOG_WARNING, "%s: Response code %d is out of range 400 -> 699." + " Defaulting to 603 DECLINE\n", + channel_name, response_code); + return PJSIP_SC_DECLINE; + } + return response_code; +} + +/*! + * \brief Called by pjsip_app_hangup and pjsip_action_hangup + * to actually perform the hangup + */ +static void pjsip_app_hangup_handler(struct ast_channel *chan, int response_code) +{ + struct ast_sip_channel_pvt *channel; + struct hangup_data hdata = { NULL, -1 }; + const char *tag = ast_channel_name(chan); + + hdata.response_code = response_code; + + ast_channel_lock(chan); + if (strcmp(ast_channel_tech(chan)->type, "PJSIP")) { + ast_log(LOG_WARNING, "%s: Not a PJSIP channel\n", tag); + ast_channel_unlock(chan); + return; + } + + channel = ast_channel_tech_pvt(chan); + hdata.session = channel->session; + + if (hdata.session->inv_session->role != PJSIP_ROLE_UAS || ( + hdata.session->inv_session->state != PJSIP_INV_STATE_INCOMING && + hdata.session->inv_session->state != PJSIP_INV_STATE_EARLY)) { + ast_log(LOG_WARNING, "%s: Not an incoming channel or invalid state '%s'\n", + tag, pjsip_inv_state_name(hdata.session->inv_session->state)); + ast_channel_unlock(chan); + return; + } + + ast_channel_unlock(chan); + + if (ast_sip_push_task_wait_serializer(channel->session->serializer, + pjsip_hangup, &hdata) != 0) { + ast_log(LOG_WARNING, "%s: failed to push hangup task to serializer\n", tag); + } + + return; +} + +/*! + * \brief PJSIPHangup Dialplan App + */ +int pjsip_app_hangup(struct ast_channel *chan, const char *data) +{ + int response_code; + const char *tag = ast_channel_name(chan); + + if (ast_strlen_zero(data)) { + ast_log(LOG_WARNING, "%s: Missing response code parameter\n", tag); + return -1; + } + + response_code = response_code_validator(tag, data); + + pjsip_app_hangup_handler(chan, response_code); + + return -1; +} + +/*! + * \brief PJSIPHangup Manager Action + */ +int pjsip_action_hangup(struct mansession *s, const struct message *m) +{ + return ast_manager_hangup_helper(s, m, + pjsip_app_hangup_handler, response_code_validator); +} diff --git a/channels/pjsip/dialplan_functions.xml b/channels/pjsip/dialplan_functions.xml new file mode 100644 index 0000000000..be4ce91b96 --- /dev/null +++ b/channels/pjsip/dialplan_functions.xml @@ -0,0 +1,659 @@ + + + + + + Hangup an incoming PJSIP channel with a SIP response code + + + + May be one of... + + A numeric response code in the range 400 ->699 + A response code name from + third-party/pjproject/source/pjsip/include/pjsip/sip_msg.h + such as USE_IDENTITY_HEADER or + PJSIP_SC_USE_IDENTITY_HEADER + + + + + + Hangs up an incoming PJSIP channel and returns the + specified SIP response code in the final response to the caller. + + + + + This function must be called BEFORE anything that + might cause any other final (non 1XX) response to be sent. + For example calling Answer() or + Playback without the + noanswer option will cause the call + to be answered and a final 200 response to be sent. + + + + As with the Hangup application, + the dialplan will terminate after calling this function. + + + The cause code set on the channel will be translated to + a standard ISDN cause code using the table defined in + ast_sip_hangup_sip2cause() in res_pjsip.c + + + + same = n,PJSIPHangup(437) + + + same = n,PJSIPHangup(UNSUPPORTED_CERTIFICATE) + + + same = n,ExecIf($[${SOMEVALUE} = ${SOME_BAD_VALUE}]?PJSIPHangup(437)) + + + + + + + Hangup an incoming PJSIP channel with a SIP response code + + + + + + + + + Hangs up an incoming PJSIP channel and returns the + specified SIP response code in the final response to the caller. + + + + + This function must be called BEFORE anything that + might cause any other final (non 1XX) response to be sent. + For example calling Answer() or + Playback without the + noanswer option will cause the call + to be answered and a final 200 response to be sent. + + + + The cause code set on the channel will be translated to + a standard ISDN cause code using the table defined in + ast_sip_hangup_sip2cause() in res_pjsip.c + + + + Action: PJSIPHangup + ActionID: 12345678 + Channel: PJSIP/alice-00000002 + Cause: 437 + + + Action: PJSIPHangup + ActionID: 12345678 + Channel: PJSIP/alice-00000002 + Cause: UNSUPPORTED_CERTIFICATE + + + + + + + Return a dial string for dialing all contacts on an AOR. + + + + Name of the endpoint + + + Name of an AOR to use, if not specified the configured AORs on the endpoint are used + + + Optional request user to use in the request URI + + + + Returns a properly formatted dial string for dialing all contacts on an AOR. + + + + + Media and codec offerings to be set on an outbound SIP channel prior to dialing. + + + + types of media offered + + + + When read, returns the codecs offered based upon the media choice. + When written, sets the codecs to offer when an outbound dial attempt is made, + or when a session refresh is sent using PJSIP_SEND_SESSION_REFRESH. + + + + PJSIP_SEND_SESSION_REFRESH + + + + + 13.18.0 + 14.7.0 + 15.1.0 + 16.0.0 + + + Get or change the DTMF mode for a SIP call. + + + + + When read, returns the current DTMF mode + When written, sets the current DTMF mode + This function uses the same DTMF mode naming as the dtmf_mode configuration option + + + + + Get or change the on-hold behavior for a SIP call. + + + + + When read, returns the current moh passthrough mode + When written, sets the current moh passthrough mode + If yes, on-hold re-INVITEs are sent. If no, music on hold is generated. + This function can be used to override the moh_passthrough configuration option + + + + + 13.12.0 + 14.1.0 + 15.0.0 + + + W/O: Initiate a session refresh via an UPDATE or re-INVITE on an established media session + + + + The type of update to send. Default is invite. + + + Send the session refresh as a re-INVITE. + + + Send the session refresh as an UPDATE. + + + + + + This function will cause the PJSIP stack to immediately refresh + the media session for the channel. This will be done using either a + re-INVITE (default) or an UPDATE request. + + This is most useful when combined with the PJSIP_MEDIA_OFFER + dialplan function, as it allows the formats in use on a channel to be + re-negotiated after call setup. + + The formats the endpoint supports are not + checked or enforced by this function. Using this function to offer + formats not supported by the endpoint may result + in a loss of media. + + + ; Within some existing extension on an answered channel + same => n,Set(PJSIP_MEDIA_OFFER(audio)=!all,g722) + same => n,Set(PJSIP_SEND_SESSION_REFRESH()=invite) + + + + PJSIP_MEDIA_OFFER + + + + + 13.24.0 + 16.1.0 + 17.0.0 + + + Parse an uri and return a type part of the URI. + + + + URI to parse + + + The type parameter specifies which URI part to read + + + Display name. + + + URI scheme. + + + User part. + + + Password part. + + + Host part. + + + Port number, or zero. + + + User parameter. + + + Method parameter. + + + Transport parameter. + + + TTL param, or -1. + + + Loose routing param, or zero. + + + Maddr param. + + + + + + Parse an URI and return a specified part of the URI. + + + + + + + R/O Retrieve media related information. + + When rtp is specified, the + type parameter must be provided. It specifies + which RTP parameter to read. + + + Retrieve the local address for RTP. + + + Retrieve the remote address for RTP. + + + If direct media is enabled, this address is the remote address + used for RTP. + + + Whether or not the media stream is encrypted. + + + The media stream is not encrypted. + + + The media stream is encrypted. + + + + + Whether or not the media stream is currently restricted + due to a call hold. + + + The media stream is not held. + + + The media stream is held. + + + + + + + When rtp is specified, the + media_type parameter may be provided. It specifies + which media stream the chosen RTP parameter should be retrieved + from. + + + Retrieve information from the audio media stream. + If not specified, audio is used + by default. + + + Retrieve information from the video media stream. + + + + + + R/O Retrieve RTCP statistics. + + When rtcp is specified, the + statistic parameter must be provided. It specifies + which RTCP statistic parameter to read. + + + Retrieve a summary of all RTCP statistics. + The following data items are returned in a semi-colon + delineated list: + + + Our Synchronization Source identifier + + + Their Synchronization Source identifier + + + Our lost packet count + + + Received packet jitter + + + Received packet count + + + Transmitted packet jitter + + + Transmitted packet count + + + Remote lost packet count + + + Round trip time + + + Transmitted Media Experience Score + + + Received Media Experience Score + + + + + Retrieve a summary of all RTCP Jitter statistics. + The following data items are returned in a semi-colon + delineated list: + + + Our minimum jitter + + + Our max jitter + + + Our average jitter + + + Our jitter standard deviation + + + Their minimum jitter + + + Their max jitter + + + Their average jitter + + + Their jitter standard deviation + + + + + Retrieve a summary of all RTCP packet loss statistics. + The following data items are returned in a semi-colon + delineated list: + + + Our minimum lost packets + + + Our max lost packets + + + Our average lost packets + + + Our lost packets standard deviation + + + Their minimum lost packets + + + Their max lost packets + + + Their average lost packets + + + Their lost packets standard deviation + + + + + Retrieve a summary of all RTCP round trip time information. + The following data items are returned in a semi-colon + delineated list: + + + Minimum round trip time + + + Maximum round trip time + + + Average round trip time + + + Standard deviation round trip time + + + + + Retrieve a summary of all RTCP Media Experience Score information. + The following data items are returned in a semi-colon + delineated list: + + + Minimum MES based on us analysing received packets. + + + Maximum MES based on us analysing received packets. + + + Average MES based on us analysing received packets. + + + Standard deviation MES based on us analysing received packets. + + + Minimum MES based on data we get in Sender and Receiver Reports sent by the remote end + + + Maximum MES based on data we get in Sender and Receiver Reports sent by the remote end + + + Average MES based on data we get in Sender and Receiver Reports sent by the remote end + + + Standard deviation MES based on data we get in Sender and Receiver Reports sent by the remote end + + + + Transmitted packet count + Received packet count + Transmitted packet jitter + Received packet jitter + Their max jitter + Their minimum jitter + Their average jitter + Their jitter standard deviation + Our max jitter + Our minimum jitter + Our average jitter + Our jitter standard deviation + Transmitted packet loss + Received packet loss + Their max lost packets + Their minimum lost packets + Their average lost packets + Their lost packets standard deviation + Our max lost packets + Our minimum lost packets + Our average lost packets + Our lost packets standard deviation + Round trip time + Maximum round trip time + Minimum round trip time + Average round trip time + Standard deviation round trip time + Our Synchronization Source identifier + Their Synchronization Source identifier + + Current MES based on us analyzing rtt, jitter and loss + in the actual received RTP stream received from the remote end. + I.E. This is the MES for the incoming audio stream. + + + Current MES based on rtt and the jitter and loss values in + RTCP sender and receiver reports we receive from the + remote end. I.E. This is the MES for the outgoing audio stream. + + Max MES based on data we get in Sender and Receiver Reports sent by the remote end + Min MES based on data we get in Sender and Receiver Reports sent by the remote end + Average MES based on data we get in Sender and Receiver Reports sent by the remote end + Standard deviation MES based on data we get in Sender and Receiver Reports sent by the remote end + Max MES based on us analyzing the received RTP stream + Min MES based on us analyzing the received RTP stream + Average MES based on us analyzing the received RTP stream + Standard deviation MES based on us analyzing the received RTP stream + + + + When rtcp is specified, the + media_type parameter may be provided. It specifies + which media stream the chosen RTCP parameter should be retrieved + from. + + + Retrieve information from the audio media stream. + If not specified, audio is used + by default. + + + Retrieve information from the video media stream. + + + + + + R/O The name of the endpoint associated with this channel. + Use the PJSIP_ENDPOINT function to obtain + further endpoint related information. + + + R/O The name of the contact associated with this channel. + Use the PJSIP_CONTACT function to obtain + further contact related information. Note this may not be present and if so + is only available on outgoing legs. + + + R/O The name of the AOR associated with this channel. + Use the PJSIP_AOR function to obtain + further AOR related information. Note this may not be present and if so + is only available on outgoing legs. + + + R/O Obtain information about the current PJSIP channel and its + session. + + When pjsip is specified, the + type parameter must be provided. It specifies + which signalling parameter to read. + + + The SIP call-id. + + + Whether or not the signalling uses a secure transport. + + The signalling uses a non-secure transport. + The signalling uses a secure transport. + + + + The contact URI where requests are sent. + + + The local URI. + + + Tag in From header + + + The remote URI. + + + Tag in To header + + + The request URI of the incoming INVITE + associated with the creation of this channel. + + + The current state of any T.38 fax on this channel. + + T.38 faxing is disabled on this channel. + Asterisk has sent a re-INVITE to the remote end to initiate a T.38 fax. + The remote end has sent a re-INVITE to Asterisk to initiate a T.38 fax. + A T.38 fax session has been enabled. + A T.38 fax session was attempted but was rejected. + + + + On inbound calls, the full IP address and port number that + the INVITE request was received on. On outbound + calls, the full IP address and port number that the INVITE + request was transmitted from. + + + On inbound calls, the full IP address and port number that + the INVITE request was received from. On outbound + calls, the full IP address and port number that the INVITE + request was transmitted to. + + + + + + + + + ; Log the current Call-ID + same => n,Log(NOTICE, ${CHANNEL(pjsip,call-id)}) + + ; Log the destination address of the audio stream + same => n,Log(NOTICE, ${CHANNEL(rtp,dest)}) + + ; Store the round-trip time associated with a + ; video stream in the CDR field video-rtt + same => n,Set(CDR(video-rtt)=${CHANNEL(rtcp,rtt,video)}) + + + \ No newline at end of file diff --git a/channels/pjsip/include/dialplan_functions.h b/channels/pjsip/include/dialplan_functions.h index d0bf130fe8..03005d5d8f 100644 --- a/channels/pjsip/include/dialplan_functions.h +++ b/channels/pjsip/include/dialplan_functions.h @@ -148,4 +148,24 @@ int pjsip_acf_dial_contacts_read(struct ast_channel *chan, const char *cmd, char */ int pjsip_acf_parse_uri_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len); -#endif /* _PJSIP_DIALPLAN_FUNCTIONS */ \ No newline at end of file +/*! + * \brief Hang up an incoming PJSIP channel with a SIP response code + * \param chan The channel the function is called on + * \param data SIP response code or name + * + * \retval 0 on success + * \retval -1 on failure + */ +int pjsip_app_hangup(struct ast_channel *chan, const char *data); + +/*! + * \brief Manager action to hang up an incoming PJSIP channel with a SIP response code + * \param s session + * \param m message + * + * \retval 0 on success + * \retval -1 on failure + */ +int pjsip_action_hangup(struct mansession *s, const struct message *m); + +#endif /* _PJSIP_DIALPLAN_FUNCTIONS */ diff --git a/include/asterisk/manager.h b/include/asterisk/manager.h index 66591c945f..1716532e55 100644 --- a/include/asterisk/manager.h +++ b/include/asterisk/manager.h @@ -621,4 +621,45 @@ void ast_manager_publish_event(const char *type, int class_type, struct ast_json */ struct stasis_message_router *ast_manager_get_message_router(void); +/*! + * \brief Callback used by ast_manager_hangup_helper + * that will actually hangup a channel + * + * \param chan The channel to hang up + * \param causecode Cause code to set on the channel + */ +typedef void (*manager_hangup_handler_t)(struct ast_channel *chan, int causecode); + +/*! + * \brief Callback used by ast_manager_hangup_helper + * that will validate the cause code. + + * \param channel_name Mostly for displaying log messages + * \param cause Cause code string + * + * \returns integer cause code + */ +typedef int (*manager_hangup_cause_validator_t)(const char *channel_name, + const char *cause); + +/*! + * \brief A manager helper function that hangs up a channel using a supplied + * channel type specific hangup function and cause code validator + * + * This function handles the lookup of channel(s) and the AMI interaction + * but uses the supplied callbacks to actually perform the hangup. It can be + * used to implement a custom AMI 'Hangup' action without having to duplicate + * all the code in the standard Hangup action. + * + * \param s Session + * \param m Message + * \param handler Function that actually performs the hangup + * \param cause_validator Function that validates the cause code + * + * \retval 0 on success. + * \retval non-zero on error. + */ +int ast_manager_hangup_helper(struct mansession *s, const struct message *m, + manager_hangup_handler_t handler, manager_hangup_cause_validator_t cause_validator); + #endif /* _ASTERISK_MANAGER_H */ diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h index 0f68a9bbcb..949f66fdc5 100644 --- a/include/asterisk/res_pjsip.h +++ b/include/asterisk/res_pjsip.h @@ -4212,5 +4212,17 @@ unsigned int ast_sip_get_all_codecs_on_empty_reinvite(void); */ const int ast_sip_hangup_sip2cause(int cause); +/*! + * \brief Convert name to SIP response code + * + * \param name SIP response code name matching one of the + * enum names defined in "enum pjsip_status_code" + * defined in sip_msg.h. May be specified with or + * without the PJSIP_SC_ prefix. + * + * \retval SIP response code + * \retval -1 if matching code not found + */ +int ast_sip_str2rc(const char *name); #endif /* _RES_PJSIP_H */ diff --git a/main/manager.c b/main/manager.c index 47ccaa6e8f..490ac116e8 100644 --- a/main/manager.c +++ b/main/manager.c @@ -4649,7 +4649,9 @@ static int action_challenge(struct mansession *s, const struct message *m) return 0; } -static int action_hangup(struct mansession *s, const struct message *m) +int ast_manager_hangup_helper(struct mansession *s, + const struct message *m, manager_hangup_handler_t hangup_handler, + manager_hangup_cause_validator_t cause_validator) { struct ast_channel *c = NULL; int causecode = 0; /* all values <= 0 mean 'do not set hangupcause in channel' */ @@ -4673,7 +4675,9 @@ static int action_hangup(struct mansession *s, const struct message *m) idText[0] = '\0'; } - if (!ast_strlen_zero(cause)) { + if (cause_validator) { + causecode = cause_validator(name_or_regex, cause); + } else if (!ast_strlen_zero(cause)) { char *endptr; causecode = strtol(cause, &endptr, 10); if (causecode < 0 || causecode > 127 || *endptr != '\0') { @@ -4700,7 +4704,7 @@ static int action_hangup(struct mansession *s, const struct message *m) ast_sockaddr_stringify_addr(&s->session->addr), ast_channel_name(c)); - ast_channel_softhangup_withcause_locked(c, causecode); + hangup_handler(c, causecode); c = ast_channel_unref(c); astman_send_ack(s, m, "Channel Hungup"); @@ -4746,7 +4750,7 @@ static int action_hangup(struct mansession *s, const struct message *m) ast_sockaddr_stringify_addr(&s->session->addr), ast_channel_name(c)); - ast_channel_softhangup_withcause_locked(c, causecode); + hangup_handler(c, causecode); channels_matched++; astman_append(s, @@ -4766,6 +4770,12 @@ static int action_hangup(struct mansession *s, const struct message *m) return 0; } +static int action_hangup(struct mansession *s, const struct message *m) +{ + return ast_manager_hangup_helper(s, m, + ast_channel_softhangup_withcause_locked, NULL); +} + static int action_setvar(struct mansession *s, const struct message *m) { struct ast_channel *c = NULL; diff --git a/res/res_pjsip.c b/res/res_pjsip.c index 781446eb2a..50bb6575fa 100644 --- a/res/res_pjsip.c +++ b/res/res_pjsip.c @@ -3618,6 +3618,118 @@ const int ast_sip_hangup_sip2cause(int cause) return 0; } +struct response_code_map { + int code; + const char *long_name; + const char *short_name; +}; + +/* + * This map was generated from sip_msg.h with + * + * sed -n -r -e 's/^\s+(PJSIP_SC_([^ =]+))\s*=\s*[0-9]+,/{ \1, "\1", "\2" },/gp' \ + * third-party/pjproject/source/pjsip/include/pjsip/sip_msg.h + * + */ +static const struct response_code_map rc_map[] = { + { PJSIP_SC_NULL, "PJSIP_SC_NULL", "NULL" }, + { PJSIP_SC_TRYING, "PJSIP_SC_TRYING", "TRYING" }, + { PJSIP_SC_RINGING, "PJSIP_SC_RINGING", "RINGING" }, + { PJSIP_SC_CALL_BEING_FORWARDED, "PJSIP_SC_CALL_BEING_FORWARDED", "CALL_BEING_FORWARDED" }, + { PJSIP_SC_QUEUED, "PJSIP_SC_QUEUED", "QUEUED" }, + { PJSIP_SC_PROGRESS, "PJSIP_SC_PROGRESS", "PROGRESS" }, + { PJSIP_SC_EARLY_DIALOG_TERMINATED, "PJSIP_SC_EARLY_DIALOG_TERMINATED", "EARLY_DIALOG_TERMINATED" }, + { PJSIP_SC_OK, "PJSIP_SC_OK", "OK" }, + { PJSIP_SC_ACCEPTED, "PJSIP_SC_ACCEPTED", "ACCEPTED" }, + { PJSIP_SC_NO_NOTIFICATION, "PJSIP_SC_NO_NOTIFICATION", "NO_NOTIFICATION" }, + { PJSIP_SC_MULTIPLE_CHOICES, "PJSIP_SC_MULTIPLE_CHOICES", "MULTIPLE_CHOICES" }, + { PJSIP_SC_MOVED_PERMANENTLY, "PJSIP_SC_MOVED_PERMANENTLY", "MOVED_PERMANENTLY" }, + { PJSIP_SC_MOVED_TEMPORARILY, "PJSIP_SC_MOVED_TEMPORARILY", "MOVED_TEMPORARILY" }, + { PJSIP_SC_USE_PROXY, "PJSIP_SC_USE_PROXY", "USE_PROXY" }, + { PJSIP_SC_ALTERNATIVE_SERVICE, "PJSIP_SC_ALTERNATIVE_SERVICE", "ALTERNATIVE_SERVICE" }, + { PJSIP_SC_BAD_REQUEST, "PJSIP_SC_BAD_REQUEST", "BAD_REQUEST" }, + { PJSIP_SC_UNAUTHORIZED, "PJSIP_SC_UNAUTHORIZED", "UNAUTHORIZED" }, + { PJSIP_SC_PAYMENT_REQUIRED, "PJSIP_SC_PAYMENT_REQUIRED", "PAYMENT_REQUIRED" }, + { PJSIP_SC_FORBIDDEN, "PJSIP_SC_FORBIDDEN", "FORBIDDEN" }, + { PJSIP_SC_NOT_FOUND, "PJSIP_SC_NOT_FOUND", "NOT_FOUND" }, + { PJSIP_SC_METHOD_NOT_ALLOWED, "PJSIP_SC_METHOD_NOT_ALLOWED", "METHOD_NOT_ALLOWED" }, + { PJSIP_SC_NOT_ACCEPTABLE, "PJSIP_SC_NOT_ACCEPTABLE", "NOT_ACCEPTABLE" }, + { PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED, "PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED", "PROXY_AUTHENTICATION_REQUIRED" }, + { PJSIP_SC_REQUEST_TIMEOUT, "PJSIP_SC_REQUEST_TIMEOUT", "REQUEST_TIMEOUT" }, + { PJSIP_SC_CONFLICT, "PJSIP_SC_CONFLICT", "CONFLICT" }, + { PJSIP_SC_GONE, "PJSIP_SC_GONE", "GONE" }, + { PJSIP_SC_LENGTH_REQUIRED, "PJSIP_SC_LENGTH_REQUIRED", "LENGTH_REQUIRED" }, + { PJSIP_SC_CONDITIONAL_REQUEST_FAILED, "PJSIP_SC_CONDITIONAL_REQUEST_FAILED", "CONDITIONAL_REQUEST_FAILED" }, + { PJSIP_SC_REQUEST_ENTITY_TOO_LARGE, "PJSIP_SC_REQUEST_ENTITY_TOO_LARGE", "REQUEST_ENTITY_TOO_LARGE" }, + { PJSIP_SC_REQUEST_URI_TOO_LONG, "PJSIP_SC_REQUEST_URI_TOO_LONG", "REQUEST_URI_TOO_LONG" }, + { PJSIP_SC_UNSUPPORTED_MEDIA_TYPE, "PJSIP_SC_UNSUPPORTED_MEDIA_TYPE", "UNSUPPORTED_MEDIA_TYPE" }, + { PJSIP_SC_UNSUPPORTED_URI_SCHEME, "PJSIP_SC_UNSUPPORTED_URI_SCHEME", "UNSUPPORTED_URI_SCHEME" }, + { PJSIP_SC_UNKNOWN_RESOURCE_PRIORITY, "PJSIP_SC_UNKNOWN_RESOURCE_PRIORITY", "UNKNOWN_RESOURCE_PRIORITY" }, + { PJSIP_SC_BAD_EXTENSION, "PJSIP_SC_BAD_EXTENSION", "BAD_EXTENSION" }, + { PJSIP_SC_EXTENSION_REQUIRED, "PJSIP_SC_EXTENSION_REQUIRED", "EXTENSION_REQUIRED" }, + { PJSIP_SC_SESSION_TIMER_TOO_SMALL, "PJSIP_SC_SESSION_TIMER_TOO_SMALL", "SESSION_TIMER_TOO_SMALL" }, + { PJSIP_SC_INTERVAL_TOO_BRIEF, "PJSIP_SC_INTERVAL_TOO_BRIEF", "INTERVAL_TOO_BRIEF" }, + { PJSIP_SC_BAD_LOCATION_INFORMATION, "PJSIP_SC_BAD_LOCATION_INFORMATION", "BAD_LOCATION_INFORMATION" }, + { PJSIP_SC_USE_IDENTITY_HEADER, "PJSIP_SC_USE_IDENTITY_HEADER", "USE_IDENTITY_HEADER" }, + { PJSIP_SC_PROVIDE_REFERRER_HEADER, "PJSIP_SC_PROVIDE_REFERRER_HEADER", "PROVIDE_REFERRER_HEADER" }, + { PJSIP_SC_FLOW_FAILED, "PJSIP_SC_FLOW_FAILED", "FLOW_FAILED" }, + { PJSIP_SC_ANONIMITY_DISALLOWED, "PJSIP_SC_ANONIMITY_DISALLOWED", "ANONIMITY_DISALLOWED" }, + { PJSIP_SC_BAD_IDENTITY_INFO, "PJSIP_SC_BAD_IDENTITY_INFO", "BAD_IDENTITY_INFO" }, + { PJSIP_SC_UNSUPPORTED_CERTIFICATE, "PJSIP_SC_UNSUPPORTED_CERTIFICATE", "UNSUPPORTED_CERTIFICATE" }, + { PJSIP_SC_INVALID_IDENTITY_HEADER, "PJSIP_SC_INVALID_IDENTITY_HEADER", "INVALID_IDENTITY_HEADER" }, + { PJSIP_SC_FIRST_HOP_LACKS_OUTBOUND_SUPPORT, "PJSIP_SC_FIRST_HOP_LACKS_OUTBOUND_SUPPORT", "FIRST_HOP_LACKS_OUTBOUND_SUPPORT" }, + { PJSIP_SC_MAX_BREADTH_EXCEEDED, "PJSIP_SC_MAX_BREADTH_EXCEEDED", "MAX_BREADTH_EXCEEDED" }, + { PJSIP_SC_BAD_INFO_PACKAGE, "PJSIP_SC_BAD_INFO_PACKAGE", "BAD_INFO_PACKAGE" }, + { PJSIP_SC_CONSENT_NEEDED, "PJSIP_SC_CONSENT_NEEDED", "CONSENT_NEEDED" }, + { PJSIP_SC_TEMPORARILY_UNAVAILABLE, "PJSIP_SC_TEMPORARILY_UNAVAILABLE", "TEMPORARILY_UNAVAILABLE" }, + { PJSIP_SC_CALL_TSX_DOES_NOT_EXIST, "PJSIP_SC_CALL_TSX_DOES_NOT_EXIST", "CALL_TSX_DOES_NOT_EXIST" }, + { PJSIP_SC_LOOP_DETECTED, "PJSIP_SC_LOOP_DETECTED", "LOOP_DETECTED" }, + { PJSIP_SC_TOO_MANY_HOPS, "PJSIP_SC_TOO_MANY_HOPS", "TOO_MANY_HOPS" }, + { PJSIP_SC_ADDRESS_INCOMPLETE, "PJSIP_SC_ADDRESS_INCOMPLETE", "ADDRESS_INCOMPLETE" }, + { PJSIP_SC_BUSY_HERE, "PJSIP_SC_BUSY_HERE", "BUSY_HERE" }, + { PJSIP_SC_REQUEST_TERMINATED, "PJSIP_SC_REQUEST_TERMINATED", "REQUEST_TERMINATED" }, + { PJSIP_SC_NOT_ACCEPTABLE_HERE, "PJSIP_SC_NOT_ACCEPTABLE_HERE", "NOT_ACCEPTABLE_HERE" }, + { PJSIP_SC_BAD_EVENT, "PJSIP_SC_BAD_EVENT", "BAD_EVENT" }, + { PJSIP_SC_REQUEST_UPDATED, "PJSIP_SC_REQUEST_UPDATED", "REQUEST_UPDATED" }, + { PJSIP_SC_REQUEST_PENDING, "PJSIP_SC_REQUEST_PENDING", "REQUEST_PENDING" }, + { PJSIP_SC_UNDECIPHERABLE, "PJSIP_SC_UNDECIPHERABLE", "UNDECIPHERABLE" }, + { PJSIP_SC_SECURITY_AGREEMENT_NEEDED, "PJSIP_SC_SECURITY_AGREEMENT_NEEDED", "SECURITY_AGREEMENT_NEEDED" }, + { PJSIP_SC_INTERNAL_SERVER_ERROR, "PJSIP_SC_INTERNAL_SERVER_ERROR", "INTERNAL_SERVER_ERROR" }, + { PJSIP_SC_NOT_IMPLEMENTED, "PJSIP_SC_NOT_IMPLEMENTED", "NOT_IMPLEMENTED" }, + { PJSIP_SC_BAD_GATEWAY, "PJSIP_SC_BAD_GATEWAY", "BAD_GATEWAY" }, + { PJSIP_SC_SERVICE_UNAVAILABLE, "PJSIP_SC_SERVICE_UNAVAILABLE", "SERVICE_UNAVAILABLE" }, + { PJSIP_SC_SERVER_TIMEOUT, "PJSIP_SC_SERVER_TIMEOUT", "SERVER_TIMEOUT" }, + { PJSIP_SC_VERSION_NOT_SUPPORTED, "PJSIP_SC_VERSION_NOT_SUPPORTED", "VERSION_NOT_SUPPORTED" }, + { PJSIP_SC_MESSAGE_TOO_LARGE, "PJSIP_SC_MESSAGE_TOO_LARGE", "MESSAGE_TOO_LARGE" }, + { PJSIP_SC_PUSH_NOTIFICATION_SERVICE_NOT_SUPPORTED, "PJSIP_SC_PUSH_NOTIFICATION_SERVICE_NOT_SUPPORTED", "PUSH_NOTIFICATION_SERVICE_NOT_SUPPORTED" }, + { PJSIP_SC_PRECONDITION_FAILURE, "PJSIP_SC_PRECONDITION_FAILURE", "PRECONDITION_FAILURE" }, + { PJSIP_SC_BUSY_EVERYWHERE, "PJSIP_SC_BUSY_EVERYWHERE", "BUSY_EVERYWHERE" }, + { PJSIP_SC_DECLINE, "PJSIP_SC_DECLINE", "DECLINE" }, + { PJSIP_SC_DOES_NOT_EXIST_ANYWHERE, "PJSIP_SC_DOES_NOT_EXIST_ANYWHERE", "DOES_NOT_EXIST_ANYWHERE" }, + { PJSIP_SC_NOT_ACCEPTABLE_ANYWHERE, "PJSIP_SC_NOT_ACCEPTABLE_ANYWHERE", "NOT_ACCEPTABLE_ANYWHERE" }, + { PJSIP_SC_UNWANTED, "PJSIP_SC_UNWANTED", "UNWANTED" }, + { PJSIP_SC_REJECTED, "PJSIP_SC_REJECTED", "REJECTED" }, +}; + +int ast_sip_str2rc(const char *name) +{ + int i; + + if (ast_strlen_zero(name)) { + return -1; + } + + for (i = 0; i < ARRAY_LEN(rc_map); i++) { + if (strcasecmp(rc_map[i].short_name, name) == 0 || + strcasecmp(rc_map[i].long_name, name) == 0) { + return rc_map[i].code; + } + } + + return -1; +} + + #ifdef TEST_FRAMEWORK AST_TEST_DEFINE(xml_sanitization_end_null) {