Asterisk - The Open Source Telephony Project  21.4.1
app_read.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2005, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
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.
13  *
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.
17  */
18 
19 /*! \file
20  *
21  * \brief Trivial application to read a variable
22  *
23  * \author Mark Spencer <markster@digium.com>
24  *
25  * \ingroup applications
26  */
27 
28 /*** MODULEINFO
29  <support_level>core</support_level>
30  ***/
31 
32 #include "asterisk.h"
33 
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"
40 
41 /*** DOCUMENTATION
42  <application name="Read" language="en_US">
43  <synopsis>
44  Read a variable.
45  </synopsis>
46  <syntax>
47  <parameter name="variable" required="true">
48  <para>The input digits will be stored in the given <replaceable>variable</replaceable>
49  name.</para>
50  </parameter>
51  <parameter name="filenames" argsep="&amp;">
52  <para>Ampersand separated list of filenames to play before
53  reading digits or tone with option <literal>i</literal>. If
54  the filename is a relative filename (it does not begin with a
55  slash), it will be searched for in the Asterisk sounds
56  directory. If the filename is able to be parsed as a URL,
57  Asterisk will download the file and then begin playback on
58  it. To include a literal <literal>&amp;</literal> in the URL
59  you can enclose the URL in single quotes.</para>
60  <argument name="filename" required="true" />
61  <argument name="filename2" multiple="true" />
62  </parameter>
63  <parameter name="maxdigits">
64  <para>Maximum acceptable number of digits. Stops reading after
65  <replaceable>maxdigits</replaceable> have been entered (without
66  requiring the user to press the <literal>#</literal> key).</para>
67  <para>Defaults to <literal>0</literal> - no limit - wait for the
68  user press the <literal>#</literal> key. Any value below
69  <literal>0</literal> means the same. Max accepted value is
70  <literal>255</literal>.</para>
71  </parameter>
72  <parameter name="options">
73  <optionlist>
74  <option name="s">
75  <para>to return immediately if the line is not up.</para>
76  </option>
77  <option name="i">
78  <para>to play filename as an indication tone from your
79  <filename>indications.conf</filename>.</para>
80  </option>
81  <option name="n">
82  <para>to read digits even if the line is not up.</para>
83  </option>
84  <option name="t">
85  <para>Terminator digit(s) to use for ending input.
86  Default is <literal>#</literal>. If you need to read
87  the digit <literal>#</literal> literally, you should
88  remove or change the terminator character. Multiple
89  terminator characters may be specified. If no terminator
90  digit is present, input cannot be ended using digits
91  and you will need to rely on duration and max digits
92  for ending input.</para>
93  </option>
94  <option name="e">
95  <para>to read the terminator as the digit string if the
96  only digit read is the terminator. This is for cases
97  where the terminator is a valid digit, but only by itself.
98  ie; <literal>1234</literal> and <literal>#</literal> are
99  valid, but <literal>1234#</literal> is not.</para>
100  </option>
101  </optionlist>
102  </parameter>
103  <parameter name="attempts">
104  <para>If greater than <literal>1</literal>, that many
105  <replaceable>attempts</replaceable> will be made in the
106  event no data is entered.</para>
107  </parameter>
108  <parameter name="timeout">
109  <para>The number of seconds to wait for a digit response. If greater
110  than <literal>0</literal>, that value will override the default timeout.
111  Can be floating point.</para>
112  </parameter>
113  </syntax>
114  <description>
115  <para>Reads a #-terminated string of digits a certain number of times from the
116  user in to the given <replaceable>variable</replaceable>.</para>
117  <para>This application sets the following channel variable upon completion:</para>
118  <variablelist>
119  <variable name="READSTATUS">
120  <para>This is the status of the read operation.</para>
121  <value name="OK" />
122  <value name="ERROR" />
123  <value name="HANGUP" />
124  <value name="INTERRUPTED" />
125  <value name="SKIPPED" />
126  <value name="TIMEOUT" />
127  </variable>
128  </variablelist>
129  </description>
130  <see-also>
131  <ref type="application">SendDTMF</ref>
132  </see-also>
133  </application>
134  ***/
135 
136 enum read_option_flags {
137  OPT_SKIP = (1 << 0),
138  OPT_INDICATION = (1 << 1),
139  OPT_NOANSWER = (1 << 2),
140  OPT_TERMINATOR = (1 << 3),
141  OPT_KEEP_TERMINATOR = (1 << 4),
142 };
143 
144 enum {
145  OPT_ARG_TERMINATOR,
146  /* note: this entry _MUST_ be the last one in the enum */
147  OPT_ARG_ARRAY_SIZE,
148 };
149 
150 AST_APP_OPTIONS(read_app_options, {
151  AST_APP_OPTION('s', OPT_SKIP),
152  AST_APP_OPTION('i', OPT_INDICATION),
153  AST_APP_OPTION('n', OPT_NOANSWER),
154  AST_APP_OPTION_ARG('t', OPT_TERMINATOR, OPT_ARG_TERMINATOR),
155  AST_APP_OPTION('e', OPT_KEEP_TERMINATOR),
156 });
157 
158 static char *app = "Read";
159 
160 static int read_exec(struct ast_channel *chan, const char *data)
161 {
162  int res = 0;
163  char tmp[256] = "";
164  int maxdigits = 255;
165  int tries = 1, to = 0, x = 0;
166  double tosec;
167  char *argcopy = NULL;
168  char *opt_args[OPT_ARG_ARRAY_SIZE];
169  struct ast_tone_zone_sound *ts = NULL;
170  struct ast_flags flags = {0};
171  const char *status = "ERROR";
172  char *terminator = "#"; /* use default terminator # by default */
173 
174  AST_DECLARE_APP_ARGS(arglist,
175  AST_APP_ARG(variable);
176  AST_APP_ARG(filename);
177  AST_APP_ARG(maxdigits);
178  AST_APP_ARG(options);
179  AST_APP_ARG(attempts);
180  AST_APP_ARG(timeout);
181  );
182 
183  pbx_builtin_setvar_helper(chan, "READSTATUS", status);
184  if (ast_strlen_zero(data)) {
185  ast_log(LOG_WARNING, "Read requires an argument (variable)\n");
186  return 0;
187  }
188 
189  argcopy = ast_strdupa(data);
190 
191  AST_STANDARD_APP_ARGS(arglist, argcopy);
192 
193  if (!ast_strlen_zero(arglist.options)) {
194  ast_app_parse_options(read_app_options, &flags, opt_args, arglist.options);
195  }
196 
197  if (!ast_strlen_zero(arglist.attempts)) {
198  tries = atoi(arglist.attempts);
199  if (tries <= 0)
200  tries = 1;
201  }
202 
203  if (!ast_strlen_zero(arglist.timeout)) {
204  tosec = atof(arglist.timeout);
205  if (tosec <= 0)
206  to = 0;
207  else
208  to = tosec * 1000.0;
209  }
210 
211  if (ast_strlen_zero(arglist.filename)) {
212  arglist.filename = NULL;
213  }
214  if (!ast_strlen_zero(arglist.maxdigits)) {
215  maxdigits = atoi(arglist.maxdigits);
216  if ((maxdigits < 1) || (maxdigits > 255)) {
217  maxdigits = 255;
218  } else
219  ast_verb(3, "Accepting a maximum of %d digits.\n", maxdigits);
220  }
221  if (ast_strlen_zero(arglist.variable)) {
222  ast_log(LOG_WARNING, "Invalid! Usage: Read(variable[,filename][,maxdigits][,option][,attempts][,timeout])\n\n");
223  return 0;
224  }
225  if (ast_test_flag(&flags, OPT_INDICATION)) {
226  if (!ast_strlen_zero(arglist.filename)) {
227  ts = ast_get_indication_tone(ast_channel_zone(chan), arglist.filename);
228  }
229  }
230  if (ast_test_flag(&flags, OPT_TERMINATOR)) {
231  if (!ast_strlen_zero(opt_args[OPT_ARG_TERMINATOR])) {
232  terminator = opt_args[OPT_ARG_TERMINATOR];
233  } else {
234  terminator = ""; /* no digit inherently will terminate input */
235  }
236  }
237  if (ast_channel_state(chan) != AST_STATE_UP) {
238  if (ast_test_flag(&flags, OPT_SKIP)) {
239  /* At the user's option, skip if the line is not up */
240  if (ts) {
241  ts = ast_tone_zone_sound_unref(ts);
242  }
243  pbx_builtin_setvar_helper(chan, arglist.variable, "");
244  pbx_builtin_setvar_helper(chan, "READSTATUS", "SKIPPED");
245  return 0;
246  } else if (!ast_test_flag(&flags, OPT_NOANSWER)) {
247  /* Otherwise answer unless we're supposed to read while on-hook */
248  res = ast_answer(chan);
249  }
250  }
251  if (!res) {
252  while (tries && !res) {
253  ast_stopstream(chan);
254  if (ts && ts->data[0]) {
255  if (!to)
256  to = ast_channel_pbx(chan) ? ast_channel_pbx(chan)->rtimeoutms : 6000;
257  res = ast_playtones_start(chan, 0, ts->data, 0);
258  for (x = 0; x < maxdigits; ) {
259  res = ast_waitfordigit(chan, to);
260  ast_playtones_stop(chan);
261  if (res < 1) {
262  if (res == 0)
263  status = "TIMEOUT";
264  tmp[x]='\0';
265  break;
266  }
267  tmp[x++] = res;
268  if (terminator && strchr(terminator, tmp[x-1])) {
269  tmp[x-1] = '\0';
270  status = "OK";
271  break;
272  }
273  if (x >= maxdigits) {
274  status = "OK";
275  }
276  }
277  } else {
278  res = ast_app_getdata_terminator(chan, arglist.filename, tmp, maxdigits, to, terminator);
279  if (res == AST_GETDATA_COMPLETE) {
280  status = "OK";
281  } else if (res == AST_GETDATA_EMPTY_END_TERMINATED) {
282  if (ast_test_flag(&flags, OPT_KEEP_TERMINATOR)) {
283  /* if the option is set to do so, read the
284  returned string as the terminator string */
285  ast_copy_string(tmp, terminator, sizeof(tmp));
286  }
287  status = "OK";
288  } else if (res == AST_GETDATA_TIMEOUT) {
289  status = "TIMEOUT";
290  } else if (res == AST_GETDATA_INTERRUPTED) {
291  status = "INTERRUPTED";
292  }
293  }
294  if (res > -1) {
295  pbx_builtin_setvar_helper(chan, arglist.variable, tmp);
296  if (!ast_strlen_zero(tmp)) {
297  ast_verb(3, "User entered '%s'\n", tmp);
298  tries = 0;
299  } else {
300  tries--;
301  if (tries)
302  ast_verb(3, "User entered nothing, %d chance%s left\n", tries, (tries != 1) ? "s" : "");
303  else
304  ast_verb(3, "User entered nothing.\n");
305  }
306  res = 0;
307  } else {
308  pbx_builtin_setvar_helper(chan, arglist.variable, tmp);
309  ast_verb(3, "User disconnected\n");
310  }
311  }
312  }
313 
314  if (ts) {
315  ts = ast_tone_zone_sound_unref(ts);
316  }
317 
318  if (ast_check_hangup(chan))
319  status = "HANGUP";
320  pbx_builtin_setvar_helper(chan, "READSTATUS", status);
321  return 0;
322 }
323 
324 static int unload_module(void)
325 {
326  return ast_unregister_application(app);
327 }
328 
329 static int load_module(void)
330 {
331  return ast_register_application_xml(app, read_exec);
332 }
333 
334 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Read Variable Application");
Tone Indication Support.
Main Channel structure associated with a channel.
Asterisk main include file. File version handling, generic pbx functions.
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
ast_channel_state
ast_channel states
Definition: channelstate.h:35
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
void ast_playtones_stop(struct ast_channel *chan)
Stop playing tones on a channel.
Definition: indications.c:393
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392
#define AST_APP_OPTIONS(holder, options...)
Declares an array of options for an application.
General Asterisk PBX channel definitions.
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
static struct ast_tone_zone_sound * ast_tone_zone_sound_unref(struct ast_tone_zone_sound *ts)
Release a reference to an ast_tone_zone_sound.
Definition: indications.h:227
int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr)
Parses a string containing application options and sets flags/arguments.
Definition: main/app.c:3066
Core PBX routines and definitions.
int ast_check_hangup(struct ast_channel *chan)
Check to see if a channel is needing hang up.
Definition: channel.c:445
enum ast_getdata_result ast_app_getdata_terminator(struct ast_channel *c, const char *prompt, char *s, int maxlen, int timeout, char *terminator)
Plays a stream and gets DTMF data from a channel.
Definition: main/app.c:193
#define AST_APP_OPTION_ARG(option, flagno, argno)
Declares an application option that accepts an argument.
Description of a tone.
Definition: indications.h:35
struct ast_tone_zone_sound * ast_get_indication_tone(const struct ast_tone_zone *zone, const char *indication)
Locate a tone zone sound.
Definition: indications.c:461
Structure used to handle boolean flags.
Definition: utils.h:199
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name...
int ast_waitfordigit(struct ast_channel *c, int ms)
Waits for a digit.
Definition: channel.c:3175
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
int ast_answer(struct ast_channel *chan)
Answer a channel.
Definition: channel.c:2805
int ast_playtones_start(struct ast_channel *chan, int vol, const char *tonelist, int interruptible)
Start playing a list of tones on a channel.
Definition: indications.c:302
const char * data
Description of a tone.
Definition: indications.h:52
#define AST_APP_OPTION(option, flagno)
Declares an application option that does not accept an argument.
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
Asterisk module definitions.
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
int ast_stopstream(struct ast_channel *c)
Stops a stream.
Definition: file.c:222
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:640
#define AST_APP_ARG(name)
Define an application argument.
int rtimeoutms
Definition: pbx.h:216