From dd980e00b4c2811c953940173dd1f98105c73659 Mon Sep 17 00:00:00 2001 From: Naveen Albert <asterisk@phreaknet.org> Date: Wed, 25 Aug 2021 11:49:06 +0000 Subject: [PATCH] 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 --- apps/app_read.c | 33 +++++++++++++++++++++++++++++--- doc/CHANGES-staging/app_read.txt | 5 +++++ include/asterisk/app.h | 17 ++++++++++++++++ main/app.c | 21 ++++++++++++++++++-- 4 files changed, 71 insertions(+), 5 deletions(-) create mode 100644 doc/CHANGES-staging/app_read.txt diff --git a/apps/app_read.c b/apps/app_read.c index 6398281bec..dd48f05e13 100644 --- a/apps/app_read.c +++ b/apps/app_read.c @@ -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) diff --git a/doc/CHANGES-staging/app_read.txt b/doc/CHANGES-staging/app_read.txt new file mode 100644 index 0000000000..df3247c1e1 --- /dev/null +++ b/doc/CHANGES-staging/app_read.txt @@ -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. diff --git a/include/asterisk/app.h b/include/asterisk/app.h index 7690364f6d..ab246c8b7d 100644 --- a/include/asterisk/app.h +++ b/include/asterisk/app.h @@ -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); diff --git a/main/app.c b/main/app.c index 09c0123604..f5fbffd7f7 100644 --- a/main/app.c +++ b/main/app.c @@ -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; } -- GitLab