2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2005, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
21 * \brief Trivial application to read a variable
23 * \author Mark Spencer <markster@digium.com>
25 * \ingroup applications
29 <support_level>core</support_level>
34 #include "asterisk/file.h"
35 #include "asterisk/pbx.h"
36 #include "asterisk/channel.h"
37 #include "asterisk/app.h"
38 #include "asterisk/module.h"
39 #include "asterisk/indications.h"
42 <application name="Read" language="en_US">
47 <parameter name="variable" required="true">
48 <para>The input digits will be stored in the given <replaceable>variable</replaceable>
51 <parameter name="filenames" argsep="&">
52 <argument name="filename" required="true">
53 <para>file(s) to play before reading digits or tone with option i</para>
55 <argument name="filename2" multiple="true" />
57 <parameter name="maxdigits">
58 <para>Maximum acceptable number of digits. Stops reading after
59 <replaceable>maxdigits</replaceable> have been entered (without
60 requiring the user to press the <literal>#</literal> key).</para>
61 <para>Defaults to <literal>0</literal> - no limit - wait for the
62 user press the <literal>#</literal> key. Any value below
63 <literal>0</literal> means the same. Max accepted value is
64 <literal>255</literal>.</para>
66 <parameter name="options">
69 <para>to return immediately if the line is not up.</para>
72 <para>to play filename as an indication tone from your
73 <filename>indications.conf</filename>.</para>
76 <para>to read digits even if the line is not up.</para>
79 <para>Terminator digit(s) to use for ending input.
80 Default is <literal>#</literal>. If you need to read
81 the digit <literal>#</literal> literally, you should
82 remove or change the terminator character. Multiple
83 terminator characters may be specified. If no terminator
84 digit is present, input cannot be ended using digits
85 and you will need to rely on duration and max digits
86 for ending input.</para>
90 <parameter name="attempts">
91 <para>If greater than <literal>1</literal>, that many
92 <replaceable>attempts</replaceable> will be made in the
93 event no data is entered.</para>
95 <parameter name="timeout">
96 <para>The number of seconds to wait for a digit response. If greater
97 than <literal>0</literal>, that value will override the default timeout.
98 Can be floating point.</para>
102 <para>Reads a #-terminated string of digits a certain number of times from the
103 user in to the given <replaceable>variable</replaceable>.</para>
104 <para>This application sets the following channel variable upon completion:</para>
106 <variable name="READSTATUS">
107 <para>This is the status of the read operation.</para>
109 <value name="ERROR" />
110 <value name="HANGUP" />
111 <value name="INTERRUPTED" />
112 <value name="SKIPPED" />
113 <value name="TIMEOUT" />
118 <ref type="application">SendDTMF</ref>
123 enum read_option_flags {
125 OPT_INDICATION = (1 << 1),
126 OPT_NOANSWER = (1 << 2),
127 OPT_TERMINATOR = (1 << 3),
132 /* note: this entry _MUST_ be the last one in the enum */
136 AST_APP_OPTIONS(read_app_options, {
137 AST_APP_OPTION('s', OPT_SKIP),
138 AST_APP_OPTION('i', OPT_INDICATION),
139 AST_APP_OPTION('n', OPT_NOANSWER),
140 AST_APP_OPTION_ARG('t', OPT_TERMINATOR, OPT_ARG_TERMINATOR),
143 static char *app = "Read";
145 static int read_exec(struct ast_channel *chan, const char *data)
150 int tries = 1, to = 0, x = 0;
152 char *argcopy = NULL;
153 char *opt_args[OPT_ARG_ARRAY_SIZE];
154 struct ast_tone_zone_sound *ts = NULL;
155 struct ast_flags flags = {0};
156 const char *status = "ERROR";
157 char *terminator = "#"; /* use default terminator # by default */
159 AST_DECLARE_APP_ARGS(arglist,
160 AST_APP_ARG(variable);
161 AST_APP_ARG(filename);
162 AST_APP_ARG(maxdigits);
163 AST_APP_ARG(options);
164 AST_APP_ARG(attempts);
165 AST_APP_ARG(timeout);
168 pbx_builtin_setvar_helper(chan, "READSTATUS", status);
169 if (ast_strlen_zero(data)) {
170 ast_log(LOG_WARNING, "Read requires an argument (variable)\n");
174 argcopy = ast_strdupa(data);
176 AST_STANDARD_APP_ARGS(arglist, argcopy);
178 if (!ast_strlen_zero(arglist.options)) {
179 ast_app_parse_options(read_app_options, &flags, opt_args, arglist.options);
182 if (!ast_strlen_zero(arglist.attempts)) {
183 tries = atoi(arglist.attempts);
188 if (!ast_strlen_zero(arglist.timeout)) {
189 tosec = atof(arglist.timeout);
196 if (ast_strlen_zero(arglist.filename)) {
197 arglist.filename = NULL;
199 if (!ast_strlen_zero(arglist.maxdigits)) {
200 maxdigits = atoi(arglist.maxdigits);
201 if ((maxdigits < 1) || (maxdigits > 255)) {
204 ast_verb(3, "Accepting a maximum of %d digits.\n", maxdigits);
206 if (ast_strlen_zero(arglist.variable)) {
207 ast_log(LOG_WARNING, "Invalid! Usage: Read(variable[,filename][,maxdigits][,option][,attempts][,timeout])\n\n");
210 if (ast_test_flag(&flags, OPT_INDICATION)) {
211 if (!ast_strlen_zero(arglist.filename)) {
212 ts = ast_get_indication_tone(ast_channel_zone(chan), arglist.filename);
215 if (ast_test_flag(&flags, OPT_TERMINATOR)) {
216 if (!ast_strlen_zero(opt_args[OPT_ARG_TERMINATOR])) {
217 terminator = opt_args[OPT_ARG_TERMINATOR];
219 terminator = ""; /* no digit inherently will terminate input */
222 if (ast_channel_state(chan) != AST_STATE_UP) {
223 if (ast_test_flag(&flags, OPT_SKIP)) {
224 /* At the user's option, skip if the line is not up */
226 ts = ast_tone_zone_sound_unref(ts);
228 pbx_builtin_setvar_helper(chan, arglist.variable, "");
229 pbx_builtin_setvar_helper(chan, "READSTATUS", "SKIPPED");
231 } else if (!ast_test_flag(&flags, OPT_NOANSWER)) {
232 /* Otherwise answer unless we're supposed to read while on-hook */
233 res = ast_answer(chan);
237 while (tries && !res) {
238 ast_stopstream(chan);
239 if (ts && ts->data[0]) {
241 to = ast_channel_pbx(chan) ? ast_channel_pbx(chan)->rtimeoutms : 6000;
242 res = ast_playtones_start(chan, 0, ts->data, 0);
243 for (x = 0; x < maxdigits; ) {
244 res = ast_waitfordigit(chan, to);
245 ast_playtones_stop(chan);
253 if (terminator && strchr(terminator, tmp[x-1])) {
258 if (x >= maxdigits) {
263 res = ast_app_getdata_terminator(chan, arglist.filename, tmp, maxdigits, to, terminator);
264 if (res == AST_GETDATA_COMPLETE || res == AST_GETDATA_EMPTY_END_TERMINATED)
266 else if (res == AST_GETDATA_TIMEOUT)
268 else if (res == AST_GETDATA_INTERRUPTED)
269 status = "INTERRUPTED";
272 pbx_builtin_setvar_helper(chan, arglist.variable, tmp);
273 if (!ast_strlen_zero(tmp)) {
274 ast_verb(3, "User entered '%s'\n", tmp);
279 ast_verb(3, "User entered nothing, %d chance%s left\n", tries, (tries != 1) ? "s" : "");
281 ast_verb(3, "User entered nothing.\n");
285 pbx_builtin_setvar_helper(chan, arglist.variable, tmp);
286 ast_verb(3, "User disconnected\n");
292 ts = ast_tone_zone_sound_unref(ts);
295 if (ast_check_hangup(chan))
297 pbx_builtin_setvar_helper(chan, "READSTATUS", status);
301 static int unload_module(void)
303 return ast_unregister_application(app);
306 static int load_module(void)
308 return ast_register_application_xml(app, read_exec);
311 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Read Variable Application");