diff --git a/CHANGES b/CHANGES index be279879d04458f1498394ad0ddf7cc952cfe685..96fdd13f7913ac8f80bd25d099d73b5fbd8ae064 100644 --- a/CHANGES +++ b/CHANGES @@ -103,6 +103,12 @@ app_confbridge instance, allows a channel to immediately exit the ConfBridge without having to wait for a leave announcement to play. +app_dial +------------------ + * Added the "Q" option which sets the Q.850/Q.931 cause on unanswered channels + when another channel answers the call. The default of ANSWERED_ELSEWHERE + is unchanged. + ------------------------------------------------------------------------------ --- Functionality changes from Asterisk 13 to Asterisk 14 -------------------- ------------------------------------------------------------------------------ diff --git a/apps/app_dial.c b/apps/app_dial.c index 316d38da15ae7bf391c09adacd592da5dfd81e4a..b5d8f50c767a0e07f7aacfb5d8479e99aae411c3 100644 --- a/apps/app_dial.c +++ b/apps/app_dial.c @@ -375,6 +375,25 @@ ASTERISK_REGISTER_FILE() <para>Enable privacy mode. Use <replaceable>x</replaceable> as the family/key in the AstDB database if it is provided. The current extension is used if a database family/key is not specified.</para> </option> + <option name="Q"> + <argument name="cause" required="true"/> + <para>Specify the Q.850/Q.931 <replaceable>cause</replaceable> to send on + unanswered channels when another channel answers the call. + As with <literal>Hangup()</literal>, <replaceable>cause</replaceable> + can be a numeric cause code or a name such as + <literal>NO_ANSWER</literal>, + <literal>USER_BUSY</literal>, + <literal>CALL_REJECTED</literal> or + <literal>ANSWERED_ELSEWHERE</literal> (the default if Q isn't specified). + You can also specify <literal>0</literal> or <literal>NONE</literal> + to send no cause. See the <filename>causes.h</filename> file for the + full list of valid causes and names. + </para> + <note> + <para>chan_sip does not support setting the cause on a CANCEL to anything + other than ANSWERED_ELSEWHERE.</para> + </note> + </option> <option name="r"> <para>Default: Indicate ringing to the calling party, even if the called party isn't actually ringing. Pass no audio to the calling party until the called channel has answered.</para> @@ -520,6 +539,9 @@ ASTERISK_REGISTER_FILE() <example title="Dial with call length limit"> same => n,Dial(PJSIP/alice,,L(60000:30000:10000)) </example> + <example title="Dial alice and bob and send NO_ANSWER to bob instead of ANSWERED_ELSEWHERE when alice answers"> + same => n,Dial(PJSIP/alice&PJSIP/bob,,Q(NO_ANSWER)) + </example> <example title="Dial with pre-dial subroutines"> [default] @@ -684,6 +706,7 @@ enum { #define OPT_PREDIAL_CALLEE (1LLU << 41) #define OPT_PREDIAL_CALLER (1LLU << 42) #define OPT_RING_WITH_EARLY_MEDIA (1LLU << 43) +#define OPT_HANGUPCAUSE (1LLU << 44) enum { OPT_ARG_ANNOUNCE = 0, @@ -705,6 +728,7 @@ enum { OPT_ARG_FORCE_CID_PRES, OPT_ARG_PREDIAL_CALLEE, OPT_ARG_PREDIAL_CALLER, + OPT_ARG_HANGUPCAUSE, /* note: this entry _MUST_ be the last one in the enum */ OPT_ARG_ARRAY_SIZE }; @@ -738,6 +762,7 @@ AST_APP_OPTIONS(dial_exec_options, BEGIN_OPTIONS AST_APP_OPTION_ARG('O', OPT_OPERMODE, OPT_ARG_OPERMODE), AST_APP_OPTION('p', OPT_SCREENING), AST_APP_OPTION_ARG('P', OPT_PRIVACY, OPT_ARG_PRIVACY), + AST_APP_OPTION_ARG('Q', OPT_HANGUPCAUSE, OPT_ARG_HANGUPCAUSE), AST_APP_OPTION_ARG('r', OPT_RINGBACK, OPT_ARG_RINGBACK), AST_APP_OPTION('R', OPT_RING_WITH_EARLY_MEDIA), AST_APP_OPTION_ARG('S', OPT_DURATION_STOP, OPT_ARG_DURATION_STOP), @@ -796,7 +821,7 @@ static void chanlist_free(struct chanlist *outgoing) ast_free(outgoing); } -static void hanguptree(struct dial_head *out_chans, struct ast_channel *exception, int answered_elsewhere) +static void hanguptree(struct dial_head *out_chans, struct ast_channel *exception, int hangupcause) { /* Hang up a tree of stuff */ struct chanlist *outgoing; @@ -804,9 +829,9 @@ static void hanguptree(struct dial_head *out_chans, struct ast_channel *exceptio while ((outgoing = AST_LIST_REMOVE_HEAD(out_chans, node))) { /* Hangup any existing lines we have open */ if (outgoing->chan && (outgoing->chan != exception)) { - if (answered_elsewhere) { + if (hangupcause >= 0) { /* This is for the channel drivers */ - ast_channel_hangupcause_set(outgoing->chan, AST_CAUSE_ANSWERED_ELSEWHERE); + ast_channel_hangupcause_set(outgoing->chan, hangupcause); } ast_hangup(outgoing->chan); } @@ -2768,6 +2793,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast } else { const char *number; int dial_end_raised = 0; + int cause = -1; if (ast_test_flag64(&opts, OPT_CALLER_ANSWER)) ast_answer(chan); @@ -2778,7 +2804,23 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast /* Ah ha! Someone answered within the desired timeframe. Of course after this we will always return with -1 so that it is hung up properly after the conversation. */ - hanguptree(&out_chans, peer, 1); + + if (ast_test_flag64(&opts, OPT_HANGUPCAUSE) + && !ast_strlen_zero(opt_args[OPT_ARG_HANGUPCAUSE])) { + cause = ast_str2cause(opt_args[OPT_ARG_HANGUPCAUSE]); + if (cause <= 0) { + if (!strcasecmp(opt_args[OPT_ARG_HANGUPCAUSE], "NONE")) { + cause = 0; + } else if (sscanf(opt_args[OPT_ARG_HANGUPCAUSE], "%30d", &cause) != 1 + || cause < 0) { + ast_log(LOG_WARNING, "Invalid cause given to Dial(...Q(<cause>)): \"%s\"\n", + opt_args[OPT_ARG_HANGUPCAUSE]); + cause = -1; + } + } + } + hanguptree(&out_chans, peer, cause >= 0 ? cause : AST_CAUSE_ANSWERED_ELSEWHERE); + /* If appropriate, log that we have a destination channel and set the answer time */ if (ast_channel_name(peer)) pbx_builtin_setvar_helper(chan, "DIALEDPEERNAME", ast_channel_name(peer)); @@ -3182,7 +3224,11 @@ out: } ast_channel_early_bridge(chan, NULL); - hanguptree(&out_chans, NULL, ast_channel_hangupcause(chan)==AST_CAUSE_ANSWERED_ELSEWHERE || ast_test_flag64(&opts, OPT_CANCEL_ELSEWHERE) ? 1 : 0 ); /* forward 'answered elsewhere' if we received it */ + /* forward 'answered elsewhere' if we received it */ + hanguptree(&out_chans, NULL, + ast_channel_hangupcause(chan) == AST_CAUSE_ANSWERED_ELSEWHERE + || ast_test_flag64(&opts, OPT_CANCEL_ELSEWHERE) + ? AST_CAUSE_ANSWERED_ELSEWHERE : -1); pbx_builtin_setvar_helper(chan, "DIALSTATUS", pa.status); ast_debug(1, "Exiting with DIALSTATUS=%s.\n", pa.status);