diff --git a/apps/app_dial.c b/apps/app_dial.c index e566f58a3c..8751d65ea4 100644 --- a/apps/app_dial.c +++ b/apps/app_dial.c @@ -99,6 +99,8 @@ static char *descrip = " Options:\n" " A(x) - Play an announcement to the called party, using 'x' as the file.\n" " C - Reset the CDR for this call.\n" +" c - If DIAL cancels this call, always set the flag to tell the channel\n" +" driver that the call is answered elsewhere.\n" " d - Allow the calling user to dial a 1 digit extension while waiting for\n" " a call to be answered. Exit to that extension if it exists in the\n" " current context, or the context defined in the EXITCONTEXT variable,\n" @@ -253,6 +255,7 @@ enum { OPT_CALLER_PARK = (1 << 26), OPT_IGNORE_FORWARDING = (1 << 27), OPT_CALLEE_GOSUB = (1 << 28), + OPT_CANCEL_ELSEWHERE = (1 << 29), }; #define DIAL_STILLGOING (1 << 30) @@ -276,6 +279,7 @@ enum { AST_APP_OPTIONS(dial_exec_options, { AST_APP_OPTION_ARG('A', OPT_ANNOUNCE, OPT_ARG_ANNOUNCE), AST_APP_OPTION('C', OPT_RESETCDR), + AST_APP_OPTION('c', OPT_CANCEL_ELSEWHERE), AST_APP_OPTION('d', OPT_DTMF_EXIT), AST_APP_OPTION_ARG('D', OPT_SENDDTMF, OPT_ARG_SENDDTMF), AST_APP_OPTION('f', OPT_FORCECLID), @@ -315,14 +319,17 @@ struct chanlist { }; -static void hanguptree(struct chanlist *outgoing, struct ast_channel *exception) +static void hanguptree(struct chanlist *outgoing, struct ast_channel *exception, int answered_elsewhere) { /* Hang up a tree of stuff */ struct chanlist *oo; while (outgoing) { /* Hangup any existing lines we have open */ - if (outgoing->chan && (outgoing->chan != exception)) + if (outgoing->chan && (outgoing->chan != exception)) { + if (answered_elsewhere) + ast_set_flag(outgoing->chan, AST_FLAG_ANSWERED_ELSEWHERE); ast_hangup(outgoing->chan); + } oo = outgoing; outgoing=outgoing->next; ast_free(oo); @@ -1314,6 +1321,7 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags goto out; if (opts.flags) { ast_copy_flags(tmp, &opts, + OPT_CANCEL_ELSEWHERE | OPT_CALLEE_TRANSFER | OPT_CALLER_TRANSFER | OPT_CALLEE_HANGUP | OPT_CALLER_HANGUP | OPT_CALLEE_MONITOR | OPT_CALLER_MONITOR | @@ -1513,7 +1521,7 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags /* 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(outgoing, peer); + hanguptree(outgoing, peer, 1); outgoing = NULL; /* If appropriate, log that we have a destination channel */ if (chan->cdr) @@ -1562,7 +1570,7 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags ast_parseable_goto(peer, opt_args[OPT_ARG_GOTO]); peer->priority++; ast_pbx_start(peer); - hanguptree(outgoing, NULL); + hanguptree(outgoing, NULL, ast_test_flag(&opts, OPT_CANCEL_ELSEWHERE ? 1 : 0)); if (continue_exec) *continue_exec = 1; res = 0; @@ -1800,7 +1808,7 @@ out: ast_indicate(chan, -1); } ast_channel_early_bridge(chan, NULL); - hanguptree(outgoing, NULL); + hanguptree(outgoing, NULL, 0); /* In this case, there's no answer anywhere */ pbx_builtin_setvar_helper(chan, "DIALSTATUS", pa.status); senddialendevent(chan, pa.status); ast_debug(1, "Exiting with DIALSTATUS=%s.\n", pa.status); diff --git a/channels/chan_local.c b/channels/chan_local.c index beac4064e1..b4072cecc4 100644 --- a/channels/chan_local.c +++ b/channels/chan_local.c @@ -495,6 +495,8 @@ static int local_hangup(struct ast_channel *ast) return -1; ast_mutex_lock(&p->lock); + if (p->chan && ast_test_flag(ast, AST_FLAG_ANSWERED_ELSEWHERE)) + ast_set_flag(p->chan, AST_FLAG_ANSWERED_ELSEWHERE); isoutbound = IS_OUTBOUND(ast, p); if (isoutbound) { const char *status = pbx_builtin_getvar_helper(p->chan, "DIALSTATUS"); diff --git a/channels/chan_sip.c b/channels/chan_sip.c index 6c9ea335df..9cebd9dab5 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -752,7 +752,7 @@ struct sip_auth { #define SIP_REALTIME (1 << 11) /*!< P: Flag for realtime users */ #define SIP_USECLIENTCODE (1 << 12) /*!< DP: Trust X-ClientCode info message */ #define SIP_OUTGOING (1 << 13) /*!< D: Direction of the last transaction in this dialog */ -#define SIP_FREE_BIT (1 << 14) /*!< ---- */ +#define SIP_DIALOG_ANSWEREDELSEWHERE (1 << 14) /*!< D: This call is cancelled due to answer on another channel */ #define SIP_DEFER_BYE_ON_TRANSFER (1 << 15) /*!< D: Do not hangup at first ast_hangup */ #define SIP_DTMF (3 << 16) /*!< DP: DTMF Support: four settings, uses two bits */ #define SIP_DTMF_RFC2833 (0 << 16) /*!< DP: DTMF Support: RTP DTMF - "rfc2833" */ @@ -3674,6 +3674,12 @@ static int sip_hangup(struct ast_channel *ast) ast_debug(1, "Asked to hangup channel that was not connected\n"); return 0; } + if (ast_test_flag(ast, AST_FLAG_ANSWERED_ELSEWHERE)) { + if (option_debug) + ast_log(LOG_DEBUG, "This call was answered elsewhere"); + append_history(p, "Cancel", "Call answered elsewhere"); + ast_set_flag(&p->flags[0], SIP_DIALOG_ANSWEREDELSEWHERE); + } if (ast_test_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER)) { if (ast_test_flag(&p->flags[0], SIP_INC_COUNT)) { @@ -8140,6 +8146,9 @@ static int transmit_request(struct sip_pvt *p, int sipmethod, int seqno, enum xm p->invitestate = INV_CONFIRMED; reqprep(&resp, p, sipmethod, seqno, newbranch); + if (sipmethod == SIP_CANCEL && ast_test_flag(&p->flags[0], SIP_DIALOG_ANSWEREDELSEWHERE)) + add_header(&resp, "Reason:", "SIP;cause=200;text=\"Call completed elsewhere\""); + add_header_contentLength(&resp, 0); return send_request(p, &resp, reliable, seqno ? seqno : p->ocseq); } diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h index 8c0b6a51a7..139a8b10e0 100644 --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -539,6 +539,9 @@ enum { /*! This is set to tell the channel not to generate DTMF begin frames, and * to instead only generate END frames. */ AST_FLAG_END_DTMF_ONLY = (1 << 14), + /*! Flag to show channels that this call is hangup due to the fact that the call + was indeed anwered, but in another channel */ + AST_FLAG_ANSWERED_ELSEWHERE = (1 << 15), }; /*! \brief ast_bridge_config flags */