app_read: Allow reading # as a digit

Allows for the digit # to be read as a digit,
just like any other DTMF digit, as opposed to
forcing it to be used as an end of input
indicator. The default behavior remains
unchanged.

ASTERISK-18454 #close

Change-Id: I3033432adb9d296ad227e76b540b8b4a2417665b
This commit is contained in:
Naveen Albert 2021-08-25 11:49:06 +00:00 committed by George Joseph
parent 18189ff594
commit 0e4a1c5079
4 changed files with 71 additions and 5 deletions

View File

@ -75,6 +75,16 @@
<option name="n">
<para>to read digits even if the line is not up.</para>
</option>
<option name="t">
<para>Terminator digit(s) to use for ending input.
Default is <literal>#</literal>. If you need to read
the digit <literal>#</literal> literally, you should
remove or change the terminator character. Multiple
terminator characters may be specified. If no terminator
digit is present, input cannot be ended using digits
and you will need to rely on duration and max digits
for ending input.</para>
</option>
</optionlist>
</parameter>
<parameter name="attempts">
@ -114,12 +124,20 @@ enum read_option_flags {
OPT_SKIP = (1 << 0),
OPT_INDICATION = (1 << 1),
OPT_NOANSWER = (1 << 2),
OPT_TERMINATOR = (1 << 3),
};
enum {
OPT_ARG_TERMINATOR,
/* note: this entry _MUST_ be the last one in the enum */
OPT_ARG_ARRAY_SIZE,
};
AST_APP_OPTIONS(read_app_options, {
AST_APP_OPTION('s', OPT_SKIP),
AST_APP_OPTION('i', OPT_INDICATION),
AST_APP_OPTION('n', OPT_NOANSWER),
AST_APP_OPTION_ARG('t', OPT_TERMINATOR, OPT_ARG_TERMINATOR),
});
static char *app = "Read";
@ -132,9 +150,11 @@ static int read_exec(struct ast_channel *chan, const char *data)
int tries = 1, to = 0, x = 0;
double tosec;
char *argcopy = NULL;
char *opt_args[OPT_ARG_ARRAY_SIZE];
struct ast_tone_zone_sound *ts = NULL;
struct ast_flags flags = {0};
const char *status = "ERROR";
char *terminator = NULL; /* use default terminator # by default */
AST_DECLARE_APP_ARGS(arglist,
AST_APP_ARG(variable);
@ -156,7 +176,7 @@ static int read_exec(struct ast_channel *chan, const char *data)
AST_STANDARD_APP_ARGS(arglist, argcopy);
if (!ast_strlen_zero(arglist.options)) {
ast_app_parse_options(read_app_options, &flags, NULL, arglist.options);
ast_app_parse_options(read_app_options, &flags, opt_args, arglist.options);
}
if (!ast_strlen_zero(arglist.attempts)) {
@ -192,6 +212,13 @@ static int read_exec(struct ast_channel *chan, const char *data)
ts = ast_get_indication_tone(ast_channel_zone(chan), arglist.filename);
}
}
if (ast_test_flag(&flags, OPT_TERMINATOR)) {
if (!ast_strlen_zero(arglist.filename)) {
terminator = opt_args[OPT_ARG_TERMINATOR];
} else {
terminator = ""; /* no digit inherently will terminate input */
}
}
if (ast_channel_state(chan) != AST_STATE_UP) {
if (ast_test_flag(&flags, OPT_SKIP)) {
/* At the user's option, skip if the line is not up */
@ -223,7 +250,7 @@ static int read_exec(struct ast_channel *chan, const char *data)
break;
}
tmp[x++] = res;
if (tmp[x-1] == '#') {
if (strchr(terminator, tmp[x-1])) {
tmp[x-1] = '\0';
status = "OK";
break;
@ -233,7 +260,7 @@ static int read_exec(struct ast_channel *chan, const char *data)
}
}
} else {
res = ast_app_getdata(chan, arglist.filename, tmp, maxdigits, to);
res = ast_app_getdata_terminator(chan, arglist.filename, tmp, maxdigits, to, terminator);
if (res == AST_GETDATA_COMPLETE || res == AST_GETDATA_EMPTY_END_TERMINATED)
status = "OK";
else if (res == AST_GETDATA_TIMEOUT)

View File

@ -0,0 +1,5 @@
Subject: app_read
A new option allows the digit '#' to be read literally,
rather than used exclusively as the input terminator
character.

View File

@ -137,6 +137,23 @@ int ast_ivr_menu_run(struct ast_channel *c, struct ast_ivr_menu *menu, void *cbd
*/
int ast_app_getdata(struct ast_channel *c, const char *prompt, char *s, int maxlen, int timeout);
/*! \brief Plays a stream and gets DTMF data from a channel
* \param c Which channel one is interacting with
* \param prompt File to pass to ast_streamfile (the one that you wish to play).
* It is also valid for this to be multiple files concatenated by "&".
* For example, "file1&file2&file3".
* \param s The location where the DTMF data will be stored
* \param maxlen Max Length of the data
* \param timeout Timeout length waiting for data(in milliseconds). Set to 0 for standard timeout(six seconds), or -1 for no time out.
* \param terminator A string of characters that may be used as terminators to end input. If NULL, "#" will be used.
*
* This function was designed for application programmers for situations where they need
* to play a message and then get some DTMF data in response to the message. If a digit
* is pressed during playback, it will immediately break out of the message and continue
* execution of your code.
*/
int ast_app_getdata_terminator(struct ast_channel *c, const char *prompt, char *s, int maxlen, int timeout, char *terminator);
/*! \brief Full version with audiofd and controlfd. NOTE: returns '2' on ctrlfd available, not '1' like other full functions */
int ast_app_getdata_full(struct ast_channel *c, const char *prompt, char *s, int maxlen, int timeout, int audiofd, int ctrlfd);

View File

@ -193,8 +193,25 @@ int ast_app_dtget(struct ast_channel *chan, const char *context, char *collect,
* \param s The string to read in to. Must be at least the size of your length
* \param maxlen How many digits to read (maximum)
* \param timeout set timeout to 0 for "standard" timeouts. Set timeout to -1 for
* "ludicrous time" (essentially never times out) */
* "ludicrous time" (essentially never times out)
*/
enum ast_getdata_result ast_app_getdata(struct ast_channel *c, const char *prompt, char *s, int maxlen, int timeout)
{
return ast_app_getdata_terminator(c, prompt, s, maxlen, timeout, NULL);
}
/*!
* \brief ast_app_getdata
* \param c The channel to read from
* \param prompt The file to stream to the channel
* \param s The string to read in to. Must be at least the size of your length
* \param maxlen How many digits to read (maximum)
* \param timeout set timeout to 0 for "standard" timeouts. Set timeout to -1 for
* "ludicrous time" (essentially never times out)
* \param terminator A string of characters that may be used as terminators to end input. Default if NULL is "#"
*/
enum ast_getdata_result ast_app_getdata_terminator(struct ast_channel *c, const char *prompt, char *s,
int maxlen, int timeout, char *terminator)
{
int res = 0, to, fto;
char *front, *filename;
@ -232,7 +249,7 @@ enum ast_getdata_result ast_app_getdata(struct ast_channel *c, const char *promp
fto = 50;
to = ast_channel_pbx(c) ? ast_channel_pbx(c)->dtimeoutms : 2000;
}
res = ast_readstring(c, s, maxlen, to, fto, "#");
res = ast_readstring(c, s, maxlen, to, fto, S_OR(terminator, "#"));
if (res == AST_GETDATA_EMPTY_END_TERMINATED) {
return res;
}