Asterisk - The Open Source Telephony Project  GIT-master-a24979a
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  <argument name="filename" required="true">
53  <para>file(s) to play before reading digits or tone with option i</para>
54  </argument>
55  <argument name="filename2" multiple="true" />
56  </parameter>
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>
65  </parameter>
66  <parameter name="options">
67  <optionlist>
68  <option name="s">
69  <para>to return immediately if the line is not up.</para>
70  </option>
71  <option name="i">
72  <para>to play filename as an indication tone from your
73  <filename>indications.conf</filename>.</para>
74  </option>
75  <option name="n">
76  <para>to read digits even if the line is not up.</para>
77  </option>
78  <option name="t">
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>
87  </option>
88  </optionlist>
89  </parameter>
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>
94  </parameter>
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>
99  </parameter>
100  </syntax>
101  <description>
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>
105  <variablelist>
106  <variable name="READSTATUS">
107  <para>This is the status of the read operation.</para>
108  <value name="OK" />
109  <value name="ERROR" />
110  <value name="HANGUP" />
111  <value name="INTERRUPTED" />
112  <value name="SKIPPED" />
113  <value name="TIMEOUT" />
114  </variable>
115  </variablelist>
116  </description>
117  <see-also>
118  <ref type="application">SendDTMF</ref>
119  </see-also>
120  </application>
121  ***/
122 
124  OPT_SKIP = (1 << 0),
125  OPT_INDICATION = (1 << 1),
126  OPT_NOANSWER = (1 << 2),
127  OPT_TERMINATOR = (1 << 3),
128 };
129 
130 enum {
132  /* note: this entry _MUST_ be the last one in the enum */
134 };
135 
137  AST_APP_OPTION('s', OPT_SKIP),
141 });
142 
143 static char *app = "Read";
144 
145 static int read_exec(struct ast_channel *chan, const char *data)
146 {
147  int res = 0;
148  char tmp[256] = "";
149  int maxdigits = 255;
150  int tries = 1, to = 0, x = 0;
151  double tosec;
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 */
158 
159  AST_DECLARE_APP_ARGS(arglist,
160  AST_APP_ARG(variable);
161  AST_APP_ARG(filename);
162  AST_APP_ARG(maxdigits);
164  AST_APP_ARG(attempts);
165  AST_APP_ARG(timeout);
166  );
167 
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");
171  return 0;
172  }
173 
174  argcopy = ast_strdupa(data);
175 
176  AST_STANDARD_APP_ARGS(arglist, argcopy);
177 
178  if (!ast_strlen_zero(arglist.options)) {
179  ast_app_parse_options(read_app_options, &flags, opt_args, arglist.options);
180  }
181 
182  if (!ast_strlen_zero(arglist.attempts)) {
183  tries = atoi(arglist.attempts);
184  if (tries <= 0)
185  tries = 1;
186  }
187 
188  if (!ast_strlen_zero(arglist.timeout)) {
189  tosec = atof(arglist.timeout);
190  if (tosec <= 0)
191  to = 0;
192  else
193  to = tosec * 1000.0;
194  }
195 
196  if (ast_strlen_zero(arglist.filename)) {
197  arglist.filename = NULL;
198  }
199  if (!ast_strlen_zero(arglist.maxdigits)) {
200  maxdigits = atoi(arglist.maxdigits);
201  if ((maxdigits < 1) || (maxdigits > 255)) {
202  maxdigits = 255;
203  } else
204  ast_verb(3, "Accepting a maximum of %d digits.\n", maxdigits);
205  }
206  if (ast_strlen_zero(arglist.variable)) {
207  ast_log(LOG_WARNING, "Invalid! Usage: Read(variable[,filename][,maxdigits][,option][,attempts][,timeout])\n\n");
208  return 0;
209  }
211  if (!ast_strlen_zero(arglist.filename)) {
212  ts = ast_get_indication_tone(ast_channel_zone(chan), arglist.filename);
213  }
214  }
216  if (!ast_strlen_zero(opt_args[OPT_ARG_TERMINATOR])) {
217  terminator = opt_args[OPT_ARG_TERMINATOR];
218  } else {
219  terminator = ""; /* no digit inherently will terminate input */
220  }
221  }
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 */
225  if (ts) {
226  ts = ast_tone_zone_sound_unref(ts);
227  }
228  pbx_builtin_setvar_helper(chan, arglist.variable, "");
229  pbx_builtin_setvar_helper(chan, "READSTATUS", "SKIPPED");
230  return 0;
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);
234  }
235  }
236  if (!res) {
237  while (tries && !res) {
238  ast_stopstream(chan);
239  if (ts && ts->data[0]) {
240  if (!to)
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);
246  if (res < 1) {
247  if (res == 0)
248  status = "TIMEOUT";
249  tmp[x]='\0';
250  break;
251  }
252  tmp[x++] = res;
253  if (terminator && strchr(terminator, tmp[x-1])) {
254  tmp[x-1] = '\0';
255  status = "OK";
256  break;
257  }
258  if (x >= maxdigits) {
259  status = "OK";
260  }
261  }
262  } else {
263  res = ast_app_getdata_terminator(chan, arglist.filename, tmp, maxdigits, to, terminator);
265  status = "OK";
266  else if (res == AST_GETDATA_TIMEOUT)
267  status = "TIMEOUT";
268  else if (res == AST_GETDATA_INTERRUPTED)
269  status = "INTERRUPTED";
270  }
271  if (res > -1) {
272  pbx_builtin_setvar_helper(chan, arglist.variable, tmp);
273  if (!ast_strlen_zero(tmp)) {
274  ast_verb(3, "User entered '%s'\n", tmp);
275  tries = 0;
276  } else {
277  tries--;
278  if (tries)
279  ast_verb(3, "User entered nothing, %d chance%s left\n", tries, (tries != 1) ? "s" : "");
280  else
281  ast_verb(3, "User entered nothing.\n");
282  }
283  res = 0;
284  } else {
285  pbx_builtin_setvar_helper(chan, arglist.variable, tmp);
286  ast_verb(3, "User disconnected\n");
287  }
288  }
289  }
290 
291  if (ts) {
292  ts = ast_tone_zone_sound_unref(ts);
293  }
294 
295  if (ast_check_hangup(chan))
296  status = "HANGUP";
297  pbx_builtin_setvar_helper(chan, "READSTATUS", status);
298  return 0;
299 }
300 
301 static int unload_module(void)
302 {
304 }
305 
306 static int load_module(void)
307 {
309 }
310 
311 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Read Variable Application");
jack_status_t status
Definition: app_jack.c:146
read_option_flags
Definition: app_mf.c:190
static int read_exec(struct ast_channel *chan, const char *data)
Definition: app_read.c:145
@ OPT_ARG_TERMINATOR
Definition: app_read.c:131
@ OPT_ARG_ARRAY_SIZE
Definition: app_read.c:133
static char * app
Definition: app_read.c:143
static int load_module(void)
Definition: app_read.c:306
static int unload_module(void)
Definition: app_read.c:301
@ OPT_TERMINATOR
Definition: app_read.c:127
@ OPT_NOANSWER
Definition: app_read.c:126
@ OPT_INDICATION
Definition: app_read.c:125
@ OPT_SKIP
Definition: app_read.c:124
static const struct ast_app_option read_app_options[128]
Definition: app_read.c:141
Asterisk main include file. File version handling, generic pbx functions.
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ast_log
Definition: astobj2.c:42
static int tmp()
Definition: bt_open.c:389
General Asterisk PBX channel definitions.
int ast_waitfordigit(struct ast_channel *c, int ms)
Waits for a digit.
Definition: channel.c:3176
struct ast_pbx * ast_channel_pbx(const struct ast_channel *chan)
struct ast_tone_zone * ast_channel_zone(const struct ast_channel *chan)
int ast_check_hangup(struct ast_channel *chan)
Check to see if a channel is needing hang up.
Definition: channel.c:445
int ast_answer(struct ast_channel *chan)
Answer a channel.
Definition: channel.c:2806
ast_channel_state
ast_channel states
Definition: channelstate.h:35
@ AST_STATE_UP
Definition: channelstate.h:42
Generic File Format Support. Should be included by clients of the file handling routines....
int ast_stopstream(struct ast_channel *c)
Stops a stream.
Definition: file.c:222
Application convenience functions, designed to give consistent look and feel to Asterisk apps.
#define AST_APP_ARG(name)
Define an application argument.
@ AST_GETDATA_INTERRUPTED
@ AST_GETDATA_COMPLETE
@ AST_GETDATA_TIMEOUT
@ AST_GETDATA_EMPTY_END_TERMINATED
#define AST_APP_OPTIONS(holder, options...)
Declares an array of options for an application.
#define AST_APP_OPTION_ARG(option, flagno, argno)
Declares an application option that accepts an argument.
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
#define AST_APP_OPTION(option, flagno)
Declares an application option that does not accept an argument.
int 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
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:3126
#define ast_verb(level,...)
#define LOG_WARNING
Tone Indication Support.
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:455
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
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
void ast_playtones_stop(struct ast_channel *chan)
Stop playing tones on a channel.
Definition: indications.c:393
Asterisk module definitions.
#define AST_MODULE_INFO_STANDARD(keystr, desc)
Definition: module.h:567
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:626
Core PBX routines and definitions.
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.
#define NULL
Definition: resample.c:96
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
Main Channel structure associated with a channel.
Structure used to handle boolean flags.
Definition: utils.h:199
unsigned int flags
Definition: utils.h:200
int rtimeoutms
Definition: pbx.h:216
Description of a tone.
Definition: indications.h:35
const char * data
Description of a tone.
Definition: indications.h:52
static struct test_options options
#define ast_test_flag(p, flag)
Definition: utils.h:63