Commit 7988d1138c for asterisk.org
commit 7988d1138cb2dfd27e3e18f4af0672618f67eb6d
Author: Naveen Albert <asterisk@phreaknet.org>
Date: Tue May 5 21:52:18 2026 -0400
app_dial: Properly handle callee hangup while sending digits.
If we are sending digits (either DTMF, MF, or SF) to the called channel
after receiving progress or a wink, and the callee hangs up before we
have finished sending it digits, there are several problems that can ensue:
* If the callee hung up without answering, the calling channel would
hang up and not continue in the dialplan.
* If the callee *did* answer before hanging up, the answer was never
passed through to the caller, since this gets "eaten" by the various
digit streaming functions and is never processed by app_dial.
This is generally an edge case that occurs due to some kind of signaling
failure, but to better handle this:
* Set to_answer to 0 to prevent hangup on the exit path, just like other
parts of wait_for_answer.
* Better document this usage of to_answer.
* If the channel did answer while it was receiving digits, manually
answer the calling channel before we abort. The call would not continue
in the dialplan anyways (either before or after this fix), but technically
the call was answered, so the CDRs should probably reflect that, and this
mirrors the behavior of calls which normally do not continue.
Resolves: #1915
UserNote: If a called channel sends progress or wink and the caller begins
sending digits but the callee answers and then hangs up before digit
sending can finish, the call is now answered before being disconnected.
If the callee hangs up without answering, the call now continues in
the dialplan.
diff --git a/apps/app_dial.c b/apps/app_dial.c
index a6c91b2ffb..e3b051e389 100644
--- a/apps/app_dial.c
+++ b/apps/app_dial.c
@@ -1291,7 +1291,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
} else {
ast_verb(3, "No one is available to answer at this time (%d:%d/%d/%d)\n", numlines, num.busy, num.congestion, num.nochan);
}
- *to_answer = 0;
+ *to_answer = 0; /* Continue in the dialplan, since nobody answered */
if (is_cc_recall) {
ast_cc_failed(cc_recall_core_id, "Everyone is busy/congested for the recall. How sad");
}
@@ -1603,6 +1603,17 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
}
if (res) {
ast_log(LOG_WARNING, "Called channel %s hung up post-progress before all digits could be sent\n", ast_channel_name(c));
+ if (ast_channel_state(c) == AST_STATE_UP) {
+ /* The called channel answered while we were sending it digits, so the answer never got processed by app_dial.
+ * The channel is dying now, but better to answer late than never? */
+ ast_debug(1, "Channel %s answered while we were sending it digits, answering %s retroactively\n", ast_channel_name(c), ast_channel_name(in));
+ /* Indicate answer supervision to the caller before we exit.
+ * We're not going to bridge, but this way at least the CDRs are correct, etc. */
+ ast_raw_answer(in);
+ strcpy(pa->status, "ANSWER");
+ } else {
+ *to_answer = 0; /* Continue in the dialplan, since nobody answered */
+ }
goto wait_over;
}
}
@@ -1630,6 +1641,14 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
}
if (res) {
ast_log(LOG_WARNING, "Called channel %s hung up post-wink before all digits could be sent\n", ast_channel_name(c));
+ if (ast_channel_state(c) == AST_STATE_UP) {
+ /* Same as in AST_CONTROL_PROGRESS */
+ ast_debug(1, "Channel %s answered while we were sending it digits, answering %s retroactively\n", ast_channel_name(c), ast_channel_name(in));
+ ast_raw_answer(in);
+ strcpy(pa->status, "ANSWER");
+ } else {
+ *to_answer = 0; /* Continue in the dialplan, since nobody answered */
+ }
goto wait_over;
}
}
@@ -1937,7 +1956,11 @@ skip_frame:;
wait_over:
if (!*to_answer || ast_check_hangup(in)) {
- ast_verb(3, "Nobody picked up in %d ms\n", orig_answer_to);
+ if (orig_answer_to != -1) {
+ ast_verb(3, "Nobody picked up in %d ms\n", orig_answer_to);
+ } else {
+ ast_verb(3, "Call terminated without answer\n");
+ }
publish_dial_end_event(in, out_chans, NULL, "NOANSWER");
} else if (!*to_progress) {
ast_verb(3, "No early media received in %d ms\n", orig_progress_to);
@@ -2998,8 +3021,10 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
if (!peer) {
if (result) {
- res = result;
+ res = result; /* User entered a DTMF digit that matched a context */
} else if (to_answer) { /* Musta gotten hung up */
+ /* This does not necessarily mean that we dialed without a timeout.
+ * to_answer is (ab)used by wait_for_answer to to indicate whether or we should continue in the dialplan or exit. */
res = -1;
} else { /* Nobody answered, next please? */
res = 0;