app_dial: Add the "Q" option to set the cause on unanswered channels
authorGeorge Joseph <gjoseph@digium.com>
Thu, 6 Oct 2016 14:58:26 +0000 (08:58 -0600)
committerGeorge Joseph <gjoseph@digium.com>
Tue, 11 Oct 2016 17:05:56 +0000 (12:05 -0500)
The "Q" option will set the cause on the unanswered channels when
another channel answers.  It overrides the default of
ANSWERED_ELSEWHERE.

NOTE:  chan_sip does not support setting the cause on a CANCEL to
anything other than ANSWERED_ELSEWHERE.

ASTERISK-26446 #close

Change-Id: I71742e0919aaa16784c30a2b2e73fbeed7672e47

CHANGES
apps/app_dial.c

diff --git a/CHANGES b/CHANGES
index be27987..96fdd13 100644 (file)
--- 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 --------------------
 ------------------------------------------------------------------------------
index 316d38d..b5d8f50 100644 (file)
@@ -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&amp;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);